@clawos-dev/clawd 0.2.95 → 0.2.96-beta.178.1cde6b7
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/dist/cli.cjs +209 -92
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -21289,10 +21289,28 @@ function cloneState(s) {
|
|
|
21289
21289
|
freshSpawn: s.freshSpawn,
|
|
21290
21290
|
procAlive: s.procAlive,
|
|
21291
21291
|
seenUuids: s.seenUuids,
|
|
21292
|
-
subSessionMeta: s.subSessionMeta
|
|
21292
|
+
subSessionMeta: s.subSessionMeta,
|
|
21293
|
+
// ReadyGate v2:readyForSend 是基础类型直接复制;pendingSend 必须浅拷贝防止
|
|
21294
|
+
// reduce 内 push/shift 在调用方原 state 上副作用(reducer 纯函数契约)
|
|
21295
|
+
readyForSend: s.readyForSend,
|
|
21296
|
+
pendingSend: s.pendingSend.slice()
|
|
21293
21297
|
};
|
|
21294
21298
|
}
|
|
21295
21299
|
var IDLE_KILL_DELAY_MS = 6e4;
|
|
21300
|
+
function tryFlushPending(state, deps) {
|
|
21301
|
+
if (!state.readyForSend || state.pendingSend.length === 0) return [];
|
|
21302
|
+
const text = state.pendingSend.shift();
|
|
21303
|
+
state.readyForSend = false;
|
|
21304
|
+
const payload = deps.encodeStdin(text, { sessionId: state.file.sessionId });
|
|
21305
|
+
return [{ kind: "write-stdin", payload }];
|
|
21306
|
+
}
|
|
21307
|
+
function rgDbg(event, info) {
|
|
21308
|
+
console.debug(`[RG] ${event}`, info);
|
|
21309
|
+
}
|
|
21310
|
+
function resetReadyGate(next) {
|
|
21311
|
+
next.pendingSend = [];
|
|
21312
|
+
next.readyForSend = true;
|
|
21313
|
+
}
|
|
21296
21314
|
var SEEN_UUID_CAP = 2e3;
|
|
21297
21315
|
function applyEventBatch(state, events, deps) {
|
|
21298
21316
|
if (events.length === 0) return { state, effects: [] };
|
|
@@ -21532,6 +21550,9 @@ function applyCommand(state, command, deps) {
|
|
|
21532
21550
|
next.nextSeq = 0;
|
|
21533
21551
|
next.turnOpen = false;
|
|
21534
21552
|
next.status = "running";
|
|
21553
|
+
if (deps.mode === "tui") {
|
|
21554
|
+
next.readyForSend = false;
|
|
21555
|
+
}
|
|
21535
21556
|
effects.push({ kind: "spawn", ctx: buildSpawnContext(next, deps) });
|
|
21536
21557
|
effects.push({
|
|
21537
21558
|
kind: "emit-frame",
|
|
@@ -21549,8 +21570,29 @@ function applyCommand(state, command, deps) {
|
|
|
21549
21570
|
};
|
|
21550
21571
|
const pushed = pushEventToBuffer(next, userEvent, deps);
|
|
21551
21572
|
effects.push(...pushed.effects);
|
|
21552
|
-
const
|
|
21553
|
-
|
|
21573
|
+
const procAliveBefore = state.procAlive;
|
|
21574
|
+
const readyBefore = state.readyForSend;
|
|
21575
|
+
const queueBefore = state.pendingSend.length;
|
|
21576
|
+
if (deps.mode === "tui") {
|
|
21577
|
+
pushed.state.pendingSend.push(command.text);
|
|
21578
|
+
effects.push(...tryFlushPending(pushed.state, deps));
|
|
21579
|
+
} else {
|
|
21580
|
+
const payload = deps.encodeStdin(command.text, { sessionId });
|
|
21581
|
+
effects.push({ kind: "write-stdin", payload });
|
|
21582
|
+
}
|
|
21583
|
+
rgDbg("send", {
|
|
21584
|
+
sid: sessionId,
|
|
21585
|
+
mode: deps.mode ?? "sdk",
|
|
21586
|
+
procAliveBefore,
|
|
21587
|
+
spawned: !procAliveBefore,
|
|
21588
|
+
readyBefore,
|
|
21589
|
+
queueBefore,
|
|
21590
|
+
textLen: command.text.length,
|
|
21591
|
+
preview: command.text.slice(0, 40),
|
|
21592
|
+
wroteStdin: effects.some((e) => e.kind === "write-stdin"),
|
|
21593
|
+
readyAfter: pushed.state.readyForSend,
|
|
21594
|
+
queueAfter: pushed.state.pendingSend.length
|
|
21595
|
+
});
|
|
21554
21596
|
return { state: pushed.state, effects };
|
|
21555
21597
|
}
|
|
21556
21598
|
case "stop": {
|
|
@@ -21566,6 +21608,7 @@ function applyCommand(state, command, deps) {
|
|
|
21566
21608
|
}
|
|
21567
21609
|
next.pendingPermissions = {};
|
|
21568
21610
|
next.pendingQuestions = {};
|
|
21611
|
+
resetReadyGate(next);
|
|
21569
21612
|
if (next.procAlive) {
|
|
21570
21613
|
next.status = "stopping";
|
|
21571
21614
|
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
@@ -21583,6 +21626,9 @@ function applyCommand(state, command, deps) {
|
|
|
21583
21626
|
next.nextSeq = 0;
|
|
21584
21627
|
next.turnOpen = false;
|
|
21585
21628
|
next.status = "running";
|
|
21629
|
+
if (deps.mode === "tui") {
|
|
21630
|
+
next.readyForSend = false;
|
|
21631
|
+
}
|
|
21586
21632
|
effects.push({ kind: "spawn", ctx: buildSpawnContext(next, deps) });
|
|
21587
21633
|
effects.push({
|
|
21588
21634
|
kind: "emit-frame",
|
|
@@ -21603,6 +21649,7 @@ function applyCommand(state, command, deps) {
|
|
|
21603
21649
|
}
|
|
21604
21650
|
next.pendingPermissions = {};
|
|
21605
21651
|
next.pendingQuestions = {};
|
|
21652
|
+
resetReadyGate(next);
|
|
21606
21653
|
if (next.procAlive) {
|
|
21607
21654
|
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
21608
21655
|
}
|
|
@@ -21651,6 +21698,7 @@ function applyCommand(state, command, deps) {
|
|
|
21651
21698
|
frame: { type: "session:cleared", sessionId }
|
|
21652
21699
|
});
|
|
21653
21700
|
}
|
|
21701
|
+
resetReadyGate(next);
|
|
21654
21702
|
if (next.procAlive) {
|
|
21655
21703
|
effects.push({ kind: "kill", signal: "SIGKILL" });
|
|
21656
21704
|
}
|
|
@@ -21683,6 +21731,7 @@ function applyCommand(state, command, deps) {
|
|
|
21683
21731
|
next.pendingPermissions = {};
|
|
21684
21732
|
next.freshSpawn = false;
|
|
21685
21733
|
next.seenUuids = [];
|
|
21734
|
+
resetReadyGate(next);
|
|
21686
21735
|
effects.push({ kind: "persist-file", file: nextFile });
|
|
21687
21736
|
effects.push(sessionInfoFrame(nextFile));
|
|
21688
21737
|
effects.push({
|
|
@@ -21717,6 +21766,7 @@ function reduceSession(state, input, deps) {
|
|
|
21717
21766
|
const next = cloneState(state);
|
|
21718
21767
|
next.procAlive = false;
|
|
21719
21768
|
next.status = "stopped";
|
|
21769
|
+
resetReadyGate(next);
|
|
21720
21770
|
const sessionId = next.file.sessionId;
|
|
21721
21771
|
const clearedEffects = Object.keys(state.pendingQuestions ?? {}).map(
|
|
21722
21772
|
(toolUseId) => ({
|
|
@@ -21748,6 +21798,7 @@ function reduceSession(state, input, deps) {
|
|
|
21748
21798
|
case "proc-error": {
|
|
21749
21799
|
const next = cloneState(state);
|
|
21750
21800
|
next.status = "error";
|
|
21801
|
+
resetReadyGate(next);
|
|
21751
21802
|
return {
|
|
21752
21803
|
state: next,
|
|
21753
21804
|
effects: [
|
|
@@ -21899,6 +21950,22 @@ function reduceSession(state, input, deps) {
|
|
|
21899
21950
|
next.pendingPermissions = keep;
|
|
21900
21951
|
return { state: next, effects: [] };
|
|
21901
21952
|
}
|
|
21953
|
+
case "ready-detected": {
|
|
21954
|
+
if (deps.mode !== "tui") {
|
|
21955
|
+
rgDbg("ready-input", { sid: state.file.sessionId, skipped: "non-tui-mode" });
|
|
21956
|
+
return { state, effects: [] };
|
|
21957
|
+
}
|
|
21958
|
+
const next = cloneState(state);
|
|
21959
|
+
next.readyForSend = true;
|
|
21960
|
+
const flushEffects = tryFlushPending(next, deps);
|
|
21961
|
+
rgDbg("ready-input", {
|
|
21962
|
+
sid: next.file.sessionId,
|
|
21963
|
+
wroteStdin: flushEffects.length > 0,
|
|
21964
|
+
readyAfter: next.readyForSend,
|
|
21965
|
+
queueAfter: next.pendingSend.length
|
|
21966
|
+
});
|
|
21967
|
+
return { state: next, effects: flushEffects };
|
|
21968
|
+
}
|
|
21902
21969
|
default:
|
|
21903
21970
|
assertNever(input);
|
|
21904
21971
|
}
|
|
@@ -22062,7 +22129,9 @@ var SessionRunner = class {
|
|
|
22062
22129
|
resolveContextWindow: this.hooks.resolveContextWindow,
|
|
22063
22130
|
genUuid: this.hooks.genUuid ?? v4_default,
|
|
22064
22131
|
ownerDisplayName: this.hooks.ownerDisplayName,
|
|
22065
|
-
personaRoot: this.hooks.personaRoot
|
|
22132
|
+
personaRoot: this.hooks.personaRoot,
|
|
22133
|
+
// ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
|
|
22134
|
+
mode: this.hooks.mode
|
|
22066
22135
|
};
|
|
22067
22136
|
const { state, effects } = reduceSession(this.state, inputMsg, deps);
|
|
22068
22137
|
this.state = state;
|
|
@@ -22241,6 +22310,12 @@ var SessionRunner = class {
|
|
|
22241
22310
|
this.doKill(effect.signal);
|
|
22242
22311
|
break;
|
|
22243
22312
|
case "write-stdin":
|
|
22313
|
+
console.debug("[RG] write-stdin", {
|
|
22314
|
+
procAlive: !!this.proc,
|
|
22315
|
+
stdinWritable: !!this.proc?.stdin,
|
|
22316
|
+
len: effect.payload.length,
|
|
22317
|
+
preview: effect.payload.replace(/\x1b\[20[01]~/g, "\xB7paste\xB7").slice(0, 60)
|
|
22318
|
+
});
|
|
22244
22319
|
this.proc?.stdin?.write(effect.payload);
|
|
22245
22320
|
this.recorder?.tapStdinWrite(effect.payload);
|
|
22246
22321
|
break;
|
|
@@ -22492,7 +22567,11 @@ function makeInitialState(file, subSessionMeta) {
|
|
|
22492
22567
|
freshSpawn: false,
|
|
22493
22568
|
procAlive: false,
|
|
22494
22569
|
seenUuids: [],
|
|
22495
|
-
subSessionMeta
|
|
22570
|
+
subSessionMeta,
|
|
22571
|
+
// ReadyGate v2:默认值兼容 SDK happy path(直写 stdin,不走队列)。
|
|
22572
|
+
// TUI 模式由 reducer send 分支按 deps.mode='tui' 主动翻 readyForSend=false
|
|
22573
|
+
readyForSend: true,
|
|
22574
|
+
pendingSend: []
|
|
22496
22575
|
};
|
|
22497
22576
|
}
|
|
22498
22577
|
var SessionManager = class {
|
|
@@ -22742,7 +22821,10 @@ var SessionManager = class {
|
|
|
22742
22821
|
tool: input.tool,
|
|
22743
22822
|
relPath: input.relPath,
|
|
22744
22823
|
cwd: input.cwd
|
|
22745
|
-
}) : void 0
|
|
22824
|
+
}) : void 0,
|
|
22825
|
+
// ReadyGate v2:透传 daemon mode,runner 装配 ReducerDeps 时让 reducer 判断
|
|
22826
|
+
// send / ready-detected 分支走暂存队列还是直写
|
|
22827
|
+
mode: this.deps.mode
|
|
22746
22828
|
});
|
|
22747
22829
|
if (this.deps.mode === "tui" && !file.toolSessionId) {
|
|
22748
22830
|
const newTsid = v4_default();
|
|
@@ -23871,6 +23953,26 @@ var SessionManager = class {
|
|
|
23871
23953
|
"broadcast"
|
|
23872
23954
|
);
|
|
23873
23955
|
}
|
|
23956
|
+
/**
|
|
23957
|
+
* ClaudeTuiAdapter onReady callback(ReadyGate v2):ReadyDetector 扫到 cc TUI 主输入框 +
|
|
23958
|
+
* quiet window 通过时投递 reducer 'ready-detected' input,触发 pendingSend 队列出队 flush。
|
|
23959
|
+
* 仅 mode='tui' 时启用;SDK 模式 reducer 内 early return 也无害(双层兜底)。
|
|
23960
|
+
*/
|
|
23961
|
+
dispatchReadyDetected(toolSessionId) {
|
|
23962
|
+
if (this.deps.mode !== "tui") {
|
|
23963
|
+
console.debug("[RG] mgr-disp", { tsid: toolSessionId, skipped: "non-tui-mode" });
|
|
23964
|
+
return;
|
|
23965
|
+
}
|
|
23966
|
+
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
23967
|
+
const runner = sid ? this.runners.get(sid) : void 0;
|
|
23968
|
+
console.debug("[RG] mgr-disp", {
|
|
23969
|
+
tsid: toolSessionId,
|
|
23970
|
+
sid: sid ?? null,
|
|
23971
|
+
runnerFound: !!runner
|
|
23972
|
+
});
|
|
23973
|
+
if (!runner) return;
|
|
23974
|
+
runner.input({ kind: "ready-detected" });
|
|
23975
|
+
}
|
|
23874
23976
|
/** toolSessionId → sessionId 反查(遍历 runners);session 数典型 < 10,O(n) 可接受 */
|
|
23875
23977
|
sessionIdByToolSid(toolSessionId) {
|
|
23876
23978
|
for (const [sid, runner] of this.runners) {
|
|
@@ -24985,26 +25087,27 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
24985
25087
|
super();
|
|
24986
25088
|
this.pty = pty;
|
|
24987
25089
|
this.pid = pty.pid;
|
|
24988
|
-
const { logger, tag
|
|
24989
|
-
this.
|
|
24990
|
-
this.bootGateLogger = logger;
|
|
24991
|
-
this.bootGateTag = tag;
|
|
24992
|
-
logger?.debug("pty-child constructed", { pid: this.pid, tag, gated: !!bootGate });
|
|
25090
|
+
const { logger, tag } = opts;
|
|
25091
|
+
logger?.debug("pty-child constructed", { pid: this.pid, tag });
|
|
24993
25092
|
const writeToPty = (data) => {
|
|
24994
25093
|
const match = data.match(/^(\x1b\[200~[\s\S]*\x1b\[201~)(\r+)$/);
|
|
24995
25094
|
if (match) {
|
|
24996
25095
|
const paste = match[1];
|
|
24997
25096
|
const enter = match[2];
|
|
25097
|
+
console.debug("[RG] pty-paste", { pasteLen: paste.length, enterLen: enter.length });
|
|
24998
25098
|
pty.write(paste);
|
|
24999
25099
|
setTimeout(() => {
|
|
25000
25100
|
try {
|
|
25001
25101
|
pty.write(enter);
|
|
25102
|
+
console.debug("[RG] pty-paste-enter", { wrote: "\\r", disposed: false });
|
|
25002
25103
|
logger?.debug("pty bracketed paste submit \\r delayed", { tag, pid: this.pid });
|
|
25003
25104
|
} catch (err) {
|
|
25105
|
+
console.debug("[RG] pty-paste-enter", { error: err.message });
|
|
25004
25106
|
logger?.warn("pty delayed \\r failed", { tag, error: err.message });
|
|
25005
25107
|
}
|
|
25006
25108
|
}, 120);
|
|
25007
25109
|
} else {
|
|
25110
|
+
console.debug("[RG] pty-write-direct", { len: data.length });
|
|
25008
25111
|
pty.write(data);
|
|
25009
25112
|
}
|
|
25010
25113
|
};
|
|
@@ -25012,17 +25115,6 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
25012
25115
|
decodeStrings: false,
|
|
25013
25116
|
write: (chunk, _enc, cb) => {
|
|
25014
25117
|
const s = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
25015
|
-
if (!this.bootReady) {
|
|
25016
|
-
this.stdinBuffer.push(s);
|
|
25017
|
-
logger?.debug("pty stdin.write buffered (boot gate)", {
|
|
25018
|
-
tag,
|
|
25019
|
-
pid: this.pid,
|
|
25020
|
-
bytes: s.length,
|
|
25021
|
-
preview: s.replace(/\x1b\[20[01]~/g, "\xB7paste\xB7").slice(0, 80)
|
|
25022
|
-
});
|
|
25023
|
-
cb();
|
|
25024
|
-
return;
|
|
25025
|
-
}
|
|
25026
25118
|
try {
|
|
25027
25119
|
writeToPty(s);
|
|
25028
25120
|
logger?.debug("pty stdin.write", {
|
|
@@ -25038,7 +25130,6 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
25038
25130
|
}
|
|
25039
25131
|
}
|
|
25040
25132
|
});
|
|
25041
|
-
this._writeToPty = writeToPty;
|
|
25042
25133
|
this.stdout = new import_node_stream.Readable({ read() {
|
|
25043
25134
|
} });
|
|
25044
25135
|
pty.onData((data) => {
|
|
@@ -25069,40 +25160,6 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
25069
25160
|
stderr;
|
|
25070
25161
|
exitCode = null;
|
|
25071
25162
|
firstDataLogged = false;
|
|
25072
|
-
// Boot gate buffer:bootGate 启用且 ready=false 时,stdin.write 入这个队列等待 flush
|
|
25073
|
-
stdinBuffer = [];
|
|
25074
|
-
bootReady;
|
|
25075
|
-
bootGateLogger;
|
|
25076
|
-
bootGateTag;
|
|
25077
|
-
// bracketed paste 拆分写入实现;构造器内闭包初始化,setReady flush 也用同一份
|
|
25078
|
-
_writeToPty = () => {
|
|
25079
|
-
};
|
|
25080
|
-
/**
|
|
25081
|
-
* 标记 boot gate 已通过(CC TUI 启动完成,可以接受用户输入)。
|
|
25082
|
-
* 立刻 flush 之前 buffered 的 stdin 字节到 pty。幂等。外部由 ClaudeTuiAdapter 在
|
|
25083
|
-
* 看到 banner / popup 处理完成 / 超时兜底时调用
|
|
25084
|
-
*/
|
|
25085
|
-
setReady() {
|
|
25086
|
-
if (this.bootReady) return;
|
|
25087
|
-
this.bootReady = true;
|
|
25088
|
-
const count = this.stdinBuffer.length;
|
|
25089
|
-
this.bootGateLogger?.debug("pty boot gate cleared, flushing buffered stdin", {
|
|
25090
|
-
tag: this.bootGateTag,
|
|
25091
|
-
pid: this.pid,
|
|
25092
|
-
bufferedWrites: count
|
|
25093
|
-
});
|
|
25094
|
-
while (this.stdinBuffer.length > 0) {
|
|
25095
|
-
const data = this.stdinBuffer.shift();
|
|
25096
|
-
try {
|
|
25097
|
-
this._writeToPty(data);
|
|
25098
|
-
} catch (err) {
|
|
25099
|
-
this.bootGateLogger?.warn("pty flush stdin write failed", {
|
|
25100
|
-
tag: this.bootGateTag,
|
|
25101
|
-
error: err.message
|
|
25102
|
-
});
|
|
25103
|
-
}
|
|
25104
|
-
}
|
|
25105
|
-
}
|
|
25106
25163
|
/** 与 ChildProcess.kill 签名对齐,返回是否成功发送信号 */
|
|
25107
25164
|
kill(signal) {
|
|
25108
25165
|
try {
|
|
@@ -25259,9 +25316,83 @@ function createPopupDetector(opts) {
|
|
|
25259
25316
|
}
|
|
25260
25317
|
};
|
|
25261
25318
|
}
|
|
25262
|
-
function
|
|
25263
|
-
const
|
|
25264
|
-
|
|
25319
|
+
function createReadyDetector(opts) {
|
|
25320
|
+
const term = new Terminal({
|
|
25321
|
+
cols: opts.cols ?? 120,
|
|
25322
|
+
rows: opts.rows ?? 40,
|
|
25323
|
+
scrollback: 200,
|
|
25324
|
+
allowProposedApi: true
|
|
25325
|
+
});
|
|
25326
|
+
let quietTimer = null;
|
|
25327
|
+
let disposed = false;
|
|
25328
|
+
const scanReadyFrame = () => {
|
|
25329
|
+
const buf = term.buffer.active;
|
|
25330
|
+
let prevHasDivider = false;
|
|
25331
|
+
for (let i = 0; i < buf.length; i++) {
|
|
25332
|
+
const line = buf.getLine(i);
|
|
25333
|
+
if (!line) {
|
|
25334
|
+
prevHasDivider = false;
|
|
25335
|
+
continue;
|
|
25336
|
+
}
|
|
25337
|
+
const text = line.translateToString(true);
|
|
25338
|
+
const hasPrompt = /^❯/.test(text);
|
|
25339
|
+
const hasDivider = /─{20,}/.test(text);
|
|
25340
|
+
if (hasPrompt && prevHasDivider) return true;
|
|
25341
|
+
prevHasDivider = hasDivider;
|
|
25342
|
+
}
|
|
25343
|
+
return false;
|
|
25344
|
+
};
|
|
25345
|
+
const clearQuiet = () => {
|
|
25346
|
+
if (quietTimer) {
|
|
25347
|
+
clearTimeout(quietTimer);
|
|
25348
|
+
quietTimer = null;
|
|
25349
|
+
}
|
|
25350
|
+
};
|
|
25351
|
+
const quietFire = () => {
|
|
25352
|
+
quietTimer = null;
|
|
25353
|
+
if (disposed) return;
|
|
25354
|
+
if (!scanReadyFrame()) {
|
|
25355
|
+
console.debug("[RG] det-state", { event: "quiet-fire-but-not-ready-anymore" });
|
|
25356
|
+
return;
|
|
25357
|
+
}
|
|
25358
|
+
console.debug("[RG] det-state", { event: "quiet-fire \u2192 onReady" });
|
|
25359
|
+
opts.onReady();
|
|
25360
|
+
};
|
|
25361
|
+
let prevReady = false;
|
|
25362
|
+
const tick = () => {
|
|
25363
|
+
if (disposed) return;
|
|
25364
|
+
const ready = scanReadyFrame();
|
|
25365
|
+
if (!ready) {
|
|
25366
|
+
clearQuiet();
|
|
25367
|
+
if (prevReady) {
|
|
25368
|
+
console.debug("[RG] det-state", { event: "ready \u2192 not-ready" });
|
|
25369
|
+
prevReady = false;
|
|
25370
|
+
}
|
|
25371
|
+
return;
|
|
25372
|
+
}
|
|
25373
|
+
if (!prevReady) {
|
|
25374
|
+
console.debug("[RG] det-state", { event: "not-ready \u2192 ready-candidate (quiet timer set)" });
|
|
25375
|
+
prevReady = true;
|
|
25376
|
+
}
|
|
25377
|
+
clearQuiet();
|
|
25378
|
+
quietTimer = setTimeout(quietFire, opts.quietMs);
|
|
25379
|
+
};
|
|
25380
|
+
return {
|
|
25381
|
+
writeBytes(data) {
|
|
25382
|
+
if (disposed) return;
|
|
25383
|
+
term.write(data, () => {
|
|
25384
|
+
if (disposed) return;
|
|
25385
|
+
tick();
|
|
25386
|
+
});
|
|
25387
|
+
},
|
|
25388
|
+
dispose() {
|
|
25389
|
+
disposed = true;
|
|
25390
|
+
clearQuiet();
|
|
25391
|
+
term.dispose();
|
|
25392
|
+
}
|
|
25393
|
+
};
|
|
25394
|
+
}
|
|
25395
|
+
function createBootGate(pty, logger) {
|
|
25265
25396
|
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;?]*C/g, " ").replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[NOPM=>78]/g, "").replace(/[\x00-\x1f\x7f]/g, " ");
|
|
25266
25397
|
const popups = {
|
|
25267
25398
|
trust: { handled: false, regex: /trust this folder/i, accept: "\r", label: "trust-folder" },
|
|
@@ -25273,22 +25404,7 @@ function createBootGate(pty, ptyChild, logger, toolSessionId) {
|
|
|
25273
25404
|
}
|
|
25274
25405
|
};
|
|
25275
25406
|
let scanBuf = "";
|
|
25276
|
-
let opened = false;
|
|
25277
|
-
let postPopupQuiet = null;
|
|
25278
|
-
const openGate = (reason) => {
|
|
25279
|
-
if (opened) return;
|
|
25280
|
-
opened = true;
|
|
25281
|
-
logger?.debug("boot gate setReady", { toolSessionId, reason });
|
|
25282
|
-
clearTimeout(fallbackTimer);
|
|
25283
|
-
if (postPopupQuiet) clearTimeout(postPopupQuiet);
|
|
25284
|
-
scanBuf = "";
|
|
25285
|
-
ptyChild.setReady();
|
|
25286
|
-
};
|
|
25287
|
-
const fallbackTimer = setTimeout(() => openGate("timeout-4s"), FALLBACK_MS);
|
|
25288
25407
|
return {
|
|
25289
|
-
get opened() {
|
|
25290
|
-
return opened;
|
|
25291
|
-
},
|
|
25292
25408
|
feedBytes(data) {
|
|
25293
25409
|
scanBuf += data.toString("utf8");
|
|
25294
25410
|
const cleanBuf = stripAnsi(scanBuf).replace(/\s+/g, " ");
|
|
@@ -25296,7 +25412,7 @@ function createBootGate(pty, ptyChild, logger, toolSessionId) {
|
|
|
25296
25412
|
if (popup.handled) continue;
|
|
25297
25413
|
if (popup.regex.test(cleanBuf)) {
|
|
25298
25414
|
popup.handled = true;
|
|
25299
|
-
logger?.debug(`${popup.label} popup auto-yes
|
|
25415
|
+
logger?.debug(`${popup.label} popup auto-yes`);
|
|
25300
25416
|
try {
|
|
25301
25417
|
pty.write(popup.accept);
|
|
25302
25418
|
} catch (err) {
|
|
@@ -25305,15 +25421,11 @@ function createBootGate(pty, ptyChild, logger, toolSessionId) {
|
|
|
25305
25421
|
});
|
|
25306
25422
|
}
|
|
25307
25423
|
scanBuf = "";
|
|
25308
|
-
if (postPopupQuiet) clearTimeout(postPopupQuiet);
|
|
25309
|
-
postPopupQuiet = setTimeout(() => openGate("post-popup-quiet"), POST_POPUP_QUIET_MS);
|
|
25310
25424
|
}
|
|
25311
25425
|
}
|
|
25312
25426
|
if (scanBuf.length > 8192) scanBuf = scanBuf.slice(-4096);
|
|
25313
25427
|
},
|
|
25314
25428
|
dispose() {
|
|
25315
|
-
clearTimeout(fallbackTimer);
|
|
25316
|
-
if (postPopupQuiet) clearTimeout(postPopupQuiet);
|
|
25317
25429
|
}
|
|
25318
25430
|
};
|
|
25319
25431
|
}
|
|
@@ -25355,13 +25467,9 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
25355
25467
|
}
|
|
25356
25468
|
const ptyChild = new PtyChildProcess(pty, {
|
|
25357
25469
|
logger: this.tuiLogger,
|
|
25358
|
-
tag: ctx.toolSessionId ?? "no-tsid"
|
|
25359
|
-
// boot gate:CC TUI 启动 + popup 处理前 stdin.write 一律 buffer,防止用户 prompt 在
|
|
25360
|
-
// CC 加载 node runtime 时被吞。bootGate scanner 在 popup 处理完成 / 4s 兜底任一条件
|
|
25361
|
-
// 触发后调 setReady() 开闸
|
|
25362
|
-
bootGate: true
|
|
25470
|
+
tag: ctx.toolSessionId ?? "no-tsid"
|
|
25363
25471
|
});
|
|
25364
|
-
const bootGate = createBootGate(pty,
|
|
25472
|
+
const bootGate = createBootGate(pty, this.tuiLogger);
|
|
25365
25473
|
const detector = createPopupDetector({
|
|
25366
25474
|
clearQuietMs: 200,
|
|
25367
25475
|
onTransition: (kind, visible) => {
|
|
@@ -25374,6 +25482,14 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
25374
25482
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
25375
25483
|
}
|
|
25376
25484
|
});
|
|
25485
|
+
const readyDetector = createReadyDetector({
|
|
25486
|
+
quietMs: 500,
|
|
25487
|
+
onReady: () => {
|
|
25488
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
25489
|
+
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
25490
|
+
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
25491
|
+
}
|
|
25492
|
+
});
|
|
25377
25493
|
if (ctx.toolSessionId && this.tuiOpts.onDetectorRegister) {
|
|
25378
25494
|
this.tuiOpts.onDetectorRegister(ctx.toolSessionId, detector);
|
|
25379
25495
|
}
|
|
@@ -25400,11 +25516,9 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
25400
25516
|
if (ctx.toolSessionId && this.tuiOpts.onPtyData) {
|
|
25401
25517
|
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
25402
25518
|
}
|
|
25403
|
-
|
|
25404
|
-
|
|
25405
|
-
|
|
25406
|
-
bootGate.feedBytes(merged);
|
|
25407
|
-
}
|
|
25519
|
+
bootGate.feedBytes(merged);
|
|
25520
|
+
void detector.writeBytes(merged.toString("utf8"), seq);
|
|
25521
|
+
readyDetector.writeBytes(merged.toString("utf8"));
|
|
25408
25522
|
if (ctx.toolSessionId) {
|
|
25409
25523
|
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
25410
25524
|
}
|
|
@@ -25422,6 +25536,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
25422
25536
|
flushCoalesced();
|
|
25423
25537
|
}
|
|
25424
25538
|
bootGate.dispose();
|
|
25539
|
+
readyDetector.dispose();
|
|
25425
25540
|
if (ctx.toolSessionId && this.tuiOpts.onDetectorUnregister) {
|
|
25426
25541
|
this.tuiOpts.onDetectorUnregister(ctx.toolSessionId);
|
|
25427
25542
|
}
|
|
@@ -30655,7 +30770,9 @@ async function startDaemon(config) {
|
|
|
30655
30770
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
30656
30771
|
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
30657
30772
|
onDetectorRegister: (tsid, detector) => manager.registerDetector(tsid, detector),
|
|
30658
|
-
onDetectorUnregister: (tsid) => manager.unregisterDetector(tsid)
|
|
30773
|
+
onDetectorUnregister: (tsid) => manager.unregisterDetector(tsid),
|
|
30774
|
+
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
30775
|
+
onReady: (tsid) => manager.dispatchReadyDetected(tsid)
|
|
30659
30776
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
|
30660
30777
|
registerAdapter("claude", claudeAdapter);
|
|
30661
30778
|
const personaRegistry = new PersonaRegistry(personaStore);
|
package/package.json
CHANGED