@clawos-dev/clawd 0.2.101-beta.194.a406ba4 → 0.2.102-beta.196.9ae3d02
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 +137 -112
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -32546,11 +32546,12 @@ var SessionManager = class {
|
|
|
32546
32546
|
// 新 client 通过 session:subscribe 接入时由 onSubscribe hook 取 getPtyReplay(sessionId)
|
|
32547
32547
|
// 把屏幕快照定向 emit,解决"刷新页面 / 第二个 tab 接入时 xterm 白屏"
|
|
32548
32548
|
ptyReplaysByToolSid = /* @__PURE__ */ new Map();
|
|
32549
|
-
// TUI 模式:toolSessionId → @xterm/headless
|
|
32550
|
-
// ClaudeTuiAdapter spawn 时通过
|
|
32551
|
-
//
|
|
32549
|
+
// TUI 模式:toolSessionId → @xterm/headless TerminalSurface(单一虚拟终端,popup / ready
|
|
32550
|
+
// observer 都从这一份 buffer 派生状态)。ClaudeTuiAdapter spawn 时通过 onSurfaceRegister
|
|
32551
|
+
// callback 写入;exit 时 onSurfaceUnregister 清理。
|
|
32552
|
+
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 surface 内部 buffer,
|
|
32552
32553
|
// 保证 snapshot serialize 时 cursor positioning 与 UI xterm 实际尺寸一致。
|
|
32553
|
-
|
|
32554
|
+
surfacesByToolSid = /* @__PURE__ */ new Map();
|
|
32554
32555
|
// 仅 mode='tui' 需要:index.ts 装配阶段在 observer 构造后注入。
|
|
32555
32556
|
// observer.onEvent 要回灌 manager.feedObserverEvents,所以 observer 必须晚于 manager
|
|
32556
32557
|
// 构造;反过来 manager 又要在 mode='tui' 下 spawn session 时 attach observer。setter 解循环
|
|
@@ -33824,18 +33825,18 @@ var SessionManager = class {
|
|
|
33824
33825
|
if (!tsid) return void 0;
|
|
33825
33826
|
return this.ptyTransports.get(tsid);
|
|
33826
33827
|
}
|
|
33827
|
-
/** ClaudeTuiAdapter spawn 时注册
|
|
33828
|
-
|
|
33829
|
-
this.
|
|
33828
|
+
/** ClaudeTuiAdapter spawn 时注册 surface ref;同一 tsid 重 spawn 会覆盖 */
|
|
33829
|
+
registerSurface(toolSessionId, surface) {
|
|
33830
|
+
this.surfacesByToolSid.set(toolSessionId, surface);
|
|
33830
33831
|
}
|
|
33831
33832
|
/** spawn 进程 exit 时清理 */
|
|
33832
|
-
|
|
33833
|
-
this.
|
|
33833
|
+
unregisterSurface(toolSessionId) {
|
|
33834
|
+
this.surfacesByToolSid.delete(toolSessionId);
|
|
33834
33835
|
}
|
|
33835
33836
|
/**
|
|
33836
|
-
* 同时 resize pty 子进程和
|
|
33837
|
-
* 顺序:pty.resize 在前,
|
|
33838
|
-
*
|
|
33837
|
+
* 同时 resize pty 子进程和 surface buffer:UI session:pty:resize 入口走这个。
|
|
33838
|
+
* 顺序:pty.resize 在前,surface.resize 在后 —— pty 是真正的 CC 子进程视图,必须先成功;
|
|
33839
|
+
* surface 只是 daemon 端镜像,任一步失败都返 false。
|
|
33839
33840
|
* 找不到 runner / toolSessionId / pty 任一缺失 → 返 false,handler 静默给 UI 报 ok=false 不抛错。
|
|
33840
33841
|
*/
|
|
33841
33842
|
resizePty(sessionId, cols, rows) {
|
|
@@ -33850,8 +33851,8 @@ var SessionManager = class {
|
|
|
33850
33851
|
} catch {
|
|
33851
33852
|
return false;
|
|
33852
33853
|
}
|
|
33853
|
-
const
|
|
33854
|
-
if (
|
|
33854
|
+
const surface = this.surfacesByToolSid.get(tsid);
|
|
33855
|
+
if (surface) surface.resize(cols, rows);
|
|
33855
33856
|
return true;
|
|
33856
33857
|
}
|
|
33857
33858
|
/**
|
|
@@ -35149,59 +35150,30 @@ var POPUP_FOOTER_PATTERNS = [
|
|
|
35149
35150
|
{ kind: "permission", regex: /Tab to amend|Do you want to proceed/i },
|
|
35150
35151
|
{ kind: "question", regex: /Enter to select|↑\/↓ to navigate/i }
|
|
35151
35152
|
];
|
|
35152
|
-
function
|
|
35153
|
+
function createTerminalSurface(opts) {
|
|
35153
35154
|
const term = new Terminal({
|
|
35154
|
-
cols: opts
|
|
35155
|
-
rows: opts
|
|
35156
|
-
scrollback: 1e3,
|
|
35155
|
+
cols: opts?.cols ?? 120,
|
|
35156
|
+
rows: opts?.rows ?? 40,
|
|
35157
|
+
scrollback: opts?.scrollback ?? 1e3,
|
|
35157
35158
|
allowProposedApi: true
|
|
35158
35159
|
});
|
|
35159
35160
|
const serializeAddon = new D();
|
|
35160
35161
|
term.loadAddon(serializeAddon);
|
|
35161
|
-
let visible = null;
|
|
35162
|
-
let pendingClear = null;
|
|
35163
35162
|
let parseSeq = 0;
|
|
35164
35163
|
let pendingParse = 0;
|
|
35165
35164
|
let drainWaiters = [];
|
|
35166
35165
|
let disposed = false;
|
|
35167
|
-
const
|
|
35166
|
+
const observers = [];
|
|
35167
|
+
const collectLines = () => {
|
|
35168
35168
|
const buf = term.buffer.active;
|
|
35169
|
+
const lines = new Array(buf.length);
|
|
35169
35170
|
for (let i = 0; i < buf.length; i++) {
|
|
35170
35171
|
const line = buf.getLine(i);
|
|
35171
|
-
|
|
35172
|
-
const text = line.translateToString(true);
|
|
35173
|
-
if (!text) continue;
|
|
35174
|
-
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35175
|
-
if (p2.regex.test(text)) return p2.kind;
|
|
35176
|
-
}
|
|
35177
|
-
}
|
|
35178
|
-
return null;
|
|
35179
|
-
};
|
|
35180
|
-
const tick = () => {
|
|
35181
|
-
const matched = scanFooter();
|
|
35182
|
-
if (matched && visible === null) {
|
|
35183
|
-
visible = matched;
|
|
35184
|
-
opts.onTransition(matched, true);
|
|
35185
|
-
} else if (matched && visible !== null) {
|
|
35186
|
-
if (pendingClear) {
|
|
35187
|
-
clearTimeout(pendingClear);
|
|
35188
|
-
pendingClear = null;
|
|
35189
|
-
}
|
|
35190
|
-
} else if (!matched && visible !== null && !pendingClear) {
|
|
35191
|
-
const toClear = visible;
|
|
35192
|
-
pendingClear = setTimeout(() => {
|
|
35193
|
-
pendingClear = null;
|
|
35194
|
-
if (visible === toClear) {
|
|
35195
|
-
visible = null;
|
|
35196
|
-
opts.onTransition(toClear, false);
|
|
35197
|
-
}
|
|
35198
|
-
}, opts.clearQuietMs);
|
|
35172
|
+
lines[i] = line ? line.translateToString(true) : "";
|
|
35199
35173
|
}
|
|
35174
|
+
return lines;
|
|
35200
35175
|
};
|
|
35201
35176
|
return {
|
|
35202
|
-
get visibleKind() {
|
|
35203
|
-
return visible;
|
|
35204
|
-
},
|
|
35205
35177
|
get parseSeq() {
|
|
35206
35178
|
return parseSeq;
|
|
35207
35179
|
},
|
|
@@ -35215,7 +35187,10 @@ function createPopupDetector(opts) {
|
|
|
35215
35187
|
}
|
|
35216
35188
|
if (typeof seq === "number" && seq > parseSeq) parseSeq = seq;
|
|
35217
35189
|
pendingParse--;
|
|
35218
|
-
|
|
35190
|
+
if (observers.length > 0) {
|
|
35191
|
+
const lines = collectLines();
|
|
35192
|
+
for (const cb of [...observers]) cb(lines);
|
|
35193
|
+
}
|
|
35219
35194
|
if (pendingParse === 0 && drainWaiters.length > 0) {
|
|
35220
35195
|
const waiters = drainWaiters;
|
|
35221
35196
|
drainWaiters = [];
|
|
@@ -35237,42 +35212,93 @@ function createPopupDetector(opts) {
|
|
|
35237
35212
|
} catch {
|
|
35238
35213
|
}
|
|
35239
35214
|
},
|
|
35215
|
+
serialize() {
|
|
35216
|
+
return serializeAddon.serialize({ scrollback: 1e3 });
|
|
35217
|
+
},
|
|
35240
35218
|
dispose() {
|
|
35241
35219
|
disposed = true;
|
|
35242
|
-
|
|
35243
|
-
clearTimeout(pendingClear);
|
|
35244
|
-
pendingClear = null;
|
|
35245
|
-
}
|
|
35220
|
+
pendingParse = 0;
|
|
35246
35221
|
const waiters = drainWaiters;
|
|
35247
35222
|
drainWaiters = [];
|
|
35248
35223
|
for (const w2 of waiters) w2();
|
|
35224
|
+
observers.length = 0;
|
|
35249
35225
|
serializeAddon.dispose();
|
|
35250
35226
|
term.dispose();
|
|
35251
35227
|
},
|
|
35252
|
-
|
|
35253
|
-
|
|
35228
|
+
onTick(cb) {
|
|
35229
|
+
observers.push(cb);
|
|
35230
|
+
return () => {
|
|
35231
|
+
const i = observers.indexOf(cb);
|
|
35232
|
+
if (i >= 0) observers.splice(i, 1);
|
|
35233
|
+
};
|
|
35254
35234
|
}
|
|
35255
35235
|
};
|
|
35256
35236
|
}
|
|
35257
|
-
function
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35237
|
+
function observePopup(surface, opts) {
|
|
35238
|
+
let visible = null;
|
|
35239
|
+
let pendingClear = null;
|
|
35240
|
+
const scanFooter = (lines) => {
|
|
35241
|
+
for (const text of lines) {
|
|
35242
|
+
if (!text) continue;
|
|
35243
|
+
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35244
|
+
if (p2.regex.test(text)) return p2.kind;
|
|
35245
|
+
}
|
|
35246
|
+
}
|
|
35247
|
+
return null;
|
|
35248
|
+
};
|
|
35249
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35250
|
+
const matched = scanFooter(lines);
|
|
35251
|
+
const inputFrameVisible = opts.getInputFrameVisible();
|
|
35252
|
+
if (visible === null) {
|
|
35253
|
+
if (matched && !inputFrameVisible) {
|
|
35254
|
+
visible = matched;
|
|
35255
|
+
opts.onTransition(matched, true);
|
|
35256
|
+
}
|
|
35257
|
+
return;
|
|
35258
|
+
}
|
|
35259
|
+
if (!inputFrameVisible) {
|
|
35260
|
+
if (pendingClear) {
|
|
35261
|
+
clearTimeout(pendingClear);
|
|
35262
|
+
pendingClear = null;
|
|
35263
|
+
}
|
|
35264
|
+
return;
|
|
35265
|
+
}
|
|
35266
|
+
if (!pendingClear) {
|
|
35267
|
+
const toClear = visible;
|
|
35268
|
+
pendingClear = setTimeout(() => {
|
|
35269
|
+
pendingClear = null;
|
|
35270
|
+
if (visible === toClear) {
|
|
35271
|
+
visible = null;
|
|
35272
|
+
opts.onTransition(toClear, false);
|
|
35273
|
+
}
|
|
35274
|
+
}, opts.clearQuietMs);
|
|
35275
|
+
}
|
|
35263
35276
|
});
|
|
35277
|
+
return {
|
|
35278
|
+
get visibleKind() {
|
|
35279
|
+
return visible;
|
|
35280
|
+
},
|
|
35281
|
+
dispose() {
|
|
35282
|
+
unsubscribe();
|
|
35283
|
+
if (pendingClear) {
|
|
35284
|
+
clearTimeout(pendingClear);
|
|
35285
|
+
pendingClear = null;
|
|
35286
|
+
}
|
|
35287
|
+
}
|
|
35288
|
+
};
|
|
35289
|
+
}
|
|
35290
|
+
function observeReady(surface, opts) {
|
|
35291
|
+
let prevReady = false;
|
|
35264
35292
|
let quietTimer = null;
|
|
35265
|
-
let
|
|
35266
|
-
|
|
35267
|
-
|
|
35293
|
+
let unsubscribed = false;
|
|
35294
|
+
let latestLines = [];
|
|
35295
|
+
const scanReadyFrame = (lines) => {
|
|
35268
35296
|
let prevHasDivider = false;
|
|
35269
|
-
for (
|
|
35270
|
-
|
|
35271
|
-
if (!line) {
|
|
35297
|
+
for (const text of lines) {
|
|
35298
|
+
if (!text) {
|
|
35272
35299
|
prevHasDivider = false;
|
|
35273
35300
|
continue;
|
|
35274
35301
|
}
|
|
35275
|
-
const text = line.translateToString(true);
|
|
35276
35302
|
const hasPrompt = /^❯/.test(text);
|
|
35277
35303
|
const hasDivider = /─{20,}/.test(text);
|
|
35278
35304
|
if (hasPrompt && prevHasDivider) return true;
|
|
@@ -35295,18 +35321,18 @@ function createReadyDetector(opts) {
|
|
|
35295
35321
|
};
|
|
35296
35322
|
const quietFire = () => {
|
|
35297
35323
|
quietTimer = null;
|
|
35298
|
-
if (
|
|
35299
|
-
if (!scanReadyFrame()) {
|
|
35324
|
+
if (unsubscribed) return;
|
|
35325
|
+
if (!scanReadyFrame(latestLines)) {
|
|
35300
35326
|
dbg({ event: "quiet-fire-but-not-ready-anymore" });
|
|
35301
35327
|
return;
|
|
35302
35328
|
}
|
|
35303
35329
|
dbg({ event: "quiet-fire \u2192 onReady" });
|
|
35304
35330
|
opts.onReady();
|
|
35305
35331
|
};
|
|
35306
|
-
|
|
35307
|
-
|
|
35308
|
-
|
|
35309
|
-
const ready = scanReadyFrame();
|
|
35332
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35333
|
+
if (unsubscribed) return;
|
|
35334
|
+
latestLines = lines;
|
|
35335
|
+
const ready = scanReadyFrame(lines);
|
|
35310
35336
|
if (!ready) {
|
|
35311
35337
|
clearQuiet();
|
|
35312
35338
|
if (prevReady) {
|
|
@@ -35321,19 +35347,15 @@ function createReadyDetector(opts) {
|
|
|
35321
35347
|
}
|
|
35322
35348
|
clearQuiet();
|
|
35323
35349
|
quietTimer = setTimeout(quietFire, opts.quietMs);
|
|
35324
|
-
};
|
|
35350
|
+
});
|
|
35325
35351
|
return {
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
term.write(data, () => {
|
|
35329
|
-
if (disposed) return;
|
|
35330
|
-
tick();
|
|
35331
|
-
});
|
|
35352
|
+
get inputFrameVisible() {
|
|
35353
|
+
return prevReady;
|
|
35332
35354
|
},
|
|
35333
35355
|
dispose() {
|
|
35334
|
-
|
|
35356
|
+
unsubscribed = true;
|
|
35357
|
+
unsubscribe();
|
|
35335
35358
|
clearQuiet();
|
|
35336
|
-
term.dispose();
|
|
35337
35359
|
}
|
|
35338
35360
|
};
|
|
35339
35361
|
}
|
|
@@ -35415,7 +35437,18 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35415
35437
|
tag: ctx.toolSessionId ?? "no-tsid"
|
|
35416
35438
|
});
|
|
35417
35439
|
const bootGate = createBootGate(pty, this.tuiLogger);
|
|
35418
|
-
const
|
|
35440
|
+
const surface = createTerminalSurface();
|
|
35441
|
+
const readyObserver = observeReady(surface, {
|
|
35442
|
+
quietMs: 500,
|
|
35443
|
+
onReady: () => {
|
|
35444
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35445
|
+
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35446
|
+
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35447
|
+
},
|
|
35448
|
+
// [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
|
|
35449
|
+
logger: this.tuiLogger
|
|
35450
|
+
});
|
|
35451
|
+
const popupObserver = observePopup(surface, {
|
|
35419
35452
|
clearQuietMs: 200,
|
|
35420
35453
|
onTransition: (kind, visible) => {
|
|
35421
35454
|
if (!ctx.toolSessionId || !this.tuiOpts.onPopupTransition) return;
|
|
@@ -35425,29 +35458,21 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35425
35458
|
visible
|
|
35426
35459
|
});
|
|
35427
35460
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
35428
|
-
}
|
|
35429
|
-
});
|
|
35430
|
-
const readyDetector = createReadyDetector({
|
|
35431
|
-
quietMs: 500,
|
|
35432
|
-
onReady: () => {
|
|
35433
|
-
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35434
|
-
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35435
|
-
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35436
35461
|
},
|
|
35437
|
-
//
|
|
35438
|
-
|
|
35462
|
+
// popup_close 信号源:ready observer 扫到 ❯ + ─{20,} 输入框结构 → 输入框可见 → popup 已关
|
|
35463
|
+
getInputFrameVisible: () => readyObserver.inputFrameVisible
|
|
35439
35464
|
});
|
|
35440
|
-
if (ctx.toolSessionId && this.tuiOpts.
|
|
35441
|
-
this.tuiOpts.
|
|
35465
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
|
|
35466
|
+
this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
|
|
35442
35467
|
}
|
|
35443
35468
|
let chunkSeq = 0;
|
|
35444
35469
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
35445
35470
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
35446
|
-
await
|
|
35471
|
+
await surface.drain();
|
|
35447
35472
|
return {
|
|
35448
|
-
data:
|
|
35449
|
-
popupKind:
|
|
35450
|
-
atSeq:
|
|
35473
|
+
data: surface.serialize(),
|
|
35474
|
+
popupKind: popupObserver.visibleKind,
|
|
35475
|
+
atSeq: surface.parseSeq
|
|
35451
35476
|
};
|
|
35452
35477
|
});
|
|
35453
35478
|
}
|
|
@@ -35464,8 +35489,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35464
35489
|
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
35465
35490
|
}
|
|
35466
35491
|
bootGate.feedBytes(merged);
|
|
35467
|
-
void
|
|
35468
|
-
readyDetector.writeBytes(merged.toString("utf8"));
|
|
35492
|
+
void surface.writeBytes(merged.toString("utf8"), seq);
|
|
35469
35493
|
if (ctx.toolSessionId) {
|
|
35470
35494
|
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
35471
35495
|
}
|
|
@@ -35483,14 +35507,15 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35483
35507
|
flushCoalesced();
|
|
35484
35508
|
}
|
|
35485
35509
|
bootGate.dispose();
|
|
35486
|
-
|
|
35487
|
-
|
|
35488
|
-
|
|
35510
|
+
readyObserver.dispose();
|
|
35511
|
+
popupObserver.dispose();
|
|
35512
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceUnregister) {
|
|
35513
|
+
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
35489
35514
|
}
|
|
35490
35515
|
if (ctx.toolSessionId && this.tuiOpts.onPtyUnregister) {
|
|
35491
35516
|
this.tuiOpts.onPtyUnregister(ctx.toolSessionId);
|
|
35492
35517
|
}
|
|
35493
|
-
|
|
35518
|
+
surface.dispose();
|
|
35494
35519
|
});
|
|
35495
35520
|
return ptyChild;
|
|
35496
35521
|
}
|
|
@@ -41943,8 +41968,8 @@ async function startDaemon(config) {
|
|
|
41943
41968
|
onPtyData: (tsid, b64, seq) => manager.emitPtyChunk(tsid, b64, seq),
|
|
41944
41969
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
41945
41970
|
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
41946
|
-
|
|
41947
|
-
|
|
41971
|
+
onSurfaceRegister: (tsid, surface) => manager.registerSurface(tsid, surface),
|
|
41972
|
+
onSurfaceUnregister: (tsid) => manager.unregisterSurface(tsid),
|
|
41948
41973
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
41949
41974
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid)
|
|
41950
41975
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
package/package.json
CHANGED