@clawos-dev/clawd 0.2.93-beta.173.df765cf → 0.2.93-beta.175.b162fd0
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 +135 -57
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -114,8 +114,13 @@ var init_methods = __esm({
|
|
|
114
114
|
"session:pty:resize",
|
|
115
115
|
// session:pty:snapshot — UI XtermPanel 重新挂载(切 session 回来 / 刷新)时主动拉一次当前
|
|
116
116
|
// 屏幕全量快照,再开始消费 incremental session:pty。daemon 调 manager.getPtyReplay(sessionId)
|
|
117
|
-
//
|
|
118
|
-
//
|
|
117
|
+
// 先 drain detector parse queue 再 serialize,保证 atSeq 与 buffer 内容严格对齐。
|
|
118
|
+
// response 字段:
|
|
119
|
+
// - data:当前屏幕 + scrollback 的 ANSI 字节串(base64-utf8),与 session:pty push 帧同格式
|
|
120
|
+
// - atSeq:此 snapshot 覆盖到的最后一帧 session:pty 的 seq(即 detector 已 parse 完成的最大 seq)
|
|
121
|
+
// UI 端 flush 时丢弃 pending 中 seq ≤ atSeq 的帧
|
|
122
|
+
// - popupKind:当前 detector 检测到的 popup 类型(null = 无 popup)
|
|
123
|
+
// session 不存在 / 非 tui / pty 已 exit 时 data=null, atSeq=0。
|
|
119
124
|
"session:pty:snapshot",
|
|
120
125
|
// ---- attachment.* file-sharing(详见 attachment-schemas.ts) ----
|
|
121
126
|
// 命名警告:这里的 `attachment.*` RPC 与 CC v2.x 上行 `type:"attachment"` 系统行
|
|
@@ -10554,20 +10559,6 @@ function withCommonFields(msg, o) {
|
|
|
10554
10559
|
if (typeof o.uuid === "string" && o.uuid.length > 0) msg.uuid = o.uuid;
|
|
10555
10560
|
return msg;
|
|
10556
10561
|
}
|
|
10557
|
-
function isRejectSentinelLine(obj) {
|
|
10558
|
-
if (!obj || typeof obj !== "object") return false;
|
|
10559
|
-
return typeof obj.toolUseResult === "string";
|
|
10560
|
-
}
|
|
10561
|
-
function isUserPlainTextLine(obj) {
|
|
10562
|
-
if (!obj || typeof obj !== "object") return false;
|
|
10563
|
-
const o = obj;
|
|
10564
|
-
if (o.type !== "user") return false;
|
|
10565
|
-
const message = o.message;
|
|
10566
|
-
const content = message?.content;
|
|
10567
|
-
if (!Array.isArray(content) || content.length !== 1) return false;
|
|
10568
|
-
const part = content[0];
|
|
10569
|
-
return part?.type === "text";
|
|
10570
|
-
}
|
|
10571
10562
|
function messageFromJsonl(obj) {
|
|
10572
10563
|
if (!obj || typeof obj !== "object") return null;
|
|
10573
10564
|
const o = obj;
|
|
@@ -10575,7 +10566,6 @@ function messageFromJsonl(obj) {
|
|
|
10575
10566
|
const ts = o.timestamp ?? o.ts;
|
|
10576
10567
|
if (type === "user" || type === "assistant") {
|
|
10577
10568
|
const message = o.message;
|
|
10578
|
-
if (type === "assistant" && message?.model === "<synthetic>") return null;
|
|
10579
10569
|
const content = message?.content;
|
|
10580
10570
|
const toolResultExtra = type === "user" ? extractToolResultExtraFromRaw(o.toolUseResult) : void 0;
|
|
10581
10571
|
const sourceToolAssistantUUID = type === "user" && typeof o.sourceToolAssistantUUID === "string" ? o.sourceToolAssistantUUID : void 0;
|
|
@@ -10889,11 +10879,7 @@ var init_claude_history = __esm({
|
|
|
10889
10879
|
);
|
|
10890
10880
|
const lines = readJsonlLines(file);
|
|
10891
10881
|
const messages = [];
|
|
10892
|
-
let prevIsRejectSentinel = false;
|
|
10893
10882
|
for (const line of lines) {
|
|
10894
|
-
const skipInterruptedMarker = prevIsRejectSentinel && isUserPlainTextLine(line);
|
|
10895
|
-
prevIsRejectSentinel = isRejectSentinelLine(line);
|
|
10896
|
-
if (skipInterruptedMarker) continue;
|
|
10897
10883
|
const msg = messageFromJsonl(line);
|
|
10898
10884
|
if (msg) messages.push(msg);
|
|
10899
10885
|
}
|
|
@@ -22600,6 +22586,11 @@ var SessionManager = class {
|
|
|
22600
22586
|
// 新 client 通过 session:subscribe 接入时由 onSubscribe hook 取 getPtyReplay(sessionId)
|
|
22601
22587
|
// 把屏幕快照定向 emit,解决"刷新页面 / 第二个 tab 接入时 xterm 白屏"
|
|
22602
22588
|
ptyReplaysByToolSid = /* @__PURE__ */ new Map();
|
|
22589
|
+
// TUI 模式:toolSessionId → @xterm/headless PopupDetector。
|
|
22590
|
+
// ClaudeTuiAdapter spawn 时通过 onDetectorRegister callback 写入;exit 时 onDetectorUnregister 清理。
|
|
22591
|
+
// manager.resizePty 用此映射把 UI 的 session:pty:resize 同步到 detector 内部 buffer,
|
|
22592
|
+
// 保证 snapshot serialize 时 cursor positioning 与 UI xterm 实际尺寸一致。
|
|
22593
|
+
detectorsByToolSid = /* @__PURE__ */ new Map();
|
|
22603
22594
|
// 仅 mode='tui' 需要:index.ts 装配阶段在 observer 构造后注入。
|
|
22604
22595
|
// observer.onEvent 要回灌 manager.feedObserverEvents,所以 observer 必须晚于 manager
|
|
22605
22596
|
// 构造;反过来 manager 又要在 mode='tui' 下 spawn session 时 attach observer。setter 解循环
|
|
@@ -23812,7 +23803,7 @@ var SessionManager = class {
|
|
|
23812
23803
|
* - session 存在但 pty 还没 spawn / 已 exit → null
|
|
23813
23804
|
* 调用方(onSubscribe hook)按返回值非空时构造 session:pty + session:control 帧定向回放
|
|
23814
23805
|
*/
|
|
23815
|
-
getPtyReplay(sessionId) {
|
|
23806
|
+
async getPtyReplay(sessionId) {
|
|
23816
23807
|
if (this.deps.mode !== "tui") {
|
|
23817
23808
|
probeEvent("replay.request", { sid: sessionId, result: "not-tui-mode" });
|
|
23818
23809
|
return null;
|
|
@@ -23841,7 +23832,7 @@ var SessionManager = class {
|
|
|
23841
23832
|
});
|
|
23842
23833
|
return null;
|
|
23843
23834
|
}
|
|
23844
|
-
const snap = replay();
|
|
23835
|
+
const snap = await replay();
|
|
23845
23836
|
const dumpFile = probeDumpReplay(sessionId, snap.data);
|
|
23846
23837
|
probeEvent("replay.dump", {
|
|
23847
23838
|
sid: sessionId,
|
|
@@ -23867,18 +23858,50 @@ var SessionManager = class {
|
|
|
23867
23858
|
if (!tsid) return void 0;
|
|
23868
23859
|
return this.ptyTransports.get(tsid);
|
|
23869
23860
|
}
|
|
23861
|
+
/** ClaudeTuiAdapter spawn 时注册 detector ref;同一 tsid 重 spawn 会覆盖 */
|
|
23862
|
+
registerDetector(toolSessionId, detector) {
|
|
23863
|
+
this.detectorsByToolSid.set(toolSessionId, detector);
|
|
23864
|
+
}
|
|
23865
|
+
/** spawn 进程 exit 时清理 */
|
|
23866
|
+
unregisterDetector(toolSessionId) {
|
|
23867
|
+
this.detectorsByToolSid.delete(toolSessionId);
|
|
23868
|
+
}
|
|
23869
|
+
/**
|
|
23870
|
+
* 同时 resize pty 子进程和 detector buffer:UI session:pty:resize 入口走这个。
|
|
23871
|
+
* 顺序:pty.resize 在前,detector.resize 在后 —— pty 是真正的 CC 子进程视图,必须先成功;
|
|
23872
|
+
* detector 只是 daemon 端镜像,任一步失败都返 false。
|
|
23873
|
+
* 找不到 runner / toolSessionId / pty 任一缺失 → 返 false,handler 静默给 UI 报 ok=false 不抛错。
|
|
23874
|
+
*/
|
|
23875
|
+
resizePty(sessionId, cols, rows) {
|
|
23876
|
+
const runner = this.runners.get(sessionId);
|
|
23877
|
+
if (!runner) return false;
|
|
23878
|
+
const tsid = runner.getState().file.toolSessionId;
|
|
23879
|
+
if (!tsid) return false;
|
|
23880
|
+
const pty = this.ptyTransports.get(tsid);
|
|
23881
|
+
if (!pty) return false;
|
|
23882
|
+
try {
|
|
23883
|
+
pty.resize(cols, rows);
|
|
23884
|
+
} catch {
|
|
23885
|
+
return false;
|
|
23886
|
+
}
|
|
23887
|
+
const detector = this.detectorsByToolSid.get(tsid);
|
|
23888
|
+
if (detector) detector.resize(cols, rows);
|
|
23889
|
+
return true;
|
|
23890
|
+
}
|
|
23870
23891
|
/**
|
|
23871
23892
|
* ClaudeTuiAdapter onPtyData callback:把 pty 字节流以 session:pty wire 帧广播给订阅者。
|
|
23893
|
+
* seq 来自 adapter spawn 路径维护的 per-session counter,与 detector.parseSeq 同源。
|
|
23894
|
+
* UI 端 snapshot/incremental dedup 必须靠这个 seq。
|
|
23872
23895
|
* 仅 mode='tui' 时发——SDK spawn 模式根本没 pty。
|
|
23873
|
-
* payload 永远只有 `{ sessionId, data }`,data = base64 UTF-8 字节流。
|
|
23896
|
+
* payload 永远只有 `{ sessionId, data, seq }`,data = base64 UTF-8 字节流。
|
|
23874
23897
|
* 详见 protocol/src/events.ts session:pty JSDoc
|
|
23875
23898
|
*/
|
|
23876
|
-
|
|
23899
|
+
emitPtyChunk(toolSessionId, b64, seq) {
|
|
23877
23900
|
if (this.deps.mode !== "tui") return;
|
|
23878
23901
|
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
23879
23902
|
if (!sid) return;
|
|
23880
23903
|
this.routeFromRunner(
|
|
23881
|
-
{ type: "session:pty", sessionId: sid, data: b64 },
|
|
23904
|
+
{ type: "session:pty", sessionId: sid, data: b64, seq },
|
|
23882
23905
|
"broadcast"
|
|
23883
23906
|
);
|
|
23884
23907
|
}
|
|
@@ -25225,6 +25248,10 @@ function createPopupDetector(opts) {
|
|
|
25225
25248
|
let pendingClear = null;
|
|
25226
25249
|
let quietTimer = null;
|
|
25227
25250
|
const QUIET_MS = opts.quietMs ?? 300;
|
|
25251
|
+
let parseSeq = 0;
|
|
25252
|
+
let pendingParse = 0;
|
|
25253
|
+
let drainWaiters = [];
|
|
25254
|
+
let disposed = false;
|
|
25228
25255
|
const scanScreen = () => {
|
|
25229
25256
|
const buf = term.buffer.active;
|
|
25230
25257
|
const lines = [];
|
|
@@ -25270,15 +25297,43 @@ function createPopupDetector(opts) {
|
|
|
25270
25297
|
get visibleKind() {
|
|
25271
25298
|
return visible;
|
|
25272
25299
|
},
|
|
25273
|
-
|
|
25300
|
+
get parseSeq() {
|
|
25301
|
+
return parseSeq;
|
|
25302
|
+
},
|
|
25303
|
+
writeBytes(data, seq) {
|
|
25304
|
+
pendingParse++;
|
|
25274
25305
|
return new Promise((resolve6) => {
|
|
25275
25306
|
term.write(data, () => {
|
|
25307
|
+
if (disposed) {
|
|
25308
|
+
resolve6();
|
|
25309
|
+
return;
|
|
25310
|
+
}
|
|
25311
|
+
if (typeof seq === "number" && seq > parseSeq) parseSeq = seq;
|
|
25312
|
+
pendingParse--;
|
|
25276
25313
|
tick();
|
|
25314
|
+
if (pendingParse === 0 && drainWaiters.length > 0) {
|
|
25315
|
+
const waiters = drainWaiters;
|
|
25316
|
+
drainWaiters = [];
|
|
25317
|
+
for (const w2 of waiters) w2();
|
|
25318
|
+
}
|
|
25277
25319
|
resolve6();
|
|
25278
25320
|
});
|
|
25279
25321
|
});
|
|
25280
25322
|
},
|
|
25323
|
+
drain() {
|
|
25324
|
+
if (pendingParse === 0) return Promise.resolve();
|
|
25325
|
+
return new Promise((resolve6) => {
|
|
25326
|
+
drainWaiters.push(resolve6);
|
|
25327
|
+
});
|
|
25328
|
+
},
|
|
25329
|
+
resize(cols, rows) {
|
|
25330
|
+
try {
|
|
25331
|
+
term.resize(cols, rows);
|
|
25332
|
+
} catch {
|
|
25333
|
+
}
|
|
25334
|
+
},
|
|
25281
25335
|
dispose() {
|
|
25336
|
+
disposed = true;
|
|
25282
25337
|
if (quietTimer) {
|
|
25283
25338
|
clearTimeout(quietTimer);
|
|
25284
25339
|
quietTimer = null;
|
|
@@ -25287,6 +25342,9 @@ function createPopupDetector(opts) {
|
|
|
25287
25342
|
clearTimeout(pendingClear);
|
|
25288
25343
|
pendingClear = null;
|
|
25289
25344
|
}
|
|
25345
|
+
const waiters = drainWaiters;
|
|
25346
|
+
drainWaiters = [];
|
|
25347
|
+
for (const w2 of waiters) w2();
|
|
25290
25348
|
serializeAddon.dispose();
|
|
25291
25349
|
term.dispose();
|
|
25292
25350
|
},
|
|
@@ -25352,23 +25410,52 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
25352
25410
|
this.tuiOpts.onPopupTransition(ctx.toolSessionId, kind, visible);
|
|
25353
25411
|
}
|
|
25354
25412
|
});
|
|
25413
|
+
if (ctx.toolSessionId && this.tuiOpts.onDetectorRegister) {
|
|
25414
|
+
this.tuiOpts.onDetectorRegister(ctx.toolSessionId, detector);
|
|
25415
|
+
}
|
|
25416
|
+
let chunkSeq = 0;
|
|
25355
25417
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
25356
|
-
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, () =>
|
|
25357
|
-
|
|
25358
|
-
|
|
25359
|
-
|
|
25418
|
+
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
25419
|
+
await detector.drain();
|
|
25420
|
+
return {
|
|
25421
|
+
data: detector.serialize(),
|
|
25422
|
+
popupKind: detector.visibleKind,
|
|
25423
|
+
atSeq: detector.parseSeq
|
|
25424
|
+
};
|
|
25425
|
+
});
|
|
25360
25426
|
}
|
|
25361
|
-
|
|
25427
|
+
let coalesceBuf = [];
|
|
25428
|
+
let coalesceScheduled = false;
|
|
25429
|
+
const flushCoalesced = () => {
|
|
25430
|
+
coalesceScheduled = false;
|
|
25431
|
+
if (coalesceBuf.length === 0) return;
|
|
25432
|
+
const merged = Buffer.concat(coalesceBuf);
|
|
25433
|
+
coalesceBuf = [];
|
|
25434
|
+
chunkSeq++;
|
|
25435
|
+
const seq = chunkSeq;
|
|
25362
25436
|
if (ctx.toolSessionId && this.tuiOpts.onPtyData) {
|
|
25363
|
-
this.tuiOpts.onPtyData(ctx.toolSessionId,
|
|
25437
|
+
this.tuiOpts.onPtyData(ctx.toolSessionId, merged.toString("base64"), seq);
|
|
25364
25438
|
}
|
|
25365
|
-
void detector.writeBytes(
|
|
25439
|
+
void detector.writeBytes(merged.toString("utf8"), seq);
|
|
25366
25440
|
if (ctx.toolSessionId) {
|
|
25367
|
-
probePtyDataThrottled(ctx.toolSessionId,
|
|
25441
|
+
probePtyDataThrottled(ctx.toolSessionId, merged.length, merged);
|
|
25442
|
+
}
|
|
25443
|
+
};
|
|
25444
|
+
ptyChild.stdout.on("data", (data) => {
|
|
25445
|
+
coalesceBuf.push(data);
|
|
25446
|
+
if (!coalesceScheduled) {
|
|
25447
|
+
coalesceScheduled = true;
|
|
25448
|
+
queueMicrotask(flushCoalesced);
|
|
25368
25449
|
}
|
|
25369
25450
|
});
|
|
25370
25451
|
ptyChild.on("exit", () => {
|
|
25371
25452
|
probeEvent("pty.exit", { tsid: ctx.toolSessionId });
|
|
25453
|
+
if (coalesceBuf.length > 0) {
|
|
25454
|
+
flushCoalesced();
|
|
25455
|
+
}
|
|
25456
|
+
if (ctx.toolSessionId && this.tuiOpts.onDetectorUnregister) {
|
|
25457
|
+
this.tuiOpts.onDetectorUnregister(ctx.toolSessionId);
|
|
25458
|
+
}
|
|
25372
25459
|
if (ctx.toolSessionId && this.tuiOpts.onPtyUnregister) {
|
|
25373
25460
|
this.tuiOpts.onPtyUnregister(ctx.toolSessionId);
|
|
25374
25461
|
}
|
|
@@ -29155,14 +29242,8 @@ function buildSessionHandlers(deps) {
|
|
|
29155
29242
|
return { response: { type: "session:pty:resize", ok: false } };
|
|
29156
29243
|
}
|
|
29157
29244
|
ensureSessionAccess(ctx, sid, "send");
|
|
29158
|
-
const
|
|
29159
|
-
|
|
29160
|
-
try {
|
|
29161
|
-
pty.resize(cols, rows);
|
|
29162
|
-
} catch {
|
|
29163
|
-
return { response: { type: "session:pty:resize", ok: false } };
|
|
29164
|
-
}
|
|
29165
|
-
return { response: { type: "session:pty:resize", ok: true } };
|
|
29245
|
+
const ok = manager.resizePty(sid, cols, rows);
|
|
29246
|
+
return { response: { type: "session:pty:resize", ok } };
|
|
29166
29247
|
};
|
|
29167
29248
|
const ptySnapshot = async (frame, _client, ctx) => {
|
|
29168
29249
|
const sid = frame.sessionId;
|
|
@@ -29170,13 +29251,14 @@ function buildSessionHandlers(deps) {
|
|
|
29170
29251
|
return { response: { type: "session:pty:snapshot", ok: false } };
|
|
29171
29252
|
}
|
|
29172
29253
|
ensureSessionAccess(ctx, sid, "read");
|
|
29173
|
-
const snap = manager.getPtyReplay(sid);
|
|
29254
|
+
const snap = await manager.getPtyReplay(sid);
|
|
29174
29255
|
if (!snap) {
|
|
29175
29256
|
return {
|
|
29176
29257
|
response: {
|
|
29177
29258
|
type: "session:pty:snapshot",
|
|
29178
29259
|
ok: true,
|
|
29179
29260
|
data: null,
|
|
29261
|
+
atSeq: 0,
|
|
29180
29262
|
popupKind: null
|
|
29181
29263
|
}
|
|
29182
29264
|
};
|
|
@@ -29186,6 +29268,7 @@ function buildSessionHandlers(deps) {
|
|
|
29186
29268
|
type: "session:pty:snapshot",
|
|
29187
29269
|
ok: true,
|
|
29188
29270
|
data: Buffer.from(snap.data, "utf8").toString("base64"),
|
|
29271
|
+
atSeq: snap.atSeq,
|
|
29189
29272
|
popupKind: snap.popupKind
|
|
29190
29273
|
}
|
|
29191
29274
|
};
|
|
@@ -30584,9 +30667,11 @@ async function startDaemon(config) {
|
|
|
30584
30667
|
historyReader: new ClaudeHistoryReader(),
|
|
30585
30668
|
onPtyRegister: (tsid, pty) => manager.registerPty(tsid, pty),
|
|
30586
30669
|
onPtyUnregister: (tsid) => manager.unregisterPty(tsid),
|
|
30587
|
-
onPtyData: (tsid, b64) => manager.
|
|
30670
|
+
onPtyData: (tsid, b64, seq) => manager.emitPtyChunk(tsid, b64, seq),
|
|
30588
30671
|
onPopupTransition: (tsid, kind, visible) => manager.emitPopupTransition(tsid, kind, visible),
|
|
30589
|
-
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay)
|
|
30672
|
+
onPtyReplayRegister: (tsid, replay) => manager.registerPtyReplay(tsid, replay),
|
|
30673
|
+
onDetectorRegister: (tsid, detector) => manager.registerDetector(tsid, detector),
|
|
30674
|
+
onDetectorUnregister: (tsid) => manager.unregisterDetector(tsid)
|
|
30590
30675
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
|
30591
30676
|
registerAdapter("claude", claudeAdapter);
|
|
30592
30677
|
const personaRegistry = new PersonaRegistry(personaStore);
|
|
@@ -30752,16 +30837,8 @@ async function startDaemon(config) {
|
|
|
30752
30837
|
questions: Array.isArray(questions) ? questions : []
|
|
30753
30838
|
});
|
|
30754
30839
|
}
|
|
30755
|
-
|
|
30756
|
-
|
|
30757
|
-
if (replay.data) {
|
|
30758
|
-
client.send({
|
|
30759
|
-
type: "session:pty",
|
|
30760
|
-
sessionId,
|
|
30761
|
-
data: Buffer.from(replay.data, "utf8").toString("base64")
|
|
30762
|
-
});
|
|
30763
|
-
}
|
|
30764
|
-
if (replay.popupKind) {
|
|
30840
|
+
void manager.getPtyReplay(sessionId).then((replay) => {
|
|
30841
|
+
if (replay && replay.popupKind) {
|
|
30765
30842
|
client.send({
|
|
30766
30843
|
type: "session:control",
|
|
30767
30844
|
sessionId,
|
|
@@ -30769,7 +30846,8 @@ async function startDaemon(config) {
|
|
|
30769
30846
|
popupKind: replay.popupKind
|
|
30770
30847
|
});
|
|
30771
30848
|
}
|
|
30772
|
-
}
|
|
30849
|
+
}).catch(() => {
|
|
30850
|
+
});
|
|
30773
30851
|
}
|
|
30774
30852
|
});
|
|
30775
30853
|
transport = wsServer;
|
package/package.json
CHANGED