better-dsh 0.3.0-g → 0.3.0-r

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.
@@ -13,40 +13,44 @@ Better Dash and Super Dash each provide the native `remote` tool and REPL route.
13
13
  | Stream a local script to fixed `bash -s` | `{target:"ssh:alias",script:"/absolute/script.sh"}` |
14
14
  | Persistent shell | `{target:"ssh:alias",mode:"pty",cmd:"cd /tmp; export CHECK=ready"}` |
15
15
  | Official background job | `{target:"ssh:alias",cmd:"long-command",run_in_background:true}` |
16
- | Binary file/tree copy | `{action:"copy",src:"/absolute/source",dst:"ssh:alias:/absolute/destination",verify:true,mkdir:true}` |
17
- | Preview native SSH delta sync | `{action:"rsync",src:"/absolute/source/",dst:"ssh:alias:/existing/destination/",dryRun:true,delete:false}` |
16
+ | Native CLI copy | `{action:"copy",src:"/absolute/source",dst:"ssh:alias:/absolute/destination",argv:["-p"]}` |
17
+ | Native rsync | `{action:"rsync",src:"/absolute/source/",dst:"ssh:alias:/existing/destination/",argv:["-a","--partial","--dry-run"]}` |
18
18
 
19
19
  `script` cannot combine with cmd/stdin/spawn/PTY. Direct local command execution is not provided; use native local shell tools. Background jobs are oneshot-only, require the official jobs service/controller, and are read/killed with `job_output`/`job_kill`; they have no default foreground timeout. An explicit timeout still applies. SSH keys/config stay on the origin; BYO identities are not persisted across new/cold Agents. PTY backlog recovery and live probes are not part of this release.
20
20
 
21
21
  Native/PTY output retains at most 4,000,000 UTF-16 units per stream; Hub oneshot retains 4 MiB bytes. Normal returned tails are smaller. Read `outputStats` units and dropped/returned counters rather than treating character counts as bytes. Discarded output is not recoverable after restart.
22
22
 
23
- ## File safety and prerequisites
23
+ ## File operations: native CLI semantics
24
24
 
25
- Copy supports local, SSH/byossh, Docker, Incus and authenticated Hub binary transports, not arbitrary BYO-PTY shells. CLI endpoints require the receiving-owner prerequisites below; Hub uses native Node. Copy fully stages on the origin before delivery, merges directories without deleting extras, and verifies SHA-256 over transferred entries only. `verify:false` skips the independent verification claim. Successful staging is removed; failure receipts identify retained diagnostic payload. Unsafe archive paths/types and symlink escapes (including preserved destination ancestors) are rejected.
25
+ `remote` addresses targets once (`ssh:<name>`, `byossh:<label>`, `docker:<name>`, `incus:<name>`, `hub:<machine>`, local paths) and hands the actual transfer to the native CLI. `argv` is an ordered array of native option tokens — one element = one argv token, appended verbatim before the parsed `src`/`dst`. There is no option whitelist, no shell parsing, and no forced defaults: no hidden `--dry-run`, no forced archive/timeout, no automatic verification. A dry run means passing `--dry-run` yourself; unknown or incompatible options fail in the native CLI and its exit code and stderr come back as-is. Success of a native command is never reported as independently verified — `verified:false` on native copy receipts means exactly that.
26
26
 
27
- Rsync is SSH-only (one endpoint may be local), with no copy fallback. Dry-run is the default; deletion is opt-in. The origin needs Bash/native rsync. SSH peers need Bash ≥4.1, Python ≥3.7 (macOS ≥3.10), Unix `fcntl.flock`, and a shared non-adjustable cross-process monotonic clock. Nothing auto-installs prerequisites. Two-remote source additionally needs native `find`. Its task-owned Python loopback proxy uses authenticated SSH stdio and an origin bridge into the destination `-L` tunnel; no source remote-forwarding permission, Linux `/proc`, pidfd or privileged sshd access is required. The full route must produce a real rsync greeting before transfer; no origin Python prerequisite is added. A normal two-remote operation uses 12 SSH startups (prerequisite/inspection, destination daemon+tunnel, source proxy owner/work and native sender); ambiguous admission can add authenticated cleanup reconnects, never transfer retries.
27
+ ### copy routes
28
28
 
29
- Two-remote relay uses an existing non-symlink destination directory; unsupported file/new/ambiguous paths and source symlinks are refused before transfer. Ordinary local↔SSH legs retain native path/link semantics. Native rsync stats/itemized changes report real delta behavior. Cancellation/disposal waits for owned cleanup. A startup channel lost before receipt may require restored authenticated connectivity to observe cleanup; recovery never restarts the transfer. Ordinary receiver progress is required, not a hard-real-time guarantee on a frozen host.
29
+ | Route | Command | Notes |
30
+ | --- | --- | --- |
31
+ | local↔SSH | `scp` | default SFTP; caller may pass `-O` explicitly |
32
+ | SSH↔SSH | `scp -3` (data through the origin) | both identities stay on the origin; differing BYO identities are separated by a temporary origin-only SSH config, removed afterwards. `scp -R` is refused before start: it would authenticate from the source host |
33
+ | local↔Docker | `docker cp` | |
34
+ | local↔Incus | `incus file push/pull` | |
35
+ | other cross-endpoint pairs (SSH↔Docker, Docker↔Incus, …) | two native commands with origin staging | each half takes its own options via `pullArgv`/`pushArgv` (mutually exclusive with `argv`); the receipt states both commands |
36
+ | Hub↔local↔local | existing staged archive lane | the only lane where `verify`/`mkdir` apply; it keeps SHA-256 verification and destination-parent creation. CLI-option fields are rejected on this lane |
30
37
 
31
- Hub addresses use `hub:<machine-id>` only when the optional provider is present; paired remote machines never mean self. Hub errors do not fall back to SSH. The runtime tool description carries the detailed current call and recovery contract.
38
+ The native CLIs do not create destination parents or verify results on their own; receipts report each command's exit, stdout/stderr and the sanitized argv. Two-segment routes stage in an origin scratch directory that is removed on normal exit (`cleanup:"released"`); an interrupted channel is reported as `cleanup:"unknown"`.
32
39
 
33
- ### Owned SSH command requirements
40
+ ### rsync routes
34
41
 
35
- Foreground/background SSH and BYO-SSH oneshots, including scripts and literal stdin, use the same portable receiving owner. Remote requirements are Bash 4.1+, Python 3.7+ (macOS 3.10+ for the shared cross-process monotonic epoch), Unix `fcntl.flock`, and `mktemp`, `mv`, `rm`, `sleep`. Ordinary commands do **not** require rsync. No origin Python or Linux/pidfd requirement is added; PTY/BYO-PTY is unchanged.
42
+ rsync supports SSH/byossh endpoints with at most one local endpoint. Local↔SSH runs `rsync` on the origin with the endpoint's SSH identity wired through `RSYNC_RSH`; an explicit `-e`/`--rsh` in `argv` replaces that wiring per rsync's own rules (the command must then carry its own port/key/config). Nothing else is injected — no archive, no timeout, no dry-run, no remote owner, no `--rsync-path`.
36
43
 
37
- Each oneshot uses three SSH startups: clock-only prerequisite, receiving control owner, and the work channel with untouched stdin/stdout/stderr. Native connection identity, port, key and ProxyJump stay canonical; agent forwarding and reusable multiplexing are disabled for these owned connections. Cleanup waits for the receiving helper and work process group, not merely local SSH exit. The existing 8-second receiving lease, counted 2-second renewals and 3.5-second ordinary-scheduling cleanup allowance also cover the gate. Ambiguous lost startup needs an authenticated same-identity observer; restore connectivity if cleanup observation remains pending. This assumes a progressing receiver, not hard real-time scheduling.
44
+ SSH↔SSH prefers the native forwarding route: the destination runs a temporary loopback-only rsync daemon, `ssh -L` brings it to an origin port, `ssh -R` carries it onto the source's loopback, and the source's own native rsync client performs one protocol conversation through the chain. The origin only forwards rsync protocol bytes; the destination's existing file data feeds real delta matching (`--stats` shows Matched data dominating for a small change on a large file). If an endpoint refuses TCP forwarding (`AllowTcpForwarding no`), a standard-stream route would be attempted; on the current OpenSSH/rsync combination (10.2 / 3.4.1) that route reports itself unavailable — the error says so and nothing is copied. rsync never falls back to a full copy or to Python.
38
45
 
39
- After cleanup proof the separate work stream gets up to 2500ms for final drain. If unavailable, the result explicitly diagnoses potentially incomplete output; counters only count received data. A successful normal completion drains the full work stream. No new registry, availability poller or job completion notifier is introduced.
46
+ Tool-owned daemons/forwards/SSH sessions are torn down on normal exit and observable cancellation; a lost channel is reported with `cleanup:"unknown"` rather than a false success claim.
40
47
 
41
- ### Container oneshot ownership
48
+ ### Prerequisites
42
49
 
43
- Docker/Incus oneshots require the same Bash/Python/fcntl/shared-monotonic/core-utility receiver prerequisites and three native exec startups. Linux containers also require Python stdlib ctypes and permitted unprivileged PR_SET_CHILD_SUBREAPER; the helper reaps only its own/adopted descendants before settlement, including ordinary non-reaping PID1 containers. Nothing installs or changes images, privileges or configuration. PTY is unchanged.
50
+ File operations require the corresponding CLI on the origin (`scp`, `rsync`, `docker`, `incus`) and on the SSH/container endpoints that execute it (`rsync` for rsync routes; none beyond the container's own CLI for docker/incus copy). No remote Python, Node or worker is deployed for file transfers. The dual-SSH forwarding route requires `AllowTcpForwarding` on both endpoints. BYO-PTY is not a file endpoint.
44
51
 
52
+ ## One-shot command requirements
45
53
 
46
- ### CLI binary receiving ownership (consumer correction, Ruling26)
54
+ One-shot SSH/byossh commands (foreground, background, script and literal stdin) run through ONE native SSH exec: the command executes under bash with a small POSIX wrapper whose TERM/HUP trap kills the command's whole process group, so backgrounded children do not outlive the session teardown. Remote prerequisites: bash (POSIX) — **no Python, no owner/lease protocol, no flock/monotonic clock, no extra sessions**. On normal completion the wrapper's EXIT trap reaps the command's whole process group (backgrounded children included — they neither outlive the call nor hold the channel open), and the exit code passes through verbatim. On timeout/cancellation the local transport dies at once and a dedicated cleanup session reads the pidfile the wrapper left and kills the group (TERM→KILL); sshd delivers no signal to a no-PTY session, so this explicit cleanup is the remote kill path.
47
55
 
48
- Actual N native SSH copy cancellation settled while the remote import process and its unnamed staging file remained live under a finite work-channel EOF hold. CLI export/import/digest now reuse the canonical portable receiving owner, with raw binary work streams and the same counted lease, admission gate and authenticated recovery. Cancellation/failure waits for receiving helper/group/temp cleanup before local transport teardown/settlement. Normal success must completely drain the binary stream; the oneshot text lane's 2500ms incomplete-output rule does not apply. Archive validation, selected manifest digests, merge/no-delete behavior and canonical SSH identity/ProxyJump are unchanged. Local and Hub file lanes are unchanged.
49
-
50
- This intentionally replaces the former Python-only minimum for CLI peers: Bash4.1+, Python3.7+ (macOS3.10+), Unix fcntl.flock/shared non-adjustable cross-process monotonic clock, mktemp/mv/rm/sleep; Linux containers also need stdlib ctypes and permitted unprivileged PR_SET_CHILD_SUBREAPER. No rsync or origin Python requirement, auto-install, second registry, fallback, peer polling or increased lease. Minimal Python-only peers no longer support this CLI lane; use a supported peer or authenticated Hub.
51
-
52
- Each remote export/import/digest costs three native transport starts: clock, independent owner/control, literal binary work. Thus verified local→CLI or CLI→local costs six; CLI→CLI twelve. verify:false skips digest operations, reducing counts to three/three/six. Local/Hub operations do not contribute CLI starts. Fault recovery may add same-identity authenticated cleanup starts and may remain pending until genuine cleanup observation is possible. Artifact O must re-prove installed cancellation and complete binary integrity; N failures remain historical evidence.
56
+ Docker/Incus one-shots use the same pure-CLI lane (`docker exec` / `incus exec` with the same wrapper); container prerequisites are the container's own bash. PTY paths are unchanged (`ssh -tt bash`, docker via script(1), incus interactive) and never required Python.
@@ -64,7 +64,7 @@ const zh = {
64
64
  };
65
65
 
66
66
  //#endregion
67
- //#region \0dsh-css:/home/u1/workspaces/superd/better-dsh/src/failover/client/FailoverRow.module.css.mjs
67
+ //#region \0dsh-css:/home/u1/workspaces/superd/.worktrees/remote-native-shortcut/better-dsh/src/failover/client/FailoverRow.module.css.mjs
68
68
  const css$1 = "\n._440e1d7b_row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n}\n\n._a4f2ceec_rowText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._9865b509_title {\n font-weight: 600;\n font-size: 14px;\n}\n\n\n._3cfec826_slots {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 6px;\n}\n\n._11c2662d_select {\n width: 280px;\n max-width: 100%;\n min-width: 0;\n}\n\n._b7c358f9_alert {\n color: var(--dsw-alias-danger, #c0392b);\n font-size: 12px;\n}\n";
69
69
  const tagId$1 = "better-dsh/FailoverRow.module.css";
70
70
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
@@ -463,7 +463,7 @@ const zh$1 = {
463
463
  };
464
464
 
465
465
  //#endregion
466
- //#region \0dsh-css:/home/u1/workspaces/superd/better-dsh/src/compaction/client/CompactionRow.module.css.mjs
466
+ //#region \0dsh-css:/home/u1/workspaces/superd/.worktrees/remote-native-shortcut/better-dsh/src/compaction/client/CompactionRow.module.css.mjs
467
467
  const css = "\n\n\n._13e3747c_thresholdRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n._8ad3ff3f_thresholdText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-right: 48px;\n}\n\n._9ffb627e_thresholdTitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n._01bea389_thresholdDesc {\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._7f9578de_thresholdError {\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n color: var(--dsw-alias-danger, #c0392b);\n}\n\n\n._cddd78cd_thresholdControl {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n._6058fea9_thresholdStepper {\n position: relative; \n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 72px;\n height: 36px;\n border-radius: 18px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._39d30469_thresholdValue {\n min-width: 18px;\n text-align: center;\n font-size: 14px;\n line-height: 22px;\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-primary);\n}\n\n._68bd35c6_thresholdUnit {\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._904da366_thresholdArrows {\n position: absolute;\n right: 8px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n opacity: 0;\n}\n\n._6058fea9_thresholdStepper:hover ._904da366_thresholdArrows,\n._6058fea9_thresholdStepper:focus-within ._904da366_thresholdArrows {\n opacity: 1;\n}\n\n\n._ae9106b1_thresholdArrow {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 17px;\n height: 12px;\n padding: 0;\n border: none;\n border-radius: 3px;\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 75%, transparent);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._ae9106b1_thresholdArrow:hover:not(:disabled) {\n background: var(--dsw-alias-bg-layer-1);\n}\n\n._ae9106b1_thresholdArrow:disabled {\n color: var(--dsw-alias-label-caption);\n cursor: default;\n}\n";
468
468
  const tagId = "better-dsh/CompactionRow.module.css";
469
469
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
@@ -6,7 +6,7 @@ import { LocalFileSystem } from "@deepseek-ai/dsh-fs-local";
6
6
  import { FsError } from "@deepseek-ai/dsh-fs";
7
7
  import { writableRoots } from "@deepseek-ai/dsh-sandbox";
8
8
 
9
- //#region ../upstream/deepseek-harness/packages/fs/fs-sandbox/lib/index.js
9
+ //#region ../../../upstream/deepseek-harness/packages/fs/fs-sandbox/lib/index.js
10
10
  /**
11
11
  * Path-containment mechanics for the filesystem sandbox. Canonical spellings
12
12
  * take the fast lexical path; filesystem identity supplies the conservative
@@ -1,5 +1,5 @@
1
1
  import { t as Schema } from "../index-DTDXUe6m.js";
2
- import { r as MobileConfig } from "../web-trust-RwK0P74B.js";
2
+ import { r as MobileConfig } from "../web-trust-CwZrTBcl.js";
3
3
  import { Context } from "@deepseek-ai/cordis";
4
4
 
5
5
  //#region src/mobile/plugin.d.ts
@@ -6,7 +6,6 @@ import { StandardSchemaV1 } from "../../third_party/schemastery/node_modules/@st
6
6
  import { JobId, JobRegistry } from "@deepseek-ai/dsh-jobs";
7
7
 
8
8
  //#region src/remote/ssh-targets.d.ts
9
- /** Origin-owned identity reference, never key contents or a persistent credential store. */
10
9
  interface SshConnection {
11
10
  readonly host: string;
12
11
  readonly user?: string;
@@ -51,71 +50,91 @@ interface TransferReceipt {
51
50
  error?: string;
52
51
  }
53
52
  //#endregion
54
- //#region src/remote/ssh-relay.d.ts
55
- type SshBuilder = typeof sshArgv;
56
- interface CommandResult {
53
+ //#region src/remote/target.d.ts
54
+ /** 解析后的传输计划:三种 CLI 传输之一。 */
55
+ type TargetPlan = {
56
+ kind: 'ssh';
57
+ host: string;
58
+ connection?: SshConnection;
59
+ } | {
60
+ kind: 'docker';
61
+ container: string;
62
+ } | {
63
+ kind: 'incus';
64
+ container: string;
65
+ };
66
+ //#endregion
67
+ //#region src/remote/native-copy.d.ts
68
+ interface NativeCopyReceipt {
69
+ action: 'copy';
70
+ src: string;
71
+ dst: string;
72
+ /** Native route actually used, stated as a fact (two-command routes never masquerade as one CLI). */
73
+ route: string;
74
+ /** Native argv actually executed per command, one token per element, no shell joining. */
75
+ argv: string[][];
76
+ exits: (number | null)[];
77
+ stdout: string[];
78
+ stderr: string[];
79
+ outputTruncated: boolean;
80
+ /** Native CLI success only — no independent digest was computed for this path. */
81
+ verified: false;
82
+ /** Tool-owned staging/config release: released on normal/observed exit, unknown when a channel was lost. */
83
+ cleanup: 'released' | 'unknown' | 'none-required';
84
+ }
85
+ interface RunResult {
57
86
  exit: number | null;
58
87
  stdout: string;
59
88
  stderr: string;
60
89
  outputTruncated: boolean;
61
90
  }
62
- interface RelayOptions {
63
- ssh?: SshBuilder;
91
+ //#endregion
92
+ //#region src/remote/ssh-rsync-native-relay.d.ts
93
+ interface RelayRsyncOptions {
94
+ /** Test seam: wraps every spawned SSH/daemon argv (fixture isolation). */
95
+ wrapArgv?: (argv: string[]) => string[];
96
+ /** Test seam: fixed daemon port instead of ephemeral allocation. */
64
97
  ports?: {
65
- destination: number;
66
- origin: number;
67
- source: number;
98
+ daemon: number;
68
99
  };
100
+ /** Test seam: skip route A (simulate denied forwarding). */
101
+ forceStdioRoute?: boolean;
69
102
  }
70
- interface Relay {
71
- url: string;
72
- /** Runs sender under a lease-owned remote supervisor, never downstream SSH. */
73
- run(argv: string[]): Promise<CommandResult>;
74
- }
75
- declare function withSshRelay<T>(source: SshConnection, destination: SshConnection, targetPath: string, signal: AbortSignal, run: (relay: Relay) => Promise<T>, options?: RelayOptions): Promise<T>;
76
103
  //#endregion
77
104
  //#region src/remote/rsync.d.ts
78
105
  interface RsyncRequest {
79
106
  src: string;
80
107
  dst: string;
81
- dryRun?: boolean;
82
- delete?: boolean;
83
- excludes?: readonly string[];
84
- preserve?: boolean;
108
+ argv?: string[];
85
109
  }
86
110
  interface RsyncEndpoint {
87
111
  path: string;
88
112
  connection?: SshConnection;
89
113
  }
90
- interface RsyncReceipt extends CommandResult {
114
+ interface RsyncReceipt extends RunResult {
91
115
  action: 'rsync';
92
116
  src: string;
93
117
  dst: string;
94
- dryRun: boolean;
95
- delete: boolean;
96
- preserve: boolean;
97
- itemizedChanges: string[];
118
+ argv: string[];
119
+ command: string;
120
+ /** Populated only when the CLI actually printed --stats output. */
121
+ itemizedChanges?: string[];
98
122
  literalBytes?: number;
99
123
  matchedBytes?: number;
100
124
  sentBytes?: number;
101
125
  receivedBytes?: number;
102
126
  transferredBytes?: number;
127
+ /** Two-SSH route actually used, when applicable. */
128
+ route?: 'local-ssh' | 'ssh-ssh-forward' | 'ssh-ssh-stdio';
103
129
  }
104
- declare function runRsync(request: RsyncRequest, source: RsyncEndpoint, destination: RsyncEndpoint, signal: AbortSignal, options?: RelayOptions): Promise<RsyncReceipt>;
105
- //#endregion
106
- //#region src/remote/target.d.ts
107
- /** 解析后的传输计划:三种 CLI 传输之一。 */
108
- type TargetPlan = {
109
- kind: 'ssh';
110
- host: string;
111
- connection?: SshConnection;
112
- } | {
113
- kind: 'docker';
114
- container: string;
115
- } | {
116
- kind: 'incus';
117
- container: string;
118
- };
130
+ /**
131
+ * Native rsync with ordered caller options. Local↔SSH runs rsync directly with
132
+ * the endpoint's SSH identity wired as RSYNC_RSH; explicit -e/--rsh in argv
133
+ * overrides that wiring per rsync's own rules (the receipt shows the argv).
134
+ * SSH↔SSH delegates to the native relay routes (forwarding first, stdio second).
135
+ * No option is injected: no archive, no timeout, no dry-run, no remote owner.
136
+ */
137
+ declare function runRsync(request: RsyncRequest, source: RsyncEndpoint, destination: RsyncEndpoint, signal: AbortSignal, options?: RelayRsyncOptions): Promise<RsyncReceipt>;
119
138
  //#endregion
120
139
  //#region src/remote/process.d.ts
121
140
  declare const PROCESS_CLEANUP_BOUND_MS: number;
@@ -126,9 +145,11 @@ interface ProcessOptions {
126
145
  /** Hub retains raw bytes; native transports retain decoded UTF16 units. */
127
146
  unit?: 'bytes' | 'utf16';
128
147
  onOutput?: () => void;
148
+ /** Overlay on process.env (tool-built values only, e.g. RSYNC_RSH). */
149
+ env?: NodeJS.ProcessEnv;
129
150
  }
130
151
  /** Real detached process. Completion includes group cleanup, even after its leader closes. */
131
- declare function startBinaryProcess(argv: string[], options?: Pick<ProcessOptions, 'cwd' | 'timeoutSec'>): {
152
+ declare function startBinaryProcess(argv: string[], options?: Pick<ProcessOptions, 'cwd' | 'timeoutSec' | 'env'>): {
132
153
  stdout: Readable;
133
154
  stderr: Readable;
134
155
  stdin: Writable;
@@ -200,13 +221,16 @@ interface RemoteAuditRecord {
200
221
  }
201
222
  interface RemoteDriverOptions {
202
223
  /** Fixture transport seam; normal consumers use the canonical native SSH builder. */
203
- rsyncSshArgv?: SshBuilder;
224
+ /** Fixture transport seam for native copy CLIs (scp/docker/incus argv isolation). */
225
+ nativeArgvFor?: (argv: string[]) => string[];
204
226
  getHub?: () => HubRemoteTransport | undefined;
205
227
  execTimeoutSec?: number;
206
228
  idleTtlSec?: number;
207
229
  maxOutputChars?: number;
208
230
  onAudit?: (r: RemoteAuditRecord) => void;
209
- oneshotArgvFor?: (plan: TargetPlan, cmd: string) => string[];
231
+ oneshotArgvFor?: (plan: TargetPlan, cmd: string, options: {
232
+ cleanupTag?: string;
233
+ }) => string[];
210
234
  ptyArgvFor?: (plan: TargetPlan) => string[];
211
235
  spawnArgvFor?: (spawnCommand: string) => string[];
212
236
  probeRunner?: ProbeOptions['runner'];
@@ -231,9 +255,17 @@ declare class RemoteDriver {
231
255
  call(params: RemoteCallParams, callCtx?: {
232
256
  sessionKey?: string;
233
257
  }): Promise<RemoteCallResult>;
234
- copy(request: CopyRequest, signal: AbortSignal, callCtx?: {
258
+ /** Native-first copy routing (spec: remote-native-shortcut): CLI peers run
259
+ * their native CLI verbatim; only Hub↔local↔local stays on the staged tree
260
+ * lane with verify/mkdir. Mixed pairs (Hub↔CLI peer) are refused: Hub has no
261
+ * native CLI and the tree lane has no CLI options. */
262
+ copy(request: CopyRequest & {
263
+ argv?: unknown;
264
+ pullArgv?: unknown;
265
+ pushArgv?: unknown;
266
+ }, signal: AbortSignal, callCtx?: {
235
267
  sessionKey?: string;
236
- }): Promise<TransferReceipt>;
268
+ }): Promise<TransferReceipt | NativeCopyReceipt>;
237
269
  rsync(request: RsyncRequest, signal: AbortSignal, callCtx?: {
238
270
  sessionKey?: string;
239
271
  }): Promise<RsyncReceipt>;
@@ -443,4 +475,4 @@ declare const _default: {
443
475
  apply: (ctx: Context, config: RemoteRowConfig) => (() => void);
444
476
  };
445
477
  //#endregion
446
- export { type BinaryFileEndpoint, ByteTailRing, Config, FILE_FRAME_BYTES, FILE_MAX_ENTRIES, FILE_MAX_METADATA, type HubRemoteTransport, type HubTarget, type OutputStats, PROCESS_CLEANUP_BOUND_MS, type PreparedRemoteInput, RemoteConfigValidator, type RemoteInput, type RemoteProcess, RemoteRowConfig, type RsyncEndpoint, type RsyncReceipt, type RsyncRequest, type SshConnection, SshTargets, type StreamOutputStats, Utf16TailRing, apply, checkAbsolute, checkSelection, _default as default, inject, localFileEndpoint, name, readUtf8Tail, runFileWorker, runRsync, sshArgv, startBinaryProcess, startOneShot, startRemoteJob, withSshRelay };
478
+ export { type BinaryFileEndpoint, ByteTailRing, Config, FILE_FRAME_BYTES, FILE_MAX_ENTRIES, FILE_MAX_METADATA, type HubRemoteTransport, type HubTarget, type OutputStats, PROCESS_CLEANUP_BOUND_MS, type PreparedRemoteInput, RemoteConfigValidator, type RemoteInput, type RemoteProcess, RemoteRowConfig, type RsyncEndpoint, type RsyncReceipt, type RsyncRequest, type SshConnection, SshTargets, type StreamOutputStats, Utf16TailRing, apply, checkAbsolute, checkSelection, _default as default, inject, localFileEndpoint, name, readUtf8Tail, runFileWorker, runRsync, sshArgv, startBinaryProcess, startOneShot, startRemoteJob };