@clawos-dev/clawd 0.2.201-beta.403.73f7019 → 0.2.202-beta.404.f50161b
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
CHANGED
|
@@ -29153,7 +29153,7 @@ var require_websocket = __commonJS({
|
|
|
29153
29153
|
var http3 = require("http");
|
|
29154
29154
|
var net4 = require("net");
|
|
29155
29155
|
var tls = require("tls");
|
|
29156
|
-
var { randomBytes, createHash:
|
|
29156
|
+
var { randomBytes, createHash: createHash3 } = require("crypto");
|
|
29157
29157
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
29158
29158
|
var { URL: URL2 } = require("url");
|
|
29159
29159
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -29813,7 +29813,7 @@ var require_websocket = __commonJS({
|
|
|
29813
29813
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
29814
29814
|
return;
|
|
29815
29815
|
}
|
|
29816
|
-
const digest =
|
|
29816
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
29817
29817
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
29818
29818
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
29819
29819
|
return;
|
|
@@ -30180,7 +30180,7 @@ var require_websocket_server = __commonJS({
|
|
|
30180
30180
|
var EventEmitter3 = require("events");
|
|
30181
30181
|
var http3 = require("http");
|
|
30182
30182
|
var { Duplex } = require("stream");
|
|
30183
|
-
var { createHash:
|
|
30183
|
+
var { createHash: createHash3 } = require("crypto");
|
|
30184
30184
|
var extension2 = require_extension();
|
|
30185
30185
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
30186
30186
|
var subprotocol2 = require_subprotocol();
|
|
@@ -30481,7 +30481,7 @@ var require_websocket_server = __commonJS({
|
|
|
30481
30481
|
);
|
|
30482
30482
|
}
|
|
30483
30483
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
30484
|
-
const digest =
|
|
30484
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
30485
30485
|
const headers = [
|
|
30486
30486
|
"HTTP/1.1 101 Switching Protocols",
|
|
30487
30487
|
"Upgrade: websocket",
|
|
@@ -41144,6 +41144,18 @@ function createLogger(opts = {}) {
|
|
|
41144
41144
|
);
|
|
41145
41145
|
return wrap(base);
|
|
41146
41146
|
}
|
|
41147
|
+
function createFileOnlyLogger(opts) {
|
|
41148
|
+
const level = opts.level ?? "debug";
|
|
41149
|
+
try {
|
|
41150
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(opts.file), { recursive: true });
|
|
41151
|
+
} catch {
|
|
41152
|
+
}
|
|
41153
|
+
const base = (0, import_pino.default)(
|
|
41154
|
+
{ level, base: {} },
|
|
41155
|
+
import_pino.default.destination({ dest: opts.file, mkdir: true, sync: true })
|
|
41156
|
+
);
|
|
41157
|
+
return wrap(base);
|
|
41158
|
+
}
|
|
41147
41159
|
function pinoLevelToString(n) {
|
|
41148
41160
|
if (typeof n !== "number") return null;
|
|
41149
41161
|
if (n >= 50) return "error";
|
|
@@ -43338,9 +43350,18 @@ var SessionManager = class {
|
|
|
43338
43350
|
// 由 observer 监听 jsonl user 行后调 recordRealUserUuid 建立映射;rewind 系列 RPC 在
|
|
43339
43351
|
// 入参 / 出参做转译,保证 UI 看到的 uuid 始终是 events 流里的 synth uuid
|
|
43340
43352
|
realUuidBySynth = /* @__PURE__ */ new Map();
|
|
43341
|
-
//
|
|
43342
|
-
//
|
|
43343
|
-
|
|
43353
|
+
// observer 收到 `turn_duration` 信号但屏幕还没稳定 5s → 挂进 pending,等 `notifyScreenIdle`
|
|
43354
|
+
// (屏幕 armed=true 触发点)flush 成真正的 turn_end 进 reducer。
|
|
43355
|
+
//
|
|
43356
|
+
// 语义澄清:`turn_duration` 是 CC 报的原始信号("本轮 API 调用完了"),**不代表 turn 真的结束**
|
|
43357
|
+
//(背景 agent 可能还在跑)。屏幕 5s 稳定 + 无 popup 才是"确认信号"。
|
|
43358
|
+
// 两者 AND 后 daemon 才产生真正的 `turn_end` 事件送给 reducer。
|
|
43359
|
+
//
|
|
43360
|
+
// pending 不设截止时间:屏幕稳定的时机由 CC UI 决定,可能几秒也可能几分钟。观察者以
|
|
43361
|
+
// "屏幕真的稳定"作为触发点,signal-driven 而非 timer-driven。
|
|
43362
|
+
//
|
|
43363
|
+
// 清理点:newSession / stop / session-delete / stopAll。
|
|
43364
|
+
pendingTurnDurationSignals = /* @__PURE__ */ new Set();
|
|
43344
43365
|
// SessionStore 按 scope 派生(root = <dataDir>/sessions/<scopeSubPath>/)。
|
|
43345
43366
|
// default scope 直接复用 deps.store;persona scope(owner / listener)第一次访问时按需创建并缓存。
|
|
43346
43367
|
// 取代旧的 storesByAgent —— agentId 概念由 SessionScope 取代,路径即身份,
|
|
@@ -43680,6 +43701,14 @@ var SessionManager = class {
|
|
|
43680
43701
|
routeFromRunner(frame, target) {
|
|
43681
43702
|
const compressed = compressFrameForWire(frame);
|
|
43682
43703
|
if (!compressed) return;
|
|
43704
|
+
if (compressed.type === "session:status") {
|
|
43705
|
+
const s = compressed;
|
|
43706
|
+
this.deps.screenIdleProbeLogger?.info("session:status wire emit", {
|
|
43707
|
+
sessionId: s.sessionId,
|
|
43708
|
+
status: s.status,
|
|
43709
|
+
target
|
|
43710
|
+
});
|
|
43711
|
+
}
|
|
43683
43712
|
if (compressed.type === "session:event" || compressed.type === "session:status") {
|
|
43684
43713
|
const sid = compressed.sessionId;
|
|
43685
43714
|
if (sid) {
|
|
@@ -43953,7 +43982,7 @@ var SessionManager = class {
|
|
|
43953
43982
|
this.runners.delete(args.sessionId);
|
|
43954
43983
|
this.realUuidBySynth.delete(args.sessionId);
|
|
43955
43984
|
this.lastUiSizeBySessionId.delete(args.sessionId);
|
|
43956
|
-
this.
|
|
43985
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
43957
43986
|
return { response: { sessionId: args.sessionId }, broadcast };
|
|
43958
43987
|
}
|
|
43959
43988
|
this.deleteOwned(args.sessionId);
|
|
@@ -43983,6 +44012,7 @@ var SessionManager = class {
|
|
|
43983
44012
|
async stop(args) {
|
|
43984
44013
|
const runner = this.runners.get(args.sessionId);
|
|
43985
44014
|
if (!runner) return { response: { ok: true }, broadcast: [] };
|
|
44015
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
43986
44016
|
const { broadcast } = this.withCollector(() => {
|
|
43987
44017
|
runner.input({ kind: "command", command: { kind: "stop" } });
|
|
43988
44018
|
});
|
|
@@ -44116,6 +44146,7 @@ var SessionManager = class {
|
|
|
44116
44146
|
newSession(args) {
|
|
44117
44147
|
const existingFile = this.getFile(args.sessionId);
|
|
44118
44148
|
const nextToolSessionId = this.deps.mode === "tui" && (existingFile.tool ?? "claude") === "claude" ? v4_default() : void 0;
|
|
44149
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
44119
44150
|
const runner = this.runners.get(args.sessionId);
|
|
44120
44151
|
if (runner) {
|
|
44121
44152
|
const { value, broadcast } = this.withCollector(() => {
|
|
@@ -44216,6 +44247,7 @@ var SessionManager = class {
|
|
|
44216
44247
|
for (const r of this.runners.values()) {
|
|
44217
44248
|
r.input({ kind: "command", command: { kind: "stop" } });
|
|
44218
44249
|
}
|
|
44250
|
+
this.pendingTurnDurationSignals.clear();
|
|
44219
44251
|
}
|
|
44220
44252
|
// 给 observer 用:拿已存在的 runner
|
|
44221
44253
|
getActive(sessionId) {
|
|
@@ -44432,7 +44464,7 @@ var SessionManager = class {
|
|
|
44432
44464
|
this.runners.delete(args.sessionId);
|
|
44433
44465
|
this.realUuidBySynth.delete(args.sessionId);
|
|
44434
44466
|
this.lastUiSizeBySessionId.delete(args.sessionId);
|
|
44435
|
-
this.
|
|
44467
|
+
this.clearPendingTurnEnd(args.sessionId);
|
|
44436
44468
|
return { response: { sessionId: args.sessionId }, broadcast };
|
|
44437
44469
|
}
|
|
44438
44470
|
this.storeFor(args.scope).delete(args.sessionId);
|
|
@@ -44678,23 +44710,93 @@ var SessionManager = class {
|
|
|
44678
44710
|
return;
|
|
44679
44711
|
}
|
|
44680
44712
|
}
|
|
44681
|
-
this.lastObserverEventAt.set(sessionId, (this.deps.now ?? Date.now)());
|
|
44682
44713
|
let feedEvents = outEvents;
|
|
44683
44714
|
if (outEvents.some((e) => e.kind === "turn_end")) {
|
|
44684
|
-
const
|
|
44685
|
-
|
|
44715
|
+
const runnerState = runner.getState();
|
|
44716
|
+
const toolSessionId = runnerState.file.toolSessionId;
|
|
44717
|
+
const adapter = this.deps.getAdapter(runnerState.file.tool ?? "claude");
|
|
44718
|
+
const gateOpen = !adapter.canAcceptTurnEnd || !toolSessionId ? true : adapter.canAcceptTurnEnd(toolSessionId);
|
|
44719
|
+
this.deps.screenIdleProbeLogger?.info("turn_duration signal received", {
|
|
44720
|
+
sessionId,
|
|
44721
|
+
toolSessionId,
|
|
44722
|
+
batchKinds: outEvents.map((e) => e.kind),
|
|
44723
|
+
gateOpen,
|
|
44724
|
+
hasCanAcceptGate: !!adapter.canAcceptTurnEnd
|
|
44725
|
+
});
|
|
44726
|
+
if (gateOpen) {
|
|
44727
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44728
|
+
"turn_duration \u2192 turn_end confirmed (gate open) \u2192 fed to reducer",
|
|
44729
|
+
{ sessionId, toolSessionId }
|
|
44730
|
+
);
|
|
44731
|
+
} else {
|
|
44686
44732
|
feedEvents = outEvents.filter((e) => e.kind !== "turn_end");
|
|
44687
|
-
this.
|
|
44688
|
-
|
|
44689
|
-
|
|
44690
|
-
|
|
44691
|
-
|
|
44692
|
-
}
|
|
44733
|
+
if (this.pendingTurnDurationSignals.has(sessionId)) {
|
|
44734
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44735
|
+
"turn_duration dedup: pending already set (repeated observer signal)",
|
|
44736
|
+
{ sessionId, toolSessionId }
|
|
44737
|
+
);
|
|
44738
|
+
} else {
|
|
44739
|
+
this.pendingTurnDurationSignals.add(sessionId);
|
|
44740
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44741
|
+
"turn_duration pending: gate closed \u2192 waiting for screen-idle signal",
|
|
44742
|
+
{ sessionId, toolSessionId }
|
|
44743
|
+
);
|
|
44744
|
+
}
|
|
44693
44745
|
}
|
|
44694
44746
|
}
|
|
44695
44747
|
if (feedEvents.length === 0) return;
|
|
44696
44748
|
runner.feedObserverEvents(feedEvents);
|
|
44697
44749
|
}
|
|
44750
|
+
/**
|
|
44751
|
+
* `ClaudeTuiAdapter.observeScreenIdle` fire triggered → armed=true 时调用(一次性触发点)。
|
|
44752
|
+
*
|
|
44753
|
+
* 语义:屏幕真的稳定了 5s。查有没有 pending 的 turn_duration 信号:
|
|
44754
|
+
* - 有 → 之前收到的 turn_duration 被屏幕稳定**确认**了,flush 成 turn_end 进 reducer
|
|
44755
|
+
* - 无 → 屏幕稳定但从没收到 turn_duration → noop(不生成 turn_end;补偿路径的错误做法)
|
|
44756
|
+
*
|
|
44757
|
+
* pending 集合是**必要前提**:turn_end 只能来自"observer 收 turn_duration + 屏幕后续稳定"
|
|
44758
|
+
* 双源确认,任何一个缺少都不能 emit。这跟 PR #962 拆掉的 dispatchTurnIdle "屏幕静止就补
|
|
44759
|
+
* turn_end" 语义不同。
|
|
44760
|
+
*
|
|
44761
|
+
* 仅 TUI 模式;SDK / codex 没有屏幕信号也就不会触发本方法。
|
|
44762
|
+
*/
|
|
44763
|
+
notifyScreenIdle(toolSessionId) {
|
|
44764
|
+
if (this.deps.mode !== "tui") return;
|
|
44765
|
+
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
44766
|
+
if (!sid) {
|
|
44767
|
+
this.deps.screenIdleProbeLogger?.warn("notifyScreenIdle: no session for toolSessionId", {
|
|
44768
|
+
toolSessionId
|
|
44769
|
+
});
|
|
44770
|
+
return;
|
|
44771
|
+
}
|
|
44772
|
+
if (!this.pendingTurnDurationSignals.has(sid)) {
|
|
44773
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44774
|
+
"notifyScreenIdle: no pending turn_duration \u2192 noop",
|
|
44775
|
+
{ sessionId: sid, toolSessionId }
|
|
44776
|
+
);
|
|
44777
|
+
return;
|
|
44778
|
+
}
|
|
44779
|
+
const runner = this.runners.get(sid);
|
|
44780
|
+
if (!runner) {
|
|
44781
|
+
this.pendingTurnDurationSignals.delete(sid);
|
|
44782
|
+
this.deps.screenIdleProbeLogger?.warn(
|
|
44783
|
+
"notifyScreenIdle: pending but no runner \u2192 cleared without inject",
|
|
44784
|
+
{ sessionId: sid, toolSessionId }
|
|
44785
|
+
);
|
|
44786
|
+
return;
|
|
44787
|
+
}
|
|
44788
|
+
this.pendingTurnDurationSignals.delete(sid);
|
|
44789
|
+
this.deps.screenIdleProbeLogger?.info(
|
|
44790
|
+
"notifyScreenIdle: pending turn_duration + screen idle confirmed \u2192 inject turn_end",
|
|
44791
|
+
{ sessionId: sid, toolSessionId }
|
|
44792
|
+
);
|
|
44793
|
+
runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
|
|
44794
|
+
}
|
|
44795
|
+
clearPendingTurnEnd(sessionId) {
|
|
44796
|
+
if (this.pendingTurnDurationSignals.delete(sessionId)) {
|
|
44797
|
+
this.deps.screenIdleProbeLogger?.info("pending turn_duration cleared", { sessionId });
|
|
44798
|
+
}
|
|
44799
|
+
}
|
|
44698
44800
|
// AskUserQuestion 表单回写(plan: clawd-ask-user-question):UI 答完所有 question 后调用。
|
|
44699
44801
|
// - session 不存在 / 无 runner → noop 幂等返回 ok(first-decider-wins)
|
|
44700
44802
|
// - reducer noop(toolUseId 不存在或已答过)也保持幂等返回,handler 不抛
|
|
@@ -44953,70 +45055,6 @@ var SessionManager = class {
|
|
|
44953
45055
|
if (!runner) return;
|
|
44954
45056
|
runner.input({ kind: "ready-detected" });
|
|
44955
45057
|
}
|
|
44956
|
-
/**
|
|
44957
|
-
* ClaudeTuiAdapter onTurnIdle callback:屏幕内容静止时**复发**本轮已出现过的权威 turn_end。
|
|
44958
|
-
* 本意:turn_duration 写盘早于尾段正文 → observer 把尾随 text 推进 buffer 盖掉 lastEventKind →
|
|
44959
|
-
* spinner 不熄;屏幕静止时再补一条 turn_end 排到尾随 text 之后。
|
|
44960
|
-
*
|
|
44961
|
-
* Fix A(修 bug1 "UI 还在变却显示结束" / bug2 "发消息后无 spinner"):补偿**只复发不 originate**——
|
|
44962
|
-
* 仅当本轮已出现过 turn_end(turnEndSeenThisTurn,即真有过尾随 text 覆盖场景)才补。turnEndSeenThisTurn
|
|
44963
|
-
* ===false 说明本轮 CC 从未结束过(仍在工作 / 刚发消息没产出 / 漏检弹框等用户),屏幕静止 ≠ turn 结束,
|
|
44964
|
-
* 凭空 inject turn_end 会误灭 spinner——故跳过,让真正的 turn_duration 来时再正常收。
|
|
44965
|
-
* 仅 tui 模式;runner 缺失 noop。turn_end 无 uuid 不参与 dedup。
|
|
44966
|
-
*/
|
|
44967
|
-
dispatchTurnIdle(toolSessionId) {
|
|
44968
|
-
if (this.deps.mode !== "tui") return;
|
|
44969
|
-
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
44970
|
-
const runner = sid ? this.runners.get(sid) : void 0;
|
|
44971
|
-
if (!runner) return;
|
|
44972
|
-
const ev = this.peekTurnEvidence(runner);
|
|
44973
|
-
const willInject = ev.turnEndSeenThisTurn;
|
|
44974
|
-
this.deps.logger?.info("[TE-PROBE] screen-idle compensation", {
|
|
44975
|
-
sessionId: sid,
|
|
44976
|
-
src: "screen-idle",
|
|
44977
|
-
willInject,
|
|
44978
|
-
...ev
|
|
44979
|
-
});
|
|
44980
|
-
if (!willInject) return;
|
|
44981
|
-
runner.input({ kind: "inject-events", events: [{ kind: "turn_end" }] });
|
|
44982
|
-
}
|
|
44983
|
-
/**
|
|
44984
|
-
* 读 runner 当前 turn 态快照,供两个 turn_end 注入源(屏幕静止补偿 / observer turn_duration)
|
|
44985
|
-
* 判断误发 + 打 [TE-PROBE] 日志。
|
|
44986
|
-
* turnEndSeenThisTurn:从 buffer 末尾回扫到最近 user_text 期间是否已出现过 turn_end
|
|
44987
|
-
* (已出现=本轮真结束过、合法尾随;未出现=本轮还没结束过)→ Fix A 复发闸。
|
|
44988
|
-
* turnHasContent:末条是否 assistant 产出(非 user_text/turn_end/空)→ Fix B 空 turn 守卫闸。
|
|
44989
|
-
*/
|
|
44990
|
-
peekTurnEvidence(runner) {
|
|
44991
|
-
const st = runner.getState();
|
|
44992
|
-
const buf = st.buffer;
|
|
44993
|
-
const lastEventKindBefore = buf.length > 0 ? buf[buf.length - 1].event.kind : null;
|
|
44994
|
-
let turnEndSeenThisTurn = false;
|
|
44995
|
-
for (let i = buf.length - 1; i >= 0; i--) {
|
|
44996
|
-
const k2 = buf[i].event.kind;
|
|
44997
|
-
if (k2 === "user_text") break;
|
|
44998
|
-
if (k2 === "turn_end") {
|
|
44999
|
-
turnEndSeenThisTurn = true;
|
|
45000
|
-
break;
|
|
45001
|
-
}
|
|
45002
|
-
}
|
|
45003
|
-
const turnHasContent = lastEventKindBefore !== null && lastEventKindBefore !== "user_text" && lastEventKindBefore !== "turn_end";
|
|
45004
|
-
return { turnOpenBefore: st.turnOpen, lastEventKindBefore, turnEndSeenThisTurn, turnHasContent };
|
|
45005
|
-
}
|
|
45006
|
-
/**
|
|
45007
|
-
* observer 还需静止多久(ms)才满 idleMs,0 = 已满。observeScreenIdle 复合条件闸:屏幕静止后
|
|
45008
|
-
* 精确等这段剩余再补 turn_end —— turn_duration 写盘早于尾段正文,observer 把尾随 text poll 落盘
|
|
45009
|
-
* 期间屏幕可能已静止,仅看屏幕会早 fire(补的 turn_end 盖不到尾随 text 之后)。
|
|
45010
|
-
* 找不到 runner / 从无事件 → 0(不阻塞 fire)。idleMs 由装配处传 SCREEN_IDLE_MS。
|
|
45011
|
-
*/
|
|
45012
|
-
observerIdleWaitMs(toolSessionId, idleMs) {
|
|
45013
|
-
const sid = this.sessionIdByToolSid(toolSessionId);
|
|
45014
|
-
if (!sid) return 0;
|
|
45015
|
-
const last = this.lastObserverEventAt.get(sid);
|
|
45016
|
-
if (last === void 0) return 0;
|
|
45017
|
-
const elapsed = (this.deps.now ?? Date.now)() - last;
|
|
45018
|
-
return Math.max(0, idleMs - elapsed);
|
|
45019
|
-
}
|
|
45020
45058
|
/** toolSessionId → sessionId 反查(遍历 runners);session 数典型 < 10,O(n) 可接受 */
|
|
45021
45059
|
sessionIdByToolSid(toolSessionId) {
|
|
45022
45060
|
for (const [sid, runner] of this.runners) {
|
|
@@ -46755,6 +46793,7 @@ var CodexAdapter = class {
|
|
|
46755
46793
|
};
|
|
46756
46794
|
|
|
46757
46795
|
// src/tools/claude-tui.ts
|
|
46796
|
+
var import_node_crypto5 = require("crypto");
|
|
46758
46797
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
46759
46798
|
var import_node_os7 = __toESM(require("os"), 1);
|
|
46760
46799
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
@@ -47565,22 +47604,56 @@ function observeScreenIdle(surface, opts) {
|
|
|
47565
47604
|
timer = null;
|
|
47566
47605
|
if (disposed) return;
|
|
47567
47606
|
if (opts.getPopupVisible()) {
|
|
47607
|
+
opts.probeLogger?.info("screen-idle fire suppressed: popup visible", {
|
|
47608
|
+
label: opts.probeLabel
|
|
47609
|
+
});
|
|
47568
47610
|
timer = setTimeout(fire, opts.idleMs);
|
|
47569
47611
|
return;
|
|
47570
47612
|
}
|
|
47571
47613
|
const obsWait = opts.getObserverWaitMs?.() ?? 0;
|
|
47572
47614
|
if (obsWait > 0) {
|
|
47615
|
+
opts.probeLogger?.info("screen-idle fire suppressed: observer not idle", {
|
|
47616
|
+
label: opts.probeLabel,
|
|
47617
|
+
obsWait
|
|
47618
|
+
});
|
|
47573
47619
|
timer = setTimeout(fire, Math.max(obsWait, REWAIT_MIN_MS));
|
|
47574
47620
|
return;
|
|
47575
47621
|
}
|
|
47576
|
-
if (armed)
|
|
47622
|
+
if (armed) {
|
|
47623
|
+
opts.probeLogger?.debug("screen-idle fire noop: already armed", {
|
|
47624
|
+
label: opts.probeLabel
|
|
47625
|
+
});
|
|
47626
|
+
return;
|
|
47627
|
+
}
|
|
47577
47628
|
armed = true;
|
|
47629
|
+
opts.probeLogger?.info("screen-idle fire triggered \u2192 armed=true, calling onIdle", {
|
|
47630
|
+
label: opts.probeLabel
|
|
47631
|
+
});
|
|
47578
47632
|
opts.onIdle();
|
|
47579
47633
|
};
|
|
47580
47634
|
const unsub = surface.onTick((lines) => {
|
|
47581
47635
|
if (disposed) return;
|
|
47582
47636
|
const snap = snapOf(lines);
|
|
47583
47637
|
if (snap === lastSnap) return;
|
|
47638
|
+
if (opts.probeLogger) {
|
|
47639
|
+
const prev = lastSnap;
|
|
47640
|
+
const meta = {
|
|
47641
|
+
label: opts.probeLabel,
|
|
47642
|
+
prevHash: prev === null ? null : shortHash(prev),
|
|
47643
|
+
nextHash: shortHash(snap),
|
|
47644
|
+
prevLen: prev?.length ?? 0,
|
|
47645
|
+
nextLen: snap.length
|
|
47646
|
+
};
|
|
47647
|
+
if (prev !== null) {
|
|
47648
|
+
const diff2 = firstLineDiff(prev, snap);
|
|
47649
|
+
if (diff2) {
|
|
47650
|
+
meta.diffRow = diff2.row;
|
|
47651
|
+
meta.prevRow = diff2.prev;
|
|
47652
|
+
meta.nextRow = diff2.next;
|
|
47653
|
+
}
|
|
47654
|
+
}
|
|
47655
|
+
opts.probeLogger.info("screen-idle tick snap changed", meta);
|
|
47656
|
+
}
|
|
47584
47657
|
lastSnap = snap;
|
|
47585
47658
|
armed = false;
|
|
47586
47659
|
clear();
|
|
@@ -47591,9 +47664,38 @@ function observeScreenIdle(surface, opts) {
|
|
|
47591
47664
|
disposed = true;
|
|
47592
47665
|
unsub();
|
|
47593
47666
|
clear();
|
|
47667
|
+
},
|
|
47668
|
+
isIdle() {
|
|
47669
|
+
const popupVisible = opts.getPopupVisible();
|
|
47670
|
+
const idle = armed && !popupVisible;
|
|
47671
|
+
if (opts.probeLogger) {
|
|
47672
|
+
opts.probeLogger.info("screen-idle isIdle check", {
|
|
47673
|
+
label: opts.probeLabel,
|
|
47674
|
+
idle,
|
|
47675
|
+
armed,
|
|
47676
|
+
popupVisible
|
|
47677
|
+
});
|
|
47678
|
+
}
|
|
47679
|
+
return idle;
|
|
47594
47680
|
}
|
|
47595
47681
|
};
|
|
47596
47682
|
}
|
|
47683
|
+
function shortHash(s) {
|
|
47684
|
+
return (0, import_node_crypto5.createHash)("sha1").update(s).digest("hex").slice(0, 8);
|
|
47685
|
+
}
|
|
47686
|
+
function firstLineDiff(prev, next) {
|
|
47687
|
+
const p2 = prev.split("\n");
|
|
47688
|
+
const n = next.split("\n");
|
|
47689
|
+
const rows = Math.max(p2.length, n.length);
|
|
47690
|
+
for (let i = 0; i < rows; i++) {
|
|
47691
|
+
const pl = p2[i] ?? "";
|
|
47692
|
+
const nl = n[i] ?? "";
|
|
47693
|
+
if (pl !== nl) {
|
|
47694
|
+
return { row: i, prev: pl.slice(0, 60), next: nl.slice(0, 60) };
|
|
47695
|
+
}
|
|
47696
|
+
}
|
|
47697
|
+
return null;
|
|
47698
|
+
}
|
|
47597
47699
|
var BYPASS_SETTLE_MS = 300;
|
|
47598
47700
|
var SCREEN_IDLE_MS = 5e3;
|
|
47599
47701
|
function createBootGate(pty, logger) {
|
|
@@ -47668,11 +47770,42 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47668
47770
|
// 用于 spawn / PtyChildProcess 链路打日志
|
|
47669
47771
|
tuiLogger;
|
|
47670
47772
|
tuiOpts;
|
|
47773
|
+
/**
|
|
47774
|
+
* per-toolSessionId 的 tui 观察者句柄,仅用于 turn_end gate 查询(`canAcceptTurnEnd`)。
|
|
47775
|
+
* onIdle / onPopupTransition 等回调仍走原有闭包(不复用这份 map),本 map 只承担
|
|
47776
|
+
* "manager 需要跨模块查屏幕/弹框状态"这单一职责。
|
|
47777
|
+
*/
|
|
47778
|
+
tuiStates = /* @__PURE__ */ new Map();
|
|
47671
47779
|
constructor(opts = {}) {
|
|
47672
47780
|
super(opts);
|
|
47673
47781
|
this.tuiLogger = opts.logger;
|
|
47674
47782
|
this.tuiOpts = opts;
|
|
47675
47783
|
}
|
|
47784
|
+
/**
|
|
47785
|
+
* TUI adapter 的 turn_end 权威判定:屏幕已 idle 且非弹框态才放行。
|
|
47786
|
+
*
|
|
47787
|
+
* `feedObserverEvents` 收到 observer 回灌 `turn_end` 时调用。屏幕仍在变(如后台 agent 在跑)
|
|
47788
|
+
* 时 drop 掉 turn_end,避免 `system/turn_duration` JSONL 帧误触发 running-idle 状态转换。
|
|
47789
|
+
*
|
|
47790
|
+
* 未跟踪的 toolSessionId(spawn 前 / spawn 失败 / 已 dispose)视为 pass —— gate 只 drop
|
|
47791
|
+
* "有证据判定为伪信号"的场景,不做 unknown → block。
|
|
47792
|
+
*/
|
|
47793
|
+
canAcceptTurnEnd(toolSessionId) {
|
|
47794
|
+
const state = this.tuiStates.get(toolSessionId);
|
|
47795
|
+
if (!state) {
|
|
47796
|
+
this.tuiOpts.screenIdleProbeLogger?.info(
|
|
47797
|
+
"canAcceptTurnEnd: no tuiState \u2192 pass (\u672A\u8DDF\u8E2A)",
|
|
47798
|
+
{ toolSessionId }
|
|
47799
|
+
);
|
|
47800
|
+
return true;
|
|
47801
|
+
}
|
|
47802
|
+
const result = state.screenIdle.isIdle();
|
|
47803
|
+
this.tuiOpts.screenIdleProbeLogger?.info("canAcceptTurnEnd", {
|
|
47804
|
+
toolSessionId,
|
|
47805
|
+
result
|
|
47806
|
+
});
|
|
47807
|
+
return result;
|
|
47808
|
+
}
|
|
47676
47809
|
spawn(ctx) {
|
|
47677
47810
|
const args = buildTuiSpawnArgs(ctx, jsonlExistsForCtx(ctx));
|
|
47678
47811
|
const cmd = process.env.CLAUDE_BIN ?? "claude";
|
|
@@ -47730,18 +47863,26 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47730
47863
|
const screenIdleObserver = observeScreenIdle(surface, {
|
|
47731
47864
|
idleMs: SCREEN_IDLE_MS,
|
|
47732
47865
|
onIdle: () => {
|
|
47733
|
-
if (!ctx.toolSessionId || !this.tuiOpts.
|
|
47734
|
-
this.tuiLogger?.debug("screen-idle \u2192
|
|
47735
|
-
this.tuiOpts.
|
|
47866
|
+
if (!ctx.toolSessionId || !this.tuiOpts.onScreenIdle) return;
|
|
47867
|
+
this.tuiLogger?.debug("screen-idle \u2192 notifyScreenIdle", { toolSessionId: ctx.toolSessionId });
|
|
47868
|
+
this.tuiOpts.onScreenIdle(ctx.toolSessionId);
|
|
47736
47869
|
},
|
|
47737
47870
|
getPopupVisible: () => popupObserver.visibleKind !== null,
|
|
47738
|
-
//
|
|
47739
|
-
|
|
47740
|
-
|
|
47871
|
+
// 取证 probe(可选,装配处传独立 file-only logger,跟主 daemon.log 解耦)
|
|
47872
|
+
...this.tuiOpts.screenIdleProbeLogger ? {
|
|
47873
|
+
probeLogger: this.tuiOpts.screenIdleProbeLogger,
|
|
47874
|
+
probeLabel: ctx.toolSessionId ?? "<no-tsid>"
|
|
47875
|
+
} : {}
|
|
47741
47876
|
});
|
|
47742
47877
|
if (ctx.toolSessionId && this.tuiOpts.onSurfaceRegister) {
|
|
47743
47878
|
this.tuiOpts.onSurfaceRegister(ctx.toolSessionId, surface);
|
|
47744
47879
|
}
|
|
47880
|
+
if (ctx.toolSessionId) {
|
|
47881
|
+
this.tuiStates.set(ctx.toolSessionId, {
|
|
47882
|
+
screenIdle: screenIdleObserver,
|
|
47883
|
+
popup: popupObserver
|
|
47884
|
+
});
|
|
47885
|
+
}
|
|
47745
47886
|
let chunkSeq = 0;
|
|
47746
47887
|
if (ctx.toolSessionId && this.tuiOpts.onPtyReplayRegister) {
|
|
47747
47888
|
this.tuiOpts.onPtyReplayRegister(ctx.toolSessionId, async () => {
|
|
@@ -47787,6 +47928,9 @@ var ClaudeTuiAdapter = class extends ClaudeAdapter {
|
|
|
47787
47928
|
readyObserver.dispose();
|
|
47788
47929
|
popupObserver.dispose();
|
|
47789
47930
|
screenIdleObserver.dispose();
|
|
47931
|
+
if (ctx.toolSessionId) {
|
|
47932
|
+
this.tuiStates.delete(ctx.toolSessionId);
|
|
47933
|
+
}
|
|
47790
47934
|
if (ctx.toolSessionId && this.tuiOpts.onSurfaceUnregister) {
|
|
47791
47935
|
this.tuiOpts.onSurfaceUnregister(ctx.toolSessionId);
|
|
47792
47936
|
}
|
|
@@ -48069,7 +48213,7 @@ async function writeInboxMcpConfig(args) {
|
|
|
48069
48213
|
// src/shift/store.ts
|
|
48070
48214
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
48071
48215
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
48072
|
-
var
|
|
48216
|
+
var import_node_crypto6 = require("crypto");
|
|
48073
48217
|
|
|
48074
48218
|
// src/shift/constants.ts
|
|
48075
48219
|
var MAX_RUNS_PER_SHIFT = 30;
|
|
@@ -48165,7 +48309,7 @@ function createShiftStore(deps) {
|
|
|
48165
48309
|
const nextRunAtMs = computeNextRunAtMs(input.schedule, now) ?? void 0;
|
|
48166
48310
|
const shift = {
|
|
48167
48311
|
...input,
|
|
48168
|
-
id: (0,
|
|
48312
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
48169
48313
|
createdAtMs: now,
|
|
48170
48314
|
updatedAtMs: now,
|
|
48171
48315
|
state: { nextRunAtMs },
|
|
@@ -51076,7 +51220,7 @@ function lookupMime(filePathOrName) {
|
|
|
51076
51220
|
}
|
|
51077
51221
|
|
|
51078
51222
|
// src/attachment/sign-url.ts
|
|
51079
|
-
var
|
|
51223
|
+
var import_node_crypto7 = __toESM(require("crypto"), 1);
|
|
51080
51224
|
var HMAC_ALGO = "sha256";
|
|
51081
51225
|
function base64urlEncode(buf) {
|
|
51082
51226
|
const b2 = typeof buf === "string" ? Buffer.from(buf, "utf8") : buf;
|
|
@@ -51093,7 +51237,7 @@ function decodeAbsPathFromUrl(encoded) {
|
|
|
51093
51237
|
}
|
|
51094
51238
|
function computeSig(secret, absPath, e) {
|
|
51095
51239
|
const msg = e === null ? absPath : `${absPath}|${e}`;
|
|
51096
|
-
return
|
|
51240
|
+
return import_node_crypto7.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
|
|
51097
51241
|
}
|
|
51098
51242
|
function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
|
|
51099
51243
|
const e = ttlSeconds === null ? null : Math.floor(now() / 1e3) + ttlSeconds;
|
|
@@ -51128,7 +51272,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
51128
51272
|
if (provided.length !== expected.length) {
|
|
51129
51273
|
return { ok: false, code: "BAD_SIG" };
|
|
51130
51274
|
}
|
|
51131
|
-
if (!
|
|
51275
|
+
if (!import_node_crypto7.default.timingSafeEqual(provided, expected)) {
|
|
51132
51276
|
return { ok: false, code: "BAD_SIG" };
|
|
51133
51277
|
}
|
|
51134
51278
|
if (e !== null && now() / 1e3 > e) {
|
|
@@ -51140,7 +51284,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
51140
51284
|
// src/attachment/upload.ts
|
|
51141
51285
|
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
51142
51286
|
var import_node_path25 = __toESM(require("path"), 1);
|
|
51143
|
-
var
|
|
51287
|
+
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
51144
51288
|
var import_promises2 = require("stream/promises");
|
|
51145
51289
|
var UploadError = class extends Error {
|
|
51146
51290
|
constructor(code, message) {
|
|
@@ -51164,11 +51308,11 @@ async function writeUploadedAttachment(args) {
|
|
|
51164
51308
|
} catch (err) {
|
|
51165
51309
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
51166
51310
|
}
|
|
51167
|
-
const hasher =
|
|
51311
|
+
const hasher = import_node_crypto8.default.createHash("sha256");
|
|
51168
51312
|
let actualSize = 0;
|
|
51169
51313
|
const tmpPath = import_node_path25.default.join(
|
|
51170
51314
|
attachmentsRoot,
|
|
51171
|
-
`.upload-${process.pid}-${Date.now()}-${
|
|
51315
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
|
|
51172
51316
|
);
|
|
51173
51317
|
try {
|
|
51174
51318
|
await (0, import_promises2.pipeline)(
|
|
@@ -52045,7 +52189,7 @@ function runAttachmentGc(args) {
|
|
|
52045
52189
|
// src/attachment/group.ts
|
|
52046
52190
|
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
52047
52191
|
var import_node_path29 = __toESM(require("path"), 1);
|
|
52048
|
-
var
|
|
52192
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
52049
52193
|
init_protocol();
|
|
52050
52194
|
var GroupFileStore = class {
|
|
52051
52195
|
dataDir;
|
|
@@ -52134,7 +52278,7 @@ var GroupFileStore = class {
|
|
|
52134
52278
|
entries[idx] = next;
|
|
52135
52279
|
} else {
|
|
52136
52280
|
next = {
|
|
52137
|
-
id: `gf-${
|
|
52281
|
+
id: `gf-${import_node_crypto9.default.randomBytes(6).toString("base64url")}`,
|
|
52138
52282
|
relPath: input.relPath,
|
|
52139
52283
|
from: input.from,
|
|
52140
52284
|
label: input.label,
|
|
@@ -52253,7 +52397,7 @@ function readDaemonSourceFromEnv(env = process.env) {
|
|
|
52253
52397
|
// src/tunnel/tunnel-manager.ts
|
|
52254
52398
|
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
52255
52399
|
var import_node_path34 = __toESM(require("path"), 1);
|
|
52256
|
-
var
|
|
52400
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
52257
52401
|
var import_node_child_process9 = require("child_process");
|
|
52258
52402
|
|
|
52259
52403
|
// src/tunnel/tunnel-store.ts
|
|
@@ -52752,7 +52896,7 @@ var TunnelManager = class {
|
|
|
52752
52896
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
52753
52897
|
});
|
|
52754
52898
|
const tomlPath = import_node_path34.default.join(this.deps.dataDir, "frpc.toml");
|
|
52755
|
-
const proxyName = `clawd-${t.subdomain}-${localPort}-${
|
|
52899
|
+
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
|
|
52756
52900
|
const toml = buildFrpcToml({
|
|
52757
52901
|
serverAddr: t.frpsHost,
|
|
52758
52902
|
serverPort: t.frpsPort,
|
|
@@ -53617,7 +53761,7 @@ function pumpWsToSshd(ws, deps, clientAddr) {
|
|
|
53617
53761
|
// src/tunnel/device-key.ts
|
|
53618
53762
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
53619
53763
|
var import_node_path41 = __toESM(require("path"), 1);
|
|
53620
|
-
var
|
|
53764
|
+
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
53621
53765
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
53622
53766
|
function deriveStableDeviceKey(opts = {}) {
|
|
53623
53767
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
@@ -53627,13 +53771,13 @@ function deriveStableDeviceKey(opts = {}) {
|
|
|
53627
53771
|
const normalizedDataDir = opts.dataDir ? import_node_path41.default.resolve(opts.dataDir) : null;
|
|
53628
53772
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
53629
53773
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
53630
|
-
return
|
|
53774
|
+
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
53631
53775
|
}
|
|
53632
53776
|
|
|
53633
53777
|
// src/auth-store.ts
|
|
53634
53778
|
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
53635
53779
|
var import_node_path42 = __toESM(require("path"), 1);
|
|
53636
|
-
var
|
|
53780
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
53637
53781
|
var AUTH_FILE_NAME = "auth.json";
|
|
53638
53782
|
function authFilePath(dataDir) {
|
|
53639
53783
|
return import_node_path42.default.join(dataDir, AUTH_FILE_NAME);
|
|
@@ -53665,10 +53809,10 @@ function loadOrCreateAuthFile(opts) {
|
|
|
53665
53809
|
return next;
|
|
53666
53810
|
}
|
|
53667
53811
|
function defaultGenerateToken() {
|
|
53668
|
-
return
|
|
53812
|
+
return import_node_crypto12.default.randomBytes(32).toString("base64url");
|
|
53669
53813
|
}
|
|
53670
53814
|
function defaultGenerateOwnerPrincipalId() {
|
|
53671
|
-
return `owner-${
|
|
53815
|
+
return `owner-${import_node_crypto12.default.randomUUID()}`;
|
|
53672
53816
|
}
|
|
53673
53817
|
function readAuthFile(file) {
|
|
53674
53818
|
try {
|
|
@@ -53790,7 +53934,7 @@ var OwnerIdentityStore = class {
|
|
|
53790
53934
|
};
|
|
53791
53935
|
|
|
53792
53936
|
// src/feishu-auth/login-flow.ts
|
|
53793
|
-
var
|
|
53937
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
53794
53938
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
53795
53939
|
var LoginFlow = class {
|
|
53796
53940
|
constructor(deps) {
|
|
@@ -53799,7 +53943,7 @@ var LoginFlow = class {
|
|
|
53799
53943
|
deps;
|
|
53800
53944
|
pendingStates = /* @__PURE__ */ new Map();
|
|
53801
53945
|
start() {
|
|
53802
|
-
const state =
|
|
53946
|
+
const state = import_node_crypto13.default.randomBytes(16).toString("base64url");
|
|
53803
53947
|
const now = (this.deps.now ?? Date.now)();
|
|
53804
53948
|
this.pendingStates.set(state, now);
|
|
53805
53949
|
this.gcExpired(now);
|
|
@@ -55834,7 +55978,7 @@ init_src();
|
|
|
55834
55978
|
// src/extension/bundle-zip.ts
|
|
55835
55979
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
55836
55980
|
var import_node_path49 = __toESM(require("path"), 1);
|
|
55837
|
-
var
|
|
55981
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
55838
55982
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
55839
55983
|
async function bundleExtensionDir(dir) {
|
|
55840
55984
|
const entries = await listFilesSorted(dir);
|
|
@@ -55849,7 +55993,7 @@ async function bundleExtensionDir(dir) {
|
|
|
55849
55993
|
compression: "DEFLATE",
|
|
55850
55994
|
compressionOptions: { level: 6 }
|
|
55851
55995
|
});
|
|
55852
|
-
const sha256 =
|
|
55996
|
+
const sha256 = import_node_crypto14.default.createHash("sha256").update(buffer).digest("hex");
|
|
55853
55997
|
return { buffer, sha256 };
|
|
55854
55998
|
}
|
|
55855
55999
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -55919,7 +56063,7 @@ function computePublishCheck(args) {
|
|
|
55919
56063
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
55920
56064
|
var import_node_path51 = __toESM(require("path"), 1);
|
|
55921
56065
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
55922
|
-
var
|
|
56066
|
+
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
55923
56067
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
55924
56068
|
init_src();
|
|
55925
56069
|
|
|
@@ -55950,7 +56094,7 @@ var InstallError = class extends Error {
|
|
|
55950
56094
|
};
|
|
55951
56095
|
async function installFromChannel(args, deps) {
|
|
55952
56096
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
55953
|
-
const computed =
|
|
56097
|
+
const computed = import_node_crypto15.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
55954
56098
|
if (computed !== snapshotHash) {
|
|
55955
56099
|
throw new InstallError(
|
|
55956
56100
|
"HASH_MISMATCH",
|
|
@@ -56042,7 +56186,7 @@ async function installFromChannel(args, deps) {
|
|
|
56042
56186
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56043
56187
|
var import_node_path52 = __toESM(require("path"), 1);
|
|
56044
56188
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56045
|
-
var
|
|
56189
|
+
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56046
56190
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
56047
56191
|
init_src();
|
|
56048
56192
|
var UpdateError = class extends Error {
|
|
@@ -56081,7 +56225,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56081
56225
|
if (e instanceof UpdateError) throw e;
|
|
56082
56226
|
throw e;
|
|
56083
56227
|
}
|
|
56084
|
-
const computed =
|
|
56228
|
+
const computed = import_node_crypto16.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
56085
56229
|
if (computed !== snapshotHash) {
|
|
56086
56230
|
throw new UpdateError(
|
|
56087
56231
|
"HASH_MISMATCH",
|
|
@@ -56787,7 +56931,7 @@ function listPidsOnPort(port) {
|
|
|
56787
56931
|
}
|
|
56788
56932
|
|
|
56789
56933
|
// src/app-builder/publish-registry.ts
|
|
56790
|
-
var
|
|
56934
|
+
var import_node_crypto17 = require("crypto");
|
|
56791
56935
|
var PublishJobRegistry = class {
|
|
56792
56936
|
jobs = /* @__PURE__ */ new Map();
|
|
56793
56937
|
has(name) {
|
|
@@ -56804,7 +56948,7 @@ var PublishJobRegistry = class {
|
|
|
56804
56948
|
if (this.jobs.has(args.name)) {
|
|
56805
56949
|
throw new Error(`already publishing: ${args.name}`);
|
|
56806
56950
|
}
|
|
56807
|
-
const jobId = args.jobId ?? `job-${(0,
|
|
56951
|
+
const jobId = args.jobId ?? `job-${(0, import_node_crypto17.randomUUID)()}`;
|
|
56808
56952
|
this.jobs.set(args.name, {
|
|
56809
56953
|
jobId,
|
|
56810
56954
|
name: args.name,
|
|
@@ -57765,7 +57909,7 @@ async function uninstall(deps) {
|
|
|
57765
57909
|
}
|
|
57766
57910
|
|
|
57767
57911
|
// src/handlers/index.ts
|
|
57768
|
-
var
|
|
57912
|
+
var import_node_crypto18 = require("crypto");
|
|
57769
57913
|
init_peer_forward();
|
|
57770
57914
|
function buildMethodHandlers(deps) {
|
|
57771
57915
|
return {
|
|
@@ -57799,7 +57943,7 @@ function buildMethodHandlers(deps) {
|
|
|
57799
57943
|
const c = deps.contactStore.get(deviceId);
|
|
57800
57944
|
return c ? { deviceId: c.deviceId, remoteUrl: c.remoteUrl, connectToken: c.connectToken } : null;
|
|
57801
57945
|
},
|
|
57802
|
-
genId: () => (0,
|
|
57946
|
+
genId: () => (0, import_node_crypto18.randomUUID)(),
|
|
57803
57947
|
now: () => Date.now(),
|
|
57804
57948
|
forwardInboxPostToPeer,
|
|
57805
57949
|
logger: deps.logger
|
|
@@ -58715,6 +58859,13 @@ async function startDaemon(config) {
|
|
|
58715
58859
|
logClient
|
|
58716
58860
|
});
|
|
58717
58861
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
58862
|
+
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
58863
|
+
file: import_node_path62.default.join(config.dataDir, "screen-idle-probe.log"),
|
|
58864
|
+
level: "debug"
|
|
58865
|
+
});
|
|
58866
|
+
logger.info("screen-idle probe logger enabled", {
|
|
58867
|
+
file: import_node_path62.default.join(config.dataDir, "screen-idle-probe.log")
|
|
58868
|
+
});
|
|
58718
58869
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
58719
58870
|
const pre = stateMgr.preflight();
|
|
58720
58871
|
if (pre.status === "active") {
|
|
@@ -58991,6 +59142,10 @@ async function startDaemon(config) {
|
|
|
58991
59142
|
// 新布局派生 (sessions/* + personas/<pid>/.clawd/sessions/owner/*)
|
|
58992
59143
|
storeFactory: sessionStoreFactory,
|
|
58993
59144
|
logger,
|
|
59145
|
+
// 取证 probe(可选,CLAWD_SCREEN_IDLE_PROBE=1 时启用):manager turn_end 判定链
|
|
59146
|
+
// 的所有决策点打到独立文件,跟 adapter 的 observeScreenIdle probe 共用同一份 file logger,
|
|
59147
|
+
// 便于 grep sessionId 时 tui 层 + manager 层交叉时序都在同一文件里
|
|
59148
|
+
...screenIdleProbeLogger ? { screenIdleProbeLogger } : {},
|
|
58994
59149
|
getAdapter,
|
|
58995
59150
|
historyReader: history,
|
|
58996
59151
|
dataDir: config.dataDir,
|
|
@@ -59108,10 +59263,10 @@ async function startDaemon(config) {
|
|
|
59108
59263
|
onSurfaceUnregister: (tsid) => manager.unregisterSurface(tsid),
|
|
59109
59264
|
// ReadyGate v2:ReadyDetector emit ready 时投递 reducer 'ready-detected' input
|
|
59110
59265
|
onReady: (tsid) => manager.dispatchReadyDetected(tsid),
|
|
59111
|
-
//
|
|
59112
|
-
|
|
59113
|
-
//
|
|
59114
|
-
|
|
59266
|
+
// 屏幕真稳定 5s 的一次性信号 → manager 查 pending turn_duration 并 flush 成 turn_end
|
|
59267
|
+
onScreenIdle: (tsid) => manager.notifyScreenIdle(tsid),
|
|
59268
|
+
// 取证 probe(默认无条件启用;见 createFileOnlyLogger)
|
|
59269
|
+
screenIdleProbeLogger
|
|
59115
59270
|
}) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
|
|
59116
59271
|
registerAdapter("claude", claudeAdapter);
|
|
59117
59272
|
registerAdapter("codex", new CodexAdapter({ logger, historyReader: new CodexHistoryReader() }));
|