@clawos-dev/clawd 0.2.103-beta.198.c756b33 → 0.2.104
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 +139 -112
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -32580,11 +32580,12 @@ var SessionManager = class {
|
|
|
32580
32580
|
// 新 client 通过 session:subscribe 接入时由 onSubscribe hook 取 getPtyReplay(sessionId)
|
|
32581
32581
|
// 把屏幕快照定向 emit,解决"刷新页面 / 第二个 tab 接入时 xterm 白屏"
|
|
32582
32582
|
ptyReplaysByToolSid = /* @__PURE__ */ new Map();
|
|
32583
|
-
// TUI 模式:toolSessionId → @xterm/headless
|
|
32584
|
-
// ClaudeTuiAdapter spawn 时通过
|
|
32585
|
-
//
|
|
32583
|
+
// TUI 模式:toolSessionId → @xterm/headless TerminalSurface(单一虚拟终端,popup / ready
|
|
32584
|
+
// observer 都从这一份 buffer 派生状态)。ClaudeTuiAdapter spawn 时通过 onSurfaceRegister
|
|
32585
|
+
// callback 写入;exit 时 onSurfaceUnregister 清理。
|
|
32586
|
+
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 surface 内部 buffer,
|
|
32586
32587
|
// 保证 snapshot serialize 时 cursor positioning 与 UI xterm 实际尺寸一致。
|
|
32587
|
-
|
|
32588
|
+
surfacesByToolSid = /* @__PURE__ */ new Map();
|
|
32588
32589
|
// 仅 mode='tui' 需要:index.ts 装配阶段在 observer 构造后注入。
|
|
32589
32590
|
// observer.onEvent 要回灌 manager.feedObserverEvents,所以 observer 必须晚于 manager
|
|
32590
32591
|
// 构造;反过来 manager 又要在 mode='tui' 下 spawn session 时 attach observer。setter 解循环
|
|
@@ -33858,18 +33859,18 @@ var SessionManager = class {
|
|
|
33858
33859
|
if (!tsid) return void 0;
|
|
33859
33860
|
return this.ptyTransports.get(tsid);
|
|
33860
33861
|
}
|
|
33861
|
-
/** ClaudeTuiAdapter spawn 时注册
|
|
33862
|
-
|
|
33863
|
-
this.
|
|
33862
|
+
/** ClaudeTuiAdapter spawn 时注册 surface ref;同一 tsid 重 spawn 会覆盖 */
|
|
33863
|
+
registerSurface(toolSessionId, surface) {
|
|
33864
|
+
this.surfacesByToolSid.set(toolSessionId, surface);
|
|
33864
33865
|
}
|
|
33865
33866
|
/** spawn 进程 exit 时清理 */
|
|
33866
|
-
|
|
33867
|
-
this.
|
|
33867
|
+
unregisterSurface(toolSessionId) {
|
|
33868
|
+
this.surfacesByToolSid.delete(toolSessionId);
|
|
33868
33869
|
}
|
|
33869
33870
|
/**
|
|
33870
|
-
* 同时 resize pty 子进程和
|
|
33871
|
-
* 顺序:pty.resize 在前,
|
|
33872
|
-
*
|
|
33871
|
+
* 同时 resize pty 子进程和 surface buffer:UI session:pty:resize 入口走这个。
|
|
33872
|
+
* 顺序:pty.resize 在前,surface.resize 在后 —— pty 是真正的 CC 子进程视图,必须先成功;
|
|
33873
|
+
* surface 只是 daemon 端镜像,任一步失败都返 false。
|
|
33873
33874
|
* 找不到 runner / toolSessionId / pty 任一缺失 → 返 false,handler 静默给 UI 报 ok=false 不抛错。
|
|
33874
33875
|
*/
|
|
33875
33876
|
resizePty(sessionId, cols, rows) {
|
|
@@ -33884,8 +33885,8 @@ var SessionManager = class {
|
|
|
33884
33885
|
} catch {
|
|
33885
33886
|
return false;
|
|
33886
33887
|
}
|
|
33887
|
-
const
|
|
33888
|
-
if (
|
|
33888
|
+
const surface = this.surfacesByToolSid.get(tsid);
|
|
33889
|
+
if (surface) surface.resize(cols, rows);
|
|
33889
33890
|
return true;
|
|
33890
33891
|
}
|
|
33891
33892
|
/**
|
|
@@ -35183,59 +35184,30 @@ var POPUP_FOOTER_PATTERNS = [
|
|
|
35183
35184
|
{ kind: "permission", regex: /Tab to amend|Do you want to proceed/i },
|
|
35184
35185
|
{ kind: "question", regex: /Enter to select|↑\/↓ to navigate/i }
|
|
35185
35186
|
];
|
|
35186
|
-
function
|
|
35187
|
+
function createTerminalSurface(opts) {
|
|
35187
35188
|
const term = new Terminal({
|
|
35188
|
-
cols: opts
|
|
35189
|
-
rows: opts
|
|
35190
|
-
scrollback: 1e3,
|
|
35189
|
+
cols: opts?.cols ?? 120,
|
|
35190
|
+
rows: opts?.rows ?? 40,
|
|
35191
|
+
scrollback: opts?.scrollback ?? 1e3,
|
|
35191
35192
|
allowProposedApi: true
|
|
35192
35193
|
});
|
|
35193
35194
|
const serializeAddon = new D();
|
|
35194
35195
|
term.loadAddon(serializeAddon);
|
|
35195
|
-
let visible = null;
|
|
35196
|
-
let pendingClear = null;
|
|
35197
35196
|
let parseSeq = 0;
|
|
35198
35197
|
let pendingParse = 0;
|
|
35199
35198
|
let drainWaiters = [];
|
|
35200
35199
|
let disposed = false;
|
|
35201
|
-
const
|
|
35200
|
+
const observers = [];
|
|
35201
|
+
const collectLines = () => {
|
|
35202
35202
|
const buf = term.buffer.active;
|
|
35203
|
+
const lines = new Array(buf.length);
|
|
35203
35204
|
for (let i = 0; i < buf.length; i++) {
|
|
35204
35205
|
const line = buf.getLine(i);
|
|
35205
|
-
|
|
35206
|
-
const text = line.translateToString(true);
|
|
35207
|
-
if (!text) continue;
|
|
35208
|
-
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35209
|
-
if (p2.regex.test(text)) return p2.kind;
|
|
35210
|
-
}
|
|
35211
|
-
}
|
|
35212
|
-
return null;
|
|
35213
|
-
};
|
|
35214
|
-
const tick = () => {
|
|
35215
|
-
const matched = scanFooter();
|
|
35216
|
-
if (matched && visible === null) {
|
|
35217
|
-
visible = matched;
|
|
35218
|
-
opts.onTransition(matched, true);
|
|
35219
|
-
} else if (matched && visible !== null) {
|
|
35220
|
-
if (pendingClear) {
|
|
35221
|
-
clearTimeout(pendingClear);
|
|
35222
|
-
pendingClear = null;
|
|
35223
|
-
}
|
|
35224
|
-
} else if (!matched && visible !== null && !pendingClear) {
|
|
35225
|
-
const toClear = visible;
|
|
35226
|
-
pendingClear = setTimeout(() => {
|
|
35227
|
-
pendingClear = null;
|
|
35228
|
-
if (visible === toClear) {
|
|
35229
|
-
visible = null;
|
|
35230
|
-
opts.onTransition(toClear, false);
|
|
35231
|
-
}
|
|
35232
|
-
}, opts.clearQuietMs);
|
|
35206
|
+
lines[i] = line ? line.translateToString(true) : "";
|
|
35233
35207
|
}
|
|
35208
|
+
return lines;
|
|
35234
35209
|
};
|
|
35235
35210
|
return {
|
|
35236
|
-
get visibleKind() {
|
|
35237
|
-
return visible;
|
|
35238
|
-
},
|
|
35239
35211
|
get parseSeq() {
|
|
35240
35212
|
return parseSeq;
|
|
35241
35213
|
},
|
|
@@ -35249,7 +35221,10 @@ function createPopupDetector(opts) {
|
|
|
35249
35221
|
}
|
|
35250
35222
|
if (typeof seq === "number" && seq > parseSeq) parseSeq = seq;
|
|
35251
35223
|
pendingParse--;
|
|
35252
|
-
|
|
35224
|
+
if (observers.length > 0) {
|
|
35225
|
+
const lines = collectLines();
|
|
35226
|
+
for (const cb of [...observers]) cb(lines);
|
|
35227
|
+
}
|
|
35253
35228
|
if (pendingParse === 0 && drainWaiters.length > 0) {
|
|
35254
35229
|
const waiters = drainWaiters;
|
|
35255
35230
|
drainWaiters = [];
|
|
@@ -35271,42 +35246,93 @@ function createPopupDetector(opts) {
|
|
|
35271
35246
|
} catch {
|
|
35272
35247
|
}
|
|
35273
35248
|
},
|
|
35249
|
+
serialize() {
|
|
35250
|
+
return serializeAddon.serialize({ scrollback: 1e3 });
|
|
35251
|
+
},
|
|
35274
35252
|
dispose() {
|
|
35275
35253
|
disposed = true;
|
|
35276
|
-
|
|
35277
|
-
clearTimeout(pendingClear);
|
|
35278
|
-
pendingClear = null;
|
|
35279
|
-
}
|
|
35254
|
+
pendingParse = 0;
|
|
35280
35255
|
const waiters = drainWaiters;
|
|
35281
35256
|
drainWaiters = [];
|
|
35282
35257
|
for (const w2 of waiters) w2();
|
|
35258
|
+
observers.length = 0;
|
|
35283
35259
|
serializeAddon.dispose();
|
|
35284
35260
|
term.dispose();
|
|
35285
35261
|
},
|
|
35286
|
-
|
|
35287
|
-
|
|
35262
|
+
onTick(cb) {
|
|
35263
|
+
observers.push(cb);
|
|
35264
|
+
return () => {
|
|
35265
|
+
const i = observers.indexOf(cb);
|
|
35266
|
+
if (i >= 0) observers.splice(i, 1);
|
|
35267
|
+
};
|
|
35288
35268
|
}
|
|
35289
35269
|
};
|
|
35290
35270
|
}
|
|
35291
|
-
function
|
|
35292
|
-
|
|
35293
|
-
|
|
35294
|
-
|
|
35295
|
-
|
|
35296
|
-
|
|
35271
|
+
function observePopup(surface, opts) {
|
|
35272
|
+
let visible = null;
|
|
35273
|
+
let pendingClear = null;
|
|
35274
|
+
const scanFooter = (lines) => {
|
|
35275
|
+
for (const text of lines) {
|
|
35276
|
+
if (!text) continue;
|
|
35277
|
+
for (const p2 of POPUP_FOOTER_PATTERNS) {
|
|
35278
|
+
if (p2.regex.test(text)) return p2.kind;
|
|
35279
|
+
}
|
|
35280
|
+
}
|
|
35281
|
+
return null;
|
|
35282
|
+
};
|
|
35283
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35284
|
+
const matched = scanFooter(lines);
|
|
35285
|
+
const inputFrameVisible = opts.getInputFrameVisible();
|
|
35286
|
+
if (visible === null) {
|
|
35287
|
+
if (matched && !inputFrameVisible) {
|
|
35288
|
+
visible = matched;
|
|
35289
|
+
opts.onTransition(matched, true);
|
|
35290
|
+
}
|
|
35291
|
+
return;
|
|
35292
|
+
}
|
|
35293
|
+
if (!inputFrameVisible) {
|
|
35294
|
+
if (pendingClear) {
|
|
35295
|
+
clearTimeout(pendingClear);
|
|
35296
|
+
pendingClear = null;
|
|
35297
|
+
}
|
|
35298
|
+
return;
|
|
35299
|
+
}
|
|
35300
|
+
if (!pendingClear) {
|
|
35301
|
+
const toClear = visible;
|
|
35302
|
+
pendingClear = setTimeout(() => {
|
|
35303
|
+
pendingClear = null;
|
|
35304
|
+
if (visible === toClear) {
|
|
35305
|
+
visible = null;
|
|
35306
|
+
opts.onTransition(toClear, false);
|
|
35307
|
+
}
|
|
35308
|
+
}, opts.clearQuietMs);
|
|
35309
|
+
}
|
|
35297
35310
|
});
|
|
35311
|
+
return {
|
|
35312
|
+
get visibleKind() {
|
|
35313
|
+
return visible;
|
|
35314
|
+
},
|
|
35315
|
+
dispose() {
|
|
35316
|
+
unsubscribe();
|
|
35317
|
+
if (pendingClear) {
|
|
35318
|
+
clearTimeout(pendingClear);
|
|
35319
|
+
pendingClear = null;
|
|
35320
|
+
}
|
|
35321
|
+
}
|
|
35322
|
+
};
|
|
35323
|
+
}
|
|
35324
|
+
function observeReady(surface, opts) {
|
|
35325
|
+
let prevReady = false;
|
|
35298
35326
|
let quietTimer = null;
|
|
35299
|
-
let
|
|
35300
|
-
|
|
35301
|
-
|
|
35327
|
+
let unsubscribed = false;
|
|
35328
|
+
let latestLines = [];
|
|
35329
|
+
const scanReadyFrame = (lines) => {
|
|
35302
35330
|
let prevHasDivider = false;
|
|
35303
|
-
for (
|
|
35304
|
-
|
|
35305
|
-
if (!line) {
|
|
35331
|
+
for (const text of lines) {
|
|
35332
|
+
if (!text) {
|
|
35306
35333
|
prevHasDivider = false;
|
|
35307
35334
|
continue;
|
|
35308
35335
|
}
|
|
35309
|
-
const text = line.translateToString(true);
|
|
35310
35336
|
const hasPrompt = /^❯/.test(text);
|
|
35311
35337
|
const hasDivider = /─{20,}/.test(text);
|
|
35312
35338
|
if (hasPrompt && prevHasDivider) return true;
|
|
@@ -35329,18 +35355,18 @@ function createReadyDetector(opts) {
|
|
|
35329
35355
|
};
|
|
35330
35356
|
const quietFire = () => {
|
|
35331
35357
|
quietTimer = null;
|
|
35332
|
-
if (
|
|
35333
|
-
if (!scanReadyFrame()) {
|
|
35358
|
+
if (unsubscribed) return;
|
|
35359
|
+
if (!scanReadyFrame(latestLines)) {
|
|
35334
35360
|
dbg({ event: "quiet-fire-but-not-ready-anymore" });
|
|
35335
35361
|
return;
|
|
35336
35362
|
}
|
|
35337
35363
|
dbg({ event: "quiet-fire \u2192 onReady" });
|
|
35338
35364
|
opts.onReady();
|
|
35339
35365
|
};
|
|
35340
|
-
|
|
35341
|
-
|
|
35342
|
-
|
|
35343
|
-
const ready = scanReadyFrame();
|
|
35366
|
+
const unsubscribe = surface.onTick((lines) => {
|
|
35367
|
+
if (unsubscribed) return;
|
|
35368
|
+
latestLines = lines;
|
|
35369
|
+
const ready = scanReadyFrame(lines);
|
|
35344
35370
|
if (!ready) {
|
|
35345
35371
|
clearQuiet();
|
|
35346
35372
|
if (prevReady) {
|
|
@@ -35355,19 +35381,15 @@ function createReadyDetector(opts) {
|
|
|
35355
35381
|
}
|
|
35356
35382
|
clearQuiet();
|
|
35357
35383
|
quietTimer = setTimeout(quietFire, opts.quietMs);
|
|
35358
|
-
};
|
|
35384
|
+
});
|
|
35359
35385
|
return {
|
|
35360
|
-
|
|
35361
|
-
|
|
35362
|
-
term.write(data, () => {
|
|
35363
|
-
if (disposed) return;
|
|
35364
|
-
tick();
|
|
35365
|
-
});
|
|
35386
|
+
get inputFrameVisible() {
|
|
35387
|
+
return prevReady;
|
|
35366
35388
|
},
|
|
35367
35389
|
dispose() {
|
|
35368
|
-
|
|
35390
|
+
unsubscribed = true;
|
|
35391
|
+
unsubscribe();
|
|
35369
35392
|
clearQuiet();
|
|
35370
|
-
term.dispose();
|
|
35371
35393
|
}
|
|
35372
35394
|
};
|
|
35373
35395
|
}
|
|
@@ -35449,7 +35471,18 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35449
35471
|
tag: ctx.toolSessionId ?? "no-tsid"
|
|
35450
35472
|
});
|
|
35451
35473
|
const bootGate = createBootGate(pty, this.tuiLogger);
|
|
35452
|
-
const
|
|
35474
|
+
const surface = createTerminalSurface();
|
|
35475
|
+
const readyObserver = observeReady(surface, {
|
|
35476
|
+
quietMs: 500,
|
|
35477
|
+
onReady: () => {
|
|
35478
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35479
|
+
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35480
|
+
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35481
|
+
},
|
|
35482
|
+
// [RG-DBG] 排查 ReadyGate 偶现卡输入用;定位完跟所有 [RG] 日志一起删
|
|
35483
|
+
logger: this.tuiLogger
|
|
35484
|
+
});
|
|
35485
|
+
const popupObserver = observePopup(surface, {
|
|
35453
35486
|
clearQuietMs: 200,
|
|
35454
35487
|
onTransition: (kind, visible) => {
|
|
35455
35488
|
if (!ctx.toolSessionId || !this.tuiOpts.onPopupTransition) return;
|
|
@@ -35459,29 +35492,21 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35459
35492
|
visible
|
|
35460
35493
|
});
|
|
35461
35494
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
35462
|
-
}
|
|
35463
|
-
});
|
|
35464
|
-
const readyDetector = createReadyDetector({
|
|
35465
|
-
quietMs: 500,
|
|
35466
|
-
onReady: () => {
|
|
35467
|
-
if (!ctx.toolSessionId || !this.tuiOpts.onReady) return;
|
|
35468
|
-
this.tuiLogger?.debug("ready-detected", { toolSessionId: ctx.toolSessionId });
|
|
35469
|
-
this.tuiOpts.onReady(ctx.toolSessionId);
|
|
35470
35495
|
},
|
|
35471
|
-
//
|
|
35472
|
-
|
|
35496
|
+
// popup_close 信号源:ready observer 扫到 ❯ + ─{20,} 输入框结构 → 输入框可见 → popup 已关
|
|
35497
|
+
getInputFrameVisible: () => readyObserver.inputFrameVisible
|
|
35473
35498
|
});
|
|
35474
|
-
if (ctx.toolSessionId && this.tuiOpts.
|
|
35475
|
-
this.tuiOpts.
|
|
35499
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
|
|
35500
|
+
this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
|
|
35476
35501
|
}
|
|
35477
35502
|
let chunkSeq = 0;
|
|
35478
35503
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
35479
35504
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
35480
|
-
await
|
|
35505
|
+
await surface.drain();
|
|
35481
35506
|
return {
|
|
35482
|
-
data:
|
|
35483
|
-
popupKind:
|
|
35484
|
-
atSeq:
|
|
35507
|
+
data: surface.serialize(),
|
|
35508
|
+
popupKind: popupObserver.visibleKind,
|
|
35509
|
+
atSeq: surface.parseSeq
|
|
35485
35510
|
};
|
|
35486
35511
|
});
|
|
35487
35512
|
}
|
|
@@ -35498,8 +35523,7 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35498
35523
|
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
35499
35524
|
}
|
|
35500
35525
|
bootGate.feedBytes(merged);
|
|
35501
|
-
void
|
|
35502
|
-
readyDetector.writeBytes(merged.toString("utf8"));
|
|
35526
|
+
void surface.writeBytes(merged.toString("utf8"), seq);
|
|
35503
35527
|
if (ctx.toolSessionId) {
|
|
35504
35528
|
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
35505
35529
|
}
|
|
@@ -35517,14 +35541,15 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
35517
35541
|
flushCoalesced();
|
|
35518
35542
|
}
|
|
35519
35543
|
bootGate.dispose();
|
|
35520
|
-
|
|
35521
|
-
|
|
35522
|
-
|
|
35544
|
+
readyObserver.dispose();
|
|
35545
|
+
popupObserver.dispose();
|
|
35546
|
+
if (ctx.toolSessionId && this.tuiOpts.onSurfaceUnregister) {
|
|
35547
|
+
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
35523
35548
|
}
|
|
35524
35549
|
if (ctx.toolSessionId && this.tuiOpts.onPtyUnregister) {
|
|
35525
35550
|
this.tuiOpts.onPtyUnregister(ctx.toolSessionId);
|
|
35526
35551
|
}
|
|
35527
|
-
|
|
35552
|
+
surface.dispose();
|
|
35528
35553
|
});
|
|
35529
35554
|
return ptyChild;
|
|
35530
35555
|
}
|
|
@@ -40278,6 +40303,7 @@ function buildReadyFrame(deps, client) {
|
|
|
40278
40303
|
fileSharing.httpBaseUrl = httpBaseUrl;
|
|
40279
40304
|
if (deps.httpToken) fileSharing.httpToken = deps.httpToken;
|
|
40280
40305
|
}
|
|
40306
|
+
const daemonSource = process.env.CLAWD_DAEMON_SOURCE === "ota" ? "ota" : "packaged";
|
|
40281
40307
|
return {
|
|
40282
40308
|
version,
|
|
40283
40309
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -40287,6 +40313,7 @@ function buildReadyFrame(deps, client) {
|
|
|
40287
40313
|
runningSessions: info.runningSessions,
|
|
40288
40314
|
tunnelUrl,
|
|
40289
40315
|
mode: deps.mode,
|
|
40316
|
+
daemonSource,
|
|
40290
40317
|
...fileSharing
|
|
40291
40318
|
};
|
|
40292
40319
|
}
|
|
@@ -41992,8 +42019,8 @@ async function startDaemon(config) {
|
|
|
41992
42019
|
onPtyData: (tsid, b64, seq) => manager.emitPtyChunk(tsid, b64, seq),
|
|
41993
42020
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
41994
42021
|
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
41995
|
-
|
|
41996
|
-
|
|
42022
|
+
onSurfaceRegister: (tsid, surface) => manager.registerSurface(tsid, surface),
|
|
42023
|
+
onSurfaceUnregister: (tsid) => manager.unregisterSurface(tsid),
|
|
41997
42024
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
41998
42025
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid)
|
|
41999
42026
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
package/package.json
CHANGED