super-dsh 0.3.1-a → 0.3.1-d

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/remote/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context } from "@deepseek-ai/cordis";
2
- import { chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises";
3
- import { constants, createReadStream, createWriteStream, mkdirSync, statSync } from "node:fs";
2
+ import { chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
3
+ import { constants, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
4
4
  import { pipeline } from "node:stream/promises";
5
5
  import { dirname, isAbsolute, join, posix, resolve } from "node:path";
6
6
  import { Readable } from "node:stream";
@@ -8,6 +8,7 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  import { homedir, tmpdir } from "node:os";
9
9
  import { spawn } from "node:child_process";
10
10
  import { createConnection, createServer } from "node:net";
11
+ import { fileURLToPath } from "node:url";
11
12
  import { defineTool } from "@deepseek-ai/dsh-tools";
12
13
 
13
14
  //#region third_party/schemastery/node_modules/@deepseek-ai/cosmokit/lib/index.js
@@ -1209,17 +1210,18 @@ async function copyTree(request, source, destination, signal, scratchRoot = join
1209
1210
  signal.throwIfAborted();
1210
1211
  if (request.argv !== void 0 || request.pullArgv !== void 0 || request.pushArgv !== void 0) throw new Error("copy: argv/pullArgv/pushArgv are native CLI options; the Hub/local staged lane does not accept CLI options");
1211
1212
  await mkdir(scratchRoot, { recursive: true });
1212
- signal.throwIfAborted();
1213
- const scratch = await mkdtemp(join(scratchRoot, "remote-copy-")), payload = join(scratch, "payload.bin");
1214
1213
  const receipt = {
1215
1214
  action: "copy",
1215
+ route: "hub-staged",
1216
1216
  src: request.src,
1217
1217
  dst: request.dst,
1218
- verified: false,
1218
+ ok: false,
1219
+ verification: "not-applicable",
1219
1220
  bytes: 0,
1220
1221
  entries: 0,
1221
1222
  scope: "transferred entries"
1222
1223
  };
1224
+ const scratch = await mkdtemp(join(scratchRoot, "remote-copy-")), payload = join(scratch, "payload.bin");
1223
1225
  try {
1224
1226
  await pipeline(await source.endpoint.exportTree(source.path, signal), createWriteStream(payload, {
1225
1227
  flags: "wx",
@@ -1243,24 +1245,79 @@ async function copyTree(request, source, destination, signal, scratchRoot = join
1243
1245
  const dest = await destination.endpoint.digest(destination.path, signal, selection);
1244
1246
  receipt.destinationDigest = dest.sha256;
1245
1247
  if (dest.sha256 !== receipt.sourceDigest) throw new Error("destination digest mismatch");
1246
- receipt.verified = true;
1248
+ receipt.verification = "sha256";
1247
1249
  }
1248
1250
  signal.throwIfAborted();
1249
1251
  await rm(scratch, {
1250
1252
  recursive: true,
1251
1253
  force: true
1252
1254
  });
1255
+ receipt.ok = true;
1253
1256
  return receipt;
1254
1257
  } catch (error) {
1258
+ const message = error instanceof Error ? error.message : String(error);
1259
+ const reason = signal.aborted ? "aborted" : /mismatch/i.test(message) ? "digest-mismatch" : "staging-error";
1255
1260
  throw new CopyError({
1256
1261
  ...receipt,
1257
- verified: false,
1262
+ ok: false,
1263
+ reason,
1258
1264
  scratch,
1259
- error: error instanceof Error ? error.message : String(error)
1265
+ error: message
1260
1266
  });
1261
1267
  }
1262
1268
  }
1263
1269
 
1270
+ //#endregion
1271
+ //#region src/remote/ssh-argv.ts
1272
+ /**
1273
+ * ONE identity-assembly policy for every SSH-speaking lane (user close-out
1274
+ * ruling 2026-10-17: "four builders necessarily mean four identity
1275
+ * semantics" — the structural root of F1).
1276
+ *
1277
+ * Assembly: explicit connection fields (byossh registrations; caller options
1278
+ * may override later by the CLI's own last-option-wins) in ONE order —
1279
+ * user, port, key, proxyJump — spoken in the target CLI's dialect.
1280
+ *
1281
+ * Resolution: config-side identity (aliases, config-only IdentityFile paths)
1282
+ * is read from the ORIGIN's own ssh config natively by every lane. The single
1283
+ * exception is the scp -3 temp config (ssh -F sees no user config), where
1284
+ * tempSshConfig inlines the `ssh -G`-resolved fields — paths only, never key
1285
+ * material (F1, 2026-10-14). That policy lives beside this builder in
1286
+ * native-copy.ts and must stay the only such inline site.
1287
+ *
1288
+ * Former duplicate builders now reduced here: sshArgv (execution/probes),
1289
+ * identityArgv (scp routes), rsyncRsh (RSYNC_RSH wiring), relaySsh
1290
+ * (dual-SSH relay). Behavior is byte-identical to each former site.
1291
+ */
1292
+ /** Identity options ONLY for one connection, in the target CLI's dialect:
1293
+ * user, port, key, proxyJump — conditional, one order, NO base flags (each
1294
+ * lane's dialect base — BatchMode, -T, hardening — stays at its call site).
1295
+ * scp carries the user inside its user@host operand, so the scp dialect
1296
+ * never emits -l; its port flag is -P. */
1297
+ function sshIdentityArgv(connection, dialect) {
1298
+ const args = [];
1299
+ if (dialect === "ssh" && connection.user !== void 0) args.push("-l", connection.user);
1300
+ if (connection.port !== void 0) args.push(dialect === "scp" ? "-P" : "-p", String(connection.port));
1301
+ if (connection.key !== void 0) args.push("-i", connection.key);
1302
+ if (connection.proxyJump !== void 0) args.push("-J", connection.proxyJump);
1303
+ return args;
1304
+ }
1305
+ /** Full ssh invocation around the shared identity core. base and extra are
1306
+ * trusted internal argv (dialect flags incl. BatchMode, forwarding options)
1307
+ * — never a model-provided shell string. */
1308
+ function sshCommandArgv(connection, base, extra, command) {
1309
+ const args = [
1310
+ "ssh",
1311
+ ...base,
1312
+ ...sshIdentityArgv(connection, "ssh"),
1313
+ ...extra,
1314
+ "--",
1315
+ connection.host
1316
+ ];
1317
+ if (command !== void 0) args.push(command);
1318
+ return args;
1319
+ }
1320
+
1264
1321
  //#endregion
1265
1322
  //#region src/remote/output-ring.ts
1266
1323
  const continuation = (byte) => (byte & 192) === 128;
@@ -1566,15 +1623,17 @@ function scpSpec(connection, path) {
1566
1623
  const host = connection.host.includes(":") ? `[${connection.host}]` : connection.host;
1567
1624
  return `${connection.user !== void 0 ? `${connection.user}@${host}` : host}:${path}`;
1568
1625
  }
1569
- /** Connection identity as scp options (port, key), placed BEFORE caller argv so
1570
- * an explicit caller -F/-i/-P/-J overrides the registered identity by scp's own
1571
- * precedence (last option wins). */
1626
+ /** Connection identity as scp options, placed BEFORE caller argv so an
1627
+ * explicit caller -F/-i/-P/-J overrides the registered identity by scp's own
1628
+ * precedence (last option wins). Assembly itself lives in ssh-argv.ts
1629
+ * (the ONE identity policy — 2026-10-17 ruling); only the scp dialect
1630
+ * placement rule is local. */
1572
1631
  function identityArgv(connection) {
1573
- const options = ["-o", "BatchMode=yes"];
1574
- if (connection.port !== void 0) options.push("-P", String(connection.port));
1575
- if (connection.key !== void 0) options.push("-i", connection.key);
1576
- if (connection.proxyJump !== void 0) options.push("-J", connection.proxyJump);
1577
- return options;
1632
+ return [
1633
+ "-o",
1634
+ "BatchMode=yes",
1635
+ ...sshIdentityArgv(connection, "scp")
1636
+ ];
1578
1637
  }
1579
1638
  function sshEndpoint(plan) {
1580
1639
  if (plan.kind !== "ssh") throw new Error("remote: native copy endpoint is not an SSH target");
@@ -1613,24 +1672,30 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1613
1672
  stdout: [],
1614
1673
  stderr: [],
1615
1674
  outputTruncated: false,
1616
- verified: false,
1675
+ verification: "native-exit-only",
1676
+ ok: true,
1617
1677
  cleanup: "none-required"
1618
1678
  };
1619
- const finish = (route, argvs, results) => {
1679
+ const finish = (route, argvs, results, internal = []) => {
1620
1680
  receipt.route = route;
1621
- receipt.argv = argvs;
1681
+ receipt.argv = argvs.map((argv) => argv.map((token) => internal.find(([secret]) => secret === token)?.[1] ?? token));
1622
1682
  receipt.exits = results.map((result) => result.exit);
1623
1683
  receipt.stdout = results.map((result) => result.stdout);
1624
1684
  receipt.stderr = results.map((result) => result.stderr);
1625
1685
  receipt.outputTruncated = results.some((result) => result.outputTruncated);
1686
+ receipt.ok = !signal.aborted && receipt.exits.every((exit) => exit === 0);
1687
+ if (!receipt.ok) receipt.reason = signal.aborted ? "aborted" : "non-zero-exit";
1626
1688
  return receipt;
1627
1689
  };
1690
+ if (request.mkdir !== false) await ensureDestinationDir(endpoints.dst, signal, options.execArgvFor, wrap, receipt);
1691
+ else receipt.created = "n-a";
1628
1692
  if (s === "local" && t === "ssh") {
1629
1693
  rejectRemoteToRemoteScp(oneShot);
1630
1694
  const connection = sshEndpoint(endpoints.dst.plan).connection;
1631
1695
  const argv = [
1632
1696
  "scp",
1633
1697
  ...identityArgv(connection),
1698
+ ...await recursiveFlag(endpoints.src.path, oneShot, "-r"),
1634
1699
  ...oneShot,
1635
1700
  endpoints.src.path,
1636
1701
  scpSpec(connection, endpoints.dst.path)
@@ -1663,7 +1728,7 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1663
1728
  dstSpec = scpSpec(destination, endpoints.dst.path);
1664
1729
  extra = identityArgv(source);
1665
1730
  } else {
1666
- config = await tempSshConfig(source, destination);
1731
+ config = await tempSshConfig(source, destination, runNative);
1667
1732
  extra = ["-F", config.path];
1668
1733
  srcSpec = `superd-scp3-source:${endpoints.src.path}`;
1669
1734
  dstSpec = `superd-scp3-destination:${endpoints.dst.path}`;
@@ -1676,7 +1741,7 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1676
1741
  srcSpec,
1677
1742
  dstSpec
1678
1743
  ];
1679
- return finish("scp -3 (data through origin)", [argv], [await runNative(wrap(argv), signal)]);
1744
+ return finish("scp-3", [argv], [await runNative(wrap(argv), signal)], config !== void 0 ? [[config.path, "<temp-ssh-config>"]] : []);
1680
1745
  } catch (error) {
1681
1746
  if (signal.aborted) unknown = true;
1682
1747
  throw error;
@@ -1695,7 +1760,9 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1695
1760
  endpoints.src.path,
1696
1761
  `${containerOf(endpoints.dst.plan)}:${endpoints.dst.path}`
1697
1762
  ];
1698
- return finish("docker cp", [argv], [await runNative(wrap(argv), signal)]);
1763
+ const result = await runNative(wrap(argv), signal);
1764
+ withSymlinkHint(result);
1765
+ return finish("docker-cp", [argv], [result]);
1699
1766
  }
1700
1767
  if (s === "docker" && t === "local") {
1701
1768
  const argv = [
@@ -1705,18 +1772,21 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1705
1772
  `${containerOf(endpoints.src.plan)}:${endpoints.src.path}`,
1706
1773
  endpoints.dst.path
1707
1774
  ];
1708
- return finish("docker cp", [argv], [await runNative(wrap(argv), signal)]);
1775
+ const result = await runNative(wrap(argv), signal);
1776
+ withSymlinkHint(result);
1777
+ return finish("docker-cp", [argv], [result]);
1709
1778
  }
1710
1779
  if (s === "local" && t === "incus") {
1711
1780
  const argv = [
1712
1781
  "incus",
1713
1782
  "file",
1714
1783
  "push",
1784
+ ...await recursiveFlag(endpoints.src.path, oneShot, "-r"),
1715
1785
  ...oneShot,
1716
1786
  endpoints.src.path,
1717
1787
  `${containerOf(endpoints.dst.plan)}${endpoints.dst.path}`
1718
1788
  ];
1719
- return finish("incus file push", [argv], [await runNative(wrap(argv), signal)]);
1789
+ return finish("incus-file-push", [argv], [await runNative(wrap(argv), signal)]);
1720
1790
  }
1721
1791
  if (s === "incus" && t === "local") {
1722
1792
  const argv = [
@@ -1727,7 +1797,7 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1727
1797
  `${containerOf(endpoints.src.plan)}${endpoints.src.path}`,
1728
1798
  endpoints.dst.path
1729
1799
  ];
1730
- return finish("incus file pull", [argv], [await runNative(wrap(argv), signal)]);
1800
+ return finish("incus-file-pull", [argv], [await runNative(wrap(argv), signal)]);
1731
1801
  }
1732
1802
  if (s === "local" || t === "local") throw new Error("remote: this endpoint pair is a single native command route; pullArgv/pushArgv apply to cross-endpoint pairs only");
1733
1803
  const pullOptions = request.pullArgv !== void 0 ? pull : oneShot;
@@ -1740,9 +1810,12 @@ async function nativeCopy(request, endpoints, signal, options = {}) {
1740
1810
  const pullArgv = segmentArgv(endpoints.src.plan, endpoints.src.path, staged, pullOptions, "pull");
1741
1811
  const pushArgv = segmentArgv(endpoints.dst.plan, endpoints.dst.path, staged, pushOptions, "push");
1742
1812
  const first = await runNative(wrap(pullArgv), signal);
1743
- if (first.exit !== 0) return finish("two native commands with origin staging (push not started: pull failed)", [pullArgv], [first]);
1813
+ if (first.exit !== 0) {
1814
+ receipt.note = "push not started: pull failed";
1815
+ return finish("staged-pull-push", [pullArgv], [first], [[staged, "<staging>"]]);
1816
+ }
1744
1817
  const second = await runNative(wrap(pushArgv), signal);
1745
- return finish("two native commands with origin staging", [pullArgv, pushArgv], [first, second]);
1818
+ return finish("staged-pull-push", [pullArgv, pushArgv], [first, second], [[staged, "<staging>"]]);
1746
1819
  } finally {
1747
1820
  receipt.cleanup = await rm(scratch, {
1748
1821
  recursive: true,
@@ -1803,40 +1876,162 @@ function segmentArgv(plan, remote, staged, opts, leg) {
1803
1876
  spec
1804
1877
  ];
1805
1878
  }
1879
+ /** D3 mkdir pre-hop: ensure the destination's PARENT directory exists before
1880
+ * the transfer (never the operand itself — see parent-only note below).
1881
+ * transfer. Local dst is node-side; remote dst runs ONE exec `mkdir -p` probe
1882
+ * whose stdout discloses created-vs-existed. Best-effort by contract: any
1883
+ * failure records created:'n-a' + note and lets the copy proceed so the native
1884
+ * CLI surfaces the real error (and stopped-container docker cp keeps working). */
1885
+ async function ensureDestinationDir(dst, signal, execArgvFor, wrap, receipt) {
1886
+ const dir = dirname(dst.path);
1887
+ if (dir === "/" || dir === ".") {
1888
+ receipt.created = false;
1889
+ return;
1890
+ }
1891
+ if (dst.plan.kind === "local") {
1892
+ try {
1893
+ if (existsSync(dir)) {
1894
+ receipt.created = false;
1895
+ return;
1896
+ }
1897
+ mkdirSync(dir, { recursive: true });
1898
+ receipt.created = true;
1899
+ } catch (error) {
1900
+ receipt.created = "n-a";
1901
+ receipt.note = `mkdir pre-hop failed: ${error instanceof Error ? error.message : String(error)}`;
1902
+ }
1903
+ return;
1904
+ }
1905
+ if (execArgvFor === void 0) {
1906
+ receipt.created = "n-a";
1907
+ return;
1908
+ }
1909
+ if (/[\r\n]/.test(dir)) {
1910
+ receipt.created = "n-a";
1911
+ receipt.note = "mkdir pre-hop skipped: newline-bearing destination path";
1912
+ return;
1913
+ }
1914
+ const quoted = `'${dir.replaceAll("'", "'\\\\''")}'`;
1915
+ const script = `if [ -d ${quoted} ]; then echo existed; else mkdir -p ${quoted} && echo created; fi`;
1916
+ try {
1917
+ const result = await runNative(wrap(execArgvFor(dst.plan, script)), signal);
1918
+ const word = result.stdout.trim().split("\n").pop();
1919
+ if (result.exit === 0 && word === "created") receipt.created = true;
1920
+ else if (result.exit === 0 && word === "existed") receipt.created = false;
1921
+ else {
1922
+ receipt.created = "n-a";
1923
+ const tail = result.stderr.trim().split("\n").filter((line) => line.length > 0).pop();
1924
+ receipt.note = `mkdir pre-hop unavailable (exit ${result.exit})${tail !== void 0 ? `: ${tail}` : ""}`;
1925
+ }
1926
+ } catch (error) {
1927
+ receipt.created = "n-a";
1928
+ receipt.note = `mkdir pre-hop failed: ${error instanceof Error ? error.message : String(error)}`;
1929
+ }
1930
+ }
1806
1931
  /** Distinguishing identities for scp -3: an origin-only temp config maps two
1807
1932
  * synthetic aliases; keys are referenced in place, never copied to any remote. */
1808
- async function tempSshConfig(source, destination) {
1809
- for (const c of [source, destination]) for (const [field, value] of [
1810
- ["host", c.host],
1811
- ["user", c.user],
1812
- ["key", c.key],
1813
- ["proxyJump", c.proxyJump]
1814
- ]) if (value !== void 0 && /[\r\n]/.test(value)) throw new Error(`remote: scp -3 temp config rejects newline in SSH ${field} (no config injection)`);
1933
+ /** D8 (2026-10-14 user adoption, third acceptance round): a LOCAL directory source gets
1934
+ * the route's recursive flag appended automatically — scp / incus file push refuse
1935
+ * directories without it. The appended flag lands in the executed argv, which the
1936
+ * receipt shows verbatim: disclosure by receipt, never silent. Remote sources stay
1937
+ * caller-explicit (we do not dial just to stat); an explicit caller flag wins. */
1938
+ async function recursiveFlag(localPath, callerArgv, flag) {
1939
+ if (callerArgv.includes(flag) || callerArgv.includes("--recursive")) return [];
1940
+ try {
1941
+ if ((await stat(localPath)).isDirectory()) return [flag];
1942
+ } catch {}
1943
+ return [];
1944
+ }
1945
+ /** docker cp follows symlinks only with -L; its native error ("invalid symlink")
1946
+ * names no remedy. Append the one-line hint on that failure path (F5, 2026-10-14). */
1947
+ function withSymlinkHint(result) {
1948
+ if (result.exit !== 0 && /invalid symlink/i.test(result.stderr)) result.stderr = `${result.stderr}\nhint: docker cp follows symlinks only with -L — pass argv:["-L"] (e.g. /etc/os-release is a symlink)`;
1949
+ }
1950
+ async function tempSshConfig(source, destination, runNative$1) {
1951
+ const resolvedSource = await resolveThroughOriginConfig(source, runNative$1);
1952
+ const resolvedDestination = await resolveThroughOriginConfig(destination, runNative$1);
1953
+ for (const { connection: c, identityFiles } of [resolvedSource, resolvedDestination]) {
1954
+ for (const [field, value] of [
1955
+ ["host", c.host],
1956
+ ["user", c.user],
1957
+ ["key", c.key],
1958
+ ["proxyJump", c.proxyJump]
1959
+ ]) if (value !== void 0 && /[\r\n]/.test(value)) throw new Error(`remote: scp -3 temp config rejects newline in SSH ${field} (no config injection)`);
1960
+ for (const idFile of identityFiles) if (/[\r\n]/.test(idFile)) throw new Error("remote: scp -3 temp config rejects newline in resolved IdentityFile (no config injection)");
1961
+ }
1815
1962
  const dir = await mkdtemp(join(tmpdir(), "superd-scp3-"));
1816
1963
  const path = join(dir, "config");
1817
- const block = (name$1, c) => {
1964
+ const block = (name$1, c, identityFiles) => {
1818
1965
  const lines = [`Host ${name$1}`, ` HostName ${c.host.replace(/^.*@/, "")}`];
1819
1966
  if (c.user !== void 0) lines.push(` User ${c.user}`);
1820
1967
  if (c.port !== void 0) lines.push(` Port ${String(c.port)}`);
1821
- if (c.key !== void 0) lines.push(` IdentityFile ${c.key}`);
1968
+ for (const idFile of [c.key !== void 0 ? [c.key] : [], identityFiles].flat()) lines.push(` IdentityFile ${idFile}`);
1822
1969
  if (c.proxyJump !== void 0) lines.push(` ProxyJump ${c.proxyJump}`);
1823
1970
  return lines.join("\n");
1824
1971
  };
1825
- await writeFile(path, `${block("superd-scp3-source", source)}\n${block("superd-scp3-destination", destination)}\n`, { mode: 384 });
1972
+ await writeFile(path, `${block("superd-scp3-source", resolvedSource.connection, resolvedSource.identityFiles)}\n${block("superd-scp3-destination", resolvedDestination.connection, resolvedDestination.identityFiles)}\n`, { mode: 384 });
1826
1973
  await chmod(path, 384);
1827
1974
  return {
1828
1975
  path,
1829
1976
  dir
1830
1977
  };
1831
1978
  }
1979
+ /** Effective fields for one connection per the origin's own ssh config (ssh -G,
1980
+ * prints resolved settings without dialing). Explicit fields win; empty/none
1981
+ * proxyjump is dropped. IdentityFile PATHS from the config are returned separately
1982
+ * for inlining into the temp config — a path is not key material and stays on the
1983
+ * origin (F1, 2026-10-14: dev4's only authorized key lived in config, not the agent;
1984
+ * dropping it made scp -3 fail with a bare Permission denied). */
1985
+ async function resolveThroughOriginConfig(c, runNative$1) {
1986
+ let probe;
1987
+ try {
1988
+ probe = await runNative$1([
1989
+ "ssh",
1990
+ "-G",
1991
+ c.host
1992
+ ], new AbortController().signal);
1993
+ } catch {
1994
+ probe = void 0;
1995
+ }
1996
+ if (probe === void 0 || probe.exit !== 0) return {
1997
+ connection: c,
1998
+ identityFiles: []
1999
+ };
2000
+ const lines = probe.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
2001
+ const pick$1 = (key) => lines.find((line) => line.startsWith(`${key} `))?.slice(key.length + 1).trim() || void 0;
2002
+ const home = process.env.HOME ?? "";
2003
+ const compiledDefault = (path) => path === "none" || [
2004
+ "id_rsa",
2005
+ "id_ecdsa",
2006
+ "id_ecdsa_sk",
2007
+ "id_ed25519",
2008
+ "id_ed25519_sk"
2009
+ ].some((name$1) => path === `~/.ssh/${name$1}` || home !== "" && path === `${home}/.ssh/${name$1}`);
2010
+ const identityFiles = [...new Set(lines.filter((line) => line.startsWith("identityfile ")).map((line) => line.slice(13).trim()).filter((path) => path !== "" && !compiledDefault(path)))];
2011
+ const host = (pick$1("hostname") ?? c.host).replace(/^.*@/, "");
2012
+ const user = c.user ?? pick$1("user");
2013
+ const portText = pick$1("port");
2014
+ const port = c.port ?? (portText !== void 0 && /^\d+$/.test(portText) ? Number(portText) : void 0);
2015
+ const proxyJumpRaw = c.proxyJump ?? pick$1("proxyjump");
2016
+ return {
2017
+ connection: {
2018
+ host,
2019
+ ...user !== void 0 && user !== "" ? { user } : {},
2020
+ ...port !== void 0 && Number.isInteger(port) && port > 0 ? { port } : {},
2021
+ ...c.key !== void 0 ? { key: c.key } : {},
2022
+ ...proxyJumpRaw !== void 0 && proxyJumpRaw !== "" && proxyJumpRaw.toLowerCase() !== "none" ? { proxyJump: proxyJumpRaw } : {}
2023
+ },
2024
+ identityFiles
2025
+ };
2026
+ }
1832
2027
 
1833
2028
  //#endregion
1834
2029
  //#region src/remote/ssh-rsync-native-relay.ts
1835
2030
  /** ssh argv for the relay: batch, no agent, no multiplexing. `command` is the
1836
- * remote command; forwarding options go before `--`, the host after it. */
2031
+ * remote command; forwarding options go before `--`, the host after it.
2032
+ * Identity core comes from ssh-argv.ts (the ONE policy — 2026-10-17 ruling). */
1837
2033
  function relaySsh(connection, extra, command) {
1838
- const args = [
1839
- "ssh",
2034
+ return sshCommandArgv(connection, [
1840
2035
  "-o",
1841
2036
  "BatchMode=yes",
1842
2037
  "-T",
@@ -1853,14 +2048,7 @@ function relaySsh(connection, extra, command) {
1853
2048
  "ServerAliveInterval=5",
1854
2049
  "-o",
1855
2050
  "ServerAliveCountMax=2"
1856
- ];
1857
- if (connection.user !== void 0) args.push("-l", connection.user);
1858
- if (connection.port !== void 0) args.push("-p", String(connection.port));
1859
- if (connection.key !== void 0) args.push("-i", connection.key);
1860
- if (connection.proxyJump !== void 0) args.push("-J", connection.proxyJump);
1861
- args.push(...extra, "--", connection.host);
1862
- if (command !== void 0) args.push(command);
1863
- return args;
2051
+ ], extra, command);
1864
2052
  }
1865
2053
  async function freePorts(count) {
1866
2054
  const servers = [];
@@ -2015,12 +2203,16 @@ async function forwardRoute(argv, source, destination, signal, options) {
2015
2203
  return await removeRemoteTemp(destination.connection, temp, wrap) ? "released" : "unknown";
2016
2204
  };
2017
2205
  try {
2206
+ signal.throwIfAborted();
2207
+ const daemonState = await waitForDaemonReady(destinationChild, 5e3);
2208
+ if (daemonState === "exited") throw new Error(`dual-SSH rsync destination daemon exited before serving${relayDetail(destinationChild, sourceHold)}`);
2209
+ if (daemonState === "timeout") throw new Error(`dual-SSH rsync destination daemon never announced readiness${relayDetail(destinationChild, sourceHold)}`);
2018
2210
  signal.throwIfAborted();
2019
2211
  if (!await originPortOpen(originPort, 15e3)) throw new Error(`dual-SSH rsync destination daemon never became reachable${relayDetail(destinationChild, sourceHold)}`);
2020
2212
  signal.throwIfAborted();
2021
2213
  const runArgv = relaySsh(source.connection, [], rsyncClientCommand(argv, source.path, sourcePort));
2022
2214
  const result = await runNative(wrap(runArgv), signal);
2023
- if (result.exit === 255) result.stderr = (result.stderr + relayDetail(destinationChild, sourceHold)).slice(-OUTPUT_LIMIT);
2215
+ if (result.exit !== 0) result.stderr = (result.stderr + relayDetail(destinationChild, sourceHold)).slice(-OUTPUT_LIMIT);
2024
2216
  if (result.exit !== 0 && /remote port forwarding failed|administratively prohibited|could not request (local|remote) forwarding/.test(relayDetail(destinationChild, sourceHold))) throw new Error(`dual-SSH rsync forwarding route refused by an endpoint: ${relayDetail(destinationChild, sourceHold)}`);
2025
2217
  return {
2026
2218
  ...result,
@@ -2045,6 +2237,19 @@ async function forwardRoute(argv, source, destination, signal, options) {
2045
2237
  await Promise.allSettled([destinationChild.stop(), sourceHold.stop()]);
2046
2238
  }
2047
2239
  }
2240
+ /** Wait until the destination daemon script announces itself (stderr marker,
2241
+ * printed after its directory check and right before the daemon exec) or exits.
2242
+ * 'ready' only when the daemon is actually about to serve — closes the 0.3.1-e
2243
+ * race where -L opens at session start but the script dies ~150ms later. */
2244
+ async function waitForDaemonReady(held, budgetMs) {
2245
+ const deadline = Date.now() + budgetMs;
2246
+ for (;;) {
2247
+ if (/^SUPERD-RSYNC-TMP \S+$/m.test(held.stderrSink.text)) return "ready";
2248
+ if (held.child.exitCode !== null) return "exited";
2249
+ if (Date.now() >= deadline) return "timeout";
2250
+ await new Promise((resolve$1) => setTimeout(resolve$1, 50));
2251
+ }
2252
+ }
2048
2253
  function relayDetail(...held) {
2049
2254
  const detail = held.map((child) => child.stderrSink.text).filter(Boolean).join("\n");
2050
2255
  return detail ? `\n[relay stderr]\n${detail}` : "";
@@ -2126,14 +2331,7 @@ async function runRsync(request, source, destination, signal, options = {}) {
2126
2331
  checkAbsolute(destination.path);
2127
2332
  const argv = checkArgv("argv", request.argv);
2128
2333
  if ("pullArgv" in request || "pushArgv" in request) throw new Error("remote: rsync takes argv only; pullArgv/pushArgv are copy-only fields");
2129
- for (const legacy of [
2130
- "dryRun",
2131
- "delete",
2132
- "excludes",
2133
- "preserve",
2134
- "verify",
2135
- "mkdir"
2136
- ]) if (legacy in request) throw new Error(`remote: rsync field "${legacy}" was removed — pass native options through argv instead (e.g. "--dry-run", "--delete", "--exclude=<pattern>", "-a"/"-r"; verify/mkdir belong to the copy Hub/local lane)`);
2334
+ for (const key of Object.keys(request)) if (key !== "src" && key !== "dst" && key !== "argv") throw new Error(`remote: rsync rejects unknown field "${key}" — native options go through argv (verify is a copy Hub-lane option; mkdir is copy-only; for destination parents pass --mkpath yourself)`);
2137
2335
  if (!source.connection && !destination.connection) throw new Error("rsync requires at least one SSH endpoint; local-only is unsupported");
2138
2336
  let result;
2139
2337
  let route;
@@ -2181,6 +2379,9 @@ async function runRsync(request, source, destination, signal, options = {}) {
2181
2379
  dst: request.dst,
2182
2380
  argv,
2183
2381
  route,
2382
+ verification: "native-exit-only",
2383
+ ok: result.exit === 0,
2384
+ ...result.exit === 0 ? {} : { reason: signal.aborted ? "aborted" : "non-zero-exit" },
2184
2385
  command: `rsync ${argv.join(" ")}`,
2185
2386
  ...itemized.length > 0 || argv.includes("--itemize-changes") || argv.includes("-i") ? { itemizedChanges: itemized } : {},
2186
2387
  ...literalBytes === void 0 ? {} : { literalBytes },
@@ -2195,21 +2396,14 @@ async function runRsync(request, source, destination, signal, options = {}) {
2195
2396
  * placeholder host rsync appends and execs the canonical SSH argv. All tokens
2196
2397
  * are tool-built argv (identity options/aliases), never model text. */
2197
2398
  function rsyncRsh(connection, wrap) {
2198
- const args = [
2199
- "ssh",
2200
- "-o",
2201
- "BatchMode=yes",
2202
- "-T"
2203
- ];
2204
- if (connection.user !== void 0) args.push("-l", connection.user);
2205
- if (connection.port !== void 0) args.push("-p", String(connection.port));
2206
- if (connection.key !== void 0) args.push("-i", connection.key);
2207
- if (connection.proxyJump !== void 0) args.push("-J", connection.proxyJump);
2208
- args.push("--", connection.host);
2209
2399
  return [
2210
2400
  "bash",
2211
2401
  "-c",
2212
- `shift; exec ${wrap(args).map(rshQuote).join(" ")} "$@"`,
2402
+ `shift; exec ${wrap(sshCommandArgv(connection, [
2403
+ "-o",
2404
+ "BatchMode=yes",
2405
+ "-T"
2406
+ ], [])).map(rshQuote).join(" ")} "$@"`,
2213
2407
  "rsync-rsh"
2214
2408
  ].map(rshQuote).join(" ");
2215
2409
  }
@@ -2305,26 +2499,19 @@ var SshTargets = class {
2305
2499
  }
2306
2500
  };
2307
2501
  /** Shared by execution, probes and file/relay consumers. extraArgs is trusted
2308
- * internal SSH argv (e.g. forwarding), never a model-provided shell string. */
2502
+ * internal SSH argv (e.g. forwarding), never a model-provided shell string.
2503
+ * Identity assembly lives in ssh-argv.ts (the ONE policy — 2026-10-17 ruling);
2504
+ * this wrapper only picks the tty/batch dialect base. */
2309
2505
  function sshArgv(connection, options = {}) {
2310
- const args = options.tty ? [
2311
- "ssh",
2506
+ return sshCommandArgv(connection, options.tty ? [
2312
2507
  "-tt",
2313
2508
  "-o",
2314
2509
  "BatchMode=yes"
2315
2510
  ] : [
2316
- "ssh",
2317
2511
  "-o",
2318
2512
  "BatchMode=yes",
2319
2513
  "-T"
2320
- ];
2321
- if (connection.user !== void 0) args.push("-l", connection.user);
2322
- if (connection.port !== void 0) args.push("-p", String(connection.port));
2323
- if (connection.key !== void 0) args.push("-i", connection.key);
2324
- if (connection.proxyJump !== void 0) args.push("-J", connection.proxyJump);
2325
- args.push(...options.extraArgs ?? [], "--", connection.host);
2326
- if (options.command !== void 0) args.push(options.command);
2327
- return args;
2514
+ ], options.extraArgs ?? [], options.command);
2328
2515
  }
2329
2516
 
2330
2517
  //#endregion
@@ -2381,6 +2568,96 @@ async function runOneShot(argv, opts = {}) {
2381
2568
  };
2382
2569
  }
2383
2570
 
2571
+ //#endregion
2572
+ //#region src/remote/helper.ts
2573
+ /** Reads helper-bin/MANIFEST.json next to the compiled module (lib/remote in the npm
2574
+ * package, super-dsh/remote in the Super closure — same relative layout). Missing or
2575
+ * malformed assets ⇒ null ⇒ shim-only mode; never throws. */
2576
+ function helperAssets(baseDir) {
2577
+ const dir = baseDir ?? join(dirname(fileURLToPath(import.meta.url)), "helper-bin");
2578
+ try {
2579
+ const manifest = JSON.parse(readFileSync(join(dir, "MANIFEST.json"), "utf8"));
2580
+ if (manifest?.format !== 1 || typeof manifest.ver !== "string" || typeof manifest.protocol !== "number" || !manifest.targets || typeof manifest.targets !== "object") return null;
2581
+ return {
2582
+ dir,
2583
+ manifest
2584
+ };
2585
+ } catch {
2586
+ return null;
2587
+ }
2588
+ }
2589
+ /** `uname -s`/`uname -m` → shipped target triple. Unknown combos ⇒ null (shim). */
2590
+ function archTriple(sys, machine, shipped) {
2591
+ const s = sys.trim().toLowerCase(), m = machine.trim().toLowerCase();
2592
+ const candidates = s === "linux" ? m === "x86_64" || m === "amd64" ? "x86_64-unknown-linux-musl" : m === "aarch64" || m === "arm64" ? "aarch64-unknown-linux-musl" : null : s === "darwin" ? m === "arm64" || m === "aarch64" ? "aarch64-apple-darwin" : null : null;
2593
+ if (candidates === null) return null;
2594
+ return shipped.has(candidates) ? candidates : null;
2595
+ }
2596
+ function planKey(plan) {
2597
+ switch (plan.kind) {
2598
+ case "ssh": {
2599
+ const c = plan.connection;
2600
+ const host = c?.host ?? plan.host;
2601
+ return `ssh|${c?.user ? c.user + "@" : ""}${host}${c?.port ? ":" + c.port : ""}${c?.proxyJump ? "|" + c.proxyJump : ""}`;
2602
+ }
2603
+ case "docker": return `docker|${plan.container}`;
2604
+ case "incus": return `incus|${plan.container}`;
2605
+ }
2606
+ }
2607
+ const PROBE_RE = /^SUPERD-PROBE (\S+) (\S+)$/m;
2608
+ const READY_RE = /^SUPERD-READY (\S+)$/m;
2609
+ var HelperRegistry = class {
2610
+ states = /* @__PURE__ */ new Map();
2611
+ constructor(assets, exec, probeArgv, uploadArgv) {
2612
+ this.assets = assets;
2613
+ this.exec = exec;
2614
+ this.probeArgv = probeArgv;
2615
+ this.uploadArgv = uploadArgv;
2616
+ }
2617
+ /** Never throws: any bootstrap failure resolves null (sticky) and the caller stays on the shim. */
2618
+ ensure(plan) {
2619
+ if (plan.kind !== "ssh" && plan.kind !== "docker" && plan.kind !== "incus") return Promise.resolve(null);
2620
+ const key = planKey(plan);
2621
+ let entry = this.states.get(key);
2622
+ if (entry === void 0) {
2623
+ entry = this.bootstrap(plan).catch(() => null);
2624
+ entry.catch(() => {});
2625
+ this.states.set(key, entry);
2626
+ }
2627
+ return entry;
2628
+ }
2629
+ async bootstrap(plan) {
2630
+ const { manifest } = this.assets;
2631
+ const first = await this.exec(this.probeArgv(plan, this.assets), { timeoutSec: 15 });
2632
+ const ready = this.parseReady(first.stdout, manifest.ver, manifest.protocol);
2633
+ if (ready !== null) return ready;
2634
+ const probe = PROBE_RE.exec(first.stdout);
2635
+ if (probe === null || first.exit !== 90 && first.exit !== 0) return null;
2636
+ const triple = archTriple(probe[1], probe[2], new Set(Object.keys(manifest.targets)));
2637
+ if (triple === null) return null;
2638
+ const target = manifest.targets[triple];
2639
+ const binary = await readFile(join(this.assets.dir, triple, target.file));
2640
+ if ((await this.exec(this.uploadArgv(plan, triple, target.file, this.assets), {
2641
+ stdin: binary,
2642
+ timeoutSec: 60
2643
+ })).exit !== 0) return null;
2644
+ const second = await this.exec(this.probeArgv(plan, this.assets), { timeoutSec: 15 });
2645
+ return this.parseReady(second.stdout, manifest.ver, manifest.protocol);
2646
+ }
2647
+ parseReady(stdout, ver, protocol) {
2648
+ const ready = READY_RE.exec(stdout);
2649
+ const banner = /^SUPERD-RUN proto=(\d+) name=superd-run ver=(\S+) target=(\S+)/m.exec(stdout);
2650
+ if (ready === null || banner === null) return null;
2651
+ if (banner[1] !== String(protocol) || banner[2] !== ver) return null;
2652
+ return {
2653
+ binPath: ready[1],
2654
+ triple: banner[3],
2655
+ ver,
2656
+ protocol
2657
+ };
2658
+ }
2659
+ };
2660
+
2384
2661
  //#endregion
2385
2662
  //#region src/remote/transports.ts
2386
2663
  /** POSIX 单引号机械转义(唯一一层转义,spec §三"一层机械转义")。 */
@@ -2460,6 +2737,104 @@ function buildSpawnArgv(spawnCommand) {
2460
2737
  "/dev/null"
2461
2738
  ];
2462
2739
  }
2740
+ /** superd-run helper argv (spec: docs/specs/remote-rescope §5). Probe and upload are single
2741
+ * `sh -c` hops (busybox/alpine/darwin-safe); run/killgroup over ssh go through exactly one
2742
+ * remote-shell parse, same discipline as buildOneShotArgv. */
2743
+ function buildHelperProbeArgv(plan, assets) {
2744
+ const { ver, targets } = assets.manifest;
2745
+ return helperShellArgv(plan, `${Object.entries(targets).map(([triple, t]) => `d="$HOME/.cache/superd/run/${triple}-${ver}"; [ -x "$d/${t.file}" ] && { echo "SUPERD-READY $d/${t.file}"; "$d/${t.file}" banner; exit 0; };`).join(" ")} echo "SUPERD-PROBE $(uname -s 2>/dev/null || echo unknown) $(uname -m 2>/dev/null || echo unknown)"; exit 90`);
2746
+ }
2747
+ function buildHelperUploadArgv(plan, triple, file, assets) {
2748
+ const dir = `$HOME/.cache/superd/run/${triple}-${assets.manifest.ver}`;
2749
+ return helperShellArgv(plan, `mkdir -p "${dir}" && cat > "${dir}/${file}.tmp" && chmod 0755 "${dir}/${file}.tmp" && mv -f "${dir}/${file}.tmp" "${dir}/${file}"`);
2750
+ }
2751
+ function buildHelperOneShotArgv(plan, binPath, options) {
2752
+ const { tag, cmd } = options;
2753
+ switch (plan.kind) {
2754
+ case "ssh": return sshArgv(plan.connection ?? { host: plan.host }, { command: `exec ${shQuote(binPath)} run --tag ${shQuote(tag)} -- bash -lc ${shQuote(cmd)}` });
2755
+ case "docker": return [
2756
+ "docker",
2757
+ "exec",
2758
+ "-i",
2759
+ "--",
2760
+ plan.container,
2761
+ binPath,
2762
+ "run",
2763
+ "--tag",
2764
+ tag,
2765
+ "--",
2766
+ "bash",
2767
+ "-lc",
2768
+ cmd
2769
+ ];
2770
+ case "incus": return [
2771
+ "incus",
2772
+ "exec",
2773
+ "--force-noninteractive",
2774
+ plan.container,
2775
+ "--",
2776
+ binPath,
2777
+ "run",
2778
+ "--tag",
2779
+ tag,
2780
+ "--",
2781
+ "bash",
2782
+ "-lc",
2783
+ cmd
2784
+ ];
2785
+ }
2786
+ }
2787
+ function buildHelperKillgroupArgv(plan, binPath, tag) {
2788
+ switch (plan.kind) {
2789
+ case "ssh": return sshArgv(plan.connection ?? { host: plan.host }, { command: `${shQuote(binPath)} killgroup --tag ${shQuote(tag)}` });
2790
+ case "docker": return [
2791
+ "docker",
2792
+ "exec",
2793
+ "--",
2794
+ plan.container,
2795
+ binPath,
2796
+ "killgroup",
2797
+ "--tag",
2798
+ tag
2799
+ ];
2800
+ case "incus": return [
2801
+ "incus",
2802
+ "exec",
2803
+ "--force-noninteractive",
2804
+ plan.container,
2805
+ "--",
2806
+ binPath,
2807
+ "killgroup",
2808
+ "--tag",
2809
+ tag
2810
+ ];
2811
+ }
2812
+ }
2813
+ function helperShellArgv(plan, script) {
2814
+ switch (plan.kind) {
2815
+ case "ssh": return sshArgv(plan.connection ?? { host: plan.host }, { command: script });
2816
+ case "docker": return [
2817
+ "docker",
2818
+ "exec",
2819
+ "-i",
2820
+ "--",
2821
+ plan.container,
2822
+ "sh",
2823
+ "-c",
2824
+ script
2825
+ ];
2826
+ case "incus": return [
2827
+ "incus",
2828
+ "exec",
2829
+ "--force-noninteractive",
2830
+ plan.container,
2831
+ "--",
2832
+ "sh",
2833
+ "-c",
2834
+ script
2835
+ ];
2836
+ }
2837
+ }
2463
2838
 
2464
2839
  //#endregion
2465
2840
  //#region src/remote/nonce-framing.ts
@@ -3189,6 +3564,9 @@ var RemoteDriver = class {
3189
3564
  starting = /* @__PURE__ */ new Set();
3190
3565
  fileAbort = new AbortController();
3191
3566
  disposed = false;
3567
+ /** undefined = 未尝试;null = 资产缺失(shim-only);registry = 每 target sticky 状态机。 */
3568
+ helpers;
3569
+ runnerOf = /* @__PURE__ */ new WeakMap();
3192
3570
  disposal;
3193
3571
  constructor(opts = {}) {
3194
3572
  this.opts = opts;
@@ -3295,13 +3673,18 @@ var RemoteDriver = class {
3295
3673
  path: dst.path,
3296
3674
  endpoint: localFileEndpoint()
3297
3675
  }, combined);
3298
- if (request.verify !== void 0 || request.mkdir !== void 0) throw new Error("copy: verify/mkdir apply to the Hub/local staged lane only; native CLI routes do not verify or create parents (mirror the CLI's own behavior with argv like -r)");
3676
+ if (request.verify !== void 0) throw new Error("copy: verify is a Hub/local staged lane option (independent SHA-256); native routes report exit-code-only verification — self-verify via oneshot digest commands when needed");
3299
3677
  const planOf = (end) => end.kind === "cli" ? end.plan : end.kind === "local" ? { kind: "local" } : void 0;
3300
3678
  const srcPlan = planOf(src), dstPlan = planOf(dst);
3301
3679
  if (srcPlan === void 0 || dstPlan === void 0) throw new Error("copy: unreachable route resolution");
3680
+ const execArgvFor = (plan, script) => {
3681
+ const argv = helperShellArgv(plan, script);
3682
+ return this.opts.nativeArgvFor ? this.opts.nativeArgvFor(argv) : argv;
3683
+ };
3302
3684
  return nativeCopy({
3303
3685
  src: request.src,
3304
3686
  dst: request.dst,
3687
+ ...request.mkdir === void 0 ? {} : { mkdir: request.mkdir },
3305
3688
  ...request.argv === void 0 ? {} : { argv: request.argv },
3306
3689
  ...request.pullArgv === void 0 ? {} : { pullArgv: request.pullArgv },
3307
3690
  ...request.pushArgv === void 0 ? {} : { pushArgv: request.pushArgv }
@@ -3314,7 +3697,10 @@ var RemoteDriver = class {
3314
3697
  plan: dstPlan,
3315
3698
  path: dst.path
3316
3699
  }
3317
- }, combined, { wrapArgv: this.opts.nativeArgvFor });
3700
+ }, combined, {
3701
+ wrapArgv: this.opts.nativeArgvFor,
3702
+ execArgvFor
3703
+ });
3318
3704
  })();
3319
3705
  this.starting.add(work);
3320
3706
  work.then(() => this.starting.delete(work), () => this.starting.delete(work));
@@ -3381,12 +3767,32 @@ var RemoteDriver = class {
3381
3767
  if (signal.aborted || this.disposed) process$1.cancel("owner closed during start");
3382
3768
  return process$1;
3383
3769
  }
3384
- nativeProcess(plan, params, timeoutSec, signal = this.fileAbort.signal) {
3770
+ async nativeProcess(plan, params, timeoutSec, signal = this.fileAbort.signal) {
3385
3771
  const cleanupTag = randomUUID().slice(0, 8);
3772
+ const helper = await this.ensureHelper(plan);
3773
+ if (helper !== null && !this.disposed) {
3774
+ const process$2 = startOneShot(buildHelperOneShotArgv(plan, helper.binPath, {
3775
+ tag: cleanupTag,
3776
+ cmd: params.cmd
3777
+ }), {
3778
+ stdin: params.stdin,
3779
+ timeoutSec
3780
+ });
3781
+ this.runnerOf.set(process$2, `superd-run ${helper.ver} (${helper.triple})`);
3782
+ process$2.done.then((result) => {
3783
+ if (!result.cancelled && !result.timedOut) return;
3784
+ startOneShot(buildHelperKillgroupArgv(plan, helper.binPath, cleanupTag), { timeoutSec: 15 }).done.catch(() => {});
3785
+ }).catch(() => {});
3786
+ const tracked = Promise.resolve(process$2);
3787
+ this.starting.add(tracked);
3788
+ tracked.then(() => this.starting.delete(tracked), () => this.starting.delete(tracked));
3789
+ return tracked;
3790
+ }
3386
3791
  const process$1 = startOneShot((this.opts.oneshotArgvFor ?? buildOneShotArgv)(plan, params.cmd, { cleanupTag }), {
3387
3792
  stdin: params.stdin,
3388
3793
  timeoutSec
3389
3794
  });
3795
+ this.runnerOf.set(process$1, "posix-shim");
3390
3796
  process$1.done.then((result) => {
3391
3797
  if (!result.cancelled && !result.timedOut) return;
3392
3798
  const pidfile = `"\${TMPDIR:-/tmp}/superd-oneshot-${cleanupTag}.pgid"`;
@@ -3398,6 +3804,38 @@ var RemoteDriver = class {
3398
3804
  work.then(() => this.starting.delete(work), () => this.starting.delete(work));
3399
3805
  return work;
3400
3806
  }
3807
+ /** Helper bootstrap is best-effort and sticky: any failure parks null for this driver
3808
+ * lifetime and the shim stays the path. First call to a target pays probe (+ upload). */
3809
+ async ensureHelper(plan) {
3810
+ if (this.helpers === void 0) {
3811
+ const assets = this.opts.helperAssetsFor?.() ?? helperAssets();
3812
+ this.helpers = assets === null ? null : new HelperRegistry(assets, (argv, options) => this.helperExec(argv, options), buildHelperProbeArgv, buildHelperUploadArgv);
3813
+ }
3814
+ if (this.helpers === null || this.disposed) return null;
3815
+ try {
3816
+ return await this.helpers.ensure(plan);
3817
+ } catch {
3818
+ return null;
3819
+ }
3820
+ }
3821
+ async helperExec(argv, options) {
3822
+ this.assertOpen();
3823
+ const stdin = options?.stdin;
3824
+ if (stdin !== void 0) {
3825
+ const proc$1 = startBinaryProcess(argv, { timeoutSec: options?.timeoutSec });
3826
+ proc$1.stdin.on("error", () => {});
3827
+ proc$1.stdin.end(stdin);
3828
+ return {
3829
+ exit: (await proc$1.done).exit,
3830
+ stdout: ""
3831
+ };
3832
+ }
3833
+ const proc = startOneShot(argv, { timeoutSec: options?.timeoutSec });
3834
+ return {
3835
+ exit: (await proc.done).exit,
3836
+ stdout: proc.read("stdout", 0).text
3837
+ };
3838
+ }
3401
3839
  own(process$1) {
3402
3840
  this.processes.add(process$1);
3403
3841
  process$1.done.then(() => this.processes.delete(process$1));
@@ -3675,7 +4113,8 @@ var RemoteDriver = class {
3675
4113
  stderr: err?.stats ?? r$1.outputStats?.stderr
3676
4114
  } } : {},
3677
4115
  durationMs: r$1.durationMs,
3678
- ...out$1.truncated !== void 0 ? { truncated: out$1.truncated } : {}
4116
+ ...out$1.truncated !== void 0 ? { truncated: out$1.truncated } : {},
4117
+ ...this.runnerOf.get(process$1) === void 0 ? {} : { runner: this.runnerOf.get(process$1) }
3679
4118
  };
3680
4119
  }
3681
4120
  const argv = (this.opts.ptyArgvFor ?? buildPtyArgv)(routing.plan);
@@ -3838,9 +4277,9 @@ const REMOTE_TOOL_DESCRIPTION = `Remote execution and file transfer over ssh / d
3838
4277
  {target:"byop:lbl", spawn, cmd} → create the named shell once; reuse as {target:"byop:lbl", cmd} (no spawn)
3839
4278
  Bare names resolve ssh config → docker → incus → live pty; unmatched goes to ssh and its native error surfaces. Prefixes ssh: / docker: / incus: / byop: force a channel. byop: same spawn repeated is reused; a different spawn is refused ([E_LABEL_CONFLICT], nothing runs); unknown or idle-expired → [E_NO_SESSION] (spawn is not retained — pass it again); a failed spawn frees the label.
3840
4279
 
3841
- oneshot: stateless bash -lc, stdout/stderr separate, exit code verbatim; the endpoint needs bash only (no Python). A POSIX wrapper reaps backgrounded children on completion and kills the whole group on timeout/cancel — they never outlive the call or hold the channel. pty: one shell per (agent, target); cwd/env survive, streams merge, Ctrl-C interrupts without killing, idle reap after 600s.
4280
+ oneshot: stateless bash -lc, stdout/stderr separate, exit code verbatim. Supervision prefers a pinned superd-run helper cached on the endpoint (~/.cache/superd/run; first call uploads it over the same exec lane) and falls back to a POSIX wrapper everywhere else — the result field "runner" says which ran. Helper or wrapper, backgrounded children are reaped on completion and the whole group is killed on timeout/cancel; the endpoint needs bash only (no Python).
3842
4281
 
3843
- Files — {action:"copy" | "rsync", src:"[target:]abs", dst:"[target:]abs", argv:["-a"]}: argv tokens pass verbatim before src/dst — no whitelist, no forced defaults (dry run = pass "--dry-run" yourself); the CLI's own exit code and stderr come back in the receipt. copy: local↔SSH scp; SSH↔SSH scp -3 through the origin (identities stay local); docker cp; incus file push/pull; any other pair = two commands with origin staging (per-half options via pullArgv/pushArgv, exclusive with argv); scp -R refused; success is NOT independently verified; verify/mkdir only on the Hub↔local lane. rsync: local↔SSH, or SSH↔SSH with one protocol conversation between the peers (real delta; forwarding denied → honest error, never full copy). rsync src trailing slash copies contents, no slash the directory itself.
4282
+ Files — {action:"copy" | "rsync", src:"[target:]abs", dst:"[target:]abs", argv:["-a"]}: argv tokens pass verbatim before src/dst — no whitelist, no forced defaults (dry run = pass "--dry-run" yourself); the CLI's own exit code and stderr come back in the receipt. copy: local↔SSH scp; SSH↔SSH scp -3 through the origin (identities stay local); docker cp; incus file push/pull; any other pair = two commands with origin staging (per-half options via pullArgv/pushArgv, exclusive with argv); scp -R (uppercase — scp's remote-client mode) is refused; copy of a directory from a LOCAL source auto-appends the route's recursive flag (-r, shown in the receipt argv); from a REMOTE source pass -r yourself or use rsync; mkdir defaults to ensuring the destination's PARENT directory first (one exec pre-hop, outcome in the receipt created field; the operand itself is never pre-created); receipts use stable route ids (scp / scp-3 / docker-cp / incus-file-push / incus-file-pull / staged-pull-push / hub-staged) with ok+reason, the verification tier actually performed (native-exit-only by default; sha256 on the Hub lane) and tool-owned temp paths in argv shown as placeholders. rsync: local↔SSH, or SSH↔SSH with one protocol conversation between the peers (real delta; forwarding denied → honest error, never full copy). rsync src trailing slash copies contents, no slash the directory itself.
3844
4283
 
3845
4284
  Output: pty tails at ~30k chars ("[truncated: …]" header) — redirect bulk output to a file. Hard retention 4,000,000 UTF-16 units per stream (Hub 4MiB bytes); outputStats reports the rest; restart cannot recover discarded output. stdin feeds the command; timeout (seconds) interrupts then kills. run_in_background:true (oneshot) → jobId for job_output/job_kill. Native transport errors pass through verbatim — self-correct from them.`;
3846
4285
  function createRemoteTool(driver, opts = {}) {
@@ -3865,7 +4304,7 @@ function createRemoteTool(driver, opts = {}) {
3865
4304
  argv: {
3866
4305
  type: "array",
3867
4306
  items: { type: "string" },
3868
- description: "Ordered native CLI options and values for one-command routes (e.g. rsync [\"-a\",\"--partial\",\"--dry-run\"], scp [\"-p\"], docker cp [\"-a\"], incus file push/pull). One element = one argv token, appended verbatim before src/dst; no shell parsing, no whitelist, no forced defaults — unknown options fail in the native CLI itself. rsync only (SSH endpoints): explicit -e/--rsh overrides the tool's SSH wiring. Two-segment cross-endpoint copy uses pullArgv/pushArgv instead."
4307
+ description: "Ordered native CLI options and values for one-command routes (e.g. rsync [\"-a\",\"--partial\",\"--dry-run\"], scp [\"-p\",\"-r\"], docker cp [\"-a\"], incus file push/pull). One element = one argv token, appended verbatim before src/dst; no shell parsing, no whitelist, no forced defaults — unknown options fail in the native CLI itself. rsync only (SSH endpoints): explicit -e/--rsh overrides the tool's SSH wiring. docker cp: pass -L to follow symlinks (a symlink source like /etc/os-release fails without it). Two-segment cross-endpoint copy uses pullArgv/pushArgv instead."
3869
4308
  },
3870
4309
  pullArgv: {
3871
4310
  type: "array",
@@ -3879,11 +4318,11 @@ function createRemoteTool(driver, opts = {}) {
3879
4318
  },
3880
4319
  verify: {
3881
4320
  type: "boolean",
3882
- description: "copy only, Hub↔local↔local staged lane only: independent SHA-256 verification (default true). Native CLI routes do not verify and report that fact."
4321
+ description: "copy only. Receipts name the verification tier actually performed (D1=A): native CLI routes are \"native-exit-only\" (no independent check — self-verify via oneshot digest commands when needed); the Hub↔local staged lane computes independent SHA-256 digests by default and verify:false skips them (\"not-applicable\"). No hash tier exists on native routes."
3883
4322
  },
3884
4323
  mkdir: {
3885
4324
  type: "boolean",
3886
- description: "copy only, Hub↔local↔local staged lane only: create missing destination parents (default true). Native CLI routes let the CLI decide."
4325
+ description: "copy only, all routes. Default true: before transfer, one best-effort exec pre-hop ensures the destination's PARENT directory exists (the operand itself is never pre-created — each CLI's own layout semantics stay byte-identical). Receipt created field: true (parent created this call) / false (already existed) / \"n-a\" (skipped or pre-hop unavailable, e.g. docker exec on a stopped container — the native CLI then reports the real error itself). false skips the pre-hop."
3887
4326
  },
3888
4327
  run_in_background: {
3889
4328
  type: "boolean",
@@ -3980,7 +4419,11 @@ function createRemoteTool(driver, opts = {}) {
3980
4419
  outputStats: { type: "json" },
3981
4420
  outputStatsUnavailable: { type: "boolean" },
3982
4421
  session: { type: "string" },
3983
- notice: { type: "string" }
4422
+ notice: { type: "string" },
4423
+ runner: {
4424
+ type: "string",
4425
+ description: "oneshot supervision path: superd-run <ver> (<triple>) or posix-shim"
4426
+ }
3984
4427
  }
3985
4428
  },
3986
4429
  render: (_args, value) => [{
@@ -4002,12 +4445,26 @@ function createRemoteTool(driver, opts = {}) {
4002
4445
  args.timeout,
4003
4446
  args.run_in_background
4004
4447
  ].some((value) => value !== void 0)) throw new Error(`${args.action} cannot be combined with execution/PTY/job arguments; register byossh separately first`);
4005
- for (const legacy of [
4006
- "dryRun",
4007
- "delete",
4008
- "excludes",
4009
- "preserve"
4010
- ]) if (args[legacy] !== void 0) throw new Error(`${legacy} was removed: pass native options through argv instead (e.g. ${legacy === "dryRun" ? "\"--dry-run\"" : legacy === "delete" ? "\"--delete\"" : legacy === "excludes" ? "\"--exclude=<pattern>\"" : "\"-a\" / \"-r\""})`);
4448
+ const KNOWN_PARAMS = new Set([
4449
+ "action",
4450
+ "src",
4451
+ "dst",
4452
+ "argv",
4453
+ "pullArgv",
4454
+ "pushArgv",
4455
+ "verify",
4456
+ "mkdir",
4457
+ "run_in_background",
4458
+ "target",
4459
+ "ssh",
4460
+ "spawn",
4461
+ "cmd",
4462
+ "script",
4463
+ "mode",
4464
+ "stdin",
4465
+ "timeout"
4466
+ ]);
4467
+ for (const key of Object.keys(args)) if (!KNOWN_PARAMS.has(key)) throw new Error(`remote: unknown parameter "${key}" — this surface has no legacy aliases (dryRun/delete/excludes/preserve are gone); pass native CLI options through argv instead`);
4011
4468
  if (args.action === "rsync") {
4012
4469
  if (args.pullArgv !== void 0 || args.pushArgv !== void 0) throw new Error("rsync takes argv only; pullArgv/pushArgv are copy-only fields");
4013
4470
  if ([args.verify, args.mkdir].some((value) => value !== void 0)) throw new Error("rsync cannot be combined with copy verify/mkdir options; the Hub/local staged lane is copy-only");
@@ -4017,15 +4474,17 @@ function createRemoteTool(driver, opts = {}) {
4017
4474
  dst: args.dst,
4018
4475
  ...args.argv === void 0 ? {} : { argv: args.argv }
4019
4476
  }, exec.signal, { sessionKey: getSessionKey(exec) });
4020
- if (receipt$1.exit !== 0) throw new Error(JSON.stringify(receipt$1));
4477
+ if (receipt$1.ok === false || receipt$1.exit !== 0) throw new Error(JSON.stringify(receipt$1));
4478
+ const rsyncDuration = Date.now() - start;
4021
4479
  return {
4022
4480
  kind: "rsync",
4023
- text: JSON.stringify(receipt$1),
4481
+ text: formatTransferLine("rsync", receipt$1, rsyncDuration),
4024
4482
  receipt: receipt$1,
4025
4483
  exit: receipt$1.exit,
4026
- durationMs: Date.now() - start
4484
+ durationMs: rsyncDuration
4027
4485
  };
4028
4486
  }
4487
+ const copyStart = Date.now();
4029
4488
  const receipt = await driver.copy({
4030
4489
  src: args.src,
4031
4490
  dst: args.dst,
@@ -4035,12 +4494,16 @@ function createRemoteTool(driver, opts = {}) {
4035
4494
  ...args.pullArgv === void 0 ? {} : { pullArgv: args.pullArgv },
4036
4495
  ...args.pushArgv === void 0 ? {} : { pushArgv: args.pushArgv }
4037
4496
  }, exec.signal, { sessionKey: getSessionKey(exec) });
4497
+ const asReceipt = receipt;
4498
+ const exits = asReceipt.exits ?? [];
4499
+ if (asReceipt.ok === false || exits.some((code) => code !== 0)) throw new Error(JSON.stringify(receipt));
4500
+ const copyDuration = Date.now() - copyStart;
4038
4501
  return {
4039
4502
  kind: "copy",
4040
- text: JSON.stringify(receipt),
4503
+ text: formatTransferLine("copy", receipt, copyDuration),
4041
4504
  receipt,
4042
- exit: null,
4043
- durationMs: 0
4505
+ exit: exits.length > 0 ? 0 : null,
4506
+ durationMs: copyDuration
4044
4507
  };
4045
4508
  }
4046
4509
  if ([
@@ -4126,6 +4589,7 @@ function createRemoteTool(driver, opts = {}) {
4126
4589
  if (r.outputStatsUnavailable) v.outputStatsUnavailable = true;
4127
4590
  if (r.session !== void 0) v.session = r.session;
4128
4591
  if (r.notice !== void 0) v.notice = r.notice;
4592
+ if (r.runner !== void 0) v.runner = r.runner;
4129
4593
  return v;
4130
4594
  }
4131
4595
  });
@@ -4136,6 +4600,26 @@ function createRemoteTool(driver, opts = {}) {
4136
4600
  });
4137
4601
  return tool;
4138
4602
  }
4603
+ /** S8 (2026-10-17): copy/rsync render as one formatted line; the full receipt
4604
+ * stays in the receipt field — raw-JSON dumps are gone from text. */
4605
+ function formatTransferLine(action, receipt, durationMs) {
4606
+ const r = receipt;
4607
+ const parts = [];
4608
+ if (r.ok === false) parts.push(`✗ ${r.reason ?? "failed"}`);
4609
+ parts.push(`${action} ${r.src} → ${r.dst}`);
4610
+ if (r.route !== void 0 && r.route !== "") parts.push(`route ${r.route}`);
4611
+ if (r.exits !== void 0) parts.push(`exits [${r.exits.join(",")}]`);
4612
+ else if (r.exit !== void 0) parts.push(`exit ${r.exit}`);
4613
+ if (r.verification !== void 0) parts.push(`verification ${r.verification}`);
4614
+ if (r.created === true) parts.push("created yes");
4615
+ else if (r.created === false) parts.push("created pre-existing");
4616
+ else if (r.created === "n-a") parts.push("created n-a");
4617
+ if (r.cleanup !== void 0 && r.cleanup !== "none-required") parts.push(`cleanup ${r.cleanup}`);
4618
+ if (typeof r.bytes === "number") parts.push(`${r.bytes} bytes / ${r.entries} entries`);
4619
+ parts.push(`${(durationMs / 1e3).toFixed(1)}s`);
4620
+ if (r.note !== void 0 && r.note.length > 0) parts.push(`(${r.note})`);
4621
+ return parts.join(" · ");
4622
+ }
4139
4623
  function renderValue(v) {
4140
4624
  if (v.kind === "status" || v.kind === "roster" || v.kind === "job" || v.kind === "copy" || v.kind === "rsync") return v.text;
4141
4625
  const parts = [];
@@ -4150,7 +4634,8 @@ function renderValue(v) {
4150
4634
  const suffix = markers.length > 0 ? ` · ${markers.join(" · ")}` : "";
4151
4635
  const cwdTag = typeof v.cwd === "string" && v.cwd.length > 0 ? ` · cwd ${v.cwd}` : "";
4152
4636
  const sessionTag = typeof v.session === "string" && v.session.length > 0 ? ` · ${v.session}` : "";
4153
- parts.push(`[exit ${v.exit} · ${secs}s${suffix}${cwdTag}${sessionTag}]`);
4637
+ const runnerTag = typeof v.runner === "string" && v.runner.length > 0 ? ` · runner ${v.runner}` : "";
4638
+ parts.push(`[exit ${v.exit} · ${secs}s${suffix}${cwdTag}${sessionTag}${runnerTag}]`);
4154
4639
  if (v.stderr !== void 0 && v.stderr.length > 0) parts.push("[stderr]", v.stderr);
4155
4640
  return parts.join("\n");
4156
4641
  }