@earendil-works/pi-env 1.0.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +22 -0
- package/bin/pi-env-android-arm64/pi-env +0 -0
- package/bin/pi-env-android-x64/pi-env +0 -0
- package/bin/pi-env-darwin-arm64/pi-env +0 -0
- package/bin/pi-env-darwin-x64/pi-env +0 -0
- package/bin/pi-env-linux-arm64/pi-env +0 -0
- package/bin/pi-env-linux-x64/pi-env +0 -0
- package/bin/pi-env-windows-arm64/pi-env.exe +0 -0
- package/bin/pi-env-windows-x64/pi-env.exe +0 -0
- package/dist/connection.d.ts +11 -0
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +10 -0
- package/dist/connection.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/list-files.d.ts +24 -0
- package/dist/list-files.d.ts.map +1 -0
- package/dist/list-files.js +75 -0
- package/dist/list-files.js.map +1 -0
- package/dist/remote-env.d.ts +3 -1
- package/dist/remote-env.d.ts.map +1 -1
- package/dist/remote-env.js +15 -3
- package/dist/remote-env.js.map +1 -1
- package/dist/ssh.d.ts +2 -0
- package/dist/ssh.d.ts.map +1 -1
- package/dist/ssh.js +8 -0
- package/dist/ssh.js.map +1 -1
- package/dist/terminal.d.ts +44 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +87 -0
- package/dist/terminal.js.map +1 -0
- package/docs/protocol.md +49 -12
- package/docs/semantics.md +3 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-10-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- The daemon's `listFiles` operation: a bounded, cancellable, gitignore-aware listing of a directory tree (files, directories and unfollowed symlinks); `hello` reports it in `features`, and older daemons answer `not_supported` without a request.
|
|
8
|
+
- `RemoteExecutionEnv.listFiles` and `LocalExecutionEnv`, a local `ExecutionEnv` on a pi-env daemon connection with the same operations.
|
|
9
|
+
- `nodePlatform(info)` maps the daemon's platform and architecture names to Node's.
|
|
10
|
+
- Pseudo-terminals for interactive programs: the daemon's `pty`, `ptyWrite`, and `ptyResize` operations stream raw terminal output as events, write input in arrival order, send `SIGWINCH` on resize, and kill the process group on cancel or a lost connection. `openTerminal()` returns a `RemoteTerminal`, and `localDaemon()` starts the daemon for this machine. Unix only; Windows answers `ENOTSUP`.
|
|
11
|
+
- `RemoteTerminal.foreground()` (daemon `ptyForeground`) reports the process group in front of a terminal when it is not the program's own, such as a job its shell runs, with the leader's name on macOS and Linux (`TerminalForeground`).
|
|
12
|
+
|
|
13
|
+
## [1.1.0] - 2026-10-07
|
|
14
|
+
|
|
3
15
|
## [1.0.4] - 2026-10-05
|
|
4
16
|
|
|
5
17
|
### Added
|
package/README.md
CHANGED
|
@@ -33,6 +33,28 @@ checking against the application's own known-hosts file under a fixed alias, no
|
|
|
33
33
|
connections or configured commands, and without forwarding the local locale. On Windows, detection and deployment go
|
|
34
34
|
through PowerShell, and the daemon starts through the server's default shell (cmd.exe or PowerShell).
|
|
35
35
|
|
|
36
|
+
`env.listFiles(path, options, context)` lists the tree below a directory in the daemon, as `fd --hidden --exclude .git`
|
|
37
|
+
does without following symbolic links: files, directories and links as relative `/` paths, skipping what git ignores
|
|
38
|
+
inside work trees, bounded by `maxEntries`, `maxBytes` and `timeoutMs` with a `truncated` flag, and stopped by aborting
|
|
39
|
+
the context. It is Durable's optional `ExecutionEnv.listFiles`. A daemon released before it reports `not_supported`
|
|
40
|
+
(`hello.features` lacks `listFiles`), and callers fall back to their own walk. For local sessions,
|
|
41
|
+
`LocalExecutionEnv` is `NodeExecutionEnv` plus `listFiles` through a daemon on the same machine:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const local = new Connection({ command: [localDaemon()] }); // owned by the caller; close() it at shutdown
|
|
45
|
+
const env = new LocalExecutionEnv({ cwd: "/home/me/project", connection: local });
|
|
46
|
+
const listing = await env.listFiles(".", { exclude: ["node_modules"] }, context);
|
|
47
|
+
// { ok: true, value: { entries: [{ path: "src", kind: "directory" }, { path: "src/a.ts", kind: "file" }], truncated } }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`nodePlatform(await connection.info())` gives the remote system in Node's names (`win32`, `darwin`, `x64`, `arm64`),
|
|
51
|
+
as `process.platform` and `process.arch` spell them.
|
|
52
|
+
|
|
53
|
+
`openTerminal(connection, options)` runs a program, by default the user's login shell, in a pseudo-terminal of the
|
|
54
|
+
daemon: raw output, input, resizing and its exit code, for interactive terminals. `localDaemon()` is the daemon for this
|
|
55
|
+
machine, for terminals without SSH (`new Connection({ command: [localDaemon()] })`). Terminals are not supported on
|
|
56
|
+
Windows yet.
|
|
57
|
+
|
|
36
58
|
Supported remote systems: Linux, macOS, Android (Termux) and Windows, on x86-64 and arm64. On Windows, string commands
|
|
37
59
|
run through Git Bash as `NodeExecutionEnv` runs them there; argv commands run directly.
|
|
38
60
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/connection.d.ts
CHANGED
|
@@ -21,7 +21,18 @@ export interface RemoteInfo {
|
|
|
21
21
|
/** Windows: per-drive working directories (`C:` → `C:\\work`) of the `=C:` variables. */
|
|
22
22
|
driveCwds: Record<string, string>;
|
|
23
23
|
pid: number;
|
|
24
|
+
/** Operations added to protocol 1 that this daemon supports, such as `listFiles`; absent from older daemons. */
|
|
25
|
+
features?: string[];
|
|
24
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Node's names for the daemon's system and architecture, which `hello` reports in Rust's spelling: `windows` is
|
|
29
|
+
* `win32`, `macos` is `darwin`, `x86_64` is `x64`, `aarch64` is `arm64`, so they compare with `process.platform` and
|
|
30
|
+
* `process.arch`. Other names are kept.
|
|
31
|
+
*/
|
|
32
|
+
export declare function nodePlatform(info: Pick<RemoteInfo, "os" | "arch">): {
|
|
33
|
+
platform: string;
|
|
34
|
+
arch: string;
|
|
35
|
+
};
|
|
25
36
|
export interface ConnectionOptions {
|
|
26
37
|
/**
|
|
27
38
|
* Command that starts the daemon, before its `serve --token <hex>` arguments, e.g. `["ssh", "-T", "--", "host",
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C,kEAAkE;AAClE,qBAAa,WAAY,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IAEtB,YAAY,MAAM,EAAE,IAAI,EAMvB;CACD;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C,kEAAkE;AAClE,qBAAa,WAAY,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IAEtB,YAAY,MAAM,EAAE,IAAI,EAMvB;CACD;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,gHAAgH;IAChH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAItG;AAED,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IAChE,mEAAmE;IACnE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,UAAU,CAAC;IACpB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;CAChB;AAQD,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACpD,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAsBD,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAExD;AAiDD;;;GAGG;AACH,qBAAa,UAAU;;IAQtB,YAAY,OAAO,EAAE,iBAAiB,EAErC;IAED,+EAA+E;IACzE,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAEhC;IAED,0DAA0D;IACpD,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAE/B;IAEK,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAUlF;IAED,uDAAuD;IACvD,KAAK,IAAI,IAAI,CAGZ;IAED,2CAA2C;IAC3C,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGtC;CA4KD"}
|
package/dist/connection.js
CHANGED
|
@@ -27,6 +27,16 @@ export class RemoteError extends Error {
|
|
|
27
27
|
this.fields = fields;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Node's names for the daemon's system and architecture, which `hello` reports in Rust's spelling: `windows` is
|
|
32
|
+
* `win32`, `macos` is `darwin`, `x86_64` is `x64`, `aarch64` is `arm64`, so they compare with `process.platform` and
|
|
33
|
+
* `process.arch`. Other names are kept.
|
|
34
|
+
*/
|
|
35
|
+
export function nodePlatform(info) {
|
|
36
|
+
const platforms = { windows: "win32", macos: "darwin" };
|
|
37
|
+
const archs = { x86_64: "x64", aarch64: "arm64", x86: "ia32" };
|
|
38
|
+
return { platform: platforms[info.os] ?? info.os, arch: archs[info.arch] ?? info.arch };
|
|
39
|
+
}
|
|
30
40
|
/** Lone surrogates become U+FFFD, as Node encodes strings for system calls. */
|
|
31
41
|
function wellFormed(_key, value) {
|
|
32
42
|
return typeof value === "string" ? value.toWellFormed() : value;
|
package/dist/connection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,sCAAsC;AACtC,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,MAAM,GAAG,CAAC,CAAC;AACjB,MAAM,KAAK,GAAG,CAAC,CAAC;AAChB,MAAM,KAAK,GAAG,CAAC,CAAC;AAChB,MAAM,MAAM,GAAG,CAAC,CAAC;AACjB,MAAM,IAAI,GAAG,CAAC,CAAC;AAEf,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,2FAA2F;AAC3F,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,6DAA6D;AAC7D,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAIhC,kEAAkE;AAClE,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC5B,IAAI,CAAS;IACb,IAAI,CAAU;IACd,MAAM,CAAO;IAEtB,YAAY,MAAY;QACvB,KAAK,CAAC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAC7D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AA0DD,+EAA+E;AAC/E,SAAS,UAAU,CAAC,IAAY,EAAE,KAAc;IAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACjE,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,EAAU,EAAE,IAAU,EAAE,OAAO,GAAe,IAAI,UAAU,CAAC,CAAC,CAAC;IAC3F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,sFAAsF;AACtF,SAAS,IAAI,CAAC,OAAe;IAC5B,OAAO,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC9C,OAAO,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC;AACnE,CAAC;AAED,SAAS,MAAM;IACd,OAAO,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,0BAA0B;AAC1B,MAAM,OAAO;IACH,EAAE,CAAS;IACX,KAAK,CAAiC;IACtC,KAAK,CAAS;IACd,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC9C,2EAA2E;IAC3E,MAAM,GAAa,EAAE,CAAC;IACtB,QAAQ,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,KAAK,CAAC;IACf,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,KAAK,CAA6C;IAClD,IAAI,GAAG,IAAI,CAAC;IAEZ,YAAY,EAAU,EAAE,KAAqC,EAAE,KAAa;QAC3E,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,+EAA+E;IAC/E,IAAI,CAAC,MAAc;QAClB,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,CAAC,MAAc;QACrB,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;QACxB,OAAO,MAAM,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC;YAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;gBAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACxC,MAAM,GAAG,CAAC,CAAC;YACZ,CAAC;QACF,CAAC;IACF,CAAC;CACD;AAED;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,QAAQ,CAAoB;IACrC,QAAQ,CAAsB;IAC9B,MAAM,CAA8D;IACpE,SAAS,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,CAAC,CAAC;IACZ,OAAO,GAAG,KAAK,CAAC;IAEhB,YAAY,OAA0B;QACrC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,IAAI;QACT,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,OAAO;QACZ,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,IAAU,EAAE,OAAO,GAAmB,EAAE;QACjE,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC9B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBAC9E,MAAM,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,uDAAuD;IACvD,KAAK;QACJ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,2CAA2C;IAC3C,IAAI,CAAC,EAAU,EAAE,OAAe;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,OAAO,EAAE,EAAE,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI;YAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC;IAED,QAAQ;QACP,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,kEAAkE;YAClE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE;gBAChB,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK;oBAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACpD,CAAC,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,OAAgB,EAAE,EAAU,EAAE,IAAU,EAAE,OAAuB;QACtE,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACzE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAClE,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE;gBACvB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBAClB,IAAI,EAAE,CAAC;oBACP,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;gBACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,IAAI,EAAE,CAAC;oBACP,MAAM,CAAC,KAAK,CAAC,CAAC;gBACf,CAAC;gBACD,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxD,CAAC,CAAC;YACH,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YAC3E,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;gBAAE,OAAO,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAgB,EAAE,IAAY;QACpC,IAAI,OAAO,CAAC,IAAI;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,MAAM;QACX,IAAI,OAA0B,CAAC;QAC/B,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACrC,OAAO,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;QACnC,IAAI,OAAO,KAAK,SAAS;YAAE,MAAM,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;QACxG,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACxG,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,UAAqD,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACtD,UAAU,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,gBAAgB,GAAG,IAAI,IAAI,CAAC,CAAC,EAC/E,gBAAgB,CAChB,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAC7B,MAAM,CAAC,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CACpF,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACnG,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACvB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QACzE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1F,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;QACjF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;QAChF,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,gBAAgB,EAAE,CAAC;gBACtD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC;YACJ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YACjG,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;gBAAE,MAAM,IAAI,CAAC,wBAAwB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC7E,OAAO,EAAE,IAAI,EAAE,IAA6B,EAAE,OAAO,EAAE,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9E,MAAM,KAAK,CAAC;QACb,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED,SAAS,CAAC,OAAgB,EAAE,KAAY;QACvC,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,OAAO;QAC1B,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC;QACrB,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;YAC1B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACzB,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,CAAC,OAAgB,EAAE,KAAa;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,OAAO;QAC1B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACrB,8FAA8F;YAC9F,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;YAChE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClB,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;oBACpC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO;YACR,CAAC;YACD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7D,IAAI,KAAK,KAAK,EAAE;gBAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;YAC/C,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/C,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,SAAS,EAAE,CAAC;gBACtC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBAC3D,OAAO;YACR,CAAC;YACD,IAAI,OAAO,CAAC,QAAQ,GAAG,CAAC,GAAG,MAAM;gBAAE,OAAO;YAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACxC,IAAI,IAAU,CAAC;YACf,IAAI,CAAC;gBACJ,IAAI,CAAC,GAAG,UAAU,GAAG,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBACzE,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBACzE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;gBAClC,IAAI,GAAG,MAAc,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACR,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBAC3D,OAAO;YACR,CAAC;YACD,uGAAuG;YACvG,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;IACF,CAAC;IAED,SAAS,CAAC,OAAgB,EAAE,IAAY,EAAE,EAAU,EAAE,IAAU,EAAE,OAAmB;QACpF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAClC,OAAO;QACR,CAAC;QACD,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO;QAC9C,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;;YACxE,OAAO,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC;CACD","sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\n\n/** Frame types (docs/protocol.md). */\nconst REQUEST = 1;\nconst RESULT = 2;\nconst ERROR = 3;\nconst EVENT = 4;\nconst CANCEL = 5;\nconst PING = 6;\n\nconst MAX_FRAME = 16 * 1024 * 1024;\nconst PING_INTERVAL_MS = 5000;\n/** The daemon pings every five seconds; this much silence means the connection is gone. */\nconst SILENCE_LIMIT_MS = 30_000;\n/** How long starting the daemon and its `hello` may take. */\nconst START_TIMEOUT_MS = 60_000;\n\nexport type Json = Record<string, unknown>;\n\n/** A failure reported by the daemon, with its Node-style code. */\nexport class RemoteError extends Error {\n\treadonly code: string;\n\treadonly path?: string;\n\treadonly fields: Json;\n\n\tconstructor(fields: Json) {\n\t\tsuper(typeof fields.message === \"string\" ? fields.message : \"Remote operation failed\");\n\t\tthis.name = \"RemoteError\";\n\t\tthis.code = typeof fields.code === \"string\" ? fields.code : \"unknown\";\n\t\tif (typeof fields.path === \"string\") this.path = fields.path;\n\t\tthis.fields = fields;\n\t}\n}\n\n/** What `hello` reports about the remote machine. */\nexport interface RemoteInfo {\n\tprotocol: number;\n\tversion: string;\n\tos: string;\n\tarch: string;\n\thome: string;\n\ttmpdir: string;\n\t/** Path separator: `/`, or `\\\\` on Windows. */\n\tseparator: string;\n\t/** The daemon's working directory, where Node's `path.resolve` would fall back to `process.cwd()`. */\n\tcwd: string;\n\t/** Windows: per-drive working directories (`C:` → `C:\\\\work`) of the `=C:` variables. */\n\tdriveCwds: Record<string, string>;\n\tpid: number;\n}\n\nexport interface ConnectionOptions {\n\t/**\n\t * Command that starts the daemon, before its `serve --token <hex>` arguments, e.g. `[\"ssh\", \"-T\", \"--\", \"host\",\n\t * \"~/.pi/mobile/tools/pi-env\"]`. A function computes it at each start (detecting and deploying first, for example);\n\t * its failure fails that start, and the next request tries again.\n\t */\n\tcommand: readonly string[] | (() => Promise<readonly string[]>);\n\t/** Receives the daemon's and the transport's diagnostic output. */\n\tonLog?: (text: string) => void;\n}\n\nexport interface Reply {\n\tjson: Json;\n\tpayload: Uint8Array;\n\t/** The daemon session that answered; handles from it are only valid while it lives. */\n\tsession: number;\n}\n\ntype Pending = {\n\tresolve: (value: Reply) => void;\n\treject: (error: Error) => void;\n\tonEvent?: (json: Json, payload: Uint8Array) => void;\n};\n\nexport interface RequestOptions {\n\tpayload?: Uint8Array;\n\t/** Aborting sends `cancel`; the request still settles with the daemon's result or error. */\n\tsignal?: AbortSignal;\n\t/** Progress events of the request, such as `exec` output. */\n\tonEvent?: (json: Json, payload: Uint8Array) => void;\n\t/** The request's id and session, for `kill`. */\n\tonStart?: (id: number, session: number) => void;\n\t/**\n\t * Only run in this daemon session, for requests on handles it opened. After the connection was lost and started\n\t * again, such requests fail instead of reaching a daemon that never opened the handle.\n\t */\n\tsession?: number;\n}\n\n/** Lone surrogates become U+FFFD, as Node encodes strings for system calls. */\nfunction wellFormed(_key: string, value: unknown): unknown {\n\treturn typeof value === \"string\" ? value.toWellFormed() : value;\n}\n\nfunction frame(type: number, id: number, json: Json, payload: Uint8Array = new Uint8Array(0)): Buffer {\n\tconst body = Buffer.from(JSON.stringify(json, wellFormed), \"utf8\");\n\tconst header = Buffer.alloc(13);\n\theader.writeUInt32BE(9 + body.length + payload.length, 0);\n\theader.writeUInt8(type, 4);\n\theader.writeUInt32BE(id, 5);\n\theader.writeUInt32BE(body.length, 9);\n\treturn Buffer.concat([header, body, payload]);\n}\n\n/** The connection failed or ended; a request may succeed once it is started again. */\nfunction lost(message: string): RemoteError {\n\treturn new RemoteError({ code: \"unknown\", message, lost: true });\n}\n\n/** Whether an error means the connection was lost, not that the daemon refused the request. */\nexport function isConnectionLost(error: unknown): boolean {\n\treturn error instanceof RemoteError && error.fields.lost === true;\n}\n\nfunction closed(): RemoteError {\n\treturn new RemoteError({ code: \"unknown\", message: \"Connection closed\" });\n}\n\n/** One running daemon. */\nclass Session {\n\treadonly id: number;\n\treadonly child: ChildProcessWithoutNullStreams;\n\treadonly token: string;\n\treadonly pending = new Map<number, Pending>();\n\t/** Received bytes not yet parsed, without concatenating on every chunk. */\n\tchunks: Buffer[] = [];\n\tbuffered = 0;\n\tsynced = false;\n\tlastSeen = Date.now();\n\ttimer: ReturnType<typeof setInterval> | undefined;\n\tlive = true;\n\n\tconstructor(id: number, child: ChildProcessWithoutNullStreams, token: string) {\n\t\tthis.id = id;\n\t\tthis.child = child;\n\t\tthis.token = token;\n\t}\n\n\t/** The first `length` buffered bytes as one buffer, without consuming them. */\n\tpeek(length: number): Buffer {\n\t\tif (this.chunks[0]!.length < length) {\n\t\t\tthis.chunks = [Buffer.concat(this.chunks)];\n\t\t}\n\t\treturn this.chunks[0]!.subarray(0, length);\n\t}\n\n\tconsume(length: number): void {\n\t\tthis.buffered -= length;\n\t\twhile (length > 0) {\n\t\t\tconst first = this.chunks[0]!;\n\t\t\tif (first.length <= length) {\n\t\t\t\tthis.chunks.shift();\n\t\t\t\tlength -= first.length;\n\t\t\t} else {\n\t\t\t\tthis.chunks[0] = first.subarray(length);\n\t\t\t\tlength = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * One connection to a pi-env daemon: started lazily on the first request and started again after it is lost. Requests\n * in flight when it is lost fail with code `unknown`; for mutations their outcome is then unknown.\n */\nexport class Connection {\n\treadonly #options: ConnectionOptions;\n\t#session: Session | undefined;\n\t#ready: Promise<{ info: RemoteInfo; session: Session }> | undefined;\n\t#sessions = 0;\n\t#nextId = 1;\n\t#closed = false;\n\n\tconstructor(options: ConnectionOptions) {\n\t\tthis.#options = options;\n\t}\n\n\t/** Connect if needed and return what the daemon reported about its machine. */\n\tasync info(): Promise<RemoteInfo> {\n\t\treturn (await this.#connect()).info;\n\t}\n\n\t/** Connect if needed and return the live session's id. */\n\tasync session(): Promise<number> {\n\t\treturn (await this.#connect()).session.id;\n\t}\n\n\tasync request(op: string, json: Json, options: RequestOptions = {}): Promise<Reply> {\n\t\tif (options.session !== undefined) {\n\t\t\tconst current = this.#session;\n\t\t\tif (current === undefined || current.id !== options.session || !current.live) {\n\t\t\t\tthrow lost(\"pi-env connection lost\");\n\t\t\t}\n\t\t\treturn this.#send(current, op, json, options);\n\t\t}\n\t\tconst { session } = await this.#connect();\n\t\treturn this.#send(session, op, json, options);\n\t}\n\n\t/** Stop the daemon; it kills everything it started. */\n\tclose(): void {\n\t\tthis.#closed = true;\n\t\tif (this.#session) this.#teardown(this.#session, closed());\n\t}\n\n\t/** Kill one `exec` without aborting it. */\n\tkill(id: number, session: number): void {\n\t\tconst current = this.#session;\n\t\tif (current?.id === session && current.live) this.#write(current, frame(CANCEL, id, { mode: \"kill\" }));\n\t}\n\n\t#connect(): Promise<{ info: RemoteInfo; session: Session }> {\n\t\tif (this.#closed) return Promise.reject(closed());\n\t\tif (this.#ready === undefined) {\n\t\t\tconst ready = this.#start();\n\t\t\tthis.#ready = ready;\n\t\t\t// A failed start is not remembered: the next request tries again.\n\t\t\tready.catch(() => {\n\t\t\t\tif (this.#ready === ready) this.#ready = undefined;\n\t\t\t});\n\t\t}\n\t\treturn this.#ready;\n\t}\n\n\t#send(session: Session, op: string, json: Json, options: RequestOptions): Promise<Reply> {\n\t\tif (!session.live) return Promise.reject(lost(\"pi-env connection lost\"));\n\t\tconst id = this.#nextId++;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst onAbort = () => this.#write(session, frame(CANCEL, id, {}));\n\t\t\tconst done = () => options.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\tsession.pending.set(id, {\n\t\t\t\tresolve: (value) => {\n\t\t\t\t\tdone();\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tdone();\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t\t...(options.onEvent ? { onEvent: options.onEvent } : {}),\n\t\t\t});\n\t\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\toptions.onStart?.(id, session.id);\n\t\t\tthis.#write(session, frame(REQUEST, id, { ...json, op }, options.payload));\n\t\t\tif (options.signal?.aborted) onAbort();\n\t\t});\n\t}\n\n\t#write(session: Session, data: Buffer): void {\n\t\tif (session.live) session.child.stdin.write(data);\n\t}\n\n\tasync #start(): Promise<{ info: RemoteInfo; session: Session }> {\n\t\tlet command: readonly string[];\n\t\ttry {\n\t\t\tconst option = this.#options.command;\n\t\t\tcommand = typeof option === \"function\" ? await option() : option;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new RemoteError({ code: \"spawn_error\", message, lost: true });\n\t\t}\n\t\tif (this.#closed) throw closed();\n\t\tconst [program, ...args] = command;\n\t\tif (program === undefined) throw new RemoteError({ code: \"spawn_error\", message: \"No daemon command\" });\n\t\tconst token = randomBytes(16).toString(\"hex\");\n\t\tconst child = spawn(program, [...args, \"serve\", \"--token\", token], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tconst session = new Session(++this.#sessions, child, token);\n\t\tthis.#session = session;\n\t\tlet startTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst failed = new Promise<never>((_resolve, reject) => {\n\t\t\tstartTimer = setTimeout(\n\t\t\t\t() => reject(lost(`pi-env did not answer within ${START_TIMEOUT_MS / 1000} s`)),\n\t\t\t\tSTART_TIMEOUT_MS,\n\t\t\t);\n\t\t\tchild.once(\"error\", (error) =>\n\t\t\t\treject(new RemoteError({ code: \"spawn_error\", message: error.message, lost: true })),\n\t\t\t);\n\t\t\tchild.once(\"exit\", (code) => reject(lost(`pi-env exited with code ${code} before it was ready`)));\n\t\t});\n\t\tfailed.catch(() => {});\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => this.#onData(session, chunk));\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => this.#options.onLog?.(chunk.toString(\"utf8\")));\n\t\tchild.on(\"error\", () => this.#teardown(session, lost(\"pi-env connection lost\")));\n\t\tchild.on(\"exit\", () => this.#teardown(session, lost(\"pi-env connection lost\")));\n\t\tchild.stdin.on(\"error\", () => {});\n\t\tsession.timer = setInterval(() => {\n\t\t\tthis.#write(session, frame(PING, 0, {}));\n\t\t\tif (Date.now() - session.lastSeen > SILENCE_LIMIT_MS) {\n\t\t\t\tthis.#teardown(session, lost(\"pi-env connection timed out\"));\n\t\t\t}\n\t\t}, PING_INTERVAL_MS);\n\t\tsession.timer.unref();\n\t\ttry {\n\t\t\tconst { json } = await Promise.race([this.#send(session, \"hello\", { protocol: 1 }, {}), failed]);\n\t\t\tif (json.protocol !== 1) throw lost(`Unsupported protocol ${json.protocol}`);\n\t\t\treturn { info: json as unknown as RemoteInfo, session };\n\t\t} catch (error) {\n\t\t\tthis.#teardown(session, error instanceof Error ? error : lost(String(error)));\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tclearTimeout(startTimer);\n\t\t}\n\t}\n\n\t#teardown(session: Session, error: Error): void {\n\t\tif (!session.live) return;\n\t\tsession.live = false;\n\t\tclearInterval(session.timer);\n\t\tif (this.#session === session) {\n\t\t\tthis.#session = undefined;\n\t\t\tthis.#ready = undefined;\n\t\t}\n\t\tsession.child.stdin.end();\n\t\tsession.child.kill();\n\t\tconst pending = [...session.pending.values()];\n\t\tsession.pending.clear();\n\t\tfor (const request of pending) request.reject(error);\n\t}\n\n\t#onData(session: Session, chunk: Buffer): void {\n\t\tif (!session.live) return;\n\t\tsession.lastSeen = Date.now();\n\t\tsession.chunks.push(chunk);\n\t\tsession.buffered += chunk.length;\n\t\tif (!session.synced) {\n\t\t\t// Shell startup files may print before the daemon runs; skip everything before its sync line.\n\t\t\tconst marker = Buffer.from(`PI-ENV ${session.token}\\n`, \"utf8\");\n\t\t\tconst all = session.peek(session.buffered);\n\t\t\tconst index = all.indexOf(marker);\n\t\t\tif (index === -1) {\n\t\t\t\tif (session.buffered > 1024 * 1024) {\n\t\t\t\t\tsession.consume(session.buffered - marker.length);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst noise = all.subarray(0, index).toString(\"utf8\").trim();\n\t\t\tif (noise !== \"\") this.#options.onLog?.(noise);\n\t\t\tsession.consume(index + marker.length);\n\t\t\tsession.synced = true;\n\t\t}\n\t\twhile (session.live && session.buffered >= 4) {\n\t\t\tconst length = session.peek(4).readUInt32BE(0);\n\t\t\tif (length < 9 || length > MAX_FRAME) {\n\t\t\t\tthis.#teardown(session, lost(\"Corrupt frame from pi-env\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.buffered < 4 + length) return;\n\t\t\tconst body = session.peek(4 + length).subarray(4);\n\t\t\tconst type = body.readUInt8(0);\n\t\t\tconst id = body.readUInt32BE(1);\n\t\t\tconst jsonLength = body.readUInt32BE(5);\n\t\t\tlet json: Json;\n\t\t\ttry {\n\t\t\t\tif (9 + jsonLength > length) throw new Error(\"JSON length out of range\");\n\t\t\t\tconst parsed: unknown = JSON.parse(body.subarray(9, 9 + jsonLength).toString(\"utf8\"));\n\t\t\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed))\n\t\t\t\t\tthrow new Error(\"not an object\");\n\t\t\t\tjson = parsed as Json;\n\t\t\t} catch {\n\t\t\t\tthis.#teardown(session, lost(\"Corrupt frame from pi-env\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// A plain Uint8Array copy, like `NodeExecutionEnv`'s reader results, not a view of the receive buffer.\n\t\t\tconst payload = new Uint8Array(body.subarray(9 + jsonLength));\n\t\t\tsession.consume(4 + length);\n\t\t\tthis.#dispatch(session, type, id, json, payload);\n\t\t}\n\t}\n\n\t#dispatch(session: Session, type: number, id: number, json: Json, payload: Uint8Array): void {\n\t\tconst pending = session.pending.get(id);\n\t\tif (type === EVENT) {\n\t\t\tpending?.onEvent?.(json, payload);\n\t\t\treturn;\n\t\t}\n\t\tif (type !== RESULT && type !== ERROR) return;\n\t\tif (pending === undefined) return;\n\t\tsession.pending.delete(id);\n\t\tif (type === RESULT) pending.resolve({ json, payload, session: session.id });\n\t\telse pending.reject(new RemoteError(json));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\n\n/** Frame types (docs/protocol.md). */\nconst REQUEST = 1;\nconst RESULT = 2;\nconst ERROR = 3;\nconst EVENT = 4;\nconst CANCEL = 5;\nconst PING = 6;\n\nconst MAX_FRAME = 16 * 1024 * 1024;\nconst PING_INTERVAL_MS = 5000;\n/** The daemon pings every five seconds; this much silence means the connection is gone. */\nconst SILENCE_LIMIT_MS = 30_000;\n/** How long starting the daemon and its `hello` may take. */\nconst START_TIMEOUT_MS = 60_000;\n\nexport type Json = Record<string, unknown>;\n\n/** A failure reported by the daemon, with its Node-style code. */\nexport class RemoteError extends Error {\n\treadonly code: string;\n\treadonly path?: string;\n\treadonly fields: Json;\n\n\tconstructor(fields: Json) {\n\t\tsuper(typeof fields.message === \"string\" ? fields.message : \"Remote operation failed\");\n\t\tthis.name = \"RemoteError\";\n\t\tthis.code = typeof fields.code === \"string\" ? fields.code : \"unknown\";\n\t\tif (typeof fields.path === \"string\") this.path = fields.path;\n\t\tthis.fields = fields;\n\t}\n}\n\n/** What `hello` reports about the remote machine. */\nexport interface RemoteInfo {\n\tprotocol: number;\n\tversion: string;\n\tos: string;\n\tarch: string;\n\thome: string;\n\ttmpdir: string;\n\t/** Path separator: `/`, or `\\\\` on Windows. */\n\tseparator: string;\n\t/** The daemon's working directory, where Node's `path.resolve` would fall back to `process.cwd()`. */\n\tcwd: string;\n\t/** Windows: per-drive working directories (`C:` → `C:\\\\work`) of the `=C:` variables. */\n\tdriveCwds: Record<string, string>;\n\tpid: number;\n\t/** Operations added to protocol 1 that this daemon supports, such as `listFiles`; absent from older daemons. */\n\tfeatures?: string[];\n}\n\n/**\n * Node's names for the daemon's system and architecture, which `hello` reports in Rust's spelling: `windows` is\n * `win32`, `macos` is `darwin`, `x86_64` is `x64`, `aarch64` is `arm64`, so they compare with `process.platform` and\n * `process.arch`. Other names are kept.\n */\nexport function nodePlatform(info: Pick<RemoteInfo, \"os\" | \"arch\">): { platform: string; arch: string } {\n\tconst platforms: Record<string, string> = { windows: \"win32\", macos: \"darwin\" };\n\tconst archs: Record<string, string> = { x86_64: \"x64\", aarch64: \"arm64\", x86: \"ia32\" };\n\treturn { platform: platforms[info.os] ?? info.os, arch: archs[info.arch] ?? info.arch };\n}\n\nexport interface ConnectionOptions {\n\t/**\n\t * Command that starts the daemon, before its `serve --token <hex>` arguments, e.g. `[\"ssh\", \"-T\", \"--\", \"host\",\n\t * \"~/.pi/mobile/tools/pi-env\"]`. A function computes it at each start (detecting and deploying first, for example);\n\t * its failure fails that start, and the next request tries again.\n\t */\n\tcommand: readonly string[] | (() => Promise<readonly string[]>);\n\t/** Receives the daemon's and the transport's diagnostic output. */\n\tonLog?: (text: string) => void;\n}\n\nexport interface Reply {\n\tjson: Json;\n\tpayload: Uint8Array;\n\t/** The daemon session that answered; handles from it are only valid while it lives. */\n\tsession: number;\n}\n\ntype Pending = {\n\tresolve: (value: Reply) => void;\n\treject: (error: Error) => void;\n\tonEvent?: (json: Json, payload: Uint8Array) => void;\n};\n\nexport interface RequestOptions {\n\tpayload?: Uint8Array;\n\t/** Aborting sends `cancel`; the request still settles with the daemon's result or error. */\n\tsignal?: AbortSignal;\n\t/** Progress events of the request, such as `exec` output. */\n\tonEvent?: (json: Json, payload: Uint8Array) => void;\n\t/** The request's id and session, for `kill`. */\n\tonStart?: (id: number, session: number) => void;\n\t/**\n\t * Only run in this daemon session, for requests on handles it opened. After the connection was lost and started\n\t * again, such requests fail instead of reaching a daemon that never opened the handle.\n\t */\n\tsession?: number;\n}\n\n/** Lone surrogates become U+FFFD, as Node encodes strings for system calls. */\nfunction wellFormed(_key: string, value: unknown): unknown {\n\treturn typeof value === \"string\" ? value.toWellFormed() : value;\n}\n\nfunction frame(type: number, id: number, json: Json, payload: Uint8Array = new Uint8Array(0)): Buffer {\n\tconst body = Buffer.from(JSON.stringify(json, wellFormed), \"utf8\");\n\tconst header = Buffer.alloc(13);\n\theader.writeUInt32BE(9 + body.length + payload.length, 0);\n\theader.writeUInt8(type, 4);\n\theader.writeUInt32BE(id, 5);\n\theader.writeUInt32BE(body.length, 9);\n\treturn Buffer.concat([header, body, payload]);\n}\n\n/** The connection failed or ended; a request may succeed once it is started again. */\nfunction lost(message: string): RemoteError {\n\treturn new RemoteError({ code: \"unknown\", message, lost: true });\n}\n\n/** Whether an error means the connection was lost, not that the daemon refused the request. */\nexport function isConnectionLost(error: unknown): boolean {\n\treturn error instanceof RemoteError && error.fields.lost === true;\n}\n\nfunction closed(): RemoteError {\n\treturn new RemoteError({ code: \"unknown\", message: \"Connection closed\" });\n}\n\n/** One running daemon. */\nclass Session {\n\treadonly id: number;\n\treadonly child: ChildProcessWithoutNullStreams;\n\treadonly token: string;\n\treadonly pending = new Map<number, Pending>();\n\t/** Received bytes not yet parsed, without concatenating on every chunk. */\n\tchunks: Buffer[] = [];\n\tbuffered = 0;\n\tsynced = false;\n\tlastSeen = Date.now();\n\ttimer: ReturnType<typeof setInterval> | undefined;\n\tlive = true;\n\n\tconstructor(id: number, child: ChildProcessWithoutNullStreams, token: string) {\n\t\tthis.id = id;\n\t\tthis.child = child;\n\t\tthis.token = token;\n\t}\n\n\t/** The first `length` buffered bytes as one buffer, without consuming them. */\n\tpeek(length: number): Buffer {\n\t\tif (this.chunks[0]!.length < length) {\n\t\t\tthis.chunks = [Buffer.concat(this.chunks)];\n\t\t}\n\t\treturn this.chunks[0]!.subarray(0, length);\n\t}\n\n\tconsume(length: number): void {\n\t\tthis.buffered -= length;\n\t\twhile (length > 0) {\n\t\t\tconst first = this.chunks[0]!;\n\t\t\tif (first.length <= length) {\n\t\t\t\tthis.chunks.shift();\n\t\t\t\tlength -= first.length;\n\t\t\t} else {\n\t\t\t\tthis.chunks[0] = first.subarray(length);\n\t\t\t\tlength = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * One connection to a pi-env daemon: started lazily on the first request and started again after it is lost. Requests\n * in flight when it is lost fail with code `unknown`; for mutations their outcome is then unknown.\n */\nexport class Connection {\n\treadonly #options: ConnectionOptions;\n\t#session: Session | undefined;\n\t#ready: Promise<{ info: RemoteInfo; session: Session }> | undefined;\n\t#sessions = 0;\n\t#nextId = 1;\n\t#closed = false;\n\n\tconstructor(options: ConnectionOptions) {\n\t\tthis.#options = options;\n\t}\n\n\t/** Connect if needed and return what the daemon reported about its machine. */\n\tasync info(): Promise<RemoteInfo> {\n\t\treturn (await this.#connect()).info;\n\t}\n\n\t/** Connect if needed and return the live session's id. */\n\tasync session(): Promise<number> {\n\t\treturn (await this.#connect()).session.id;\n\t}\n\n\tasync request(op: string, json: Json, options: RequestOptions = {}): Promise<Reply> {\n\t\tif (options.session !== undefined) {\n\t\t\tconst current = this.#session;\n\t\t\tif (current === undefined || current.id !== options.session || !current.live) {\n\t\t\t\tthrow lost(\"pi-env connection lost\");\n\t\t\t}\n\t\t\treturn this.#send(current, op, json, options);\n\t\t}\n\t\tconst { session } = await this.#connect();\n\t\treturn this.#send(session, op, json, options);\n\t}\n\n\t/** Stop the daemon; it kills everything it started. */\n\tclose(): void {\n\t\tthis.#closed = true;\n\t\tif (this.#session) this.#teardown(this.#session, closed());\n\t}\n\n\t/** Kill one `exec` without aborting it. */\n\tkill(id: number, session: number): void {\n\t\tconst current = this.#session;\n\t\tif (current?.id === session && current.live) this.#write(current, frame(CANCEL, id, { mode: \"kill\" }));\n\t}\n\n\t#connect(): Promise<{ info: RemoteInfo; session: Session }> {\n\t\tif (this.#closed) return Promise.reject(closed());\n\t\tif (this.#ready === undefined) {\n\t\t\tconst ready = this.#start();\n\t\t\tthis.#ready = ready;\n\t\t\t// A failed start is not remembered: the next request tries again.\n\t\t\tready.catch(() => {\n\t\t\t\tif (this.#ready === ready) this.#ready = undefined;\n\t\t\t});\n\t\t}\n\t\treturn this.#ready;\n\t}\n\n\t#send(session: Session, op: string, json: Json, options: RequestOptions): Promise<Reply> {\n\t\tif (!session.live) return Promise.reject(lost(\"pi-env connection lost\"));\n\t\tconst id = this.#nextId++;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst onAbort = () => this.#write(session, frame(CANCEL, id, {}));\n\t\t\tconst done = () => options.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\tsession.pending.set(id, {\n\t\t\t\tresolve: (value) => {\n\t\t\t\t\tdone();\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tdone();\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t\t...(options.onEvent ? { onEvent: options.onEvent } : {}),\n\t\t\t});\n\t\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\toptions.onStart?.(id, session.id);\n\t\t\tthis.#write(session, frame(REQUEST, id, { ...json, op }, options.payload));\n\t\t\tif (options.signal?.aborted) onAbort();\n\t\t});\n\t}\n\n\t#write(session: Session, data: Buffer): void {\n\t\tif (session.live) session.child.stdin.write(data);\n\t}\n\n\tasync #start(): Promise<{ info: RemoteInfo; session: Session }> {\n\t\tlet command: readonly string[];\n\t\ttry {\n\t\t\tconst option = this.#options.command;\n\t\t\tcommand = typeof option === \"function\" ? await option() : option;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new RemoteError({ code: \"spawn_error\", message, lost: true });\n\t\t}\n\t\tif (this.#closed) throw closed();\n\t\tconst [program, ...args] = command;\n\t\tif (program === undefined) throw new RemoteError({ code: \"spawn_error\", message: \"No daemon command\" });\n\t\tconst token = randomBytes(16).toString(\"hex\");\n\t\tconst child = spawn(program, [...args, \"serve\", \"--token\", token], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tconst session = new Session(++this.#sessions, child, token);\n\t\tthis.#session = session;\n\t\tlet startTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst failed = new Promise<never>((_resolve, reject) => {\n\t\t\tstartTimer = setTimeout(\n\t\t\t\t() => reject(lost(`pi-env did not answer within ${START_TIMEOUT_MS / 1000} s`)),\n\t\t\t\tSTART_TIMEOUT_MS,\n\t\t\t);\n\t\t\tchild.once(\"error\", (error) =>\n\t\t\t\treject(new RemoteError({ code: \"spawn_error\", message: error.message, lost: true })),\n\t\t\t);\n\t\t\tchild.once(\"exit\", (code) => reject(lost(`pi-env exited with code ${code} before it was ready`)));\n\t\t});\n\t\tfailed.catch(() => {});\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => this.#onData(session, chunk));\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => this.#options.onLog?.(chunk.toString(\"utf8\")));\n\t\tchild.on(\"error\", () => this.#teardown(session, lost(\"pi-env connection lost\")));\n\t\tchild.on(\"exit\", () => this.#teardown(session, lost(\"pi-env connection lost\")));\n\t\tchild.stdin.on(\"error\", () => {});\n\t\tsession.timer = setInterval(() => {\n\t\t\tthis.#write(session, frame(PING, 0, {}));\n\t\t\tif (Date.now() - session.lastSeen > SILENCE_LIMIT_MS) {\n\t\t\t\tthis.#teardown(session, lost(\"pi-env connection timed out\"));\n\t\t\t}\n\t\t}, PING_INTERVAL_MS);\n\t\tsession.timer.unref();\n\t\ttry {\n\t\t\tconst { json } = await Promise.race([this.#send(session, \"hello\", { protocol: 1 }, {}), failed]);\n\t\t\tif (json.protocol !== 1) throw lost(`Unsupported protocol ${json.protocol}`);\n\t\t\treturn { info: json as unknown as RemoteInfo, session };\n\t\t} catch (error) {\n\t\t\tthis.#teardown(session, error instanceof Error ? error : lost(String(error)));\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tclearTimeout(startTimer);\n\t\t}\n\t}\n\n\t#teardown(session: Session, error: Error): void {\n\t\tif (!session.live) return;\n\t\tsession.live = false;\n\t\tclearInterval(session.timer);\n\t\tif (this.#session === session) {\n\t\t\tthis.#session = undefined;\n\t\t\tthis.#ready = undefined;\n\t\t}\n\t\tsession.child.stdin.end();\n\t\tsession.child.kill();\n\t\tconst pending = [...session.pending.values()];\n\t\tsession.pending.clear();\n\t\tfor (const request of pending) request.reject(error);\n\t}\n\n\t#onData(session: Session, chunk: Buffer): void {\n\t\tif (!session.live) return;\n\t\tsession.lastSeen = Date.now();\n\t\tsession.chunks.push(chunk);\n\t\tsession.buffered += chunk.length;\n\t\tif (!session.synced) {\n\t\t\t// Shell startup files may print before the daemon runs; skip everything before its sync line.\n\t\t\tconst marker = Buffer.from(`PI-ENV ${session.token}\\n`, \"utf8\");\n\t\t\tconst all = session.peek(session.buffered);\n\t\t\tconst index = all.indexOf(marker);\n\t\t\tif (index === -1) {\n\t\t\t\tif (session.buffered > 1024 * 1024) {\n\t\t\t\t\tsession.consume(session.buffered - marker.length);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst noise = all.subarray(0, index).toString(\"utf8\").trim();\n\t\t\tif (noise !== \"\") this.#options.onLog?.(noise);\n\t\t\tsession.consume(index + marker.length);\n\t\t\tsession.synced = true;\n\t\t}\n\t\twhile (session.live && session.buffered >= 4) {\n\t\t\tconst length = session.peek(4).readUInt32BE(0);\n\t\t\tif (length < 9 || length > MAX_FRAME) {\n\t\t\t\tthis.#teardown(session, lost(\"Corrupt frame from pi-env\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.buffered < 4 + length) return;\n\t\t\tconst body = session.peek(4 + length).subarray(4);\n\t\t\tconst type = body.readUInt8(0);\n\t\t\tconst id = body.readUInt32BE(1);\n\t\t\tconst jsonLength = body.readUInt32BE(5);\n\t\t\tlet json: Json;\n\t\t\ttry {\n\t\t\t\tif (9 + jsonLength > length) throw new Error(\"JSON length out of range\");\n\t\t\t\tconst parsed: unknown = JSON.parse(body.subarray(9, 9 + jsonLength).toString(\"utf8\"));\n\t\t\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed))\n\t\t\t\t\tthrow new Error(\"not an object\");\n\t\t\t\tjson = parsed as Json;\n\t\t\t} catch {\n\t\t\t\tthis.#teardown(session, lost(\"Corrupt frame from pi-env\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// A plain Uint8Array copy, like `NodeExecutionEnv`'s reader results, not a view of the receive buffer.\n\t\t\tconst payload = new Uint8Array(body.subarray(9 + jsonLength));\n\t\t\tsession.consume(4 + length);\n\t\t\tthis.#dispatch(session, type, id, json, payload);\n\t\t}\n\t}\n\n\t#dispatch(session: Session, type: number, id: number, json: Json, payload: Uint8Array): void {\n\t\tconst pending = session.pending.get(id);\n\t\tif (type === EVENT) {\n\t\t\tpending?.onEvent?.(json, payload);\n\t\t\treturn;\n\t\t}\n\t\tif (type !== RESULT && type !== ERROR) return;\n\t\tif (pending === undefined) return;\n\t\tsession.pending.delete(id);\n\t\tif (type === RESULT) pending.resolve({ json, payload, session: session.id });\n\t\telse pending.reject(new RemoteError(json));\n\t}\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { Connection, type ConnectionOptions, isConnectionLost, RemoteError, type RemoteInfo, type Reply, type RequestOptions, } from "./connection.ts";
|
|
1
|
+
export { Connection, type ConnectionOptions, isConnectionLost, nodePlatform, RemoteError, type RemoteInfo, type Reply, type RequestOptions, } from "./connection.ts";
|
|
2
|
+
export { LocalExecutionEnv, type LocalExecutionEnvOptions } from "./list-files.ts";
|
|
2
3
|
export { RemoteExecutionEnv, type RemoteExecutionEnvOptions } from "./remote-env.ts";
|
|
3
|
-
export { acceptHostKey, connectSsh, deployDaemon, detectPlatform, forgetHostKey, HostKeyChangedError, HostKeyUnknownError, packagedDaemon, type RemotePlatform, type SshConnectOptions, SshError, type SshTarget, scanHostKey, sshArguments, sshConnection, } from "./ssh.ts";
|
|
4
|
+
export { acceptHostKey, connectSsh, deployDaemon, detectPlatform, forgetHostKey, HostKeyChangedError, HostKeyUnknownError, localDaemon, packagedDaemon, type RemotePlatform, type SshConnectOptions, SshError, type SshTarget, scanHostKey, sshArguments, sshConnection, } from "./ssh.ts";
|
|
5
|
+
export { openTerminal, RemoteTerminal, type TerminalForeground, type TerminalOptions } from "./terminal.ts";
|
|
4
6
|
export { RemoteWatcher, type RemoteWatchOptions } from "./watch.ts";
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,WAAW,EACX,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,cAAc,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,QAAQ,EACR,KAAK,SAAS,EACd,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,cAAc,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,QAAQ,EACR,KAAK,SAAS,EACd,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { Connection, isConnectionLost, RemoteError, } from "./connection.js";
|
|
1
|
+
export { Connection, isConnectionLost, nodePlatform, RemoteError, } from "./connection.js";
|
|
2
|
+
export { LocalExecutionEnv } from "./list-files.js";
|
|
2
3
|
export { RemoteExecutionEnv } from "./remote-env.js";
|
|
3
|
-
export { acceptHostKey, connectSsh, deployDaemon, detectPlatform, forgetHostKey, HostKeyChangedError, HostKeyUnknownError, packagedDaemon, SshError, scanHostKey, sshArguments, sshConnection, } from "./ssh.js";
|
|
4
|
+
export { acceptHostKey, connectSsh, deployDaemon, detectPlatform, forgetHostKey, HostKeyChangedError, HostKeyUnknownError, localDaemon, packagedDaemon, SshError, scanHostKey, sshArguments, sshConnection, } from "./ssh.js";
|
|
5
|
+
export { openTerminal, RemoteTerminal } from "./terminal.js";
|
|
4
6
|
export { RemoteWatcher } from "./watch.js";
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EAEV,gBAAgB,EAChB,WAAW,GAIX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAkC,MAAM,iBAAiB,CAAC;AACrF,OAAO,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EAGd,QAAQ,EAER,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAA2B,MAAM,YAAY,CAAC","sourcesContent":["export {\n\tConnection,\n\ttype ConnectionOptions,\n\tisConnectionLost,\n\tRemoteError,\n\ttype RemoteInfo,\n\ttype Reply,\n\ttype RequestOptions,\n} from \"./connection.ts\";\nexport { RemoteExecutionEnv, type RemoteExecutionEnvOptions } from \"./remote-env.ts\";\nexport {\n\tacceptHostKey,\n\tconnectSsh,\n\tdeployDaemon,\n\tdetectPlatform,\n\tforgetHostKey,\n\tHostKeyChangedError,\n\tHostKeyUnknownError,\n\tpackagedDaemon,\n\ttype RemotePlatform,\n\ttype SshConnectOptions,\n\tSshError,\n\ttype SshTarget,\n\tscanHostKey,\n\tsshArguments,\n\tsshConnection,\n} from \"./ssh.ts\";\nexport { RemoteWatcher, type RemoteWatchOptions } from \"./watch.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EAEV,gBAAgB,EAChB,YAAY,EACZ,WAAW,GAIX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAiC,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAkC,MAAM,iBAAiB,CAAC;AACrF,OAAO,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,cAAc,EAGd,QAAQ,EAER,WAAW,EACX,YAAY,EACZ,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAiD,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,aAAa,EAA2B,MAAM,YAAY,CAAC","sourcesContent":["export {\n\tConnection,\n\ttype ConnectionOptions,\n\tisConnectionLost,\n\tnodePlatform,\n\tRemoteError,\n\ttype RemoteInfo,\n\ttype Reply,\n\ttype RequestOptions,\n} from \"./connection.ts\";\nexport { LocalExecutionEnv, type LocalExecutionEnvOptions } from \"./list-files.ts\";\nexport { RemoteExecutionEnv, type RemoteExecutionEnvOptions } from \"./remote-env.ts\";\nexport {\n\tacceptHostKey,\n\tconnectSsh,\n\tdeployDaemon,\n\tdetectPlatform,\n\tforgetHostKey,\n\tHostKeyChangedError,\n\tHostKeyUnknownError,\n\tlocalDaemon,\n\tpackagedDaemon,\n\ttype RemotePlatform,\n\ttype SshConnectOptions,\n\tSshError,\n\ttype SshTarget,\n\tscanHostKey,\n\tsshArguments,\n\tsshConnection,\n} from \"./ssh.ts\";\nexport { openTerminal, RemoteTerminal, type TerminalForeground, type TerminalOptions } from \"./terminal.ts\";\nexport { RemoteWatcher, type RemoteWatchOptions } from \"./watch.ts\";\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord";
|
|
2
|
+
import { type ExecutionEnv, FileError, type FileListing, type ListFilesOptions, type Result } from "@earendil-works/pi-durable/env";
|
|
3
|
+
import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";
|
|
4
|
+
import type { Connection } from "./connection.ts";
|
|
5
|
+
/**
|
|
6
|
+
* `listFiles` of an absolute path through the daemon: fails with `not_supported` if the daemon does not list files
|
|
7
|
+
* (a release before `listFiles`), so callers fall back to their own walk.
|
|
8
|
+
*/
|
|
9
|
+
export declare function listFilesOver(connection: Connection, path: string, options: ListFilesOptions | undefined, context: Context): Promise<Result<FileListing, FileError>>;
|
|
10
|
+
export type LocalExecutionEnvOptions = ConstructorParameters<typeof NodeExecutionEnv>[0] & {
|
|
11
|
+
/** A connection to this machine's daemon, `new Connection({ command: [localDaemon()] })`, for `listFiles`. */
|
|
12
|
+
connection: Connection;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* `NodeExecutionEnv` with `listFiles` through a pi-env daemon on the same machine, so local and remote environments
|
|
16
|
+
* list files with the same code. Everything else is `NodeExecutionEnv`'s. The connection belongs to the caller, who
|
|
17
|
+
* closes it; several environments may share it.
|
|
18
|
+
*/
|
|
19
|
+
export declare class LocalExecutionEnv extends NodeExecutionEnv implements ExecutionEnv {
|
|
20
|
+
#private;
|
|
21
|
+
constructor(options: LocalExecutionEnvOptions);
|
|
22
|
+
listFiles(path: string, options: ListFilesOptions | undefined, context: Context): Promise<Result<FileListing, FileError>>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=list-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-files.d.ts","sourceRoot":"","sources":["../src/list-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACN,KAAK,YAAY,EAEjB,SAAS,EAGT,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAErB,KAAK,MAAM,EACX,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD;;;GAGG;AACH,wBAAsB,aAAa,CAClC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CA2CzC;AAED,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG;IAC1F,8GAA8G;IAC9G,UAAU,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,gBAAiB,YAAW,YAAY;;IAG9E,YAAY,OAAO,EAAE,wBAAwB,EAI5C;IAEK,SAAS,CACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAIzC;CACD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { err, FileError, ok, } from "@earendil-works/pi-durable/env";
|
|
2
|
+
import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";
|
|
3
|
+
import { abortResult, toFileError } from "./errors.js";
|
|
4
|
+
/** Kind bytes of `listFiles` records: `f`, `d`, `l`. */
|
|
5
|
+
const KINDS = { 102: "file", 100: "directory", 108: "symlink" };
|
|
6
|
+
/**
|
|
7
|
+
* `listFiles` of an absolute path through the daemon: fails with `not_supported` if the daemon does not list files
|
|
8
|
+
* (a release before `listFiles`), so callers fall back to their own walk.
|
|
9
|
+
*/
|
|
10
|
+
export async function listFilesOver(connection, path, options, context) {
|
|
11
|
+
for (const key of ["maxEntries", "maxBytes", "timeoutMs"]) {
|
|
12
|
+
const value = options?.[key];
|
|
13
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
|
|
14
|
+
return err(new FileError("invalid", `${key} must be a non-negative safe integer`, path));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const aborted = abortResult(context.abortSignal, path);
|
|
18
|
+
if (aborted)
|
|
19
|
+
return aborted;
|
|
20
|
+
try {
|
|
21
|
+
if (!(await connection.info()).features?.includes("listFiles")) {
|
|
22
|
+
return err(new FileError("not_supported", "The pi-env daemon does not list files; update it", path));
|
|
23
|
+
}
|
|
24
|
+
const { json, payload } = await connection.request("listFiles",
|
|
25
|
+
// Fields left undefined are not sent; the daemon applies the defaults.
|
|
26
|
+
{
|
|
27
|
+
path,
|
|
28
|
+
hidden: options?.hidden,
|
|
29
|
+
gitignore: options?.gitignore,
|
|
30
|
+
exclude: options?.exclude,
|
|
31
|
+
maxEntries: options?.maxEntries,
|
|
32
|
+
maxBytes: options?.maxBytes,
|
|
33
|
+
timeoutMs: options?.timeoutMs,
|
|
34
|
+
}, context.abortSignal ? { signal: context.abortSignal } : {});
|
|
35
|
+
const afterAbort = abortResult(context.abortSignal, path);
|
|
36
|
+
if (afterAbort)
|
|
37
|
+
return afterAbort;
|
|
38
|
+
// Records of a kind byte, the UTF-8 path, and a NUL.
|
|
39
|
+
const entries = [];
|
|
40
|
+
const bytes = Buffer.from(payload.buffer, payload.byteOffset, payload.length);
|
|
41
|
+
for (let start = 0; start < bytes.length;) {
|
|
42
|
+
const end = bytes.indexOf(0, start);
|
|
43
|
+
if (end === -1)
|
|
44
|
+
break;
|
|
45
|
+
const kind = KINDS[bytes[start]];
|
|
46
|
+
if (kind !== undefined)
|
|
47
|
+
entries.push({ path: bytes.toString("utf8", start + 1, end), kind });
|
|
48
|
+
start = end + 1;
|
|
49
|
+
}
|
|
50
|
+
return ok({ entries, truncated: json.truncated === true });
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return err(toFileError(error, path));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `NodeExecutionEnv` with `listFiles` through a pi-env daemon on the same machine, so local and remote environments
|
|
58
|
+
* list files with the same code. Everything else is `NodeExecutionEnv`'s. The connection belongs to the caller, who
|
|
59
|
+
* closes it; several environments may share it.
|
|
60
|
+
*/
|
|
61
|
+
export class LocalExecutionEnv extends NodeExecutionEnv {
|
|
62
|
+
#connection;
|
|
63
|
+
constructor(options) {
|
|
64
|
+
const { connection, ...node } = options;
|
|
65
|
+
super(node);
|
|
66
|
+
this.#connection = connection;
|
|
67
|
+
}
|
|
68
|
+
async listFiles(path, options, context) {
|
|
69
|
+
const resolved = await this.absolutePath(path, context);
|
|
70
|
+
if (!resolved.ok)
|
|
71
|
+
return resolved;
|
|
72
|
+
return listFilesOver(this.#connection, resolved.value, options, context);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=list-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-files.js","sourceRoot":"","sources":["../src/list-files.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport {\n\ttype ExecutionEnv,\n\terr,\n\tFileError,\n\ttype FileKind,\n\ttype FileListEntry,\n\ttype FileListing,\n\ttype ListFilesOptions,\n\tok,\n\ttype Result,\n} from \"@earendil-works/pi-durable/env\";\nimport { NodeExecutionEnv } from \"@earendil-works/pi-durable/env/node\";\nimport type { Connection } from \"./connection.ts\";\nimport { abortResult, toFileError } from \"./errors.ts\";\n\n/** Kind bytes of `listFiles` records: `f`, `d`, `l`. */\nconst KINDS: Record<number, FileKind> = { 102: \"file\", 100: \"directory\", 108: \"symlink\" };\n\n/**\n * `listFiles` of an absolute path through the daemon: fails with `not_supported` if the daemon does not list files\n * (a release before `listFiles`), so callers fall back to their own walk.\n */\nexport async function listFilesOver(\n\tconnection: Connection,\n\tpath: string,\n\toptions: ListFilesOptions | undefined,\n\tcontext: Context,\n): Promise<Result<FileListing, FileError>> {\n\tfor (const key of [\"maxEntries\", \"maxBytes\", \"timeoutMs\"] as const) {\n\t\tconst value = options?.[key];\n\t\tif (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {\n\t\t\treturn err(new FileError(\"invalid\", `${key} must be a non-negative safe integer`, path));\n\t\t}\n\t}\n\tconst aborted = abortResult<FileListing>(context.abortSignal, path);\n\tif (aborted) return aborted;\n\ttry {\n\t\tif (!(await connection.info()).features?.includes(\"listFiles\")) {\n\t\t\treturn err(new FileError(\"not_supported\", \"The pi-env daemon does not list files; update it\", path));\n\t\t}\n\t\tconst { json, payload } = await connection.request(\n\t\t\t\"listFiles\",\n\t\t\t// Fields left undefined are not sent; the daemon applies the defaults.\n\t\t\t{\n\t\t\t\tpath,\n\t\t\t\thidden: options?.hidden,\n\t\t\t\tgitignore: options?.gitignore,\n\t\t\t\texclude: options?.exclude,\n\t\t\t\tmaxEntries: options?.maxEntries,\n\t\t\t\tmaxBytes: options?.maxBytes,\n\t\t\t\ttimeoutMs: options?.timeoutMs,\n\t\t\t},\n\t\t\tcontext.abortSignal ? { signal: context.abortSignal } : {},\n\t\t);\n\t\tconst afterAbort = abortResult<FileListing>(context.abortSignal, path);\n\t\tif (afterAbort) return afterAbort;\n\t\t// Records of a kind byte, the UTF-8 path, and a NUL.\n\t\tconst entries: FileListEntry[] = [];\n\t\tconst bytes = Buffer.from(payload.buffer, payload.byteOffset, payload.length);\n\t\tfor (let start = 0; start < bytes.length; ) {\n\t\t\tconst end = bytes.indexOf(0, start);\n\t\t\tif (end === -1) break;\n\t\t\tconst kind = KINDS[bytes[start]!];\n\t\t\tif (kind !== undefined) entries.push({ path: bytes.toString(\"utf8\", start + 1, end), kind });\n\t\t\tstart = end + 1;\n\t\t}\n\t\treturn ok({ entries, truncated: json.truncated === true });\n\t} catch (error) {\n\t\treturn err(toFileError(error, path));\n\t}\n}\n\nexport type LocalExecutionEnvOptions = ConstructorParameters<typeof NodeExecutionEnv>[0] & {\n\t/** A connection to this machine's daemon, `new Connection({ command: [localDaemon()] })`, for `listFiles`. */\n\tconnection: Connection;\n};\n\n/**\n * `NodeExecutionEnv` with `listFiles` through a pi-env daemon on the same machine, so local and remote environments\n * list files with the same code. Everything else is `NodeExecutionEnv`'s. The connection belongs to the caller, who\n * closes it; several environments may share it.\n */\nexport class LocalExecutionEnv extends NodeExecutionEnv implements ExecutionEnv {\n\treadonly #connection: Connection;\n\n\tconstructor(options: LocalExecutionEnvOptions) {\n\t\tconst { connection, ...node } = options;\n\t\tsuper(node);\n\t\tthis.#connection = connection;\n\t}\n\n\tasync listFiles(\n\t\tpath: string,\n\t\toptions: ListFilesOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<FileListing, FileError>> {\n\t\tconst resolved = await this.absolutePath(path, context);\n\t\tif (!resolved.ok) return resolved;\n\t\treturn listFilesOver(this.#connection, resolved.value, options, context);\n\t}\n}\n"]}
|
package/dist/remote-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type PlatformPath } from "node:path";
|
|
2
2
|
import type { Context } from "@earendil-works/chord";
|
|
3
|
-
import { type BinaryReader, type DirReader, type ExecutionEnv, ExecutionError, FileError, type FileInfo, type FileWatcher, type Result, type ShellExecOptions, type ShellExecResult, type TextLineReader, type WatchChange, type WatchTarget } from "@earendil-works/pi-durable/env";
|
|
3
|
+
import { type BinaryReader, type DirReader, type ExecutionEnv, ExecutionError, FileError, type FileInfo, type FileListing, type FileWatcher, type ListFilesOptions, type Result, type ShellExecOptions, type ShellExecResult, type TextLineReader, type WatchChange, type WatchTarget } from "@earendil-works/pi-durable/env";
|
|
4
4
|
import { type Connection, type RemoteInfo } from "./connection.ts";
|
|
5
5
|
import { type RemoteWatchOptions } from "./watch.ts";
|
|
6
6
|
export interface RemoteExecutionEnvOptions {
|
|
@@ -44,6 +44,8 @@ export declare class RemoteExecutionEnv implements ExecutionEnv {
|
|
|
44
44
|
renameFile(sourcePath: string, destinationPath: string, context: Context): Promise<Result<void, FileError>>;
|
|
45
45
|
fileInfo(path: string, context: Context): Promise<Result<FileInfo, FileError>>;
|
|
46
46
|
listDir(path: string, context: Context): Promise<Result<FileInfo[], FileError>>;
|
|
47
|
+
/** Through the daemon; `not_supported` with a daemon released before `listFiles`. */
|
|
48
|
+
listFiles(path: string, options: ListFilesOptions | undefined, context: Context): Promise<Result<FileListing, FileError>>;
|
|
47
49
|
openDirReader(path: string, context: Context): Promise<Result<DirReader, FileError>>;
|
|
48
50
|
canonicalPath(path: string, context: Context): Promise<Result<string, FileError>>;
|
|
49
51
|
exists(path: string, context: Context): Promise<Result<boolean, FileError>>;
|
package/dist/remote-env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-env.d.ts","sourceRoot":"","sources":["../src/remote-env.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,WAAW,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,cAAc,EAEd,SAAS,EACT,KAAK,QAAQ,EAEb,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"remote-env.d.ts","sourceRoot":"","sources":["../src/remote-env.ts"],"names":[],"mappings":"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"}
|
package/dist/remote-env.js
CHANGED
|
@@ -3,8 +3,9 @@ import { posix, win32 } from "node:path";
|
|
|
3
3
|
import { StringDecoder } from "node:string_decoder";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { ExecutionError, err, FileError, ok, StreamDecoder, } from "@earendil-works/pi-durable/env";
|
|
6
|
-
import { RemoteError } from "./connection.js";
|
|
6
|
+
import { nodePlatform, RemoteError } from "./connection.js";
|
|
7
7
|
import { abortResult, toFileError } from "./errors.js";
|
|
8
|
+
import { listFilesOver } from "./list-files.js";
|
|
8
9
|
import { RemoteWatcher } from "./watch.js";
|
|
9
10
|
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
10
11
|
const MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;
|
|
@@ -22,7 +23,7 @@ const READ_FILE_MAX = 2 ** 31 - 1;
|
|
|
22
23
|
const LINE_CHUNK = 64 * 1024;
|
|
23
24
|
/** Node's path rules of the remote system. */
|
|
24
25
|
export async function remotePath(connection) {
|
|
25
|
-
return (await connection.info()).
|
|
26
|
+
return nodePlatform(await connection.info()).platform === "win32" ? win32 : posix;
|
|
26
27
|
}
|
|
27
28
|
function toInfo(path, remote, paths) {
|
|
28
29
|
if (remote.kind === "other")
|
|
@@ -304,7 +305,7 @@ export class RemoteExecutionEnv {
|
|
|
304
305
|
/** Node's `resolvePath` on the remote system, with its home directory and working directories. */
|
|
305
306
|
async #resolve(path) {
|
|
306
307
|
const info = await this.connection.info();
|
|
307
|
-
const windows = info.
|
|
308
|
+
const windows = nodePlatform(info).platform === "win32";
|
|
308
309
|
const paths = windows ? win32 : posix;
|
|
309
310
|
let normalized = path;
|
|
310
311
|
if (normalized === "~") {
|
|
@@ -634,6 +635,17 @@ export class RemoteExecutionEnv {
|
|
|
634
635
|
return infos;
|
|
635
636
|
});
|
|
636
637
|
}
|
|
638
|
+
/** Through the daemon; `not_supported` with a daemon released before `listFiles`. */
|
|
639
|
+
async listFiles(path, options, context) {
|
|
640
|
+
let resolved;
|
|
641
|
+
try {
|
|
642
|
+
resolved = await this.#resolve(path);
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
return err(toFileError(error, path));
|
|
646
|
+
}
|
|
647
|
+
return listFilesOver(this.connection, resolved, options, context);
|
|
648
|
+
}
|
|
637
649
|
async #openDir(resolved) {
|
|
638
650
|
const reply = await this.connection.request("opendir", { path: resolved });
|
|
639
651
|
return { handle: new Handle(this, reply.json.handle, reply.session, resolved) };
|