better-dsh 0.3.3 → 0.3.4-c
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.
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# ADR 0015 — Independent bundles and Agent-owned remote tools
|
|
2
2
|
|
|
3
|
-
Status: accepted design (2026-10-10); consumer acceptance tracked separately.
|
|
3
|
+
Status: accepted design (2026-10-10); consumer acceptance tracked separately. The
|
|
4
|
+
companion-install and Super remote-tool-row legs are superseded by
|
|
5
|
+
[ADR 0016](0016-two-independent-packages-no-companion.md) (2026-11-02); the
|
|
6
|
+
Better-owned remote tools and the hub-target integration are retained.
|
|
4
7
|
|
|
5
8
|
## Context
|
|
6
9
|
|
package/docs/remote-tools.md
CHANGED
|
@@ -13,10 +13,10 @@ Better Dash and Super Dash each provide the native `remote` tool and REPL route.
|
|
|
13
13
|
| Register an Agent-local SSH identity (no dialing) | `remote {action:"add",target:"ssh:build",ssh:{host:"host",user:"user",key:"/origin/key"}}`; omit `target` for an auto id (`ssh-xxxx`) |
|
|
14
14
|
| Register a BYO PTY from a spawn command | `remote {action:"add",spawn:"docker exec -it img bash",target:"pty:build"}`; then address it like any session |
|
|
15
15
|
| Stateless command and stdin over an identity | `remote {action:"exec",target:"ssh:build",cmd:"cat",stdin:"hello"}` |
|
|
16
|
-
|
|
|
17
|
-
|
|
|
16
|
+
| Run a local script as one `bash -c` command (stdin = the script's own input) | `remote {action:"exec",target:"ssh:alias",scriptPath:"/absolute/script.sh",stdin:"payload\n"}` |
|
|
17
|
+
| Use a machine's persistent shell (default PTY) | `remote {action:"exec",target:"ssh:alias",cmd:"cd /tmp; export CHECK=ready"}` — consecutive execs reuse one shell (cwd/env persist) |
|
|
18
18
|
| Reuse a persistent shell | `remote {action:"exec",target:"pty:<id\|label>",cmd:"…"}` |
|
|
19
|
-
| Answer a prompt / feed a running command (password, OTP, token, choice, REPL line, `\u0003`/`\u0004`) | `remote {action:"
|
|
19
|
+
| Answer a prompt / feed a running command (password, OTP, token, choice, REPL line, `\u0003`/`\u0004`) | `remote {action:"exec",target:"pty:<id\|label>",stdin:"the-answer\n"}` (stdin-only raw write) — optional `commandId` guards against stale prompts |
|
|
20
20
|
| Close a PTY session | `remote {action:"close",target:"pty:<id\|label>",force:true}` |
|
|
21
21
|
| Native CLI copy | `remote_transfer {mode:"copy",src:"/absolute/source",dst:"ssh:alias:/absolute/destination",argv:["-p"]}` |
|
|
22
22
|
| Native rsync | `remote_transfer {mode:"rsync",src:"/absolute/source/",dst:"ssh:alias:/existing/destination/",argv:["-a","--partial","--dry-run"]}` |
|
|
@@ -29,16 +29,16 @@ Interactive flow — see a prompt, answer it, read the response:
|
|
|
29
29
|
|
|
30
30
|
1. Start the program: `exec` with `cmd` (foreground, `run_in_background`, or after a timeout handoff — identity is preserved either way). The running receipt carries the output so far plus `session` / `commandId` / `jobId`.
|
|
31
31
|
2. Watch for the prompt in that output; use `job_output <jobId>` when you need more.
|
|
32
|
-
3. Send the answer: `{action:"
|
|
32
|
+
3. Send the answer: `{action:"exec", target:"pty:<id|label>", stdin:"the-answer\n"}` (stdin alone = raw write; the `input` action is gone). `stdin` passes through verbatim — include the trailing `\n` to submit a line; `\u0003` is Ctrl-C and `\u0004` is Ctrl-D as raw characters; a literal backslash-x-0-4 stays four characters; `""` writes zero bytes (not EOF). The optional `commandId` must equal the running command's id; a stale id refuses the write instead of feeding the next command.
|
|
33
33
|
4. Read the response with `job_output` on the SAME job. The input receipt only confirms N bytes were written — it never means the password was accepted or the login succeeded; judge from the program's output and the job's exit.
|
|
34
34
|
|
|
35
35
|
Honest limits of the input lane: Ctrl-D is not a universal EOF (on a line without a trailing newline it first delivers that text — send it again on the emptied line to end a reader like `cat`); Ctrl-C only interrupts where the terminal mode allows; `job_kill` remains the explicit cancel; `input` never creates, dials, reconnects or replays — address a live session as `pty:<id|label>`, any transport. A command waiting for input IS the busy state — a new `cmd` gets `[E_SESSION_BUSY]`; feed the running command instead of queueing a second one. On a oneshot, `stdin` is startup-only: known data may ride the initial `exec`, but a oneshot cannot be fed after it starts and its timeout still cancels the whole group — a prompt there means cancel and switch to PTY. Heredocs and input redirection remain valid bulk-input alternatives; there is no global stdin ban. Cancelling is reliable (0.3.2-d): `job_kill` writes its status marker after the terminal's ^C input flush, so even a cancel that follows a bare Ctrl-C settles `killed · exit 130` with the session alive — and hub-lane control bytes cross the local raw half as bytes, interpreted by the peer pty, so ^C/^D behave like every other transport.
|
|
36
36
|
|
|
37
37
|
Use the stateless oneshot when a step is genuinely one-shot (a probe, a pipeline stage, a supervised build you will not revisit) or when the `runner` supervision guarantee matters. On dumb-pipe transports the supervisor is the pinned `superd-run` helper (or its POSIX wrapper) — receipts say which ran. On the Hub lane the peer IS our own server: supervision is native there (`bash -lc` in a detached group, TERM→2s→KILL, output rings, lease lifecycle) and receipts say `hub-lease`; the helper is a dumb-pipe artifact and is never uploaded over the Hub. Note that `exec` always executes something: a warm session is created by running a command (`true` is enough), never by omitting `cmd`.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
A machine target defaults to its reusable PTY; `mode:"oneshot"` selects the isolated stateless lane. The old `:pty` creation suffix and the `input` action are gone — multiple addressable shells come from `handoff {target:<machine>}` (detaches the default as `pty:<id>`), and prompt input is `exec` with `stdin` alone. `add` only registers (BYO identity or BYO PTY) and never runs a command. `scriptPath` is an alternative to `cmd` and may carry `stdin` (startup input rides after the script bytes) in both lanes. Direct local command execution is not provided; use native local shell tools. Background jobs require the official jobs service/controller and are read/killed with `job_output`/`job_kill`; on PTY lanes a background job's jobId is the commandId (a timeout handoff keeps them distinct until adoption backfills the job). 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.
|
|
40
40
|
|
|
41
|
-
PTY state is durable and readable: every dispatch returns exit, cwd and `envDelta` (variable names and change markers only — values never leave the endpoint; fetch them yourself if needed). `close` is idempotent (`closed:false, reason:"not-found"|"already-closed-or-reaped"`); a session with a running job refuses close unless `force:true`, which first interrupts the running command — its job settles in the killed family (`interrupted: exit 130`, or `session closed` when the frame cannot arrive) — and then kills the group. A busy session rejects new commands instead of queueing (`[E_SESSION_BUSY]` names the running id and the ways out:
|
|
41
|
+
PTY state is durable and readable: every dispatch returns exit, cwd and `envDelta` (variable names and change markers only — values never leave the endpoint; fetch them yourself if needed). `close` is idempotent (`closed:false, reason:"not-found"|"already-closed-or-reaped"`); a session with a running job refuses close unless `force:true`, which first interrupts the running command — its job settles in the killed family (`interrupted: exit 130`, or `session closed` when the frame cannot arrive) — and then kills the group. A busy session rejects new commands instead of queueing (`[E_SESSION_BUSY]` names the running id and the ways out: exec stdin-only, `job_output`, `job_kill`, handoff, or another session/oneshot); open another PTY or use a oneshot for parallel work. Idle PTYs are reaped after 15 minutes; an Agent holds at most 30 live sessions across connections (defaults and handed-off shells included; dead sessions do not count; the limit is disclosed only when reached). `hub:<m4>` as a machine target carries the same default-PTY semantics over the Hub WebSocket lane — identity, labels, stdin-only writes, parallelism and background work exactly like ssh/docker/incus/BYO.
|
|
42
42
|
|
|
43
43
|
Output returns IN FULL up to retention (0.3.2-e: the self-made 30K returned-tail window is removed — model-context sizing is the native agent runtime's job, e.g. the DSH spill policy or a foreign runtime's own guard). Native/PTY retains at most 4,000,000 UTF-16 units per stream; Hub oneshot 4 MiB bytes. Beyond retention the returned text is the retained tail, headed by a one-line disclosure banner, with `outputStats` giving total/retained/dropped/returned and `truncated` set to the original total. Read `outputStats` units and dropped/returned counters rather than treating character counts as bytes. Discarded output is not recoverable after restart.
|
|
44
44
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Remote 工具 enhancement — 交付、生命周期与传输规格
|
|
2
2
|
|
|
3
3
|
- 日期:2026-10-10(Asia/Hong_Kong)
|
|
4
|
+
- **2026-11-02 修订(ADR 0016,user 决策)**:Super 不再注册模型侧 remote 工具——`superd-remote` 组合行与公开 `super-dsh/remote` 导出已删除,`remote`/`remote_transfer` 只由 Better Dash 交付;companion 自动/手动安装一并移除。Super 的生成闭包**保留为 hub 内部私有依赖**(hub exec/PTY/task/file lanes 引用其进程原语;无导出、无组合行,provenance 管线不变)。下文涉及 Super 组合行/公开导出与 companion 的条款仅作历史。Better 侧源码与「Super hub 在场即作 target」的检测逻辑不变。
|
|
4
5
|
- 状态:2026-10-10 user 已审阅并明确授权执行;Task0–7 已独立审阅通过;Task8 完整双包消费者验收进行中,L 实装暴露的 worker/SSH cleanup 缺陷已进入修复验证。用户亲验与发布尚未放行。
|
|
5
6
|
- 2026-10-25 surface 修订(user 指令,本轮实现):`remote` 的判别字段改为**必填** `action: exec|add|close|status`;文件传输字段(`src`/`dst`/`argv`/`pullArgv`/`pushArgv`/`verify`/`mkdir`)迁到独立工具 `remote_transfer`,判别字段为**必填** `mode: copy|rsync`。`add` 只注册 BYO 身份/终端,不执行命令;`exec` 必填 `target` 且必须执行东西(warm 会话用 `cmd:"true"`)。工具简介把 PTY 重新定位为「stateful oneshot」并鼓励优先使用。下文 §5 的旧 `action:"copy"|"rsync"` 调用形与「无 action」形仅作历史。
|
|
6
7
|
- 0.3.2-b 对齐(2026-10-19):`script` 已定名 `scriptPath`;`mode` 字段已移除(用 `<transport>:pty[:label]` 创建、`pty:<id|label>` 复用);`byossh:`/`byop:` 前缀已移除(`ssh`/`spawn` 参数在场即 BYO)。下文旧调用形仅作历史。
|
package/lib/remote/plugin.d.ts
CHANGED
|
@@ -148,9 +148,13 @@ interface ProcessOptions {
|
|
|
148
148
|
onOutput?: () => void;
|
|
149
149
|
/** Overlay on process.env (tool-built values only, e.g. RSYNC_RSH). */
|
|
150
150
|
env?: NodeJS.ProcessEnv;
|
|
151
|
+
/** Task E(SSH MUX):取消(timeout 或显式 cancel)即同步回调,不等 close。mux master 会经
|
|
152
|
+
* fd-passing 持有被杀客户端的会话 fd 直到远端会话结束——若远端清理 killer 等 close 后才发射,
|
|
153
|
+
* 就成了「close 等远端结束、远端结束等 killer」死锁。此处先行启动 killer 才能解开 close。至多一次。 */
|
|
154
|
+
onCancel?: () => void;
|
|
151
155
|
}
|
|
152
156
|
/** Real detached process. Completion includes group cleanup, even after its leader closes. */
|
|
153
|
-
declare function startBinaryProcess(argv: string[], options?: Pick<ProcessOptions, 'cwd' | 'timeoutSec' | 'env'>): {
|
|
157
|
+
declare function startBinaryProcess(argv: string[], options?: Pick<ProcessOptions, 'cwd' | 'timeoutSec' | 'env' | 'onCancel'>): {
|
|
154
158
|
stdout: Readable;
|
|
155
159
|
stderr: Readable;
|
|
156
160
|
stdin: Writable;
|
|
@@ -257,15 +261,6 @@ interface PtyDispatchResult {
|
|
|
257
261
|
running?: boolean;
|
|
258
262
|
job?: PtyRunningJob;
|
|
259
263
|
}
|
|
260
|
-
/** 账本条目(§5):commandId / 摘要 / exit / 耗时;保留最近 20 条(user 定案)。 */
|
|
261
|
-
interface LedgerEntry {
|
|
262
|
-
commandId: string;
|
|
263
|
-
jobId?: string;
|
|
264
|
-
summary: string;
|
|
265
|
-
exit: number | null;
|
|
266
|
-
durationMs: number;
|
|
267
|
-
running: boolean;
|
|
268
|
-
}
|
|
269
264
|
type PtyState = 'cold' | 'starting' | 'ready' | 'dead' | 'disposed';
|
|
270
265
|
/** 会话运行时快照(status 面消费,Ruling 16 词汇源)。 */
|
|
271
266
|
interface SessionSnapshot {
|
|
@@ -284,12 +279,11 @@ declare class PtySession {
|
|
|
284
279
|
private deathWaiters;
|
|
285
280
|
private diag;
|
|
286
281
|
private inFlight;
|
|
282
|
+
private cmdSeq;
|
|
287
283
|
/** P4/P5:当前占用前台的 detached job(background/超时 handoff)。非 null 时新命令拒绝(Q6)。 */
|
|
288
284
|
private detachedJob;
|
|
289
285
|
/** §3.2(2026-10-25):当前占用会话的命令——dispatch 同步占位,settle/cleanup 释放;input 的唯一写入对象。 */
|
|
290
286
|
private activeCommand;
|
|
291
|
-
/** 账本(§5):本会话最近 20 条命令(FIFO,超出丢最旧)。 */
|
|
292
|
-
private ledger;
|
|
293
287
|
/** env 侧信道基线(init E 帧);undefined = 侧信道不可用(不报假 delta)。 */
|
|
294
288
|
private envBaseline;
|
|
295
289
|
/** idle 起点(ready / settle 时重置);null = 尚无 idle 时钟(cold 或已 dispose)。 */
|
|
@@ -310,7 +304,12 @@ declare class PtySession {
|
|
|
310
304
|
get lastCwd(): string | null;
|
|
311
305
|
/** 占用前台的 detached job 的 commandId(无 = null;close 拒绝与 busy 文案共用)。 */
|
|
312
306
|
get detachedCommandId(): string | null;
|
|
313
|
-
/**
|
|
307
|
+
/** handoff/card 面(2026-11-01 默认 PTY 轮):当前占用会话的命令与可选 job 句柄;无 = null。 */
|
|
308
|
+
get active(): {
|
|
309
|
+
commandId: string;
|
|
310
|
+
jobId?: string;
|
|
311
|
+
} | null;
|
|
312
|
+
/** job 通道收养后回填 jobId(timeout handoff 时 registry id ≠ commandId);运行中的命令同步携带(stdin-only 写入回执用)。 */
|
|
314
313
|
attachJob(commandId: string, jobId: string): void;
|
|
315
314
|
/** Workbench 身份(§3):id + 可选 label + connection + byo 标记。 */
|
|
316
315
|
get identity(): {
|
|
@@ -320,8 +319,6 @@ declare class PtySession {
|
|
|
320
319
|
byo: boolean;
|
|
321
320
|
builtOn: boolean;
|
|
322
321
|
};
|
|
323
|
-
/** 账本快照(session card 面,只读)。 */
|
|
324
|
-
ledgerSnapshot(): readonly LedgerEntry[];
|
|
325
322
|
/** spawn 创建原文(仅 BYO 会话;v2 起仅存档,不参与复用判定)。 */
|
|
326
323
|
get origin(): string | undefined;
|
|
327
324
|
/** status 面快照:state + busy(有命令在跑)+ idleMs(上次 settle 至今)+ pid(进程组头,kill 兜底可观测)。 */
|
|
@@ -520,6 +517,41 @@ interface RsyncReceipt extends RunResult {
|
|
|
520
517
|
*/
|
|
521
518
|
declare function runRsync(request: RsyncRequest, source: RsyncEndpoint, destination: RsyncEndpoint, signal: AbortSignal, options?: RelayRsyncOptions): Promise<RsyncReceipt>;
|
|
522
519
|
//#endregion
|
|
520
|
+
//#region src/remote/ssh-mux.d.ts
|
|
521
|
+
interface SshMuxIdentity {
|
|
522
|
+
/** 原始 target 串(如 `ssh:dev4` / `dev4`)——同一 effective 身份不同拼法也不串 socket。 */
|
|
523
|
+
target: string;
|
|
524
|
+
connection: SshConnection;
|
|
525
|
+
}
|
|
526
|
+
interface SshMuxOptions {
|
|
527
|
+
persistSec?: number;
|
|
528
|
+
/** socket 目录根(默认 os.tmpdir());测试注入口。 */
|
|
529
|
+
rootDir?: string;
|
|
530
|
+
runner?: (argv: string[], opts?: {
|
|
531
|
+
timeoutSec?: number;
|
|
532
|
+
}) => Promise<{
|
|
533
|
+
exit: number | null;
|
|
534
|
+
}>;
|
|
535
|
+
}
|
|
536
|
+
/** 每 Agent 一个(driver 私有):tool-owned 0700 socket 目录 + 按连接身份的 socket 名。
|
|
537
|
+
* 目录名含随机 owner 后缀,跨 Agent 天然不共享;dispose 逐 socket `-O exit` 后整目录删除。 */
|
|
538
|
+
declare class SshMux {
|
|
539
|
+
private readonly persistSec;
|
|
540
|
+
private readonly rootDir;
|
|
541
|
+
private readonly runner;
|
|
542
|
+
private readonly owner;
|
|
543
|
+
private readonly sockets;
|
|
544
|
+
private dir;
|
|
545
|
+
private disposal;
|
|
546
|
+
constructor(options?: SshMuxOptions);
|
|
547
|
+
/** 注入 SSH argv 的 MUX 选项(trusted internal argv,非模型输入)。目录懒建(纯 docker/hub
|
|
548
|
+
* Agent 永不触盘);超限/已 dispose 返回 [] = 诚实退化为普通连接。 */
|
|
549
|
+
argvFor(identity: SshMuxIdentity): string[];
|
|
550
|
+
/** Teardown:先 `-O exit` 逐 socket 礼貌拆除 master(不拨号、5s 上限、错误全吞——
|
|
551
|
+
* master 已死/残 socket 属正常),再删目录。单飞(重复调用同一 Promise)。 */
|
|
552
|
+
dispose(): Promise<void>;
|
|
553
|
+
}
|
|
554
|
+
//#endregion
|
|
523
555
|
//#region src/remote/oneshot.d.ts
|
|
524
556
|
interface OneShotOptions {
|
|
525
557
|
stdin?: string;
|
|
@@ -539,6 +571,8 @@ interface ProbeOptions {
|
|
|
539
571
|
connectTimeoutSec?: number;
|
|
540
572
|
hardTimeoutSec?: number;
|
|
541
573
|
runner?: (argv: string[], opts?: OneShotOptions) => Promise<OneShotResult>;
|
|
574
|
+
/** Task E:SSH 探测与 oneshot 执行链共用 MUX(同一身份 → 同一 master,探测零额外握手)。 */
|
|
575
|
+
muxArgvFor?: (plan: TargetPlan) => readonly string[];
|
|
542
576
|
}
|
|
543
577
|
//#endregion
|
|
544
578
|
//#region src/remote/roster.d.ts
|
|
@@ -603,6 +637,7 @@ interface RemoteDriverOptions {
|
|
|
603
637
|
onAudit?: (r: RemoteAuditRecord) => void;
|
|
604
638
|
oneshotArgvFor?: (plan: TargetPlan, cmd: string, options: {
|
|
605
639
|
cleanupTag?: string;
|
|
640
|
+
muxArgv?: readonly string[];
|
|
606
641
|
}) => string[];
|
|
607
642
|
ptyArgvFor?: (plan: TargetPlan) => string[];
|
|
608
643
|
spawnArgvFor?: (spawnCommand: string) => string[];
|
|
@@ -611,6 +646,8 @@ interface RemoteDriverOptions {
|
|
|
611
646
|
probeRunner?: ProbeOptions['runner'];
|
|
612
647
|
/** 测试/验收缝:helper 资产来源(默认 lib 旁挂 helper-bin);显式 null 强制 shim 路径。 */
|
|
613
648
|
helperAssetsFor?: () => HelperAssets | null;
|
|
649
|
+
/** 测试/验收缝:SSH oneshot MUX 状态(默认每 driver 一个 SshMux = 每 Agent 隔离)。 */
|
|
650
|
+
sshMux?: SshMux;
|
|
614
651
|
/** 测试缝:roster 扫描执行器(裸名解析复用)。 */
|
|
615
652
|
rosterRunner?: RosterOptions['runner'];
|
|
616
653
|
/** 测试缝:ssh config 读取(裸名解析复用)。 */
|
|
@@ -626,6 +663,10 @@ declare function hubPtyAssets(): {
|
|
|
626
663
|
declare class RemoteDriver {
|
|
627
664
|
private readonly opts;
|
|
628
665
|
private readonly pool;
|
|
666
|
+
/** 默认 PTY 槽(spec remote-default-pty §1):sessionKey → 归一 machineKey → pool key。
|
|
667
|
+
* 会话本体是普通 PtySession(id 创建即认领,roster 全可见);槽登记在 dispose/TTL 后悬空,
|
|
668
|
+
* 取槽时经 pool.get(disposed 视同出池)惰性清理。 */
|
|
669
|
+
private readonly defaultSlots;
|
|
629
670
|
private readonly labels;
|
|
630
671
|
private readonly hubOwner;
|
|
631
672
|
private readonly hubProviders;
|
|
@@ -638,6 +679,7 @@ declare class RemoteDriver {
|
|
|
638
679
|
private readonly runnerOf;
|
|
639
680
|
private disposal;
|
|
640
681
|
private readonly ptyIdleTtlSec;
|
|
682
|
+
private readonly mux;
|
|
641
683
|
constructor(opts?: RemoteDriverOptions);
|
|
642
684
|
call(params: RemoteCallParams, callCtx?: {
|
|
643
685
|
sessionKey?: string;
|
|
@@ -676,6 +718,10 @@ declare class RemoteDriver {
|
|
|
676
718
|
private planOf;
|
|
677
719
|
/** ssh:<name> 的池感知解析:BYO 身份 → 其 connection;否则按 config 别名/主机名。 */
|
|
678
720
|
private sshPlanFor;
|
|
721
|
+
/** Task E MUX 身份:仅 SSH plan 注入(docker/incus/hub 返回 undefined)。身份键 = 原始
|
|
722
|
+
* target 串 + 有效连接字段(host/user/port/key/ProxyJump)——同身份共享 master,跨身份
|
|
723
|
+
* 不串 socket;目录/寿命/拆除全部由 SshMux 自持。 */
|
|
724
|
+
private muxArgvFor;
|
|
679
725
|
/** BYO ssh 注册(v2:无 spec 对比——label 占用即拒;注册成功才认领)。返回实际名(label 或 s-id)。 */
|
|
680
726
|
private registerByoSsh;
|
|
681
727
|
/** PTY 会话创建(native/spawn 同一形状):上限 → label 认领 → 惰性会话(失败由 onDead 释放 label)。 */
|
|
@@ -683,8 +729,36 @@ declare class RemoteDriver {
|
|
|
683
729
|
private ttlMinutes;
|
|
684
730
|
/** connection 由 BYO ssh 身份建立 → card 面标 `(built on)`(与 roster 同词,R2 复核 P2-10)。 */
|
|
685
731
|
private isBuiltOn;
|
|
686
|
-
/** PTY 创建形唯一实现(execute 与 background 预建共用):spawn BYO
|
|
732
|
+
/** PTY 创建形唯一实现(execute 与 background 预建共用):spawn BYO(spec 2026-11-01:native `:pty`/hub-pty 创建语法已删,machine 会话经默认槽 + handoff)。 */
|
|
687
733
|
private createPtyLane;
|
|
734
|
+
/** 池条目的槽键归一:同一实体(host/container/machine 身份)跨裸名/前缀/撞名前缀共享同一默认槽。 */
|
|
735
|
+
private machineKeyOfEntry;
|
|
736
|
+
/** 默认槽的 machine 规格:归一 key + 创建 argv + 展示 connection(hub 每次经 ptyEndpoint 重验 pairing,P10)。 */
|
|
737
|
+
private machineSlotSpec;
|
|
738
|
+
/** 取/懒建 machine 的默认 PTY 槽:dead/disposed 槽惰性重建;id 创建即认领(roster 全可见 + 防撞)。 */
|
|
739
|
+
private defaultSlot;
|
|
740
|
+
private releaseDefaultSlot;
|
|
741
|
+
/** 按池键反查并解绑默认槽(background 提交即 handoff 用);非槽会话无操作。 */
|
|
742
|
+
private releaseSlotByPoolKey;
|
|
743
|
+
/** roster/status 面:本 session 的默认槽标注(session id → machineKey;悬空条目过滤)。 */
|
|
744
|
+
private defaultMarks;
|
|
745
|
+
/** spec §1 handoff(2026-11-01 二/三轮裁决):把 machine 默认 PTY 解绑成可寻址 pty:<id>——同引用移动
|
|
746
|
+
* (不重启、不复制输出流);fresh(尚无默认槽)= 后台创建空闲 shell 再隔离(默认 PTY 思维模型恒
|
|
747
|
+
* ready,创建时机是实现细节);busy = 附当前命令与 job 句柄。machine 下次 exec 按需新建默认 PTY。 */
|
|
748
|
+
handoff(target: string, callCtx?: {
|
|
749
|
+
sessionKey?: string;
|
|
750
|
+
}): Promise<RemoteCallResult>;
|
|
751
|
+
/** close 统一入口(spec §1 + 三轮补充裁决):machine target 只关默认槽;pty selector 关该会话;
|
|
752
|
+
* 不存在 job 定位——取消运行中命令的唯一入口是官方 job_kill。 */
|
|
753
|
+
close(target: string, sessionKey: string, force?: boolean): Promise<{
|
|
754
|
+
session: string;
|
|
755
|
+
connection: string;
|
|
756
|
+
closed: boolean;
|
|
757
|
+
reason?: string;
|
|
758
|
+
killed: string[];
|
|
759
|
+
message?: string;
|
|
760
|
+
}>;
|
|
761
|
+
private closeDefault;
|
|
688
762
|
/** background PTY lane:会话先占位(不拨号)再进官方 jobs registry——首回执必须能寻址;
|
|
689
763
|
* registry 拒绝时把新建的会话拆掉(复用形的会话绝不拆)。返回 undefined = 走普通 oneshot job。 */
|
|
690
764
|
preparePtyLane(params: RemoteCallParams, callCtx?: {
|
|
@@ -709,10 +783,9 @@ declare class RemoteDriver {
|
|
|
709
783
|
};
|
|
710
784
|
/** job 通道收养回填(timeout handoff 时 registry id ≠ commandId,账本记录两者)。 */
|
|
711
785
|
attachPtyJob(commandId: string, jobId: string, sessionKey: string): void;
|
|
712
|
-
/** §
|
|
713
|
-
* 追加 stdin
|
|
714
|
-
*
|
|
715
|
-
* 一律明确拒绝并给出可恢复指引。回执附 connection/label 供工具层渲染。 */
|
|
786
|
+
/** spec remote-default-pty §1(2026-11-01):exec stdin-only 写入核心——向已有 live PTY 的当前运行命令
|
|
787
|
+
* 追加 exact-byte stdin。寻址:pty:<id|label> / 裸 label 命中 pty 项 / machine target(其默认槽的
|
|
788
|
+
* 当前命令,无槽不懒建、如实报错)。不触发拨号、创建或重连;回执附 connection/label 供工具层渲染。 */
|
|
716
789
|
input(target: string, options: PtyInputOptions, callCtx?: {
|
|
717
790
|
sessionKey?: string;
|
|
718
791
|
}): Promise<PtyInputReceipt & {
|