Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

### 单连接启动

`lithe_remote_client::launch` 在远端跑一段单行 POSIX `sh` 脚本:
`lithe_remote::client::launch` 在远端跑一段单行 POSIX `sh` 脚本:

1. 先输出主机详细信息和平台标记块;
2. 客户端按平台选出随 App 附带的服务,把“版本 SHA-256 大小 目录名”作为一行写回;
Expand All @@ -41,7 +41,7 @@

### 随 App 附带的远端服务

`scripts/build-remote-servers.sh` 用 Rust 自带的 `rust-lld` 构建两个 Linux musl 静态版,输出到 `rust/target/remote-servers/`,同时写一个 `manifest.json` 记录服务版本。打包时复制到 `Contents/Resources/RemoteServers/<目标>/lithe-remote-server`。
`scripts/build-remote-servers.sh` 用 Rust 自带的 `rust-lld` 从 `lithe-remote` crate 只启用 `server` feature,构建两个 Linux musl 静态版,输出到 `rust/target/remote-servers/`,同时写一个 `manifest.json` 记录服务版本。打包时复制到 `Contents/Resources/RemoteServers/<目标>/lithe-remote-server`。源码合并不改变这个可执行文件名或安装身份,构建组合见远程工作区路由 Note。

- 这是只读的固定输入:运行时只读取它并上传给主机,不往这个目录写任何东西,所以不影响代码签名和 Sparkle 增量包。
- Linux ELF 文件对 `codesign` 来说只是资源。macOS 版服务是 Mach-O,需要 Developer ID 签名和公证,所以暂时不附带;远程 Mac 会显示“暂不支持这个平台”。
Expand Down Expand Up @@ -74,8 +74,8 @@
## 验证

```bash
cargo test --manifest-path rust/Cargo.toml -p lithe-remote-client
cargo test --manifest-path rust/Cargo.toml -p lithe-remote-server --test server
cargo test --manifest-path rust/Cargo.toml -p lithe-remote --lib
cargo test --manifest-path rust/Cargo.toml -p lithe-remote --all-features --test server
cargo test --manifest-path rust/Cargo.toml -p lithe-core remote::
./scripts/verify-runtime-bundle-immutability.sh
```
Expand All @@ -89,5 +89,5 @@ cargo test --manifest-path rust/Cargo.toml -p lithe-core remote::
- `macos/Sources/Lithe/Services/Remote/RemoteConnectionStore.swift`
- `macos/Sources/Lithe/Platform/MacOS/Remote/`
- `rust/lithe-core/src/remote/`
- `rust/lithe-remote-client/src/launch.rs`
- `rust/lithe-remote/src/client/launch.rs`
- `scripts/build-remote-servers.sh`

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ IDE -- DAP(调试适配器协议,stdio 或 TCP)--> 调试适配器
- `macos/Sources/Lithe/Platform/MacOS/Debug/`
- `windows/tauri/src-tauri/src/debug.rs`
- `shared/contracts/plugins/v1/`
- `rust/lithe-remote-client/src/transport.rs`
- `rust/lithe-remote-client/src/forward.rs`
- `rust/lithe-remote/src/client/transport.rs`
- `rust/lithe-remote/src/client/forward.rs`

## 依据

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
## 适用范围

- `rust/lithe-core/src/debug/`
- `rust/lithe-remote-client/src/transport.rs`
- `rust/lithe-remote/src/client/transport.rs`
- `shared/contracts/plugins/v1/`
- `macos/Sources/Lithe/Platform/MacOS/Debug/`
- `shared/i18n/`
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,8 @@ run:
```bash
node .agents/skills/write-stable-tests/scripts/generate-test-report.mjs
```

For feature-gated Rust targets, pass `--no-default-features --features client,server`
to `run-rust-tests-with-timing.mjs` (as in `scripts/verify-remote-workspace.sh`).
The runner forwards these flags to Cargo and records the selected features in
JSON so a passing client-only run cannot be mistaken for server coverage.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function parseArguments(arguments_) {
const options = {
manifest: null,
package: null,
features: null,
noDefaultFeatures: false,
warnMs: 1000,
maxMs: 15000,
testBudgets: [],
Expand All @@ -32,6 +34,11 @@ export function parseArguments(arguments_) {
const argument = arguments_[index];
if (argument === "--manifest") options.manifest = arguments_[++index];
else if (argument === "--package") options.package = arguments_[++index];
else if (argument === "--features") {
options.features = arguments_[++index];
if (!options.features || options.features.startsWith("--")) throw new Error("--features requires a feature list.");
}
else if (argument === "--no-default-features") options.noDefaultFeatures = true;
else if (argument === "--warn-ms") options.warnMs = positiveInteger(arguments_[++index], "--warn-ms");
else if (argument === "--max-ms") options.maxMs = positiveInteger(arguments_[++index], "--max-ms");
else if (argument === "--test-budget") {
Expand Down Expand Up @@ -132,6 +139,8 @@ export async function run(
runner: "rust",
manifest: options.manifest,
package: options.package,
features: options.features ?? null,
noDefaultFeatures: options.noDefaultFeatures ?? false,
warnMs: options.warnMs,
maxMs: options.maxMs,
testBudgets: options.testBudgets ?? [],
Expand All @@ -158,6 +167,8 @@ export async function run(
"--message-format=json",
];
if (options.package) cargoArguments.push("--package", options.package);
if (options.features) cargoArguments.push("--features", options.features);
if (options.noDefaultFeatures) cargoArguments.push("--no-default-features");

console.log(`Compiling Rust tests from ${options.manifest}`);
const buildBudget = timeoutBudget("compilation", options.buildTimeoutMs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,12 @@ try {
"--warn-ms", "10", "--max-ms", "100", "--test-budget", "tests::native_=300",
"--test-budget", "tests::native=250",
"--test-budget", linkedBudget,
"--no-default-features", "--features", "client,server",
]);
assert.equal(options.features, "client,server");
assert.equal(options.noDefaultFeatures, true);
assert.throws(() => parseRustTimingArguments(["--manifest", "Cargo.toml", "--features"]));
assert.throws(() => parseRustTimingArguments(["--manifest", "Cargo.toml", "--features", "--no-default-features"]));
for (const budget of ["=300", "tests::native_=0", "tests::native_=5", "invalid"]) {
assert.throws(() => parseRustTimingArguments(["--manifest", "Cargo.toml", "--test-budget", budget]));
}
Expand All @@ -793,6 +798,8 @@ try {
let durationMs = 0;
let timedOut = false;
if (onStdoutLine) {
assert.ok(args.includes("--no-default-features"));
assert.equal(args[args.indexOf("--features") + 1], "client,server");
onStdoutLine(JSON.stringify({ reason: "compiler-artifact", profile: { test: true },
executable: path.join(budgetFixtureRoot, "fake-tests"), manifest_path: options.manifest,
target: { name: "budget-fixture" } }));
Expand All @@ -809,6 +816,8 @@ try {
};
await assert.rejects(runRustTestsWithTiming(options, { runProcessImpl, now: () => currentTime }), /1 Rust test/);
const result = JSON.parse(readFileSync(report, "utf8"));
assert.equal(result.features, "client,server");
assert.equal(result.noDefaultFeatures, true);
assert.deepEqual(result.tests.map(({ status, maxMs }) => ({ status, maxMs })), [
{ status: "passed", maxMs: 100 }, { status: "passed", maxMs: 300 },
{ status: "passed", maxMs: 30000 }, { status: "timeout", maxMs: 300 },
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci-remote.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
targets: x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, aarch64-apple-darwin, x86_64-apple-darwin
targets: x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, aarch64-apple-darwin, x86_64-apple-darwin, x86_64-pc-windows-msvc

- name: Restore Cargo dependencies
uses: actions/cache@v6
Expand All @@ -112,7 +112,7 @@ jobs:
- name: Check formatting, lints and remote server targets
run: ./scripts/verify-remote-workspace.sh --static

- name: Test remote workspace crates
- name: Test remote workspace client, protocol and server
run: ./scripts/verify-remote-workspace.sh --tests

- name: Upload test timing report
Expand Down
29 changes: 28 additions & 1 deletion docs/ci-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ HTTP/SSE 响应体和 stdio 元数据限制为 2 MiB,原生文件由现有平
用户明确运行包命令可能产生的 npm/uv 缓存归上游运行时管理,不属于可跨工作树复用的 Lithe 构建资源。

随 App 附带的远端工作区服务登记为 `remote-server-artifacts`。`scripts/build-remote-servers.sh`
每次打包都用本工作树源码重新构建两个 Linux musl 静态版(约 25 秒),输出到
每次打包都从 `rust/lithe-remote/` 用 `--no-default-features --features server`
只构建 `lithe-remote-server` 可执行文件,不编译客户端;两个 Linux musl 静态版输出到
`rust/target/remote-servers/`,再复制到 `Contents/Resources/RemoteServers/`;构建机需要
`x86_64-unknown-linux-musl` 和 `aarch64-unknown-linux-musl` 两个 Rust 目标。清单只记录服务版本,
不带工具链或源码身份,所以不跨工作树复用。运行时只读取这份副本并上传给 SSH 主机,
Expand All @@ -406,6 +407,32 @@ HTTP/SSE 响应体和 stdio 元数据限制为 2 MiB,原生文件由现有平
`remote` 中记住的密码或口令、`Application Support/Lithe/RemoteWorkspaces/` 下打开远程文件夹时
创建的空占位根目录,以及远端主机上 `~/.lithe-server/bin/` 中按 SHA-256 校验后复用的服务。
它们属于用户资料或远端主机,没有构建身份,所有复用阶段均拒绝复制。
远端常驻服务的 socket、锁和日志使用“会话键 + 完整二进制 SHA-256”的散列作为身份;
即使版本号不变,重新构建后也不会连回旧二进制启动的服务。这些路径仍属于远端用户状态,
不复制进工作树或应用安装目录。

远程 Git 的临时索引、行历史快照,以及连接内存中的运行时、Shell、Git 可执行文件和搜索索引
登记为 `remote-workspace-session-state`。临时文件通过主机的临时目录或 Git common directory
创建,由单次操作清理;本地占位根目录不保存远端文件。探测与索引快照按连接隔离,显式刷新或
替换连接后重建。它们没有可校验的构建身份,所有复用阶段在调用校验器前拒绝复制。
运行时写入仅发生在远端可写目录或进程内存中,不影响签名、安装资源或 Sparkle delta。
文件树复制还会在远端目标父目录创建 `.lithe-copy-<pid>-<sequence>/` 私有暂存目录,
操作完成后清理;它与文件监听的连接资源同属 `remote-workspace-session-state`,没有复用阶段。
移到废纸篓的内容和恢复元数据属于主机账户(Linux 的 XDG 数据目录/卷废纸篓,macOS 的系统 Trash),
由系统和用户保管,不是缓存或构建输入,也不复制到本机工作树。

远端构建输出、插件运行快照和分块上传同样登记在 `remote-workspace-session-state`,
没有跨工作树复制阶段。上传在目标父目录用 `.lithe-upload-<pid>-<sequence>` 暂存,
按连接持有,失败、断线或五分钟期限后删除;SHA-256 与架构校验只授权本次运行。
完整插件快照位于远端 `~/.lithe-server/plugin-runs/<UUID>/package`,由 `fs.createRunDirectory`
登记为会话所有,进程销毁后按同一身份释放;非驻留断线、会话关闭和空闲到期时由服务端回收。
常驻会话短暂断线保留快照;客户端崩溃后按会话空闲期限回收。远端服务被强制杀死后的安全回收仍为 TODO,
不能按目录年龄删除其他会话或仍有进程使用的输入,也不接管旧版未登记目录。插件配置、索引等持久状态位于远端
`~/.lithe-server/state/plugins/<plugin-hash>/<provider-scope>/<provider-hash>/workspaces/<root-hash>`,
由该工作区和提供者持有,构建临时输出按运行清理。异平台包下载复用市场下载器,
在平台临时目录解压,传输完成或失败后删除;校验来源包括市场归档 SHA-256、安装来源、
已批准命令、声明逐字节一致性和签名。上述内容没有版本/平台/架构/工具链构建 stamp,
复用脚本在校验和复制前拒绝这些路径;运行时不改写本机或远端安装资源,不影响签名或 Sparkle delta。

MCP 编辑器、工具面板和指令页的原生验收截图分别输出到
`.artifacts/test-stability/mcp-ui/` 与 `.artifacts/test-stability/instructions-ui/`,随
Expand Down
Loading
Loading