@clawos-dev/clawd 0.2.582 → 0.2.584
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/app-builder-plugin/mcp-server.cjs +1 -1
- package/dist/cli.cjs +1911 -1697
- package/dist/cloud-persona-conversations.html +312 -0
- package/dist/cloud-persona-metrics.html +20 -20
- package/dist/dispatch/mcp-server.cjs +1 -1
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/mcp-server.cjs +1 -1
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/mcp-server.cjs +1 -1
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/guest-CY5N5r-U.css +32 -0
- package/dist/share-ui/assets/{guest-vguZTQav.js → guest-DVxTImMk.js} +1 -1
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-DHhedWaX.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -7691,7 +7691,7 @@ var init_dashboard_schemas = __esm({
|
|
|
7691
7691
|
"../protocol/src/dashboard-schemas.ts"() {
|
|
7692
7692
|
"use strict";
|
|
7693
7693
|
init_zod();
|
|
7694
|
-
DASHBOARD_PAGES = ["cloud-persona-metrics"];
|
|
7694
|
+
DASHBOARD_PAGES = ["cloud-persona-metrics", "cloud-persona-conversations"];
|
|
7695
7695
|
DashboardPageSchema = external_exports.enum(DASHBOARD_PAGES);
|
|
7696
7696
|
DASHBOARD_LINK_TTL_SECONDS = 24 * 3600;
|
|
7697
7697
|
DashboardOpenUrlArgs = external_exports.object({ page: DashboardPageSchema }).strict();
|
|
@@ -15911,13 +15911,13 @@ var init_claude_history = __esm({
|
|
|
15911
15911
|
function probeViaWhich() {
|
|
15912
15912
|
try {
|
|
15913
15913
|
const out = (0, import_node_child_process2.execFileSync)("which", ["claude"], { encoding: "utf8" }).trim();
|
|
15914
|
-
if (out &&
|
|
15914
|
+
if (out && import_node_fs20.default.existsSync(out)) return out;
|
|
15915
15915
|
} catch {
|
|
15916
15916
|
}
|
|
15917
15917
|
return null;
|
|
15918
15918
|
}
|
|
15919
15919
|
async function probeClaude(env = process.env) {
|
|
15920
|
-
if (env.CLAUDE_BIN &&
|
|
15920
|
+
if (env.CLAUDE_BIN && import_node_fs20.default.existsSync(env.CLAUDE_BIN)) {
|
|
15921
15921
|
return { available: true, path: env.CLAUDE_BIN };
|
|
15922
15922
|
}
|
|
15923
15923
|
const w3 = probeViaWhich();
|
|
@@ -16383,13 +16383,13 @@ function encodeClaudeStdin(text) {
|
|
|
16383
16383
|
};
|
|
16384
16384
|
return JSON.stringify(frame) + "\n";
|
|
16385
16385
|
}
|
|
16386
|
-
var import_node_child_process, import_node_child_process2,
|
|
16386
|
+
var import_node_child_process, import_node_child_process2, import_node_fs20, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODEL_IDS, CLAUDE_MODELS, _assertModelIdsAligned, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
|
|
16387
16387
|
var init_claude = __esm({
|
|
16388
16388
|
"src/tools/claude.ts"() {
|
|
16389
16389
|
"use strict";
|
|
16390
16390
|
import_node_child_process = require("child_process");
|
|
16391
16391
|
import_node_child_process2 = require("child_process");
|
|
16392
|
-
|
|
16392
|
+
import_node_fs20 = __toESM(require("fs"), 1);
|
|
16393
16393
|
init_protocol();
|
|
16394
16394
|
init_claude_history();
|
|
16395
16395
|
init_tool_result_extra();
|
|
@@ -51423,19 +51423,37 @@ function verifyDashboardLink(secret, page, eRaw, s2, now = Date.now) {
|
|
|
51423
51423
|
}
|
|
51424
51424
|
|
|
51425
51425
|
// src/dashboard/routes.ts
|
|
51426
|
+
var DashboardDataError = class extends Error {
|
|
51427
|
+
constructor(status, code, message) {
|
|
51428
|
+
super(message);
|
|
51429
|
+
this.status = status;
|
|
51430
|
+
this.code = code;
|
|
51431
|
+
this.name = "DashboardDataError";
|
|
51432
|
+
}
|
|
51433
|
+
status;
|
|
51434
|
+
code;
|
|
51435
|
+
};
|
|
51436
|
+
var SUBROUTES = {
|
|
51437
|
+
"cloud-persona-metrics": ["personas", "data"],
|
|
51438
|
+
"cloud-persona-conversations": ["personas", "threads", "thread"]
|
|
51439
|
+
};
|
|
51440
|
+
var UPSTREAM_DOWN = { personas: "UPSTREAM_UNREACHABLE", data: "GATEWAY_UNREACHABLE", threads: "CENTER_UNREACHABLE", thread: "CENTER_UNREACHABLE" };
|
|
51426
51441
|
var NO_STORE = { "cache-control": "no-store" };
|
|
51427
51442
|
function send(res, status, contentType, body) {
|
|
51428
51443
|
res.writeHead(status, { "content-type": contentType, ...NO_STORE });
|
|
51429
51444
|
res.end(body);
|
|
51430
51445
|
}
|
|
51431
51446
|
var sendJson = (res, status, body) => send(res, status, "application/json; charset=utf-8", JSON.stringify(body));
|
|
51447
|
+
var sendPassthrough = (res, r2) => send(res, r2.status, "application/json; charset=utf-8", r2.body);
|
|
51432
51448
|
var PERSONA_RE = /^[a-z0-9-]+$/;
|
|
51449
|
+
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
51433
51450
|
function createDashboardRoutes(deps) {
|
|
51434
51451
|
const now = deps.now ?? Date.now;
|
|
51435
51452
|
return async (req, res, url) => {
|
|
51436
|
-
const m3 = /^\/dashboard\/([^/]+)(?:\/(
|
|
51453
|
+
const m3 = /^\/dashboard\/([^/]+)(?:\/([a-z]+))?$/.exec(url.pathname);
|
|
51437
51454
|
const page = m3 && DASHBOARD_PAGES.includes(m3[1]) ? m3[1] : null;
|
|
51438
|
-
|
|
51455
|
+
const sub = m3?.[2];
|
|
51456
|
+
if (!m3 || !page || req.method !== "GET" || sub !== void 0 && !SUBROUTES[page].includes(sub)) {
|
|
51439
51457
|
sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
|
|
51440
51458
|
return true;
|
|
51441
51459
|
}
|
|
@@ -51445,8 +51463,7 @@ function createDashboardRoutes(deps) {
|
|
|
51445
51463
|
return true;
|
|
51446
51464
|
}
|
|
51447
51465
|
const v3 = verifyDashboardLink(secret, page, url.searchParams.get("e"), url.searchParams.get("s"), now);
|
|
51448
|
-
|
|
51449
|
-
if (!sub) {
|
|
51466
|
+
if (sub === void 0) {
|
|
51450
51467
|
if (!v3.ok && v3.code !== "EXPIRED") {
|
|
51451
51468
|
send(res, 403, "text/plain; charset=utf-8", "\u94FE\u63A5\u65E0\u6548\uFF1A\u56DE\u5230 clawd \u7684\u300C\u8BBE\u7F6E \u2192 Dashboard\u300D\u91CD\u65B0\u6253\u5F00");
|
|
51452
51469
|
return true;
|
|
@@ -51463,296 +51480,1523 @@ function createDashboardRoutes(deps) {
|
|
|
51463
51480
|
sendJson(res, 403, v3.code === "EXPIRED" ? { code: "LINK_EXPIRED", message: "\u94FE\u63A5\u5DF2\u8FC7\u671F" } : { code: "LINK_INVALID", message: "\u94FE\u63A5\u65E0\u6548" });
|
|
51464
51481
|
return true;
|
|
51465
51482
|
}
|
|
51483
|
+
const q3 = url.searchParams;
|
|
51466
51484
|
try {
|
|
51467
51485
|
if (sub === "personas") {
|
|
51468
|
-
sendJson(res, 200, { personas: await deps.
|
|
51486
|
+
sendJson(res, 200, { personas: await deps.data.personas() });
|
|
51469
51487
|
return true;
|
|
51470
51488
|
}
|
|
51471
|
-
const personaId2 =
|
|
51489
|
+
const personaId2 = q3.get("persona") ?? "";
|
|
51472
51490
|
if (!PERSONA_RE.test(personaId2)) {
|
|
51473
51491
|
sendJson(res, 400, { code: "INVALID_PARAM", message: "persona \u8981\u662F\u4E91 persona \u540D\uFF08[a-z0-9-]\uFF09" });
|
|
51474
51492
|
return true;
|
|
51475
51493
|
}
|
|
51476
|
-
|
|
51477
|
-
|
|
51494
|
+
if (sub === "data") {
|
|
51495
|
+
sendPassthrough(res, await deps.data.metrics({ personaId: personaId2, window: q3.get("window"), entry: q3.get("entry") }));
|
|
51496
|
+
return true;
|
|
51497
|
+
}
|
|
51498
|
+
if (sub === "threads") {
|
|
51499
|
+
const limit = q3.get("limit");
|
|
51500
|
+
if (limit !== null && !/^\d{1,3}$/.test(limit)) {
|
|
51501
|
+
sendJson(res, 400, { code: "INVALID_PARAM", message: "limit \u8981\u662F\u6B63\u6574\u6570" });
|
|
51502
|
+
return true;
|
|
51503
|
+
}
|
|
51504
|
+
sendPassthrough(res, await deps.data.threads({ personaId: personaId2, before: q3.get("before"), limit: limit === null ? null : Number(limit) }));
|
|
51505
|
+
return true;
|
|
51506
|
+
}
|
|
51507
|
+
const unionId = q3.get("unionId") ?? "";
|
|
51508
|
+
const threadId = q3.get("threadId") ?? "";
|
|
51509
|
+
if (!ID_RE.test(unionId) || !ID_RE.test(threadId)) {
|
|
51510
|
+
sendJson(res, 400, { code: "INVALID_PARAM", message: "unionId / threadId \u53EA\u8BA4 [A-Za-z0-9_-]" });
|
|
51511
|
+
return true;
|
|
51512
|
+
}
|
|
51513
|
+
sendJson(res, 200, await deps.data.thread({ personaId: personaId2, unionId, threadId }));
|
|
51478
51514
|
} catch (e) {
|
|
51479
|
-
|
|
51515
|
+
if (e instanceof DashboardDataError) {
|
|
51516
|
+
sendJson(res, e.status, { code: e.code, message: e.message });
|
|
51517
|
+
return true;
|
|
51518
|
+
}
|
|
51519
|
+
sendJson(res, 502, { code: UPSTREAM_DOWN[sub], message: e.message });
|
|
51480
51520
|
}
|
|
51481
51521
|
return true;
|
|
51482
51522
|
};
|
|
51483
51523
|
}
|
|
51484
51524
|
|
|
51485
|
-
// src/
|
|
51486
|
-
|
|
51487
|
-
|
|
51488
|
-
var import_node_url4 = require("url");
|
|
51489
|
-
|
|
51490
|
-
// src/share-ui/asset-loader.ts
|
|
51491
|
-
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
51492
|
-
var import_node_path13 = __toESM(require("path"), 1);
|
|
51493
|
-
function createShareUiAssetLoader(opts) {
|
|
51494
|
-
let indexHtml;
|
|
51495
|
-
try {
|
|
51496
|
-
indexHtml = import_node_fs14.default.readFileSync(opts.indexHtmlPath, "utf8");
|
|
51497
|
-
} catch (err) {
|
|
51498
|
-
throw new Error(`share-ui index.html not found at ${opts.indexHtmlPath}: ${err.message}`);
|
|
51499
|
-
}
|
|
51500
|
-
const assetsDir = import_node_path13.default.resolve(opts.assetsDir);
|
|
51501
|
-
return {
|
|
51502
|
-
renderIndexHtml() {
|
|
51503
|
-
return indexHtml;
|
|
51504
|
-
},
|
|
51505
|
-
resolveAssetPath(rel) {
|
|
51506
|
-
const resolved = import_node_path13.default.resolve(assetsDir, rel);
|
|
51507
|
-
if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path13.default.sep)) return null;
|
|
51508
|
-
try {
|
|
51509
|
-
if (import_node_fs14.default.statSync(resolved).isFile()) return resolved;
|
|
51510
|
-
} catch {
|
|
51511
|
-
}
|
|
51512
|
-
return null;
|
|
51513
|
-
}
|
|
51514
|
-
};
|
|
51525
|
+
// src/tools/codex-rollout.ts
|
|
51526
|
+
function rec(v3) {
|
|
51527
|
+
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : void 0;
|
|
51515
51528
|
}
|
|
51516
|
-
|
|
51517
|
-
|
|
51518
|
-
|
|
51519
|
-
function
|
|
51520
|
-
|
|
51521
|
-
|
|
51529
|
+
function str(v3) {
|
|
51530
|
+
return typeof v3 === "string" ? v3 : void 0;
|
|
51531
|
+
}
|
|
51532
|
+
function num(v3) {
|
|
51533
|
+
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
51534
|
+
}
|
|
51535
|
+
function parseLine(line) {
|
|
51536
|
+
const t2 = line.trim();
|
|
51537
|
+
if (!t2) return void 0;
|
|
51522
51538
|
try {
|
|
51523
|
-
|
|
51539
|
+
return rec(JSON.parse(t2));
|
|
51524
51540
|
} catch {
|
|
51541
|
+
return void 0;
|
|
51525
51542
|
}
|
|
51526
|
-
return import_node_path14.default.dirname(real);
|
|
51527
51543
|
}
|
|
51528
|
-
function
|
|
51529
|
-
|
|
51530
|
-
|
|
51531
|
-
|
|
51532
|
-
|
|
51533
|
-
|
|
51534
|
-
|
|
51535
|
-
|
|
51544
|
+
function metaFrom(obj) {
|
|
51545
|
+
if (obj.type !== "session_meta") return null;
|
|
51546
|
+
const p3 = rec(obj.payload);
|
|
51547
|
+
const threadId = str(p3?.id) ?? str(p3?.session_id);
|
|
51548
|
+
if (!p3 || !threadId) return null;
|
|
51549
|
+
const meta = { threadId };
|
|
51550
|
+
const cwd = str(p3.cwd);
|
|
51551
|
+
if (cwd) meta.cwd = cwd;
|
|
51552
|
+
const createdAt = str(p3.timestamp) ?? str(obj.timestamp);
|
|
51553
|
+
if (createdAt) meta.createdAt = createdAt;
|
|
51554
|
+
const parent = str(p3.parent_thread_id);
|
|
51555
|
+
if (parent) meta.parentThreadId = parent;
|
|
51556
|
+
const cli = str(p3.cli_version);
|
|
51557
|
+
if (cli) meta.cliVersion = cli;
|
|
51558
|
+
return meta;
|
|
51559
|
+
}
|
|
51560
|
+
function parseCodexRolloutMeta(firstLine) {
|
|
51561
|
+
const obj = parseLine(firstLine);
|
|
51562
|
+
return obj ? metaFrom(obj) : null;
|
|
51563
|
+
}
|
|
51564
|
+
function firstUserMessageText(text) {
|
|
51565
|
+
for (const line of text.split("\n")) {
|
|
51566
|
+
const obj = parseLine(line);
|
|
51567
|
+
if (!obj || obj.type !== "event_msg") continue;
|
|
51568
|
+
const p3 = rec(obj.payload);
|
|
51569
|
+
if (p3?.type === "user_message") return str(p3.message);
|
|
51536
51570
|
}
|
|
51537
|
-
|
|
51538
|
-
|
|
51539
|
-
|
|
51540
|
-
|
|
51541
|
-
|
|
51542
|
-
|
|
51571
|
+
return void 0;
|
|
51572
|
+
}
|
|
51573
|
+
function outputText(output) {
|
|
51574
|
+
if (typeof output === "string") return output;
|
|
51575
|
+
if (Array.isArray(output)) {
|
|
51576
|
+
return output.map((part) => typeof part === "string" ? part : str(rec(part)?.text) ?? "").join("");
|
|
51577
|
+
}
|
|
51578
|
+
return "";
|
|
51579
|
+
}
|
|
51580
|
+
function functionCallInput(name, rawArgs) {
|
|
51581
|
+
let args = rawArgs;
|
|
51582
|
+
if (typeof rawArgs === "string") {
|
|
51543
51583
|
try {
|
|
51544
|
-
|
|
51545
|
-
logger?.info("share-ui.assets-root.resolved", { root: shareUiDir });
|
|
51546
|
-
return createShareUiAssetLoader({ indexHtmlPath, assetsDir });
|
|
51547
|
-
}
|
|
51584
|
+
args = JSON.parse(rawArgs);
|
|
51548
51585
|
} catch {
|
|
51586
|
+
args = { arguments: rawArgs };
|
|
51549
51587
|
}
|
|
51550
51588
|
}
|
|
51551
|
-
|
|
51552
|
-
|
|
51553
|
-
|
|
51554
|
-
|
|
51555
|
-
|
|
51589
|
+
const a2 = rec(args) ?? {};
|
|
51590
|
+
if (name === "exec_command" || name === "shell" || name === "container.exec") {
|
|
51591
|
+
const cmd = a2.cmd ?? a2.command;
|
|
51592
|
+
const command = Array.isArray(cmd) ? cmd.map(String).join(" ") : str(cmd) ?? "";
|
|
51593
|
+
return { tool: "commandExecution", input: { command } };
|
|
51594
|
+
}
|
|
51595
|
+
return { tool: name, input: args };
|
|
51596
|
+
}
|
|
51597
|
+
function parseCodexRollout(text) {
|
|
51598
|
+
const out = { meta: null, messages: [], subagents: [], turns: [] };
|
|
51599
|
+
const collabCallIds = /* @__PURE__ */ new Set();
|
|
51600
|
+
for (const line of text.split("\n")) {
|
|
51601
|
+
const obj = parseLine(line);
|
|
51602
|
+
if (!obj) continue;
|
|
51603
|
+
const ts = str(obj.timestamp);
|
|
51604
|
+
const p3 = rec(obj.payload);
|
|
51605
|
+
if (!p3) continue;
|
|
51606
|
+
if (obj.type === "session_meta") {
|
|
51607
|
+
if (!out.meta) out.meta = metaFrom(obj);
|
|
51608
|
+
continue;
|
|
51609
|
+
}
|
|
51610
|
+
if (obj.type === "event_msg") {
|
|
51611
|
+
switch (p3.type) {
|
|
51612
|
+
case "user_message": {
|
|
51613
|
+
const t2 = str(p3.message);
|
|
51614
|
+
if (t2) out.messages.push({ kind: "user-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
51615
|
+
break;
|
|
51616
|
+
}
|
|
51617
|
+
case "agent_message": {
|
|
51618
|
+
const t2 = str(p3.message);
|
|
51619
|
+
if (t2) out.messages.push({ kind: "assistant-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
51620
|
+
break;
|
|
51621
|
+
}
|
|
51622
|
+
case "sub_agent_activity": {
|
|
51623
|
+
if (p3.kind !== "started") break;
|
|
51624
|
+
const toolUseId = str(p3.event_id);
|
|
51625
|
+
const agentId = str(p3.agent_thread_id);
|
|
51626
|
+
const agentPath = str(p3.agent_path);
|
|
51627
|
+
if (!toolUseId || !agentId || !agentPath) break;
|
|
51628
|
+
const segments = agentPath.split("/").filter(Boolean);
|
|
51629
|
+
const description = segments[segments.length - 1] ?? agentId;
|
|
51630
|
+
out.subagents.push({ toolUseId, agentId, agentPath, description });
|
|
51631
|
+
out.messages.push({
|
|
51632
|
+
kind: "tool_call",
|
|
51633
|
+
tool: "Agent",
|
|
51634
|
+
toolUseId,
|
|
51635
|
+
input: { agentId, description, agentPath },
|
|
51636
|
+
raw: obj,
|
|
51637
|
+
...ts ? { ts } : {}
|
|
51638
|
+
});
|
|
51639
|
+
break;
|
|
51640
|
+
}
|
|
51641
|
+
case "task_complete":
|
|
51642
|
+
case "turn_aborted": {
|
|
51643
|
+
const durationMs = num(p3.duration_ms);
|
|
51644
|
+
const turnId = str(p3.turn_id);
|
|
51645
|
+
const reason = str(p3.reason);
|
|
51646
|
+
out.turns.push({
|
|
51647
|
+
status: p3.type === "task_complete" ? "completed" : "interrupted",
|
|
51648
|
+
...turnId ? { turnId } : {},
|
|
51649
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
51650
|
+
...reason ? { reason } : {}
|
|
51651
|
+
});
|
|
51652
|
+
out.messages.push({
|
|
51653
|
+
kind: "turn-end",
|
|
51654
|
+
raw: obj,
|
|
51655
|
+
...ts ? { ts } : {},
|
|
51656
|
+
...durationMs !== void 0 ? { durationMs } : {}
|
|
51657
|
+
});
|
|
51658
|
+
break;
|
|
51659
|
+
}
|
|
51660
|
+
default:
|
|
51661
|
+
break;
|
|
51662
|
+
}
|
|
51663
|
+
continue;
|
|
51664
|
+
}
|
|
51665
|
+
if (obj.type === "response_item") {
|
|
51666
|
+
switch (p3.type) {
|
|
51667
|
+
case "function_call": {
|
|
51668
|
+
const callId = str(p3.call_id);
|
|
51669
|
+
const name = str(p3.name);
|
|
51670
|
+
if (!callId || !name) break;
|
|
51671
|
+
if (p3.namespace === "collaboration") {
|
|
51672
|
+
collabCallIds.add(callId);
|
|
51673
|
+
break;
|
|
51674
|
+
}
|
|
51675
|
+
const { tool, input } = functionCallInput(name, p3.arguments);
|
|
51676
|
+
out.messages.push({ kind: "tool_call", tool, toolUseId: callId, input, raw: obj, ...ts ? { ts } : {} });
|
|
51677
|
+
break;
|
|
51678
|
+
}
|
|
51679
|
+
case "custom_tool_call": {
|
|
51680
|
+
const callId = str(p3.call_id);
|
|
51681
|
+
const name = str(p3.name);
|
|
51682
|
+
if (!callId || !name) break;
|
|
51683
|
+
out.messages.push({
|
|
51684
|
+
kind: "tool_call",
|
|
51685
|
+
tool: name,
|
|
51686
|
+
toolUseId: callId,
|
|
51687
|
+
input: { input: str(p3.input) ?? "" },
|
|
51688
|
+
raw: obj,
|
|
51689
|
+
...ts ? { ts } : {}
|
|
51690
|
+
});
|
|
51691
|
+
break;
|
|
51692
|
+
}
|
|
51693
|
+
case "function_call_output":
|
|
51694
|
+
case "custom_tool_call_output": {
|
|
51695
|
+
const callId = str(p3.call_id);
|
|
51696
|
+
if (!callId || collabCallIds.has(callId)) break;
|
|
51697
|
+
out.messages.push({
|
|
51698
|
+
kind: "tool_result",
|
|
51699
|
+
toolUseId: callId,
|
|
51700
|
+
output: outputText(p3.output),
|
|
51701
|
+
raw: obj,
|
|
51702
|
+
...ts ? { ts } : {}
|
|
51703
|
+
});
|
|
51704
|
+
break;
|
|
51705
|
+
}
|
|
51706
|
+
default:
|
|
51707
|
+
break;
|
|
51708
|
+
}
|
|
51709
|
+
continue;
|
|
51710
|
+
}
|
|
51711
|
+
}
|
|
51712
|
+
return out;
|
|
51713
|
+
}
|
|
51714
|
+
function subagentTerminalResult(started, child) {
|
|
51715
|
+
const first = child.turns[0];
|
|
51716
|
+
if (!first) return void 0;
|
|
51717
|
+
if (first.status === "completed") return { kind: "tool_result", toolUseId: started.toolUseId };
|
|
51718
|
+
return { kind: "tool_result", toolUseId: started.toolUseId, error: first.reason ?? "Codex subagent interrupted" };
|
|
51556
51719
|
}
|
|
51557
51720
|
|
|
51558
|
-
// src/
|
|
51559
|
-
var
|
|
51560
|
-
|
|
51561
|
-
// ../protocol/src/index.ts
|
|
51562
|
-
init_runtime();
|
|
51721
|
+
// src/tools/codex-history.ts
|
|
51722
|
+
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
51723
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
51563
51724
|
|
|
51564
|
-
// src/
|
|
51565
|
-
var
|
|
51566
|
-
|
|
51567
|
-
|
|
51725
|
+
// src/tools/transcript-search.ts
|
|
51726
|
+
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
51727
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
51728
|
+
var SNIPPET_PAD = 60;
|
|
51729
|
+
var INJECTED_ENVELOPE_RE = /^<(system-reminder|command-name|command-message|command-args|local-command-stdout|local-command-stderr|task-notification|user-prompt-submit-hook|environment_context|user_instructions)\b/;
|
|
51730
|
+
var CODEX_WRAPPER_RE = /<(related-dir|environment_context|user_instructions)>[\s\S]*?<\/\1>/g;
|
|
51731
|
+
function asRecord(v3) {
|
|
51732
|
+
return typeof v3 === "object" && v3 !== null ? v3 : null;
|
|
51568
51733
|
}
|
|
51569
|
-
function
|
|
51570
|
-
|
|
51571
|
-
|
|
51734
|
+
function textOfBlocks(content, types) {
|
|
51735
|
+
if (!Array.isArray(content)) return null;
|
|
51736
|
+
const parts = [];
|
|
51737
|
+
for (const block of content) {
|
|
51738
|
+
const b3 = asRecord(block);
|
|
51739
|
+
if (!b3 || typeof b3.type !== "string" || !types.includes(b3.type)) continue;
|
|
51740
|
+
const text = b3.text;
|
|
51741
|
+
if (typeof text === "string" && text !== "") parts.push(text);
|
|
51742
|
+
}
|
|
51743
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
51572
51744
|
}
|
|
51573
|
-
function
|
|
51574
|
-
const
|
|
51575
|
-
if (
|
|
51576
|
-
|
|
51577
|
-
|
|
51578
|
-
|
|
51579
|
-
|
|
51580
|
-
|
|
51581
|
-
|
|
51745
|
+
function lineToSearchableText(raw) {
|
|
51746
|
+
const line = asRecord(raw);
|
|
51747
|
+
if (!line) return null;
|
|
51748
|
+
if (line.type === "response_item") {
|
|
51749
|
+
const payload = asRecord(line.payload);
|
|
51750
|
+
if (!payload || payload.type !== "message") return null;
|
|
51751
|
+
const role = payload.role;
|
|
51752
|
+
if (role !== "user" && role !== "assistant") return null;
|
|
51753
|
+
const text2 = textOfBlocks(payload.content, ["input_text", "output_text"]);
|
|
51754
|
+
if (text2 === null) return null;
|
|
51755
|
+
if (role !== "user") return text2;
|
|
51756
|
+
const stripped = text2.replace(CODEX_WRAPPER_RE, "").trim();
|
|
51757
|
+
if (stripped === "") return null;
|
|
51758
|
+
return INJECTED_ENVELOPE_RE.test(stripped) ? null : stripped;
|
|
51582
51759
|
}
|
|
51583
|
-
|
|
51760
|
+
if (line.type !== "user" && line.type !== "assistant") return null;
|
|
51761
|
+
const message = asRecord(line.message);
|
|
51762
|
+
if (!message) return null;
|
|
51763
|
+
const content = message.content;
|
|
51764
|
+
const text = typeof content === "string" && content !== "" ? content : textOfBlocks(content, ["text"]);
|
|
51765
|
+
if (text === null) return null;
|
|
51766
|
+
if (line.type === "user" && (line.isMeta === true || line.isSynthetic === true)) return null;
|
|
51767
|
+
if (line.type === "user" && INJECTED_ENVELOPE_RE.test(text.trimStart())) return null;
|
|
51768
|
+
return text;
|
|
51769
|
+
}
|
|
51770
|
+
function escapeRegExp(s2) {
|
|
51771
|
+
return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51772
|
+
}
|
|
51773
|
+
function makeSnippet(text, at3, needleLength) {
|
|
51774
|
+
const from = Math.max(0, at3 - SNIPPET_PAD);
|
|
51775
|
+
const to = Math.min(text.length, at3 + needleLength + SNIPPET_PAD);
|
|
51776
|
+
const body = text.slice(from, to).replace(/\s+/g, " ").trim();
|
|
51777
|
+
return `${from > 0 ? "\u2026 " : ""}${body}${to < text.length ? " \u2026" : ""}`;
|
|
51778
|
+
}
|
|
51779
|
+
function searchTranscriptFile(file, query) {
|
|
51780
|
+
let buf;
|
|
51584
51781
|
try {
|
|
51585
|
-
|
|
51586
|
-
JSON.parse(Buffer.from(body, "base64url").toString("utf8"))
|
|
51587
|
-
);
|
|
51588
|
-
if (!parsed.success) return { ok: false, reason: "BAD_FORMAT" };
|
|
51589
|
-
payload = parsed.data;
|
|
51782
|
+
buf = import_node_fs14.default.readFileSync(file);
|
|
51590
51783
|
} catch {
|
|
51591
|
-
return
|
|
51784
|
+
return null;
|
|
51592
51785
|
}
|
|
51593
|
-
|
|
51594
|
-
|
|
51595
|
-
|
|
51596
|
-
|
|
51597
|
-
|
|
51598
|
-
|
|
51599
|
-
|
|
51600
|
-
|
|
51786
|
+
const haystack = buf.toString("latin1");
|
|
51787
|
+
const needle = Buffer.from(query, "utf8").toString("latin1");
|
|
51788
|
+
const re2 = new RegExp(escapeRegExp(needle), "gi");
|
|
51789
|
+
let m3;
|
|
51790
|
+
while ((m3 = re2.exec(haystack)) !== null) {
|
|
51791
|
+
const lineStart = haystack.lastIndexOf("\n", m3.index) + 1;
|
|
51792
|
+
let lineEnd = haystack.indexOf("\n", m3.index);
|
|
51793
|
+
if (lineEnd < 0) lineEnd = haystack.length;
|
|
51794
|
+
re2.lastIndex = lineEnd + 1;
|
|
51795
|
+
let parsed;
|
|
51601
51796
|
try {
|
|
51602
|
-
|
|
51797
|
+
parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
|
|
51603
51798
|
} catch {
|
|
51604
|
-
|
|
51799
|
+
continue;
|
|
51605
51800
|
}
|
|
51606
|
-
|
|
51607
|
-
|
|
51608
|
-
|
|
51609
|
-
|
|
51610
|
-
|
|
51611
|
-
|
|
51612
|
-
|
|
51613
|
-
const iat = Math.floor(deps.now() / 1e3);
|
|
51614
|
-
const visitorToken = signVisitorToken(deps.getSecret(), {
|
|
51615
|
-
v: 1,
|
|
51616
|
-
visitorId: id.ownerId,
|
|
51617
|
-
displayName: id.displayName,
|
|
51618
|
-
provider: id.provider,
|
|
51619
|
-
iat,
|
|
51620
|
-
exp: iat + deps.ttlSeconds
|
|
51621
|
-
});
|
|
51622
|
-
return { ok: true, visitorToken };
|
|
51623
|
-
};
|
|
51801
|
+
const text = lineToSearchableText(parsed);
|
|
51802
|
+
if (text === null) continue;
|
|
51803
|
+
const at3 = text.toLowerCase().indexOf(query.toLowerCase());
|
|
51804
|
+
if (at3 < 0) continue;
|
|
51805
|
+
return makeSnippet(text, at3, query.length);
|
|
51806
|
+
}
|
|
51807
|
+
return null;
|
|
51624
51808
|
}
|
|
51625
|
-
|
|
51626
|
-
|
|
51627
|
-
|
|
51628
|
-
|
|
51629
|
-
|
|
51630
|
-
const file = import_node_path15.default.join(opts.dir, "visitors.json");
|
|
51631
|
-
const read = () => {
|
|
51809
|
+
var ROLLOUT_NAME_RE = /-([0-9a-fA-F-]{36})\.jsonl$/;
|
|
51810
|
+
function createCodexRolloutLocator(roots) {
|
|
51811
|
+
let index = null;
|
|
51812
|
+
const walk2 = (dir, into) => {
|
|
51813
|
+
let entries;
|
|
51632
51814
|
try {
|
|
51633
|
-
|
|
51815
|
+
entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
|
|
51634
51816
|
} catch {
|
|
51635
|
-
return
|
|
51817
|
+
return;
|
|
51636
51818
|
}
|
|
51637
|
-
|
|
51638
|
-
|
|
51639
|
-
|
|
51640
|
-
|
|
51641
|
-
|
|
51642
|
-
return {
|
|
51643
|
-
upsert(r2) {
|
|
51644
|
-
const rows = read();
|
|
51645
|
-
const ex = rows.find((x2) => x2.visitorId === r2.visitorId);
|
|
51646
|
-
if (ex) {
|
|
51647
|
-
ex.displayName = r2.displayName;
|
|
51648
|
-
ex.provider = r2.provider;
|
|
51649
|
-
ex.lastSeen = r2.now;
|
|
51650
|
-
if (r2.avatarUrl) ex.avatarUrl = r2.avatarUrl;
|
|
51651
|
-
} else {
|
|
51652
|
-
rows.push({
|
|
51653
|
-
visitorId: r2.visitorId,
|
|
51654
|
-
displayName: r2.displayName,
|
|
51655
|
-
provider: r2.provider,
|
|
51656
|
-
...r2.avatarUrl ? { avatarUrl: r2.avatarUrl } : {},
|
|
51657
|
-
firstSeen: r2.now,
|
|
51658
|
-
lastSeen: r2.now
|
|
51659
|
-
});
|
|
51819
|
+
for (const entry of entries) {
|
|
51820
|
+
const full = import_node_path13.default.join(dir, entry.name);
|
|
51821
|
+
if (entry.isDirectory()) {
|
|
51822
|
+
walk2(full, into);
|
|
51823
|
+
continue;
|
|
51660
51824
|
}
|
|
51661
|
-
|
|
51662
|
-
|
|
51663
|
-
list() {
|
|
51664
|
-
return read();
|
|
51825
|
+
const matched = ROLLOUT_NAME_RE.exec(entry.name);
|
|
51826
|
+
if (matched) into.set(matched[1].toLowerCase(), full);
|
|
51665
51827
|
}
|
|
51666
51828
|
};
|
|
51667
|
-
|
|
51668
|
-
|
|
51669
|
-
|
|
51670
|
-
|
|
51671
|
-
|
|
51672
|
-
// src/tools/codex.ts
|
|
51673
|
-
var import_node_child_process7 = require("child_process");
|
|
51674
|
-
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
51675
|
-
|
|
51676
|
-
// src/tools/codex-session.ts
|
|
51677
|
-
var import_node_child_process4 = require("child_process");
|
|
51678
|
-
var import_node_path19 = __toESM(require("path"), 1);
|
|
51679
|
-
|
|
51680
|
-
// src/tools/codex-app-server-client.ts
|
|
51681
|
-
var import_node_readline = require("readline");
|
|
51682
|
-
function stdioLineStream(proc) {
|
|
51683
|
-
const rl = (0, import_node_readline.createInterface)({ input: proc.stdout, crlfDelay: Infinity });
|
|
51684
|
-
const lineCbs = [];
|
|
51685
|
-
const closeCbs = [];
|
|
51686
|
-
rl.on("line", (l2) => {
|
|
51687
|
-
for (const cb of lineCbs) cb(l2);
|
|
51688
|
-
});
|
|
51689
|
-
rl.on("close", () => {
|
|
51690
|
-
for (const cb of closeCbs) cb();
|
|
51691
|
-
});
|
|
51692
|
-
return {
|
|
51693
|
-
write: (line) => {
|
|
51694
|
-
proc.stdin.write(line + "\n");
|
|
51695
|
-
},
|
|
51696
|
-
onLine: (cb) => {
|
|
51697
|
-
lineCbs.push(cb);
|
|
51698
|
-
},
|
|
51699
|
-
onClose: (cb) => {
|
|
51700
|
-
closeCbs.push(cb);
|
|
51701
|
-
},
|
|
51702
|
-
close: () => {
|
|
51703
|
-
proc.stdin?.end();
|
|
51829
|
+
return (toolSessionId) => {
|
|
51830
|
+
if (index === null) {
|
|
51831
|
+
index = /* @__PURE__ */ new Map();
|
|
51832
|
+
for (const root of roots) walk2(root, index);
|
|
51704
51833
|
}
|
|
51834
|
+
return index.get(toolSessionId.toLowerCase()) ?? null;
|
|
51705
51835
|
};
|
|
51706
51836
|
}
|
|
51707
|
-
|
|
51708
|
-
|
|
51709
|
-
|
|
51710
|
-
|
|
51711
|
-
|
|
51712
|
-
|
|
51713
|
-
|
|
51714
|
-
|
|
51715
|
-
|
|
51716
|
-
|
|
51837
|
+
|
|
51838
|
+
// src/tools/codex-history.ts
|
|
51839
|
+
var CODEX_LIST_SESSIONS_LIMIT = 100;
|
|
51840
|
+
var CODEX_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
51841
|
+
var PEEK_BYTES = 8 * 1024;
|
|
51842
|
+
var TITLE_BYTES = 256 * 1024;
|
|
51843
|
+
function readHead(file, bytes) {
|
|
51844
|
+
let fd;
|
|
51845
|
+
try {
|
|
51846
|
+
fd = import_node_fs15.default.openSync(file, "r");
|
|
51847
|
+
} catch {
|
|
51848
|
+
return null;
|
|
51717
51849
|
}
|
|
51718
|
-
|
|
51719
|
-
|
|
51720
|
-
|
|
51721
|
-
|
|
51722
|
-
|
|
51723
|
-
|
|
51724
|
-
|
|
51725
|
-
|
|
51726
|
-
this.write({ jsonrpc: "2.0", method, id, params });
|
|
51727
|
-
});
|
|
51850
|
+
try {
|
|
51851
|
+
const buf = Buffer.alloc(bytes);
|
|
51852
|
+
const n2 = import_node_fs15.default.readSync(fd, buf, 0, bytes, 0);
|
|
51853
|
+
return buf.subarray(0, n2).toString("utf8");
|
|
51854
|
+
} catch {
|
|
51855
|
+
return null;
|
|
51856
|
+
} finally {
|
|
51857
|
+
import_node_fs15.default.closeSync(fd);
|
|
51728
51858
|
}
|
|
51729
|
-
|
|
51730
|
-
|
|
51859
|
+
}
|
|
51860
|
+
function peekSessionCwd(head) {
|
|
51861
|
+
if (!head.includes('"session_meta"')) return null;
|
|
51862
|
+
const m3 = /"cwd":"((?:[^"\\]|\\.)*)"/.exec(head);
|
|
51863
|
+
if (!m3) return null;
|
|
51864
|
+
try {
|
|
51865
|
+
return JSON.parse(`"${m3[1]}"`);
|
|
51866
|
+
} catch {
|
|
51867
|
+
return null;
|
|
51731
51868
|
}
|
|
51732
|
-
|
|
51733
|
-
|
|
51734
|
-
|
|
51869
|
+
}
|
|
51870
|
+
function readFirstLine(file, max = 512 * 1024) {
|
|
51871
|
+
let fd;
|
|
51872
|
+
try {
|
|
51873
|
+
fd = import_node_fs15.default.openSync(file, "r");
|
|
51874
|
+
} catch {
|
|
51875
|
+
return null;
|
|
51735
51876
|
}
|
|
51736
|
-
|
|
51737
|
-
const
|
|
51738
|
-
|
|
51739
|
-
let
|
|
51740
|
-
|
|
51741
|
-
|
|
51742
|
-
|
|
51743
|
-
|
|
51744
|
-
|
|
51745
|
-
|
|
51746
|
-
|
|
51747
|
-
|
|
51748
|
-
|
|
51749
|
-
|
|
51750
|
-
|
|
51751
|
-
|
|
51752
|
-
|
|
51753
|
-
|
|
51754
|
-
|
|
51755
|
-
|
|
51877
|
+
try {
|
|
51878
|
+
const chunk = Buffer.alloc(64 * 1024);
|
|
51879
|
+
const parts = [];
|
|
51880
|
+
let total = 0;
|
|
51881
|
+
while (total < max) {
|
|
51882
|
+
const n2 = import_node_fs15.default.readSync(fd, chunk, 0, chunk.length, total);
|
|
51883
|
+
if (n2 <= 0) break;
|
|
51884
|
+
const nl = chunk.subarray(0, n2).indexOf(10);
|
|
51885
|
+
if (nl >= 0) {
|
|
51886
|
+
parts.push(Buffer.from(chunk.subarray(0, nl)));
|
|
51887
|
+
return Buffer.concat(parts).toString("utf8");
|
|
51888
|
+
}
|
|
51889
|
+
parts.push(Buffer.from(chunk.subarray(0, n2)));
|
|
51890
|
+
total += n2;
|
|
51891
|
+
}
|
|
51892
|
+
return total < max && parts.length > 0 ? Buffer.concat(parts).toString("utf8") : null;
|
|
51893
|
+
} catch {
|
|
51894
|
+
return null;
|
|
51895
|
+
} finally {
|
|
51896
|
+
import_node_fs15.default.closeSync(fd);
|
|
51897
|
+
}
|
|
51898
|
+
}
|
|
51899
|
+
function readThreadNames(sharedHome) {
|
|
51900
|
+
const names = /* @__PURE__ */ new Map();
|
|
51901
|
+
if (!sharedHome) return names;
|
|
51902
|
+
let raw;
|
|
51903
|
+
try {
|
|
51904
|
+
raw = import_node_fs15.default.readFileSync(import_node_path14.default.join(sharedHome, "session_index.jsonl"), "utf8");
|
|
51905
|
+
} catch {
|
|
51906
|
+
return names;
|
|
51907
|
+
}
|
|
51908
|
+
for (const line of raw.split("\n")) {
|
|
51909
|
+
if (!line.trim()) continue;
|
|
51910
|
+
try {
|
|
51911
|
+
const o2 = JSON.parse(line);
|
|
51912
|
+
if (typeof o2.id === "string" && typeof o2.thread_name === "string" && o2.thread_name) {
|
|
51913
|
+
names.set(o2.id.toLowerCase(), o2.thread_name);
|
|
51914
|
+
}
|
|
51915
|
+
} catch {
|
|
51916
|
+
}
|
|
51917
|
+
}
|
|
51918
|
+
return names;
|
|
51919
|
+
}
|
|
51920
|
+
function walkRollouts(dir, visit) {
|
|
51921
|
+
let entries;
|
|
51922
|
+
try {
|
|
51923
|
+
entries = import_node_fs15.default.readdirSync(dir, { withFileTypes: true });
|
|
51924
|
+
} catch {
|
|
51925
|
+
return true;
|
|
51926
|
+
}
|
|
51927
|
+
entries.sort((a2, b3) => a2.name < b3.name ? 1 : a2.name > b3.name ? -1 : 0);
|
|
51928
|
+
for (const entry of entries) {
|
|
51929
|
+
const full = import_node_path14.default.join(dir, entry.name);
|
|
51930
|
+
if (entry.isDirectory()) {
|
|
51931
|
+
if (!walkRollouts(full, visit)) return false;
|
|
51932
|
+
} else if (ROLLOUT_NAME_RE.test(entry.name)) {
|
|
51933
|
+
if (visit(full) === false) return false;
|
|
51934
|
+
}
|
|
51935
|
+
}
|
|
51936
|
+
return true;
|
|
51937
|
+
}
|
|
51938
|
+
async function withSubagentResults(parent, parentThreadId, loadChild, logger) {
|
|
51939
|
+
const messages = [];
|
|
51940
|
+
const started = new Map(parent.subagents.map((s2) => [s2.toolUseId, s2]));
|
|
51941
|
+
for (const m3 of parent.messages) {
|
|
51942
|
+
messages.push(m3);
|
|
51943
|
+
const sub = m3.kind === "tool_call" && m3.toolUseId ? started.get(m3.toolUseId) : void 0;
|
|
51944
|
+
if (!sub) continue;
|
|
51945
|
+
let text;
|
|
51946
|
+
try {
|
|
51947
|
+
text = await loadChild(sub.agentId);
|
|
51948
|
+
} catch (error) {
|
|
51949
|
+
logger?.warn("codex subagent rollout unreadable", { childThreadId: sub.agentId, message: error.message });
|
|
51950
|
+
continue;
|
|
51951
|
+
}
|
|
51952
|
+
if (text === null) {
|
|
51953
|
+
logger?.warn("codex subagent rollout not found", { parentThreadId, childThreadId: sub.agentId });
|
|
51954
|
+
continue;
|
|
51955
|
+
}
|
|
51956
|
+
const child = parseCodexRollout(text);
|
|
51957
|
+
if (child.meta?.parentThreadId !== parentThreadId) {
|
|
51958
|
+
logger?.warn("codex subagent rollout does not belong to parent", {
|
|
51959
|
+
parentThreadId,
|
|
51960
|
+
childThreadId: sub.agentId,
|
|
51961
|
+
actualParent: child.meta?.parentThreadId ?? null
|
|
51962
|
+
});
|
|
51963
|
+
continue;
|
|
51964
|
+
}
|
|
51965
|
+
const result = subagentTerminalResult(sub, child);
|
|
51966
|
+
if (result) messages.push(result);
|
|
51967
|
+
}
|
|
51968
|
+
return messages;
|
|
51969
|
+
}
|
|
51970
|
+
function pageMessages(messages, args) {
|
|
51971
|
+
const total = messages.length;
|
|
51972
|
+
const limit = args.limit ?? total;
|
|
51973
|
+
const offset = typeof args.offset === "number" ? Math.min(Math.max(0, args.offset), total) : Math.max(0, total - limit);
|
|
51974
|
+
return { messages: messages.slice(offset, offset + limit), total, offset };
|
|
51975
|
+
}
|
|
51976
|
+
function subagentSummaries(parent) {
|
|
51977
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51978
|
+
const out = [];
|
|
51979
|
+
for (const s2 of parent.subagents) {
|
|
51980
|
+
if (seen.has(s2.agentId)) continue;
|
|
51981
|
+
seen.add(s2.agentId);
|
|
51982
|
+
out.push({ subagentId: s2.agentId, summary: s2.description, messageCount: 0 });
|
|
51983
|
+
}
|
|
51984
|
+
return out;
|
|
51985
|
+
}
|
|
51986
|
+
var CodexHistoryReader = class {
|
|
51987
|
+
deps;
|
|
51988
|
+
constructor(deps) {
|
|
51989
|
+
this.deps = deps;
|
|
51990
|
+
}
|
|
51991
|
+
// 仍返回 []:projects/list 非 session-scoped、不知 tool,跨-tool 项目浏览器合并排后续。
|
|
51992
|
+
// codex 旧会话仍能经 clawd 活跃列表(session:list, tool-agnostic) + read 打开。
|
|
51993
|
+
async listProjects() {
|
|
51994
|
+
return [];
|
|
51995
|
+
}
|
|
51996
|
+
async listSessions(args) {
|
|
51997
|
+
const want = import_node_path14.default.resolve(args.projectPath);
|
|
51998
|
+
const names = readThreadNames(this.deps.sharedHome);
|
|
51999
|
+
const found = [];
|
|
52000
|
+
let scanned = 0;
|
|
52001
|
+
for (const root of this.deps.roots) {
|
|
52002
|
+
const goOn = walkRollouts(root, (file) => {
|
|
52003
|
+
if (++scanned > CODEX_LIST_SESSIONS_SCAN_CAP) return false;
|
|
52004
|
+
const head = readHead(file, PEEK_BYTES);
|
|
52005
|
+
const cwd = head === null ? null : peekSessionCwd(head);
|
|
52006
|
+
if (!cwd || import_node_path14.default.resolve(cwd) !== want) return;
|
|
52007
|
+
const first = readFirstLine(file);
|
|
52008
|
+
const meta = first === null ? null : parseCodexRolloutMeta(first);
|
|
52009
|
+
if (!meta) return;
|
|
52010
|
+
let mtimeMs;
|
|
52011
|
+
try {
|
|
52012
|
+
mtimeMs = import_node_fs15.default.statSync(file).mtimeMs;
|
|
52013
|
+
} catch {
|
|
52014
|
+
return;
|
|
52015
|
+
}
|
|
52016
|
+
found.push({ file, threadId: meta.threadId, mtimeMs });
|
|
52017
|
+
});
|
|
52018
|
+
if (!goOn) break;
|
|
52019
|
+
}
|
|
52020
|
+
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
52021
|
+
return found.slice(0, CODEX_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
52022
|
+
let summary = names.get(f2.threadId.toLowerCase());
|
|
52023
|
+
if (!summary) {
|
|
52024
|
+
const head = readHead(f2.file, TITLE_BYTES);
|
|
52025
|
+
summary = head === null ? void 0 : firstUserMessageText(head);
|
|
52026
|
+
}
|
|
52027
|
+
return {
|
|
52028
|
+
toolSessionId: f2.threadId,
|
|
52029
|
+
summary: summary || "(\u7A7A\u4F1A\u8BDD)",
|
|
52030
|
+
messageCount: 0,
|
|
52031
|
+
// 与此前 thread/list 一致:列表不数条数
|
|
52032
|
+
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
52033
|
+
};
|
|
52034
|
+
});
|
|
52035
|
+
}
|
|
52036
|
+
async read(args) {
|
|
52037
|
+
const locate = createCodexRolloutLocator(this.deps.roots);
|
|
52038
|
+
const parent = this.parseThread(locate, args.toolSessionId);
|
|
52039
|
+
const messages = await withSubagentResults(
|
|
52040
|
+
parent,
|
|
52041
|
+
args.toolSessionId,
|
|
52042
|
+
async (agentId) => {
|
|
52043
|
+
const file = locate(agentId);
|
|
52044
|
+
return file ? import_node_fs15.default.readFileSync(file, "utf8") : null;
|
|
52045
|
+
},
|
|
52046
|
+
this.deps.logger
|
|
52047
|
+
);
|
|
52048
|
+
return pageMessages(messages, args);
|
|
52049
|
+
}
|
|
52050
|
+
async listSubagents(args) {
|
|
52051
|
+
return subagentSummaries(this.parseThread(createCodexRolloutLocator(this.deps.roots), args.toolSessionId));
|
|
52052
|
+
}
|
|
52053
|
+
async readSubagent(args) {
|
|
52054
|
+
const child = this.parseThread(createCodexRolloutLocator(this.deps.roots), args.subagentId);
|
|
52055
|
+
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
52056
|
+
throw new Error(
|
|
52057
|
+
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
52058
|
+
);
|
|
52059
|
+
}
|
|
52060
|
+
return { messages: child.messages };
|
|
52061
|
+
}
|
|
52062
|
+
computeRewindDiff() {
|
|
52063
|
+
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
52064
|
+
}
|
|
52065
|
+
listRewindableUserMessageIds() {
|
|
52066
|
+
return [];
|
|
52067
|
+
}
|
|
52068
|
+
parseThread(locate, threadId) {
|
|
52069
|
+
const file = locate(threadId);
|
|
52070
|
+
if (!file) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
52071
|
+
return parseCodexRollout(import_node_fs15.default.readFileSync(file, "utf8"));
|
|
52072
|
+
}
|
|
52073
|
+
};
|
|
52074
|
+
|
|
52075
|
+
// src/tools/codex-cloud-history.ts
|
|
52076
|
+
function personaIdFromCloudCwd(cwd) {
|
|
52077
|
+
const m3 = /\/p\/([^/?#]+)(?:[/?#]|$)/.exec(cwd);
|
|
52078
|
+
if (!m3) return void 0;
|
|
52079
|
+
try {
|
|
52080
|
+
return decodeURIComponent(m3[1]);
|
|
52081
|
+
} catch {
|
|
52082
|
+
return void 0;
|
|
52083
|
+
}
|
|
52084
|
+
}
|
|
52085
|
+
var CloudCodexHistoryReader = class {
|
|
52086
|
+
constructor(client, logger) {
|
|
52087
|
+
this.client = client;
|
|
52088
|
+
this.logger = logger;
|
|
52089
|
+
}
|
|
52090
|
+
client;
|
|
52091
|
+
logger;
|
|
52092
|
+
async listProjects() {
|
|
52093
|
+
return [];
|
|
52094
|
+
}
|
|
52095
|
+
/** 云 persona 没有「关联已有 thread」的动线:会话列表就是本机的会话记录 */
|
|
52096
|
+
async listSessions() {
|
|
52097
|
+
return [];
|
|
52098
|
+
}
|
|
52099
|
+
async read(args) {
|
|
52100
|
+
const parent = await this.fetchThread(args.toolSessionId);
|
|
52101
|
+
const messages = await withSubagentResults(
|
|
52102
|
+
parent,
|
|
52103
|
+
args.toolSessionId,
|
|
52104
|
+
(agentId) => this.client.fetchRollout(agentId),
|
|
52105
|
+
this.logger
|
|
52106
|
+
);
|
|
52107
|
+
return pageMessages(messages, args);
|
|
52108
|
+
}
|
|
52109
|
+
async listSubagents(args) {
|
|
52110
|
+
return subagentSummaries(await this.fetchThread(args.toolSessionId));
|
|
52111
|
+
}
|
|
52112
|
+
async readSubagent(args) {
|
|
52113
|
+
const child = await this.fetchThread(args.subagentId);
|
|
52114
|
+
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
52115
|
+
throw new Error(
|
|
52116
|
+
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
52117
|
+
);
|
|
52118
|
+
}
|
|
52119
|
+
return { messages: child.messages };
|
|
52120
|
+
}
|
|
52121
|
+
computeRewindDiff() {
|
|
52122
|
+
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
52123
|
+
}
|
|
52124
|
+
listRewindableUserMessageIds() {
|
|
52125
|
+
return [];
|
|
52126
|
+
}
|
|
52127
|
+
async fetchThread(threadId) {
|
|
52128
|
+
const text = await this.client.fetchRollout(threadId);
|
|
52129
|
+
if (text === null) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
52130
|
+
return parseCodexRollout(text);
|
|
52131
|
+
}
|
|
52132
|
+
};
|
|
52133
|
+
|
|
52134
|
+
// src/tools/pi-history.ts
|
|
52135
|
+
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
52136
|
+
var import_node_os8 = __toESM(require("os"), 1);
|
|
52137
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
52138
|
+
|
|
52139
|
+
// src/tools/pi-json.ts
|
|
52140
|
+
function rec2(v3) {
|
|
52141
|
+
return typeof v3 === "object" && v3 !== null && !Array.isArray(v3) ? v3 : void 0;
|
|
52142
|
+
}
|
|
52143
|
+
function str2(v3) {
|
|
52144
|
+
return typeof v3 === "string" ? v3 : void 0;
|
|
52145
|
+
}
|
|
52146
|
+
function num2(v3) {
|
|
52147
|
+
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
52148
|
+
}
|
|
52149
|
+
function arr(v3) {
|
|
52150
|
+
return Array.isArray(v3) ? v3 : [];
|
|
52151
|
+
}
|
|
52152
|
+
function contentText(v3, imagePlaceholder = "") {
|
|
52153
|
+
if (typeof v3 === "string") return v3;
|
|
52154
|
+
return arr(v3).map((b3) => {
|
|
52155
|
+
const r2 = rec2(b3);
|
|
52156
|
+
if (!r2) return "";
|
|
52157
|
+
if (r2.type === "text") return str2(r2.text) ?? "";
|
|
52158
|
+
if (r2.type === "image") return imagePlaceholder;
|
|
52159
|
+
return "";
|
|
52160
|
+
}).filter(Boolean).join("\n");
|
|
52161
|
+
}
|
|
52162
|
+
|
|
52163
|
+
// src/tools/pi-history.ts
|
|
52164
|
+
var PI_LIST_SESSIONS_LIMIT = 100;
|
|
52165
|
+
var PI_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
52166
|
+
function piAgentDir(env = process.env) {
|
|
52167
|
+
return env.PI_CODING_AGENT_DIR || import_node_path15.default.join(import_node_os8.default.homedir(), ".pi", "agent");
|
|
52168
|
+
}
|
|
52169
|
+
function piSessionsRoot(env = process.env) {
|
|
52170
|
+
return import_node_path15.default.join(piAgentDir(env), "sessions");
|
|
52171
|
+
}
|
|
52172
|
+
function encodePiCwd(cwd) {
|
|
52173
|
+
return `--${cwd.replace(/^[\\/]+/, "").replace(/[\\/:]/g, "-")}--`;
|
|
52174
|
+
}
|
|
52175
|
+
function piProjectSessionDir(sessionsRoot, cwd) {
|
|
52176
|
+
return import_node_path15.default.join(sessionsRoot, encodePiCwd(cwd));
|
|
52177
|
+
}
|
|
52178
|
+
function findInDir(dir, toolSessionId) {
|
|
52179
|
+
let names;
|
|
52180
|
+
try {
|
|
52181
|
+
names = import_node_fs16.default.readdirSync(dir);
|
|
52182
|
+
} catch {
|
|
52183
|
+
return null;
|
|
52184
|
+
}
|
|
52185
|
+
const suffix = `_${toolSessionId}.jsonl`;
|
|
52186
|
+
const hit = names.filter((n2) => n2.endsWith(suffix)).sort().at(-1);
|
|
52187
|
+
return hit ? import_node_path15.default.join(dir, hit) : null;
|
|
52188
|
+
}
|
|
52189
|
+
function locatePiSessionFile(sessionsRoot, cwd, toolSessionId) {
|
|
52190
|
+
const direct = findInDir(piProjectSessionDir(sessionsRoot, cwd), toolSessionId);
|
|
52191
|
+
if (direct) return direct;
|
|
52192
|
+
let dirs;
|
|
52193
|
+
try {
|
|
52194
|
+
dirs = import_node_fs16.default.readdirSync(sessionsRoot, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
|
|
52195
|
+
} catch {
|
|
52196
|
+
return null;
|
|
52197
|
+
}
|
|
52198
|
+
for (const d2 of dirs) {
|
|
52199
|
+
const hit = findInDir(import_node_path15.default.join(sessionsRoot, d2), toolSessionId);
|
|
52200
|
+
if (hit) return hit;
|
|
52201
|
+
}
|
|
52202
|
+
return null;
|
|
52203
|
+
}
|
|
52204
|
+
function parseLine2(line) {
|
|
52205
|
+
const t2 = line.trim();
|
|
52206
|
+
if (!t2) return null;
|
|
52207
|
+
try {
|
|
52208
|
+
return rec2(JSON.parse(t2)) ?? null;
|
|
52209
|
+
} catch {
|
|
52210
|
+
return null;
|
|
52211
|
+
}
|
|
52212
|
+
}
|
|
52213
|
+
function parsePiSession(text) {
|
|
52214
|
+
let meta = null;
|
|
52215
|
+
const entries = /* @__PURE__ */ new Map();
|
|
52216
|
+
let last = null;
|
|
52217
|
+
for (const line of text.split("\n")) {
|
|
52218
|
+
const o2 = parseLine2(line);
|
|
52219
|
+
if (!o2) continue;
|
|
52220
|
+
if (o2.type === "session") {
|
|
52221
|
+
if (!meta) {
|
|
52222
|
+
const timestamp = str2(o2.timestamp);
|
|
52223
|
+
meta = { id: str2(o2.id) ?? "", cwd: str2(o2.cwd) ?? "", ...timestamp ? { timestamp } : {} };
|
|
52224
|
+
}
|
|
52225
|
+
continue;
|
|
52226
|
+
}
|
|
52227
|
+
const id = str2(o2.id);
|
|
52228
|
+
if (!id) continue;
|
|
52229
|
+
entries.set(id, o2);
|
|
52230
|
+
last = o2;
|
|
52231
|
+
}
|
|
52232
|
+
const chain = [];
|
|
52233
|
+
const seen = /* @__PURE__ */ new Set();
|
|
52234
|
+
let cur = last;
|
|
52235
|
+
while (cur) {
|
|
52236
|
+
const id = str2(cur.id);
|
|
52237
|
+
if (seen.has(id)) break;
|
|
52238
|
+
seen.add(id);
|
|
52239
|
+
chain.push(cur);
|
|
52240
|
+
const pid = str2(cur.parentId);
|
|
52241
|
+
cur = pid ? entries.get(pid) ?? null : null;
|
|
52242
|
+
}
|
|
52243
|
+
chain.reverse();
|
|
52244
|
+
const messages = [];
|
|
52245
|
+
for (const e of chain) {
|
|
52246
|
+
if (e.type !== "message") continue;
|
|
52247
|
+
const m3 = rec2(e.message);
|
|
52248
|
+
if (!m3) continue;
|
|
52249
|
+
const ts = str2(e.timestamp);
|
|
52250
|
+
const base = { raw: e, uuid: str2(e.id), ...ts ? { ts } : {} };
|
|
52251
|
+
switch (m3.role) {
|
|
52252
|
+
case "user": {
|
|
52253
|
+
const t2 = contentText(m3.content, "[image]");
|
|
52254
|
+
if (t2) messages.push({ kind: "user-text", text: t2, ...base });
|
|
52255
|
+
break;
|
|
52256
|
+
}
|
|
52257
|
+
case "assistant": {
|
|
52258
|
+
for (const b3 of arr(m3.content)) {
|
|
52259
|
+
const block = rec2(b3);
|
|
52260
|
+
if (!block) continue;
|
|
52261
|
+
if (block.type === "text") {
|
|
52262
|
+
const t2 = str2(block.text);
|
|
52263
|
+
if (t2) messages.push({ kind: "assistant-text", text: t2, ...base });
|
|
52264
|
+
} else if (block.type === "toolCall") {
|
|
52265
|
+
const toolUseId = str2(block.id);
|
|
52266
|
+
const tool = str2(block.name);
|
|
52267
|
+
if (toolUseId && tool) messages.push({ kind: "tool_call", tool, toolUseId, input: block.arguments ?? {}, ...base });
|
|
52268
|
+
}
|
|
52269
|
+
}
|
|
52270
|
+
if (str2(m3.stopReason) !== "toolUse") messages.push({ kind: "turn-end", ...base });
|
|
52271
|
+
break;
|
|
52272
|
+
}
|
|
52273
|
+
case "toolResult": {
|
|
52274
|
+
const toolUseId = str2(m3.toolCallId);
|
|
52275
|
+
if (!toolUseId) break;
|
|
52276
|
+
const t2 = contentText(m3.content);
|
|
52277
|
+
const msg = { kind: "tool_result", toolUseId, output: t2, ...base };
|
|
52278
|
+
if (m3.isError === true) msg.error = t2 || "tool failed";
|
|
52279
|
+
messages.push(msg);
|
|
52280
|
+
break;
|
|
52281
|
+
}
|
|
52282
|
+
default:
|
|
52283
|
+
break;
|
|
52284
|
+
}
|
|
52285
|
+
}
|
|
52286
|
+
return { meta, messages };
|
|
52287
|
+
}
|
|
52288
|
+
var PiHistoryReader = class {
|
|
52289
|
+
constructor(deps) {
|
|
52290
|
+
this.deps = deps;
|
|
52291
|
+
}
|
|
52292
|
+
deps;
|
|
52293
|
+
// projects/list 非 session-scoped、不知 tool(同 codex 的取舍)
|
|
52294
|
+
async listProjects() {
|
|
52295
|
+
return [];
|
|
52296
|
+
}
|
|
52297
|
+
async listSessions(args) {
|
|
52298
|
+
const want = import_node_path15.default.resolve(args.projectPath);
|
|
52299
|
+
const dir = piProjectSessionDir(this.deps.sessionsRoot, want);
|
|
52300
|
+
let names;
|
|
52301
|
+
try {
|
|
52302
|
+
names = import_node_fs16.default.readdirSync(dir).filter((n2) => n2.endsWith(".jsonl"));
|
|
52303
|
+
} catch {
|
|
52304
|
+
return [];
|
|
52305
|
+
}
|
|
52306
|
+
const found = [];
|
|
52307
|
+
let scanned = 0;
|
|
52308
|
+
for (const n2 of names) {
|
|
52309
|
+
if (++scanned > PI_LIST_SESSIONS_SCAN_CAP) break;
|
|
52310
|
+
const file = import_node_path15.default.join(dir, n2);
|
|
52311
|
+
const head = parseLine2(readFirstLine(file) ?? "");
|
|
52312
|
+
if (!head || head.type !== "session") continue;
|
|
52313
|
+
const cwd = str2(head.cwd);
|
|
52314
|
+
const id = str2(head.id);
|
|
52315
|
+
if (!cwd || !id || import_node_path15.default.resolve(cwd) !== want) continue;
|
|
52316
|
+
let mtimeMs;
|
|
52317
|
+
try {
|
|
52318
|
+
mtimeMs = import_node_fs16.default.statSync(file).mtimeMs;
|
|
52319
|
+
} catch {
|
|
52320
|
+
continue;
|
|
52321
|
+
}
|
|
52322
|
+
found.push({ file, id, mtimeMs });
|
|
52323
|
+
}
|
|
52324
|
+
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
52325
|
+
return found.slice(0, PI_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
52326
|
+
const parsed = parsePiSession(import_node_fs16.default.readFileSync(f2.file, "utf8"));
|
|
52327
|
+
const firstUser = parsed.messages.find((m3) => m3.kind === "user-text");
|
|
52328
|
+
return {
|
|
52329
|
+
toolSessionId: f2.id,
|
|
52330
|
+
summary: firstUser?.text?.slice(0, 80) || "(\u7A7A\u4F1A\u8BDD)",
|
|
52331
|
+
messageCount: parsed.messages.length,
|
|
52332
|
+
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
52333
|
+
};
|
|
52334
|
+
});
|
|
52335
|
+
}
|
|
52336
|
+
async read(args) {
|
|
52337
|
+
const file = locatePiSessionFile(this.deps.sessionsRoot, args.cwd, args.toolSessionId);
|
|
52338
|
+
if (!file) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
52339
|
+
return pageMessages(parsePiSession(import_node_fs16.default.readFileSync(file, "utf8")).messages, args);
|
|
52340
|
+
}
|
|
52341
|
+
async listSubagents(_args) {
|
|
52342
|
+
return [];
|
|
52343
|
+
}
|
|
52344
|
+
async readSubagent(args) {
|
|
52345
|
+
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
52346
|
+
}
|
|
52347
|
+
computeRewindDiff() {
|
|
52348
|
+
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
52349
|
+
}
|
|
52350
|
+
listRewindableUserMessageIds() {
|
|
52351
|
+
return [];
|
|
52352
|
+
}
|
|
52353
|
+
};
|
|
52354
|
+
|
|
52355
|
+
// src/tools/pi-cloud-history.ts
|
|
52356
|
+
var CloudPiHistoryReader = class {
|
|
52357
|
+
constructor(client, logger) {
|
|
52358
|
+
this.client = client;
|
|
52359
|
+
this.logger = logger;
|
|
52360
|
+
}
|
|
52361
|
+
client;
|
|
52362
|
+
logger;
|
|
52363
|
+
async listProjects() {
|
|
52364
|
+
return [];
|
|
52365
|
+
}
|
|
52366
|
+
/** 云 persona 没有「关联已有会话」的动线:会话列表就是本机的会话记录 */
|
|
52367
|
+
async listSessions() {
|
|
52368
|
+
return [];
|
|
52369
|
+
}
|
|
52370
|
+
async read(args) {
|
|
52371
|
+
const text = await this.client.fetchRollout(args.toolSessionId);
|
|
52372
|
+
if (text === null) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
52373
|
+
const parsed = parsePiSession(text);
|
|
52374
|
+
if (!parsed.meta) this.logger?.warn("cloud pi session has no header line", { toolSessionId: args.toolSessionId });
|
|
52375
|
+
return pageMessages(parsed.messages, args);
|
|
52376
|
+
}
|
|
52377
|
+
async listSubagents(_args) {
|
|
52378
|
+
return [];
|
|
52379
|
+
}
|
|
52380
|
+
async readSubagent(args) {
|
|
52381
|
+
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
52382
|
+
}
|
|
52383
|
+
computeRewindDiff() {
|
|
52384
|
+
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
52385
|
+
}
|
|
52386
|
+
listRewindableUserMessageIds() {
|
|
52387
|
+
return [];
|
|
52388
|
+
}
|
|
52389
|
+
};
|
|
52390
|
+
|
|
52391
|
+
// src/feishu-auth/central-client.ts
|
|
52392
|
+
var CentralClientError = class extends Error {
|
|
52393
|
+
constructor(code, message, cause, status) {
|
|
52394
|
+
super(message);
|
|
52395
|
+
this.code = code;
|
|
52396
|
+
this.cause = cause;
|
|
52397
|
+
this.status = status;
|
|
52398
|
+
this.name = "CentralClientError";
|
|
52399
|
+
}
|
|
52400
|
+
code;
|
|
52401
|
+
cause;
|
|
52402
|
+
status;
|
|
52403
|
+
};
|
|
52404
|
+
async function centralRequest(opts, path102, init) {
|
|
52405
|
+
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
52406
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path102}`;
|
|
52407
|
+
const ctrl = new AbortController();
|
|
52408
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
52409
|
+
let res;
|
|
52410
|
+
try {
|
|
52411
|
+
res = await f2(url, {
|
|
52412
|
+
method: init.method,
|
|
52413
|
+
headers: {
|
|
52414
|
+
"content-type": "application/json",
|
|
52415
|
+
...init.bearer ? { authorization: `Bearer ${init.bearer}` } : {}
|
|
52416
|
+
},
|
|
52417
|
+
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {},
|
|
52418
|
+
signal: ctrl.signal
|
|
52419
|
+
});
|
|
52420
|
+
} catch (err) {
|
|
52421
|
+
clearTimeout(timer);
|
|
52422
|
+
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
52423
|
+
}
|
|
52424
|
+
clearTimeout(timer);
|
|
52425
|
+
if (res.status === 401) {
|
|
52426
|
+
throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
52427
|
+
}
|
|
52428
|
+
if (!res.ok) {
|
|
52429
|
+
let detail = "";
|
|
52430
|
+
try {
|
|
52431
|
+
const body = await res.json();
|
|
52432
|
+
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
52433
|
+
} catch {
|
|
52434
|
+
}
|
|
52435
|
+
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`, void 0, res.status);
|
|
52436
|
+
}
|
|
52437
|
+
try {
|
|
52438
|
+
return await res.json();
|
|
52439
|
+
} catch (err) {
|
|
52440
|
+
throw new CentralClientError("BAD_RESPONSE", "central server: invalid json response", err);
|
|
52441
|
+
}
|
|
52442
|
+
}
|
|
52443
|
+
async function centralExchange(opts) {
|
|
52444
|
+
const body = await centralRequest(opts, "/api/clawd-identity/exchange", {
|
|
52445
|
+
method: "POST",
|
|
52446
|
+
bearer: opts.loginPass,
|
|
52447
|
+
body: { deviceId: opts.deviceId }
|
|
52448
|
+
});
|
|
52449
|
+
if (typeof body.token !== "string" || body.token.length === 0 || typeof body.ownerId !== "string" || typeof body.provider !== "string" || typeof body.displayName !== "string") {
|
|
52450
|
+
throw new CentralClientError("BAD_RESPONSE", "exchange: missing token/ownerId/provider/displayName");
|
|
52451
|
+
}
|
|
52452
|
+
const deviceUrl = typeof body.deviceUrl === "string" && body.deviceUrl.length > 0 ? body.deviceUrl : null;
|
|
52453
|
+
return { token: body.token, ownerId: body.ownerId, provider: body.provider, displayName: body.displayName, deviceUrl };
|
|
52454
|
+
}
|
|
52455
|
+
async function centralGetPublicKey(opts) {
|
|
52456
|
+
const body = await centralRequest(opts, "/api/clawd-identity/public-key", {
|
|
52457
|
+
method: "GET"
|
|
52458
|
+
});
|
|
52459
|
+
if (typeof body.publicKeyPem !== "string" || !body.publicKeyPem.includes("PUBLIC KEY")) {
|
|
52460
|
+
throw new CentralClientError("BAD_RESPONSE", "public-key: missing or malformed publicKeyPem");
|
|
52461
|
+
}
|
|
52462
|
+
return { publicKeyPem: body.publicKeyPem };
|
|
52463
|
+
}
|
|
52464
|
+
async function centralUpsertDevice(opts) {
|
|
52465
|
+
const body = await centralRequest(opts, "/api/clawd-identity/devices/self", {
|
|
52466
|
+
method: "PUT",
|
|
52467
|
+
bearer: opts.loginPass,
|
|
52468
|
+
body: {
|
|
52469
|
+
...opts.url ? { url: opts.url } : {},
|
|
52470
|
+
...opts.name ? { name: opts.name } : {}
|
|
52471
|
+
}
|
|
52472
|
+
});
|
|
52473
|
+
if (body.ok !== true) {
|
|
52474
|
+
throw new CentralClientError("BAD_RESPONSE", "upsertDevice: server did not ack ok");
|
|
52475
|
+
}
|
|
52476
|
+
return { ok: true };
|
|
52477
|
+
}
|
|
52478
|
+
async function centralPrincipalStatus(opts) {
|
|
52479
|
+
const query = `provider=${encodeURIComponent(opts.provider)}&ownerId=${encodeURIComponent(opts.ownerId)}`;
|
|
52480
|
+
const body = await centralRequest(opts, `/api/clawd-identity/principal-status?${query}`, {
|
|
52481
|
+
method: "GET"
|
|
52482
|
+
});
|
|
52483
|
+
if (typeof body.active !== "boolean") {
|
|
52484
|
+
throw new CentralClientError("BAD_RESPONSE", "principal-status: missing active flag");
|
|
52485
|
+
}
|
|
52486
|
+
return { active: body.active };
|
|
52487
|
+
}
|
|
52488
|
+
async function centralGetApiKey(opts) {
|
|
52489
|
+
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
52490
|
+
method: "GET",
|
|
52491
|
+
bearer: opts.loginPass
|
|
52492
|
+
});
|
|
52493
|
+
if (body.apiKey !== null && typeof body.apiKey !== "string") {
|
|
52494
|
+
throw new CentralClientError("BAD_RESPONSE", "api-key: apiKey must be string or null");
|
|
52495
|
+
}
|
|
52496
|
+
return {
|
|
52497
|
+
apiKey: body.apiKey,
|
|
52498
|
+
createdAt: typeof body.createdAt === "string" ? body.createdAt : null,
|
|
52499
|
+
// 中心没配就是 null —— 不当成错误,key 本身照样有效
|
|
52500
|
+
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
52501
|
+
};
|
|
52502
|
+
}
|
|
52503
|
+
async function centralGetThread(opts) {
|
|
52504
|
+
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
52505
|
+
const url = `${opts.api.replace(/\/+$/, "")}/api/persona-cloud/threads/${encodeURIComponent(opts.threadId)}`;
|
|
52506
|
+
const ctrl = new AbortController();
|
|
52507
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
52508
|
+
let res;
|
|
52509
|
+
try {
|
|
52510
|
+
res = await f2(url, { method: "GET", headers: { authorization: `Bearer ${opts.loginPass}` }, signal: ctrl.signal });
|
|
52511
|
+
} catch (err) {
|
|
52512
|
+
clearTimeout(timer);
|
|
52513
|
+
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
52514
|
+
}
|
|
52515
|
+
clearTimeout(timer);
|
|
52516
|
+
if (res.status === 404) return null;
|
|
52517
|
+
if (res.status === 401) throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
52518
|
+
if (!res.ok) {
|
|
52519
|
+
let detail = "";
|
|
52520
|
+
try {
|
|
52521
|
+
const body = await res.json();
|
|
52522
|
+
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
52523
|
+
} catch {
|
|
52524
|
+
}
|
|
52525
|
+
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`);
|
|
52526
|
+
}
|
|
52527
|
+
return await res.text();
|
|
52528
|
+
}
|
|
52529
|
+
async function centralIssueApiKey(opts) {
|
|
52530
|
+
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
52531
|
+
method: "POST",
|
|
52532
|
+
bearer: opts.loginPass
|
|
52533
|
+
});
|
|
52534
|
+
if (typeof body.apiKey !== "string" || body.apiKey.length === 0) {
|
|
52535
|
+
throw new CentralClientError("BAD_RESPONSE", "api-key: missing apiKey");
|
|
52536
|
+
}
|
|
52537
|
+
if (typeof body.createdAt !== "string" || body.createdAt.length === 0) {
|
|
52538
|
+
throw new CentralClientError("BAD_RESPONSE", "api-key: missing createdAt");
|
|
52539
|
+
}
|
|
52540
|
+
return {
|
|
52541
|
+
apiKey: body.apiKey,
|
|
52542
|
+
createdAt: body.createdAt,
|
|
52543
|
+
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
52544
|
+
};
|
|
52545
|
+
}
|
|
52546
|
+
async function centralRevokeApiKey(opts) {
|
|
52547
|
+
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
52548
|
+
method: "DELETE",
|
|
52549
|
+
bearer: opts.loginPass
|
|
52550
|
+
});
|
|
52551
|
+
if (typeof body.revoked !== "boolean") {
|
|
52552
|
+
throw new CentralClientError("BAD_RESPONSE", "api-key: revoked must be boolean");
|
|
52553
|
+
}
|
|
52554
|
+
return { revoked: body.revoked };
|
|
52555
|
+
}
|
|
52556
|
+
async function centralListDevices(opts) {
|
|
52557
|
+
const body = await centralRequest(opts, "/api/clawd-identity/devices", {
|
|
52558
|
+
method: "GET",
|
|
52559
|
+
bearer: opts.loginPass
|
|
52560
|
+
});
|
|
52561
|
+
if (!Array.isArray(body.devices)) {
|
|
52562
|
+
throw new CentralClientError("BAD_RESPONSE", "devices: missing devices array");
|
|
52563
|
+
}
|
|
52564
|
+
const out = [];
|
|
52565
|
+
for (const raw of body.devices) {
|
|
52566
|
+
const d2 = raw;
|
|
52567
|
+
if (typeof d2.deviceId !== "string" || typeof d2.name !== "string" || typeof d2.url !== "string") {
|
|
52568
|
+
throw new CentralClientError("BAD_RESPONSE", "devices: malformed device entry");
|
|
52569
|
+
}
|
|
52570
|
+
out.push({ deviceId: d2.deviceId, name: d2.name, url: d2.url });
|
|
52571
|
+
}
|
|
52572
|
+
return out;
|
|
52573
|
+
}
|
|
52574
|
+
async function centralConnectorsMine(opts) {
|
|
52575
|
+
const body = await centralRequest(opts, "/api/persona-cloud/connectors/mine", {
|
|
52576
|
+
method: "POST",
|
|
52577
|
+
bearer: opts.loginPass,
|
|
52578
|
+
body: { personaId: opts.personaId, connectors: opts.connectors }
|
|
52579
|
+
});
|
|
52580
|
+
if (!Array.isArray(body.connectors)) throw new CentralClientError("BAD_RESPONSE", "connectors/mine: missing connectors array");
|
|
52581
|
+
return { connectors: body.connectors };
|
|
52582
|
+
}
|
|
52583
|
+
async function centralConnectorFill(opts) {
|
|
52584
|
+
const body = await centralRequest(opts, "/api/persona-cloud/connectors/fields", {
|
|
52585
|
+
method: "PUT",
|
|
52586
|
+
bearer: opts.loginPass,
|
|
52587
|
+
body: { connector: opts.connector, values: opts.values }
|
|
52588
|
+
});
|
|
52589
|
+
if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
|
|
52590
|
+
}
|
|
52591
|
+
async function centralGithubAuthOauthStart(opts) {
|
|
52592
|
+
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
|
|
52593
|
+
method: "POST",
|
|
52594
|
+
bearer: opts.loginPass,
|
|
52595
|
+
body: { ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
|
|
52596
|
+
});
|
|
52597
|
+
if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
|
|
52598
|
+
return { authUrl: body.authUrl };
|
|
52599
|
+
}
|
|
52600
|
+
async function centralGithubAuthToken(opts) {
|
|
52601
|
+
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
|
|
52602
|
+
method: "POST",
|
|
52603
|
+
bearer: opts.loginPass,
|
|
52604
|
+
body: {}
|
|
52605
|
+
});
|
|
52606
|
+
if (body.token === null || body.token === void 0) return void 0;
|
|
52607
|
+
if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
|
|
52608
|
+
return { token: body.token, login: typeof body.login === "string" ? body.login : "" };
|
|
52609
|
+
}
|
|
52610
|
+
async function centralClaimPublisher(opts) {
|
|
52611
|
+
await centralRequest(opts, `/api/persona-cloud/personas/${encodeURIComponent(opts.personaId)}/publisher`, {
|
|
52612
|
+
method: "PUT",
|
|
52613
|
+
bearer: opts.loginPass
|
|
52614
|
+
});
|
|
52615
|
+
}
|
|
52616
|
+
async function centralVisiblePersonas(opts) {
|
|
52617
|
+
const body = await centralRequest(opts, "/api/persona-cloud/personas/visible", { method: "GET", bearer: opts.loginPass });
|
|
52618
|
+
if (typeof body.all !== "boolean" || !Array.isArray(body.personaIds) || !body.personaIds.every((p3) => typeof p3 === "string")) {
|
|
52619
|
+
throw new CentralClientError("BAD_RESPONSE", "personas/visible: expected { all: boolean, personaIds: string[] }");
|
|
52620
|
+
}
|
|
52621
|
+
return { all: body.all, personaIds: body.personaIds };
|
|
52622
|
+
}
|
|
52623
|
+
async function centralPersonaThreads(opts) {
|
|
52624
|
+
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
52625
|
+
const q3 = new URLSearchParams();
|
|
52626
|
+
if (opts.before) q3.set("before", opts.before);
|
|
52627
|
+
if (opts.limit !== null) q3.set("limit", String(opts.limit));
|
|
52628
|
+
const qs = String(q3) ? `?${q3}` : "";
|
|
52629
|
+
const url = `${opts.api.replace(/\/+$/, "")}/api/persona-cloud/personas/${encodeURIComponent(opts.personaId)}/threads${qs}`;
|
|
52630
|
+
const ctrl = new AbortController();
|
|
52631
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
52632
|
+
try {
|
|
52633
|
+
const res = await f2(url, { method: "GET", headers: { authorization: `Bearer ${opts.loginPass}` }, signal: ctrl.signal });
|
|
52634
|
+
return { status: res.status, body: await res.text() };
|
|
52635
|
+
} catch (err) {
|
|
52636
|
+
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
52637
|
+
} finally {
|
|
52638
|
+
clearTimeout(timer);
|
|
52639
|
+
}
|
|
52640
|
+
}
|
|
52641
|
+
async function centralPersonaThread(opts) {
|
|
52642
|
+
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
52643
|
+
const url = `${opts.api.replace(/\/+$/, "")}/api/persona-cloud/personas/${encodeURIComponent(opts.personaId)}/threads/${encodeURIComponent(opts.unionId)}/${encodeURIComponent(opts.threadId)}`;
|
|
52644
|
+
const ctrl = new AbortController();
|
|
52645
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
52646
|
+
let res;
|
|
52647
|
+
try {
|
|
52648
|
+
res = await f2(url, { method: "GET", headers: { authorization: `Bearer ${opts.loginPass}` }, signal: ctrl.signal });
|
|
52649
|
+
} catch (err) {
|
|
52650
|
+
clearTimeout(timer);
|
|
52651
|
+
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
52652
|
+
}
|
|
52653
|
+
clearTimeout(timer);
|
|
52654
|
+
if (res.status === 404) return null;
|
|
52655
|
+
if (res.status === 401) throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
52656
|
+
if (!res.ok) {
|
|
52657
|
+
let detail = "";
|
|
52658
|
+
try {
|
|
52659
|
+
const body = await res.json();
|
|
52660
|
+
const m3 = body?.message;
|
|
52661
|
+
const said = typeof m3 === "string" ? m3 : Array.isArray(m3) ? m3.map(String).join("\uFF1B") : "";
|
|
52662
|
+
if (said) detail = `: ${said}`;
|
|
52663
|
+
} catch {
|
|
52664
|
+
}
|
|
52665
|
+
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`, void 0, res.status);
|
|
52666
|
+
}
|
|
52667
|
+
return await res.text();
|
|
52668
|
+
}
|
|
52669
|
+
|
|
52670
|
+
// src/dashboard/conversations.ts
|
|
52671
|
+
function visiblePersonaIds(registered, visible) {
|
|
52672
|
+
if (visible.all) return registered;
|
|
52673
|
+
const mine = new Set(visible.personaIds);
|
|
52674
|
+
return registered.filter((id) => mine.has(id));
|
|
52675
|
+
}
|
|
52676
|
+
function engineOfRollout(text) {
|
|
52677
|
+
const nl = text.indexOf("\n");
|
|
52678
|
+
try {
|
|
52679
|
+
const first = JSON.parse(nl < 0 ? text : text.slice(0, nl));
|
|
52680
|
+
return first?.type === "session" ? "pi" : "codex";
|
|
52681
|
+
} catch {
|
|
52682
|
+
return "codex";
|
|
52683
|
+
}
|
|
52684
|
+
}
|
|
52685
|
+
async function readConversation(args) {
|
|
52686
|
+
const { threadId, fetchThread, logger } = args;
|
|
52687
|
+
const first = await fetchThread(threadId);
|
|
52688
|
+
if (first === null) throw new DashboardDataError(404, "THREAD_NOT_FOUND", "\u8FD9\u6761\u5BF9\u8BDD\u7684\u8BB0\u5F55\u4E0D\u5728\u4E86");
|
|
52689
|
+
const engine = engineOfRollout(first);
|
|
52690
|
+
const source = { fetchRollout: (id) => id === threadId ? Promise.resolve(first) : fetchThread(id) };
|
|
52691
|
+
const reader = engine === "pi" ? new CloudPiHistoryReader(source, logger) : new CloudCodexHistoryReader(source, logger);
|
|
52692
|
+
const { messages } = await reader.read({ cwd: "", toolSessionId: threadId, limit: Number.MAX_SAFE_INTEGER });
|
|
52693
|
+
return { engine, messages };
|
|
52694
|
+
}
|
|
52695
|
+
function dashboardErrorOf(err) {
|
|
52696
|
+
if (err instanceof CentralClientError && err.status === 403) {
|
|
52697
|
+
const said = err.message.replace(/^central server HTTP \d+:?\s*/, "");
|
|
52698
|
+
return new DashboardDataError(403, "NOT_PERSONA_VIEWER", said || "\u6CA1\u6709\u8FD9\u4E2A persona \u7684\u67E5\u770B\u6743\u9650");
|
|
52699
|
+
}
|
|
52700
|
+
return err;
|
|
52701
|
+
}
|
|
52702
|
+
function createDashboardData(deps) {
|
|
52703
|
+
const mustLoginPass = () => {
|
|
52704
|
+
const lp = deps.loginPass();
|
|
52705
|
+
if (!lp) throw new Error("\u8FD8\u6CA1\u767B\u5F55 clawd\uFF1A\u5148\u5728\u8BBE\u7F6E\u91CC\u767B\u5F55\u98DE\u4E66");
|
|
52706
|
+
return lp;
|
|
52707
|
+
};
|
|
52708
|
+
return {
|
|
52709
|
+
async personas() {
|
|
52710
|
+
const lp = mustLoginPass();
|
|
52711
|
+
const [registered, visible] = await Promise.all([deps.registeredPersonas(), deps.visiblePersonas(lp)]);
|
|
52712
|
+
return visiblePersonaIds(registered, visible);
|
|
52713
|
+
},
|
|
52714
|
+
metrics: (q3) => deps.metrics(q3),
|
|
52715
|
+
async threads(q3) {
|
|
52716
|
+
return deps.threads(mustLoginPass(), q3);
|
|
52717
|
+
},
|
|
52718
|
+
async thread({ personaId: personaId2, unionId, threadId }) {
|
|
52719
|
+
const lp = mustLoginPass();
|
|
52720
|
+
try {
|
|
52721
|
+
return await readConversation({ threadId, fetchThread: (id) => deps.thread(lp, { personaId: personaId2, unionId, threadId: id }), logger: deps.logger });
|
|
52722
|
+
} catch (err) {
|
|
52723
|
+
throw dashboardErrorOf(err);
|
|
52724
|
+
}
|
|
52725
|
+
}
|
|
52726
|
+
};
|
|
52727
|
+
}
|
|
52728
|
+
|
|
52729
|
+
// src/share-ui/load.ts
|
|
52730
|
+
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
52731
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
52732
|
+
var import_node_url4 = require("url");
|
|
52733
|
+
|
|
52734
|
+
// src/share-ui/asset-loader.ts
|
|
52735
|
+
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
52736
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
52737
|
+
function createShareUiAssetLoader(opts) {
|
|
52738
|
+
let indexHtml;
|
|
52739
|
+
try {
|
|
52740
|
+
indexHtml = import_node_fs17.default.readFileSync(opts.indexHtmlPath, "utf8");
|
|
52741
|
+
} catch (err) {
|
|
52742
|
+
throw new Error(`share-ui index.html not found at ${opts.indexHtmlPath}: ${err.message}`);
|
|
52743
|
+
}
|
|
52744
|
+
const assetsDir = import_node_path16.default.resolve(opts.assetsDir);
|
|
52745
|
+
return {
|
|
52746
|
+
renderIndexHtml() {
|
|
52747
|
+
return indexHtml;
|
|
52748
|
+
},
|
|
52749
|
+
resolveAssetPath(rel) {
|
|
52750
|
+
const resolved = import_node_path16.default.resolve(assetsDir, rel);
|
|
52751
|
+
if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path16.default.sep)) return null;
|
|
52752
|
+
try {
|
|
52753
|
+
if (import_node_fs17.default.statSync(resolved).isFile()) return resolved;
|
|
52754
|
+
} catch {
|
|
52755
|
+
}
|
|
52756
|
+
return null;
|
|
52757
|
+
}
|
|
52758
|
+
};
|
|
52759
|
+
}
|
|
52760
|
+
|
|
52761
|
+
// src/share-ui/load.ts
|
|
52762
|
+
var import_meta4 = {};
|
|
52763
|
+
function bundleDirFromArgv(argv1) {
|
|
52764
|
+
if (!argv1) return null;
|
|
52765
|
+
let real = argv1;
|
|
52766
|
+
try {
|
|
52767
|
+
real = import_node_fs18.default.realpathSync(argv1);
|
|
52768
|
+
} catch {
|
|
52769
|
+
}
|
|
52770
|
+
return import_node_path17.default.dirname(real);
|
|
52771
|
+
}
|
|
52772
|
+
function tryLoadShareUi(logger) {
|
|
52773
|
+
const candidates = [];
|
|
52774
|
+
try {
|
|
52775
|
+
const here = import_node_path17.default.dirname((0, import_node_url4.fileURLToPath)(import_meta4.url));
|
|
52776
|
+
candidates.push(here);
|
|
52777
|
+
candidates.push(import_node_path17.default.resolve(here, ".."));
|
|
52778
|
+
candidates.push(import_node_path17.default.resolve(here, "..", "..", "dist"));
|
|
52779
|
+
} catch {
|
|
52780
|
+
}
|
|
52781
|
+
const argvDir = bundleDirFromArgv(process.argv[1]);
|
|
52782
|
+
if (argvDir) candidates.push(argvDir);
|
|
52783
|
+
for (const root of candidates) {
|
|
52784
|
+
const shareUiDir = import_node_path17.default.join(root, "share-ui");
|
|
52785
|
+
const indexHtmlPath = import_node_path17.default.join(shareUiDir, "guest.html");
|
|
52786
|
+
const assetsDir = import_node_path17.default.join(shareUiDir, "assets");
|
|
52787
|
+
try {
|
|
52788
|
+
if (import_node_fs18.default.statSync(indexHtmlPath).isFile() && import_node_fs18.default.statSync(assetsDir).isDirectory()) {
|
|
52789
|
+
logger?.info("share-ui.assets-root.resolved", { root: shareUiDir });
|
|
52790
|
+
return createShareUiAssetLoader({ indexHtmlPath, assetsDir });
|
|
52791
|
+
}
|
|
52792
|
+
} catch {
|
|
52793
|
+
}
|
|
52794
|
+
}
|
|
52795
|
+
logger?.warn("share-ui.assets-not-found", {
|
|
52796
|
+
candidates,
|
|
52797
|
+
hint: "run `pnpm -F @clawos/clawd-ui build && pnpm -F @clawos-dev/clawd build`"
|
|
52798
|
+
});
|
|
52799
|
+
return null;
|
|
52800
|
+
}
|
|
52801
|
+
|
|
52802
|
+
// src/visitor/visitor-token.ts
|
|
52803
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
52804
|
+
|
|
52805
|
+
// ../protocol/src/index.ts
|
|
52806
|
+
init_runtime();
|
|
52807
|
+
|
|
52808
|
+
// src/visitor/visitor-token.ts
|
|
52809
|
+
var DOMAIN_PREFIX3 = "clawd-visitor-v1|";
|
|
52810
|
+
function hmac(secret, body) {
|
|
52811
|
+
return import_node_crypto6.default.createHmac("sha256", secret).update(`${DOMAIN_PREFIX3}${body}`).digest("base64url");
|
|
52812
|
+
}
|
|
52813
|
+
function signVisitorToken(secret, payload) {
|
|
52814
|
+
const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
52815
|
+
return `${body}.${hmac(secret, body)}`;
|
|
52816
|
+
}
|
|
52817
|
+
function verifyVisitorToken(secret, token, now) {
|
|
52818
|
+
const dot = token.indexOf(".");
|
|
52819
|
+
if (dot <= 0) return { ok: false, reason: "BAD_FORMAT" };
|
|
52820
|
+
const body = token.slice(0, dot);
|
|
52821
|
+
const sig = token.slice(dot + 1);
|
|
52822
|
+
const expected = hmac(secret, body);
|
|
52823
|
+
if (sig.length !== expected.length) return { ok: false, reason: "BAD_SIGNATURE" };
|
|
52824
|
+
if (!import_node_crypto6.default.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
|
|
52825
|
+
return { ok: false, reason: "BAD_SIGNATURE" };
|
|
52826
|
+
}
|
|
52827
|
+
let payload;
|
|
52828
|
+
try {
|
|
52829
|
+
const parsed = VisitorTokenPayloadSchema.safeParse(
|
|
52830
|
+
JSON.parse(Buffer.from(body, "base64url").toString("utf8"))
|
|
52831
|
+
);
|
|
52832
|
+
if (!parsed.success) return { ok: false, reason: "BAD_FORMAT" };
|
|
52833
|
+
payload = parsed.data;
|
|
52834
|
+
} catch {
|
|
52835
|
+
return { ok: false, reason: "BAD_FORMAT" };
|
|
52836
|
+
}
|
|
52837
|
+
if (now >= payload.exp) return { ok: false, reason: "EXPIRED" };
|
|
52838
|
+
return { ok: true, payload };
|
|
52839
|
+
}
|
|
52840
|
+
|
|
52841
|
+
// src/visitor/visitor-login.ts
|
|
52842
|
+
function buildVisitorLogin(deps) {
|
|
52843
|
+
return async function exchangeVisitorToken(args) {
|
|
52844
|
+
let id;
|
|
52845
|
+
try {
|
|
52846
|
+
id = await deps.fetchUserInfo(args.ttcToken);
|
|
52847
|
+
} catch {
|
|
52848
|
+
return { ok: false, reason: "BAD_TTC_TOKEN" };
|
|
52849
|
+
}
|
|
52850
|
+
deps.visitorStore.upsert({
|
|
52851
|
+
visitorId: id.ownerId,
|
|
52852
|
+
displayName: id.displayName,
|
|
52853
|
+
provider: id.provider,
|
|
52854
|
+
...id.avatarUrl ? { avatarUrl: id.avatarUrl } : {},
|
|
52855
|
+
now: deps.now()
|
|
52856
|
+
});
|
|
52857
|
+
const iat = Math.floor(deps.now() / 1e3);
|
|
52858
|
+
const visitorToken = signVisitorToken(deps.getSecret(), {
|
|
52859
|
+
v: 1,
|
|
52860
|
+
visitorId: id.ownerId,
|
|
52861
|
+
displayName: id.displayName,
|
|
52862
|
+
provider: id.provider,
|
|
52863
|
+
iat,
|
|
52864
|
+
exp: iat + deps.ttlSeconds
|
|
52865
|
+
});
|
|
52866
|
+
return { ok: true, visitorToken };
|
|
52867
|
+
};
|
|
52868
|
+
}
|
|
52869
|
+
|
|
52870
|
+
// src/visitor/visitor-store.ts
|
|
52871
|
+
var import_node_fs19 = __toESM(require("fs"), 1);
|
|
52872
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
52873
|
+
function createVisitorStore(opts) {
|
|
52874
|
+
const file = import_node_path18.default.join(opts.dir, "visitors.json");
|
|
52875
|
+
const read = () => {
|
|
52876
|
+
try {
|
|
52877
|
+
return JSON.parse(import_node_fs19.default.readFileSync(file, "utf8"));
|
|
52878
|
+
} catch {
|
|
52879
|
+
return [];
|
|
52880
|
+
}
|
|
52881
|
+
};
|
|
52882
|
+
const write = (rows) => {
|
|
52883
|
+
import_node_fs19.default.mkdirSync(opts.dir, { recursive: true });
|
|
52884
|
+
import_node_fs19.default.writeFileSync(file, JSON.stringify(rows, null, 2));
|
|
52885
|
+
};
|
|
52886
|
+
return {
|
|
52887
|
+
upsert(r2) {
|
|
52888
|
+
const rows = read();
|
|
52889
|
+
const ex = rows.find((x2) => x2.visitorId === r2.visitorId);
|
|
52890
|
+
if (ex) {
|
|
52891
|
+
ex.displayName = r2.displayName;
|
|
52892
|
+
ex.provider = r2.provider;
|
|
52893
|
+
ex.lastSeen = r2.now;
|
|
52894
|
+
if (r2.avatarUrl) ex.avatarUrl = r2.avatarUrl;
|
|
52895
|
+
} else {
|
|
52896
|
+
rows.push({
|
|
52897
|
+
visitorId: r2.visitorId,
|
|
52898
|
+
displayName: r2.displayName,
|
|
52899
|
+
provider: r2.provider,
|
|
52900
|
+
...r2.avatarUrl ? { avatarUrl: r2.avatarUrl } : {},
|
|
52901
|
+
firstSeen: r2.now,
|
|
52902
|
+
lastSeen: r2.now
|
|
52903
|
+
});
|
|
52904
|
+
}
|
|
52905
|
+
write(rows);
|
|
52906
|
+
},
|
|
52907
|
+
list() {
|
|
52908
|
+
return read();
|
|
52909
|
+
}
|
|
52910
|
+
};
|
|
52911
|
+
}
|
|
52912
|
+
|
|
52913
|
+
// src/index.ts
|
|
52914
|
+
init_claude();
|
|
52915
|
+
|
|
52916
|
+
// src/tools/codex.ts
|
|
52917
|
+
var import_node_child_process7 = require("child_process");
|
|
52918
|
+
var import_node_fs24 = __toESM(require("fs"), 1);
|
|
52919
|
+
|
|
52920
|
+
// src/tools/codex-session.ts
|
|
52921
|
+
var import_node_child_process4 = require("child_process");
|
|
52922
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
52923
|
+
|
|
52924
|
+
// src/tools/codex-app-server-client.ts
|
|
52925
|
+
var import_node_readline = require("readline");
|
|
52926
|
+
function stdioLineStream(proc) {
|
|
52927
|
+
const rl = (0, import_node_readline.createInterface)({ input: proc.stdout, crlfDelay: Infinity });
|
|
52928
|
+
const lineCbs = [];
|
|
52929
|
+
const closeCbs = [];
|
|
52930
|
+
rl.on("line", (l2) => {
|
|
52931
|
+
for (const cb of lineCbs) cb(l2);
|
|
52932
|
+
});
|
|
52933
|
+
rl.on("close", () => {
|
|
52934
|
+
for (const cb of closeCbs) cb();
|
|
52935
|
+
});
|
|
52936
|
+
return {
|
|
52937
|
+
write: (line) => {
|
|
52938
|
+
proc.stdin.write(line + "\n");
|
|
52939
|
+
},
|
|
52940
|
+
onLine: (cb) => {
|
|
52941
|
+
lineCbs.push(cb);
|
|
52942
|
+
},
|
|
52943
|
+
onClose: (cb) => {
|
|
52944
|
+
closeCbs.push(cb);
|
|
52945
|
+
},
|
|
52946
|
+
close: () => {
|
|
52947
|
+
proc.stdin?.end();
|
|
52948
|
+
}
|
|
52949
|
+
};
|
|
52950
|
+
}
|
|
52951
|
+
var CodexAppServerClient = class {
|
|
52952
|
+
constructor(stream, opts = {}) {
|
|
52953
|
+
this.stream = stream;
|
|
52954
|
+
this.opts = opts;
|
|
52955
|
+
stream.onLine((l2) => this.onLine(l2));
|
|
52956
|
+
stream.onClose(() => {
|
|
52957
|
+
for (const p3 of this.pending.values()) p3.reject(new Error("app-server connection closed"));
|
|
52958
|
+
this.pending.clear();
|
|
52959
|
+
this.opts.onClose?.();
|
|
52960
|
+
});
|
|
52961
|
+
}
|
|
52962
|
+
stream;
|
|
52963
|
+
opts;
|
|
52964
|
+
nextId = 1;
|
|
52965
|
+
pending = /* @__PURE__ */ new Map();
|
|
52966
|
+
request(method, params) {
|
|
52967
|
+
const id = this.nextId++;
|
|
52968
|
+
return new Promise((resolve9, reject) => {
|
|
52969
|
+
this.pending.set(id, { resolve: resolve9, reject });
|
|
52970
|
+
this.write({ jsonrpc: "2.0", method, id, params });
|
|
52971
|
+
});
|
|
52972
|
+
}
|
|
52973
|
+
notify(method, params) {
|
|
52974
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
52975
|
+
}
|
|
52976
|
+
// server-request 的响应也必须带 jsonrpc:'2.0',否则 codex app-server 忽略它 → 审批/问答永远卡住。
|
|
52977
|
+
respond(id, result) {
|
|
52978
|
+
this.write({ jsonrpc: "2.0", id, result });
|
|
52979
|
+
}
|
|
52980
|
+
onLine(line) {
|
|
52981
|
+
const t2 = line.trim();
|
|
52982
|
+
if (!t2) return;
|
|
52983
|
+
let m3;
|
|
52984
|
+
try {
|
|
52985
|
+
m3 = JSON.parse(t2);
|
|
52986
|
+
} catch {
|
|
52987
|
+
return;
|
|
52988
|
+
}
|
|
52989
|
+
if (!isRecord(m3)) return;
|
|
52990
|
+
const hasId = typeof m3.id === "number", hasMethod = typeof m3.method === "string";
|
|
52991
|
+
if (hasId && !hasMethod) {
|
|
52992
|
+
const p3 = this.pending.get(m3.id);
|
|
52993
|
+
if (!p3) return;
|
|
52994
|
+
this.pending.delete(m3.id);
|
|
52995
|
+
if (m3.error) {
|
|
52996
|
+
const e = isRecord(m3.error) ? m3.error : {};
|
|
52997
|
+
p3.reject(new Error(typeof e.message === "string" ? e.message : "app-server error"));
|
|
52998
|
+
} else p3.resolve(m3.result);
|
|
52999
|
+
return;
|
|
51756
53000
|
}
|
|
51757
53001
|
if (hasId && hasMethod) {
|
|
51758
53002
|
this.opts.onServerRequest?.(m3.id, m3.method, m3.params);
|
|
@@ -51770,49 +53014,49 @@ function isRecord(v3) {
|
|
|
51770
53014
|
|
|
51771
53015
|
// src/tools/codex-app-server-events.ts
|
|
51772
53016
|
function translateNotification(method, params) {
|
|
51773
|
-
const p3 =
|
|
53017
|
+
const p3 = rec3(params);
|
|
51774
53018
|
switch (method) {
|
|
51775
53019
|
case "thread/started": {
|
|
51776
|
-
const id =
|
|
53020
|
+
const id = str3(rec3(p3?.thread)?.id);
|
|
51777
53021
|
return id ? [{ kind: "session_init", toolSessionId: id }] : [];
|
|
51778
53022
|
}
|
|
51779
53023
|
case "item/agentMessage/delta": {
|
|
51780
|
-
const text =
|
|
51781
|
-
const id =
|
|
53024
|
+
const text = str3(p3?.delta);
|
|
53025
|
+
const id = str3(p3?.itemId);
|
|
51782
53026
|
return text ? [{ kind: "text", text, ...id ? { partialId: id } : {} }] : [];
|
|
51783
53027
|
}
|
|
51784
53028
|
case "item/reasoning/textDelta":
|
|
51785
53029
|
case "item/reasoning/summaryTextDelta": {
|
|
51786
|
-
const text =
|
|
51787
|
-
const id =
|
|
53030
|
+
const text = str3(p3?.delta);
|
|
53031
|
+
const id = str3(p3?.itemId);
|
|
51788
53032
|
return text ? [{ kind: "thinking", text, ...id ? { partialId: id } : {} }] : [];
|
|
51789
53033
|
}
|
|
51790
53034
|
case "item/commandExecution/outputDelta":
|
|
51791
53035
|
return [];
|
|
51792
53036
|
// 命令输出增量 v1 不逐字(完成时整段给)
|
|
51793
53037
|
case "item/started":
|
|
51794
|
-
return itemStarted(
|
|
53038
|
+
return itemStarted(rec3(p3?.item));
|
|
51795
53039
|
case "item/completed":
|
|
51796
|
-
return itemCompleted(
|
|
53040
|
+
return itemCompleted(rec3(p3?.item));
|
|
51797
53041
|
case "thread/tokenUsage/updated":
|
|
51798
|
-
return tokenUsage(
|
|
53042
|
+
return tokenUsage(rec3(rec3(p3?.tokenUsage)));
|
|
51799
53043
|
// codex 自动生成的 thread 标题(实测 0.145:payload = { threadId, threadName })
|
|
51800
53044
|
case "thread/name/updated": {
|
|
51801
|
-
const name =
|
|
53045
|
+
const name = str3(p3?.threadName);
|
|
51802
53046
|
return name ? [{ kind: "meta_update", patch: { aiLabel: name } }] : [];
|
|
51803
53047
|
}
|
|
51804
53048
|
case "turn/completed":
|
|
51805
|
-
return turnCompleted(
|
|
53049
|
+
return turnCompleted(rec3(p3?.turn));
|
|
51806
53050
|
default:
|
|
51807
53051
|
return [];
|
|
51808
53052
|
}
|
|
51809
53053
|
}
|
|
51810
53054
|
function itemStarted(item) {
|
|
51811
53055
|
if (!item) return [];
|
|
51812
|
-
const id =
|
|
53056
|
+
const id = str3(item.id);
|
|
51813
53057
|
if (!id) return [];
|
|
51814
53058
|
if (item.type === "commandExecution") {
|
|
51815
|
-
return [{ kind: "tool_call", toolUseId: id, tool: "commandExecution", toolKind: "codex", input: { command:
|
|
53059
|
+
return [{ kind: "tool_call", toolUseId: id, tool: "commandExecution", toolKind: "codex", input: { command: str3(item.command) ?? "" } }];
|
|
51816
53060
|
}
|
|
51817
53061
|
if (item.type === "fileChange") {
|
|
51818
53062
|
return [{ kind: "tool_call", toolUseId: id, tool: "fileChange", toolKind: "codex", input: { changes: Array.isArray(item.changes) ? item.changes : [] } }];
|
|
@@ -51826,28 +53070,28 @@ function itemCompleted(item) {
|
|
|
51826
53070
|
return [];
|
|
51827
53071
|
// 用户输入回显, clawd 自己 synthesize user_text
|
|
51828
53072
|
case "agentMessage": {
|
|
51829
|
-
const text =
|
|
53073
|
+
const text = str3(item.text);
|
|
51830
53074
|
return text ? [{ kind: "text", text }] : [];
|
|
51831
53075
|
}
|
|
51832
53076
|
case "reasoning": {
|
|
51833
|
-
const text =
|
|
53077
|
+
const text = str3(item.text);
|
|
51834
53078
|
return text ? [{ kind: "thinking", text }] : [];
|
|
51835
53079
|
}
|
|
51836
53080
|
case "commandExecution": {
|
|
51837
|
-
const id =
|
|
53081
|
+
const id = str3(item.id);
|
|
51838
53082
|
if (!id) return [];
|
|
51839
|
-
const exit =
|
|
51840
|
-
const ev = { kind: "tool_result", toolUseId: id, output:
|
|
53083
|
+
const exit = num3(item.exitCode);
|
|
53084
|
+
const ev = { kind: "tool_result", toolUseId: id, output: str3(item.aggregatedOutput ?? item.output) ?? "" };
|
|
51841
53085
|
if (exit !== void 0 && exit !== 0) ev.error = `exit ${exit}`;
|
|
51842
53086
|
return [ev];
|
|
51843
53087
|
}
|
|
51844
53088
|
case "fileChange": {
|
|
51845
|
-
const id =
|
|
53089
|
+
const id = str3(item.id);
|
|
51846
53090
|
if (!id) return [];
|
|
51847
53091
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
51848
|
-
const output = changes.map((c2) =>
|
|
53092
|
+
const output = changes.map((c2) => str3(c2.diff) ?? "").filter(Boolean).join("\n");
|
|
51849
53093
|
const ev = { kind: "tool_result", toolUseId: id, output };
|
|
51850
|
-
const status =
|
|
53094
|
+
const status = str3(item.status);
|
|
51851
53095
|
if (status === "failed" || status === "declined") ev.error = `patch ${status}`;
|
|
51852
53096
|
return [ev];
|
|
51853
53097
|
}
|
|
@@ -51857,26 +53101,26 @@ function itemCompleted(item) {
|
|
|
51857
53101
|
}
|
|
51858
53102
|
function tokenUsage(usage) {
|
|
51859
53103
|
if (!usage) return [];
|
|
51860
|
-
const last =
|
|
53104
|
+
const last = rec3(usage.last);
|
|
51861
53105
|
if (!last) return [];
|
|
51862
|
-
const i2 =
|
|
53106
|
+
const i2 = num3(last.inputTokens), o2 = num3(last.outputTokens);
|
|
51863
53107
|
const patch = {};
|
|
51864
53108
|
if (i2 !== void 0 && o2 !== void 0) patch.contextUsage = { inputTokens: i2, outputTokens: o2 };
|
|
51865
|
-
const w3 =
|
|
53109
|
+
const w3 = num3(usage.modelContextWindow);
|
|
51866
53110
|
if (w3 !== void 0) patch.contextWindowSize = w3;
|
|
51867
53111
|
return Object.keys(patch).length ? [{ kind: "meta_update", patch }] : [];
|
|
51868
53112
|
}
|
|
51869
53113
|
function turnCompleted(turn) {
|
|
51870
|
-
if (turn?.status === "failed") return [{ kind: "error", message:
|
|
53114
|
+
if (turn?.status === "failed") return [{ kind: "error", message: str3(rec3(turn.error)?.message) ?? "codex turn failed" }, { kind: "turn_end" }];
|
|
51871
53115
|
return [{ kind: "turn_end" }];
|
|
51872
53116
|
}
|
|
51873
|
-
function
|
|
53117
|
+
function rec3(v3) {
|
|
51874
53118
|
return typeof v3 === "object" && v3 !== null ? v3 : void 0;
|
|
51875
53119
|
}
|
|
51876
|
-
function
|
|
53120
|
+
function str3(v3) {
|
|
51877
53121
|
return typeof v3 === "string" ? v3 : void 0;
|
|
51878
53122
|
}
|
|
51879
|
-
function
|
|
53123
|
+
function num3(v3) {
|
|
51880
53124
|
return typeof v3 === "number" ? v3 : void 0;
|
|
51881
53125
|
}
|
|
51882
53126
|
|
|
@@ -52069,6 +53313,23 @@ function stringValue(value) {
|
|
|
52069
53313
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
52070
53314
|
}
|
|
52071
53315
|
|
|
53316
|
+
// src/tools/first-byte.ts
|
|
53317
|
+
var VISIBLE = /* @__PURE__ */ new Set(["thinking", "tool_call", "text"]);
|
|
53318
|
+
function createFirstByteClock(now = Date.now) {
|
|
53319
|
+
let at3 = null;
|
|
53320
|
+
return {
|
|
53321
|
+
reset() {
|
|
53322
|
+
at3 = null;
|
|
53323
|
+
},
|
|
53324
|
+
see(events) {
|
|
53325
|
+
if (at3 === null && events.some((e) => VISIBLE.has(e.kind))) at3 = now();
|
|
53326
|
+
},
|
|
53327
|
+
afterFirstByteMs() {
|
|
53328
|
+
return at3 === null ? void 0 : Math.max(0, Math.round(now() - at3));
|
|
53329
|
+
}
|
|
53330
|
+
};
|
|
53331
|
+
}
|
|
53332
|
+
|
|
52072
53333
|
// src/tools/codex-app-server-params.ts
|
|
52073
53334
|
function resolveApprovalPolicy(ctx) {
|
|
52074
53335
|
if (ctx.personaMode === "owner" || ctx.personaMode === "guest") return "never";
|
|
@@ -52204,6 +53465,7 @@ function driveCodexThread(opts) {
|
|
|
52204
53465
|
let threadId = ctx.toolSessionId;
|
|
52205
53466
|
let turnId;
|
|
52206
53467
|
let turnOpen = false;
|
|
53468
|
+
const firstByte = createFirstByteClock(opts.now);
|
|
52207
53469
|
const approvalIds = /* @__PURE__ */ new Map();
|
|
52208
53470
|
const subagents = new CodexSubagentTracker();
|
|
52209
53471
|
const effort = ctx.effort;
|
|
@@ -52235,10 +53497,13 @@ function driveCodexThread(opts) {
|
|
|
52235
53497
|
...subagents.handleParentNotification(method, params),
|
|
52236
53498
|
...translateNotification(method, params)
|
|
52237
53499
|
] : [];
|
|
52238
|
-
if (events.length)
|
|
53500
|
+
if (events.length) {
|
|
53501
|
+
if (turnOpen) firstByte.see(events);
|
|
53502
|
+
sink.pushEvents(events);
|
|
53503
|
+
}
|
|
52239
53504
|
if (route === "foreground" && method === "turn/completed") {
|
|
52240
53505
|
turnOpen = false;
|
|
52241
|
-
opts.onTurnCompleted?.(turnOutcomeOf(params));
|
|
53506
|
+
opts.onTurnCompleted?.(turnOutcomeOf(params), firstByte.afterFirstByteMs());
|
|
52242
53507
|
}
|
|
52243
53508
|
},
|
|
52244
53509
|
onServerRequest: (id, method, params) => {
|
|
@@ -52287,6 +53552,7 @@ function driveCodexThread(opts) {
|
|
|
52287
53552
|
const opened = !turnOpen;
|
|
52288
53553
|
if (opened) {
|
|
52289
53554
|
turnOpen = true;
|
|
53555
|
+
firstByte.reset();
|
|
52290
53556
|
opts.onTurnStarting?.();
|
|
52291
53557
|
}
|
|
52292
53558
|
void client.request("turn/start", {
|
|
@@ -52303,7 +53569,7 @@ function driveCodexThread(opts) {
|
|
|
52303
53569
|
]);
|
|
52304
53570
|
if (!opened) return;
|
|
52305
53571
|
turnOpen = false;
|
|
52306
|
-
opts.onTurnCompleted?.("failure");
|
|
53572
|
+
opts.onTurnCompleted?.("failure", firstByte.afterFirstByteMs());
|
|
52307
53573
|
});
|
|
52308
53574
|
}
|
|
52309
53575
|
});
|
|
@@ -52340,17 +53606,17 @@ function stringValue2(value) {
|
|
|
52340
53606
|
}
|
|
52341
53607
|
|
|
52342
53608
|
// src/tools/mcp-server-defs.ts
|
|
52343
|
-
var
|
|
52344
|
-
var
|
|
53609
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
53610
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
52345
53611
|
var HTTP_TYPES = /* @__PURE__ */ new Set(["http", "sse", "streamable_http", "streamable-http"]);
|
|
52346
53612
|
function loadMcpServerDefs(args) {
|
|
52347
53613
|
const out = [];
|
|
52348
53614
|
const seen = /* @__PURE__ */ new Set();
|
|
52349
|
-
const sources = [...args.mcpConfigPaths,
|
|
53615
|
+
const sources = [...args.mcpConfigPaths, import_node_path19.default.join(args.cwd, ".mcp.json")];
|
|
52350
53616
|
for (const file of sources) {
|
|
52351
53617
|
let parsed;
|
|
52352
53618
|
try {
|
|
52353
|
-
parsed = JSON.parse(
|
|
53619
|
+
parsed = JSON.parse(import_node_fs21.default.readFileSync(file, "utf8"));
|
|
52354
53620
|
} catch (e) {
|
|
52355
53621
|
const code = e.code;
|
|
52356
53622
|
if (code !== "ENOENT") {
|
|
@@ -52414,18 +53680,18 @@ function loadMcpServerDefs(args) {
|
|
|
52414
53680
|
}
|
|
52415
53681
|
|
|
52416
53682
|
// src/tools/codex-agents-config.ts
|
|
52417
|
-
var
|
|
52418
|
-
var
|
|
53683
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
53684
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
52419
53685
|
var AGENTS_SECTION = /^\[agents\][^\n]*\n([\s\S]*?)(?=^\[|$(?![\s\S]))/m;
|
|
52420
53686
|
function readKey(section, key) {
|
|
52421
53687
|
const m3 = new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*(["'])(.*?)\\1`, "m").exec(section);
|
|
52422
53688
|
return m3?.[2].trim() || void 0;
|
|
52423
53689
|
}
|
|
52424
53690
|
function loadCodexAgentsConfig(args) {
|
|
52425
|
-
const file =
|
|
53691
|
+
const file = import_node_path20.default.join(args.cwd, ".codex", "config.toml");
|
|
52426
53692
|
let text;
|
|
52427
53693
|
try {
|
|
52428
|
-
text =
|
|
53694
|
+
text = import_node_fs22.default.readFileSync(file, "utf8");
|
|
52429
53695
|
} catch (e) {
|
|
52430
53696
|
const code = e.code;
|
|
52431
53697
|
if (code !== "ENOENT") {
|
|
@@ -52481,7 +53747,7 @@ function warmCodexFeatureCache() {
|
|
|
52481
53747
|
}
|
|
52482
53748
|
|
|
52483
53749
|
// src/attachment/mime.ts
|
|
52484
|
-
var
|
|
53750
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
52485
53751
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
52486
53752
|
var EXT_TO_NATIVE_MIME = {
|
|
52487
53753
|
// 图片
|
|
@@ -52588,7 +53854,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
52588
53854
|
".mk"
|
|
52589
53855
|
]);
|
|
52590
53856
|
function lookupMime(filePathOrName) {
|
|
52591
|
-
const ext =
|
|
53857
|
+
const ext = import_node_path21.default.extname(filePathOrName).toLowerCase();
|
|
52592
53858
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
52593
53859
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
52594
53860
|
return "application/octet-stream";
|
|
@@ -52637,7 +53903,7 @@ function createCodexSession(ctx, sink, deps = {}) {
|
|
|
52637
53903
|
threadParams: threadStartParams(ctx),
|
|
52638
53904
|
// persona 惯例装在 <cwd>/.claude/skills/,经 extraRoots 注入 discovery
|
|
52639
53905
|
// (skills:list RPC 侧 codex-skills.ts 同款注入,两处保持一致)。
|
|
52640
|
-
skillsExtraRoots: [
|
|
53906
|
+
skillsExtraRoots: [import_node_path22.default.join(ctx.cwd, ".claude", "skills")],
|
|
52641
53907
|
// 附件图片:验签认出本机 absPath → localImage,codex 自己读文件(TUI 拖图 / SDK local_image 同一条路)。
|
|
52642
53908
|
// 0.145 起 http(s) URL 在 turn/start 入口被拒,不能再按 url 传(codex-app-server-params.ts 头注释)。
|
|
52643
53909
|
// 与云上 cloudImageInput 同一道 mime 门:非图片(签过的 .md 之类)留文本,别让 codex 读到一半才炸。
|
|
@@ -52791,18 +54057,18 @@ function mergeProviderModels(native, provider) {
|
|
|
52791
54057
|
|
|
52792
54058
|
// src/tools/codex-provider-models.ts
|
|
52793
54059
|
var import_node_child_process6 = require("child_process");
|
|
52794
|
-
var
|
|
52795
|
-
var
|
|
52796
|
-
var
|
|
54060
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
54061
|
+
var import_node_os9 = __toESM(require("os"), 1);
|
|
54062
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
52797
54063
|
var import_node_util = require("util");
|
|
52798
54064
|
var PROVIDER_FETCH_TIMEOUT_MS = 3e3;
|
|
52799
|
-
function
|
|
54065
|
+
function asRecord2(v3) {
|
|
52800
54066
|
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : null;
|
|
52801
54067
|
}
|
|
52802
54068
|
function readAuthKey(codexHome) {
|
|
52803
54069
|
try {
|
|
52804
|
-
const raw = JSON.parse(
|
|
52805
|
-
const key =
|
|
54070
|
+
const raw = JSON.parse(import_node_fs23.default.readFileSync(import_node_path23.default.join(codexHome, "auth.json"), "utf8"));
|
|
54071
|
+
const key = asRecord2(raw)?.OPENAI_API_KEY;
|
|
52806
54072
|
return typeof key === "string" && key ? key : void 0;
|
|
52807
54073
|
} catch {
|
|
52808
54074
|
return void 0;
|
|
@@ -52829,19 +54095,19 @@ async function runAuthCommand(name, auth, deps) {
|
|
|
52829
54095
|
return void 0;
|
|
52830
54096
|
}
|
|
52831
54097
|
async function resolveProviderEndpoint(rawConfig, deps = {}) {
|
|
52832
|
-
const config =
|
|
54098
|
+
const config = asRecord2(asRecord2(rawConfig)?.config);
|
|
52833
54099
|
if (!config) return null;
|
|
52834
54100
|
const name = config.model_provider;
|
|
52835
54101
|
if (typeof name !== "string" || !name) return null;
|
|
52836
|
-
const entry =
|
|
54102
|
+
const entry = asRecord2(asRecord2(config.model_providers)?.[name]);
|
|
52837
54103
|
if (!entry) return null;
|
|
52838
54104
|
const baseUrl = entry.base_url;
|
|
52839
54105
|
if (typeof baseUrl !== "string" || !baseUrl) return null;
|
|
52840
54106
|
const env = deps.env ?? process.env;
|
|
52841
|
-
const codexHome = deps.codexHome ?? env.CODEX_HOME ??
|
|
54107
|
+
const codexHome = deps.codexHome ?? env.CODEX_HOME ?? import_node_path23.default.join(import_node_os9.default.homedir(), ".codex");
|
|
52842
54108
|
const envKey = typeof entry.env_key === "string" && entry.env_key ? entry.env_key : null;
|
|
52843
54109
|
const inlineToken = typeof entry.experimental_bearer_token === "string" && entry.experimental_bearer_token ? entry.experimental_bearer_token : void 0;
|
|
52844
|
-
const auth =
|
|
54110
|
+
const auth = asRecord2(entry.auth);
|
|
52845
54111
|
let token;
|
|
52846
54112
|
if (envKey) token = env[envKey];
|
|
52847
54113
|
else if (inlineToken) token = inlineToken;
|
|
@@ -52869,11 +54135,11 @@ async function fetchProviderModels(endpoint, deps = {}) {
|
|
|
52869
54135
|
try {
|
|
52870
54136
|
const res = await fetchImpl(url, { headers: endpoint.headers, signal: controller.signal });
|
|
52871
54137
|
if (!res.ok) throw new Error(`provider model list failed: HTTP ${res.status} from ${url}`);
|
|
52872
|
-
const data =
|
|
54138
|
+
const data = asRecord2(await res.json())?.data;
|
|
52873
54139
|
if (!Array.isArray(data)) throw new Error(`provider model list: \`data\` is not an array (${url})`);
|
|
52874
54140
|
const out = [];
|
|
52875
54141
|
for (const item of data) {
|
|
52876
|
-
const e =
|
|
54142
|
+
const e = asRecord2(item);
|
|
52877
54143
|
if (!e || typeof e.id !== "string" || !e.id) continue;
|
|
52878
54144
|
out.push({ id: e.id, ...typeof e.owned_by === "string" ? { ownedBy: e.owned_by } : {} });
|
|
52879
54145
|
}
|
|
@@ -52953,10 +54219,10 @@ function sanitizeCodexModel(model) {
|
|
|
52953
54219
|
return model;
|
|
52954
54220
|
}
|
|
52955
54221
|
async function probeCodex(env = process.env) {
|
|
52956
|
-
if (env.CODEX_BIN &&
|
|
54222
|
+
if (env.CODEX_BIN && import_node_fs24.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
|
|
52957
54223
|
try {
|
|
52958
54224
|
const out = (0, import_node_child_process7.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
|
|
52959
|
-
if (out &&
|
|
54225
|
+
if (out && import_node_fs24.default.existsSync(out)) return { available: true, path: out };
|
|
52960
54226
|
} catch {
|
|
52961
54227
|
}
|
|
52962
54228
|
return { available: false };
|
|
@@ -53076,9 +54342,9 @@ var CodexAdapter = class {
|
|
|
53076
54342
|
|
|
53077
54343
|
// src/tools/claude-tui.ts
|
|
53078
54344
|
var import_node_crypto7 = require("crypto");
|
|
53079
|
-
var
|
|
53080
|
-
var
|
|
53081
|
-
var
|
|
54345
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
54346
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
54347
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
53082
54348
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
53083
54349
|
|
|
53084
54350
|
// ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
|
|
@@ -54303,10 +55569,10 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
|
|
|
54303
55569
|
}
|
|
54304
55570
|
function jsonlExistsForCtx(ctx) {
|
|
54305
55571
|
if (!ctx.toolSessionId) return false;
|
|
54306
|
-
const home =
|
|
54307
|
-
const file =
|
|
55572
|
+
const home = import_node_os10.default.homedir();
|
|
55573
|
+
const file = import_node_path24.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
|
|
54308
55574
|
try {
|
|
54309
|
-
return
|
|
55575
|
+
return import_node_fs25.default.statSync(file).isFile();
|
|
54310
55576
|
} catch {
|
|
54311
55577
|
return false;
|
|
54312
55578
|
}
|
|
@@ -54391,7 +55657,7 @@ var PersonaDispatchManager = class {
|
|
|
54391
55657
|
|
|
54392
55658
|
// src/dispatch/store.ts
|
|
54393
55659
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
54394
|
-
var
|
|
55660
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
54395
55661
|
|
|
54396
55662
|
// src/shift/constants.ts
|
|
54397
55663
|
var MAX_RUNS_PER_SHIFT = 30;
|
|
@@ -54429,7 +55695,7 @@ function createDispatchStore(deps) {
|
|
|
54429
55695
|
flushTimer = null;
|
|
54430
55696
|
}
|
|
54431
55697
|
const content = { version: 1, records };
|
|
54432
|
-
await import_promises.default.mkdir(
|
|
55698
|
+
await import_promises.default.mkdir(import_node_path25.default.dirname(deps.filePath), { recursive: true });
|
|
54433
55699
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54434
55700
|
await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54435
55701
|
await import_promises.default.rename(tmp, deps.filePath);
|
|
@@ -54498,7 +55764,7 @@ function createDispatchStore(deps) {
|
|
|
54498
55764
|
|
|
54499
55765
|
// src/channel/json-store.ts
|
|
54500
55766
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
54501
|
-
var
|
|
55767
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
54502
55768
|
function createJsonStore(deps) {
|
|
54503
55769
|
let items = [];
|
|
54504
55770
|
let flushTimer = null;
|
|
@@ -54509,7 +55775,7 @@ function createJsonStore(deps) {
|
|
|
54509
55775
|
flushTimer = null;
|
|
54510
55776
|
}
|
|
54511
55777
|
const content = { version: version2, [deps.key]: items };
|
|
54512
|
-
await import_promises2.default.mkdir(
|
|
55778
|
+
await import_promises2.default.mkdir(import_node_path26.default.dirname(deps.filePath), { recursive: true });
|
|
54513
55779
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54514
55780
|
await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54515
55781
|
await import_promises2.default.rename(tmp, deps.filePath);
|
|
@@ -54801,10 +56067,10 @@ function createTopicStore(deps) {
|
|
|
54801
56067
|
}
|
|
54802
56068
|
|
|
54803
56069
|
// src/dispatch/mcp-config.ts
|
|
54804
|
-
var
|
|
54805
|
-
var
|
|
56070
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
56071
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
54806
56072
|
function dispatchMcpConfigPath(dataDir) {
|
|
54807
|
-
return
|
|
56073
|
+
return import_node_path27.default.join(dataDir, "dispatch.mcp.json");
|
|
54808
56074
|
}
|
|
54809
56075
|
function writeDispatchMcpConfig(args) {
|
|
54810
56076
|
const cfgPath = dispatchMcpConfigPath(args.dataDir);
|
|
@@ -54823,16 +56089,16 @@ function writeDispatchMcpConfig(args) {
|
|
|
54823
56089
|
}
|
|
54824
56090
|
}
|
|
54825
56091
|
};
|
|
54826
|
-
|
|
54827
|
-
|
|
56092
|
+
import_node_fs26.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56093
|
+
import_node_fs26.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54828
56094
|
return cfgPath;
|
|
54829
56095
|
}
|
|
54830
56096
|
|
|
54831
56097
|
// src/ticket/mcp-config.ts
|
|
54832
|
-
var
|
|
54833
|
-
var
|
|
56098
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
56099
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
54834
56100
|
function ticketMcpConfigPath(dataDir) {
|
|
54835
|
-
return
|
|
56101
|
+
return import_node_path28.default.join(dataDir, "ticket.mcp.json");
|
|
54836
56102
|
}
|
|
54837
56103
|
function writeTicketMcpConfig(args) {
|
|
54838
56104
|
const cfgPath = ticketMcpConfigPath(args.dataDir);
|
|
@@ -54854,16 +56120,16 @@ function writeTicketMcpConfig(args) {
|
|
|
54854
56120
|
}
|
|
54855
56121
|
}
|
|
54856
56122
|
};
|
|
54857
|
-
|
|
54858
|
-
|
|
56123
|
+
import_node_fs27.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56124
|
+
import_node_fs27.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54859
56125
|
return cfgPath;
|
|
54860
56126
|
}
|
|
54861
56127
|
|
|
54862
56128
|
// src/shift/mcp-config.ts
|
|
54863
|
-
var
|
|
54864
|
-
var
|
|
56129
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
56130
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
54865
56131
|
function shiftMcpConfigPath(dataDir) {
|
|
54866
|
-
return
|
|
56132
|
+
return import_node_path29.default.join(dataDir, "shift.mcp.json");
|
|
54867
56133
|
}
|
|
54868
56134
|
async function writeShiftMcpConfig(args) {
|
|
54869
56135
|
const cfgPath = shiftMcpConfigPath(args.dataDir);
|
|
@@ -54882,16 +56148,16 @@ async function writeShiftMcpConfig(args) {
|
|
|
54882
56148
|
}
|
|
54883
56149
|
}
|
|
54884
56150
|
};
|
|
54885
|
-
await
|
|
54886
|
-
await
|
|
56151
|
+
await import_node_fs28.default.promises.mkdir(args.dataDir, { recursive: true });
|
|
56152
|
+
await import_node_fs28.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54887
56153
|
return cfgPath;
|
|
54888
56154
|
}
|
|
54889
56155
|
|
|
54890
56156
|
// src/inbox/mcp-config.ts
|
|
54891
|
-
var
|
|
54892
|
-
var
|
|
56157
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
56158
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
54893
56159
|
function inboxMcpConfigPath(dataDir) {
|
|
54894
|
-
return
|
|
56160
|
+
return import_node_path30.default.join(dataDir, "inbox.mcp.json");
|
|
54895
56161
|
}
|
|
54896
56162
|
async function writeInboxMcpConfig(args) {
|
|
54897
56163
|
const cfgPath = inboxMcpConfigPath(args.dataDir);
|
|
@@ -54910,16 +56176,16 @@ async function writeInboxMcpConfig(args) {
|
|
|
54910
56176
|
}
|
|
54911
56177
|
}
|
|
54912
56178
|
};
|
|
54913
|
-
await
|
|
54914
|
-
await
|
|
56179
|
+
await import_node_fs29.default.promises.mkdir(args.dataDir, { recursive: true });
|
|
56180
|
+
await import_node_fs29.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54915
56181
|
return cfgPath;
|
|
54916
56182
|
}
|
|
54917
56183
|
|
|
54918
56184
|
// src/peer-ops/mcp-config.ts
|
|
54919
|
-
var
|
|
54920
|
-
var
|
|
56185
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
56186
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
54921
56187
|
function peerOpsMcpConfigPath(dataDir) {
|
|
54922
|
-
return
|
|
56188
|
+
return import_node_path31.default.join(dataDir, "peer-ops.mcp.json");
|
|
54923
56189
|
}
|
|
54924
56190
|
function writePeerOpsMcpConfig(args) {
|
|
54925
56191
|
const cfgPath = peerOpsMcpConfigPath(args.dataDir);
|
|
@@ -54936,16 +56202,16 @@ function writePeerOpsMcpConfig(args) {
|
|
|
54936
56202
|
}
|
|
54937
56203
|
}
|
|
54938
56204
|
};
|
|
54939
|
-
|
|
54940
|
-
|
|
56205
|
+
import_node_fs30.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56206
|
+
import_node_fs30.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
54941
56207
|
return cfgPath;
|
|
54942
56208
|
}
|
|
54943
56209
|
|
|
54944
56210
|
// src/rpc-tool/mcp-config.ts
|
|
54945
|
-
var
|
|
54946
|
-
var
|
|
56211
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
56212
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
54947
56213
|
function rpcToolMcpConfigPath(dataDir) {
|
|
54948
|
-
return
|
|
56214
|
+
return import_node_path32.default.join(dataDir, "rpc-tool.mcp.json");
|
|
54949
56215
|
}
|
|
54950
56216
|
function writeRpcToolMcpConfig(args) {
|
|
54951
56217
|
const cfgPath = rpcToolMcpConfigPath(args.dataDir);
|
|
@@ -54964,29 +56230,29 @@ function writeRpcToolMcpConfig(args) {
|
|
|
54964
56230
|
}
|
|
54965
56231
|
}
|
|
54966
56232
|
};
|
|
54967
|
-
|
|
54968
|
-
|
|
56233
|
+
import_node_fs31.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56234
|
+
import_node_fs31.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54969
56235
|
return cfgPath;
|
|
54970
56236
|
}
|
|
54971
56237
|
|
|
54972
56238
|
// src/deploy/secret-table.ts
|
|
54973
|
-
var
|
|
54974
|
-
var
|
|
56239
|
+
var import_node_fs32 = __toESM(require("fs"), 1);
|
|
56240
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
54975
56241
|
var import_node_url5 = require("url");
|
|
54976
56242
|
var import_meta5 = {};
|
|
54977
56243
|
var LOCAL_SECRETS_DIRNAME = "secrets";
|
|
54978
56244
|
function findSecretsBundleRoot(logger) {
|
|
54979
56245
|
const candidates = [];
|
|
54980
56246
|
try {
|
|
54981
|
-
const here =
|
|
54982
|
-
candidates.push(
|
|
56247
|
+
const here = import_node_path33.default.dirname((0, import_node_url5.fileURLToPath)(import_meta5.url));
|
|
56248
|
+
candidates.push(import_node_path33.default.resolve(here, "..", "secrets"));
|
|
54983
56249
|
} catch {
|
|
54984
56250
|
}
|
|
54985
56251
|
const fromArgv = bundleSiblingFromArgv(process.argv[1], "secrets");
|
|
54986
56252
|
if (fromArgv) candidates.push(fromArgv);
|
|
54987
56253
|
for (const c2 of candidates) {
|
|
54988
56254
|
try {
|
|
54989
|
-
if (
|
|
56255
|
+
if (import_node_fs32.default.statSync(c2).isDirectory()) return c2;
|
|
54990
56256
|
} catch {
|
|
54991
56257
|
}
|
|
54992
56258
|
}
|
|
@@ -55012,7 +56278,7 @@ function readDir(dir, logger) {
|
|
|
55012
56278
|
const out = {};
|
|
55013
56279
|
let files;
|
|
55014
56280
|
try {
|
|
55015
|
-
files =
|
|
56281
|
+
files = import_node_fs32.default.readdirSync(dir).filter((f2) => f2.endsWith(".env")).sort();
|
|
55016
56282
|
} catch (e) {
|
|
55017
56283
|
const code = e.code;
|
|
55018
56284
|
if (code !== "ENOENT") logger?.warn("secrets.dir-unreadable", { dir, code: code ?? "unknown" });
|
|
@@ -55020,19 +56286,19 @@ function readDir(dir, logger) {
|
|
|
55020
56286
|
}
|
|
55021
56287
|
for (const f2 of files) {
|
|
55022
56288
|
try {
|
|
55023
|
-
Object.assign(out, parseEnvFile(
|
|
56289
|
+
Object.assign(out, parseEnvFile(import_node_fs32.default.readFileSync(import_node_path33.default.join(dir, f2), "utf8")));
|
|
55024
56290
|
} catch (e) {
|
|
55025
|
-
logger?.warn("secrets.file-unreadable", { file:
|
|
56291
|
+
logger?.warn("secrets.file-unreadable", { file: import_node_path33.default.join(dir, f2), error: e.message });
|
|
55026
56292
|
}
|
|
55027
56293
|
}
|
|
55028
56294
|
return out;
|
|
55029
56295
|
}
|
|
55030
56296
|
function upsertEnvFileVar(file, key, value) {
|
|
55031
56297
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55032
|
-
|
|
56298
|
+
import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(file), { recursive: true, mode: 448 });
|
|
55033
56299
|
let lines = [];
|
|
55034
56300
|
try {
|
|
55035
|
-
lines =
|
|
56301
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55036
56302
|
if (lines[lines.length - 1] === "") lines.pop();
|
|
55037
56303
|
} catch {
|
|
55038
56304
|
}
|
|
@@ -55040,12 +56306,12 @@ function upsertEnvFileVar(file, key, value) {
|
|
|
55040
56306
|
let replaced = false;
|
|
55041
56307
|
const out = lines.map((l2) => re2.test(l2) ? (replaced = true, `${key}=${value}`) : l2);
|
|
55042
56308
|
if (!replaced) out.push(`${key}=${value}`);
|
|
55043
|
-
|
|
56309
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55044
56310
|
`, { encoding: "utf8", mode: 384 });
|
|
55045
56311
|
}
|
|
55046
56312
|
function readEnvFileVar(file, key) {
|
|
55047
56313
|
try {
|
|
55048
|
-
return parseEnvFile(
|
|
56314
|
+
return parseEnvFile(import_node_fs32.default.readFileSync(file, "utf8"))[key];
|
|
55049
56315
|
} catch {
|
|
55050
56316
|
return void 0;
|
|
55051
56317
|
}
|
|
@@ -55054,7 +56320,7 @@ function removeEnvFileVar(file, key) {
|
|
|
55054
56320
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55055
56321
|
let lines;
|
|
55056
56322
|
try {
|
|
55057
|
-
lines =
|
|
56323
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55058
56324
|
} catch {
|
|
55059
56325
|
return;
|
|
55060
56326
|
}
|
|
@@ -55063,10 +56329,10 @@ function removeEnvFileVar(file, key) {
|
|
|
55063
56329
|
const out = lines.filter((l2) => !re2.test(l2));
|
|
55064
56330
|
if (out.length === lines.length) return;
|
|
55065
56331
|
if (out.every((l2) => l2.trim() === "" || l2.trim().startsWith("#"))) {
|
|
55066
|
-
|
|
56332
|
+
import_node_fs32.default.rmSync(file, { force: true });
|
|
55067
56333
|
return;
|
|
55068
56334
|
}
|
|
55069
|
-
|
|
56335
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55070
56336
|
`, { encoding: "utf8", mode: 384 });
|
|
55071
56337
|
}
|
|
55072
56338
|
function readSecretTable(args) {
|
|
@@ -55077,12 +56343,12 @@ function readSecretTable(args) {
|
|
|
55077
56343
|
}
|
|
55078
56344
|
|
|
55079
56345
|
// src/dispatch/source-transcript.ts
|
|
55080
|
-
var
|
|
56346
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
55081
56347
|
init_claude_history();
|
|
55082
56348
|
var NO_SOURCE_TRANSCRIPT = "(no transcript yet \u2014 operate from the task description alone)";
|
|
55083
56349
|
function resolveSourceJsonlPath(sourceFile, home) {
|
|
55084
56350
|
if (!sourceFile?.toolSessionId) return NO_SOURCE_TRANSCRIPT;
|
|
55085
|
-
return
|
|
56351
|
+
return import_node_path34.default.join(
|
|
55086
56352
|
home,
|
|
55087
56353
|
".claude",
|
|
55088
56354
|
"projects",
|
|
@@ -55093,7 +56359,7 @@ function resolveSourceJsonlPath(sourceFile, home) {
|
|
|
55093
56359
|
|
|
55094
56360
|
// src/shift/store.ts
|
|
55095
56361
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
55096
|
-
var
|
|
56362
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
55097
56363
|
var import_node_crypto8 = require("crypto");
|
|
55098
56364
|
|
|
55099
56365
|
// src/shift/schedule.ts
|
|
@@ -55168,7 +56434,7 @@ function createShiftStore(deps) {
|
|
|
55168
56434
|
flushTimer = null;
|
|
55169
56435
|
}
|
|
55170
56436
|
const content = { version: 1, shifts };
|
|
55171
|
-
await import_promises3.default.mkdir(
|
|
56437
|
+
await import_promises3.default.mkdir(import_node_path35.default.dirname(deps.filePath), { recursive: true });
|
|
55172
56438
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
55173
56439
|
await import_promises3.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
55174
56440
|
await import_promises3.default.rename(tmp, deps.filePath);
|
|
@@ -55622,14 +56888,14 @@ function redactEvidence(evidence) {
|
|
|
55622
56888
|
|
|
55623
56889
|
// src/deploy/deploy-file.ts
|
|
55624
56890
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
55625
|
-
var
|
|
56891
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
55626
56892
|
var import_node_crypto9 = require("crypto");
|
|
55627
56893
|
init_zod();
|
|
55628
56894
|
init_protocol();
|
|
55629
56895
|
|
|
55630
56896
|
// src/deploy/connectors.ts
|
|
55631
|
-
var
|
|
55632
|
-
var
|
|
56897
|
+
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
56898
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
55633
56899
|
init_zod();
|
|
55634
56900
|
init_protocol();
|
|
55635
56901
|
var PLACEHOLDER2 = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -55679,11 +56945,11 @@ var PACKAGE_RUNNERS = /* @__PURE__ */ new Set(["npx", "uvx", "pnpx", "bunx"]);
|
|
|
55679
56945
|
var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["node", "bun", "deno", "python", "python3"]);
|
|
55680
56946
|
var HOST_ARG_PLACEHOLDER = "CLAWD_PERSONA_DIR";
|
|
55681
56947
|
function scanMcpConnectors(personaDir) {
|
|
55682
|
-
const file =
|
|
56948
|
+
const file = import_node_path36.default.join(personaDir, ".mcp.json");
|
|
55683
56949
|
const out = { readable: true, connectors: [], evidence: [], warnings: [] };
|
|
55684
56950
|
let parsed;
|
|
55685
56951
|
try {
|
|
55686
|
-
parsed = JSON.parse(
|
|
56952
|
+
parsed = JSON.parse(import_node_fs33.default.readFileSync(file, "utf8"));
|
|
55687
56953
|
} catch (e) {
|
|
55688
56954
|
if (e.code === "ENOENT") return out;
|
|
55689
56955
|
out.readable = false;
|
|
@@ -55778,8 +57044,8 @@ function mergeScannedConnectors(existing, scanned) {
|
|
|
55778
57044
|
}
|
|
55779
57045
|
|
|
55780
57046
|
// src/deploy/deploy-file.ts
|
|
55781
|
-
var DEPLOY_FILE =
|
|
55782
|
-
var SCAN_FILE =
|
|
57047
|
+
var DEPLOY_FILE = import_node_path37.default.join(".clawd", "deploy.json");
|
|
57048
|
+
var SCAN_FILE = import_node_path37.default.join(".clawd", "deploy-scan.json");
|
|
55783
57049
|
async function readJson(file) {
|
|
55784
57050
|
let raw;
|
|
55785
57051
|
try {
|
|
@@ -55795,14 +57061,14 @@ async function readJson(file) {
|
|
|
55795
57061
|
}
|
|
55796
57062
|
}
|
|
55797
57063
|
async function writeJson(file, value) {
|
|
55798
|
-
await import_promises4.default.mkdir(
|
|
57064
|
+
await import_promises4.default.mkdir(import_node_path37.default.dirname(file), { recursive: true });
|
|
55799
57065
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
55800
57066
|
await import_promises4.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
|
|
55801
57067
|
`);
|
|
55802
57068
|
await import_promises4.default.rename(tmp, file);
|
|
55803
57069
|
}
|
|
55804
57070
|
async function readDeployFile(dir) {
|
|
55805
|
-
const raw = await readJson(
|
|
57071
|
+
const raw = await readJson(import_node_path37.default.join(dir, DEPLOY_FILE));
|
|
55806
57072
|
if (raw === void 0) return void 0;
|
|
55807
57073
|
const parsed = DeployFileSchema.safeParse(raw);
|
|
55808
57074
|
if (!parsed.success) {
|
|
@@ -55812,18 +57078,18 @@ async function readDeployFile(dir) {
|
|
|
55812
57078
|
return parsed.data;
|
|
55813
57079
|
}
|
|
55814
57080
|
async function writeDeployFile(dir, file) {
|
|
55815
|
-
await writeJson(
|
|
57081
|
+
await writeJson(import_node_path37.default.join(dir, DEPLOY_FILE), DeployFileSchema.parse(file));
|
|
55816
57082
|
}
|
|
55817
57083
|
async function readDeployFileRaw(dir) {
|
|
55818
57084
|
try {
|
|
55819
|
-
return await import_promises4.default.readFile(
|
|
57085
|
+
return await import_promises4.default.readFile(import_node_path37.default.join(dir, DEPLOY_FILE), "utf8");
|
|
55820
57086
|
} catch (err) {
|
|
55821
57087
|
if (err.code === "ENOENT") return void 0;
|
|
55822
57088
|
throw err;
|
|
55823
57089
|
}
|
|
55824
57090
|
}
|
|
55825
57091
|
async function restoreDeployFileRaw(dir, raw) {
|
|
55826
|
-
const file =
|
|
57092
|
+
const file = import_node_path37.default.join(dir, DEPLOY_FILE);
|
|
55827
57093
|
if (raw === void 0) await import_promises4.default.rm(file, { force: true });
|
|
55828
57094
|
else await import_promises4.default.writeFile(file, raw);
|
|
55829
57095
|
}
|
|
@@ -55864,7 +57130,7 @@ function normalizeLegacyScan(raw) {
|
|
|
55864
57130
|
return fix(raw);
|
|
55865
57131
|
}
|
|
55866
57132
|
async function readScanFile(dir) {
|
|
55867
|
-
const raw = normalizeLegacyScan(await readJson(
|
|
57133
|
+
const raw = normalizeLegacyScan(await readJson(import_node_path37.default.join(dir, SCAN_FILE)));
|
|
55868
57134
|
if (raw === void 0) return void 0;
|
|
55869
57135
|
const badShape = (issues) => {
|
|
55870
57136
|
const first = issues[0];
|
|
@@ -55885,7 +57151,7 @@ async function readScanFile(dir) {
|
|
|
55885
57151
|
return parsed.data;
|
|
55886
57152
|
}
|
|
55887
57153
|
async function writeScanFile(dir, file) {
|
|
55888
|
-
await writeJson(
|
|
57154
|
+
await writeJson(import_node_path37.default.join(dir, SCAN_FILE), DeployScanFileSchema.parse(file));
|
|
55889
57155
|
}
|
|
55890
57156
|
function replaceScan(file, entry) {
|
|
55891
57157
|
const idx = file.scans.findIndex((s2) => s2.id === entry.id);
|
|
@@ -55968,9 +57234,9 @@ function requiredEnvNames(manifest) {
|
|
|
55968
57234
|
|
|
55969
57235
|
// src/deploy/artifact.ts
|
|
55970
57236
|
var import_node_crypto10 = require("crypto");
|
|
55971
|
-
var
|
|
55972
|
-
var
|
|
55973
|
-
var
|
|
57237
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
57238
|
+
var import_node_os11 = __toESM(require("os"), 1);
|
|
57239
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
55974
57240
|
var import_node_stream3 = require("stream");
|
|
55975
57241
|
var import_promises5 = require("stream/promises");
|
|
55976
57242
|
var ARTIFACT_MAX_BYTES = 200 * 1024 * 1024;
|
|
@@ -56004,8 +57270,8 @@ function downloadArtifact(exec) {
|
|
|
56004
57270
|
if (!res.ok || !res.body) throw new Error(`\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}\uFF1A${url}`);
|
|
56005
57271
|
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
56006
57272
|
if (declared > ARTIFACT_MAX_BYTES) throw new Error(`\u8D44\u4EA7 ${declared} \u5B57\u8282\uFF0C\u8D85\u8FC7\u4E0A\u9650 ${ARTIFACT_MAX_BYTES}\uFF1A${url}`);
|
|
56007
|
-
const dir = await
|
|
56008
|
-
const file =
|
|
57273
|
+
const dir = await import_node_fs34.default.promises.mkdtemp(import_node_path38.default.join(import_node_os11.default.tmpdir(), "clawd-artifact-"));
|
|
57274
|
+
const file = import_node_path38.default.join(dir, "pkg");
|
|
56009
57275
|
const hash = (0, import_node_crypto10.createHash)("sha256");
|
|
56010
57276
|
let size = 0;
|
|
56011
57277
|
const meter = new import_node_stream3.Transform({
|
|
@@ -56017,7 +57283,7 @@ function downloadArtifact(exec) {
|
|
|
56017
57283
|
}
|
|
56018
57284
|
});
|
|
56019
57285
|
try {
|
|
56020
|
-
await (0, import_promises5.pipeline)(import_node_stream3.Readable.fromWeb(res.body), meter,
|
|
57286
|
+
await (0, import_promises5.pipeline)(import_node_stream3.Readable.fromWeb(res.body), meter, import_node_fs34.default.createWriteStream(file));
|
|
56021
57287
|
let entries;
|
|
56022
57288
|
if (listEntries) {
|
|
56023
57289
|
const r2 = await exec("tar", ["-tzf", file], { timeoutMs: 3e4 });
|
|
@@ -56026,7 +57292,7 @@ function downloadArtifact(exec) {
|
|
|
56026
57292
|
}
|
|
56027
57293
|
return { sha256: hash.digest("hex"), size, ...entries ? { entries } : {} };
|
|
56028
57294
|
} finally {
|
|
56029
|
-
await
|
|
57295
|
+
await import_node_fs34.default.promises.rm(dir, { recursive: true, force: true });
|
|
56030
57296
|
}
|
|
56031
57297
|
};
|
|
56032
57298
|
}
|
|
@@ -56077,7 +57343,7 @@ init_protocol();
|
|
|
56077
57343
|
|
|
56078
57344
|
// src/deploy/repo.ts
|
|
56079
57345
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56080
|
-
var
|
|
57346
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
56081
57347
|
init_protocol();
|
|
56082
57348
|
|
|
56083
57349
|
// ../node_modules/.pnpm/libsodium@0.8.4/node_modules/libsodium/dist/modules-esm/libsodium.mjs
|
|
@@ -60019,7 +61285,7 @@ var runCommand = (cmd, args, opts = {}) => new Promise((resolve9) => {
|
|
|
60019
61285
|
});
|
|
60020
61286
|
|
|
60021
61287
|
// src/deploy/repo.ts
|
|
60022
|
-
var DEPLOY_FILE_REL = DEPLOY_FILE.split(
|
|
61288
|
+
var DEPLOY_FILE_REL = DEPLOY_FILE.split(import_node_path39.default.sep).join("/");
|
|
60023
61289
|
var PERSONA_FILE_REL = ".clawd/persona.json";
|
|
60024
61290
|
var DIRTY_IGNORED = [DEPLOY_FILE_REL, PERSONA_FILE_REL];
|
|
60025
61291
|
var RepoError = class extends Error {
|
|
@@ -60034,6 +61300,7 @@ async function mustGit(exec, dir, args, what, opts) {
|
|
|
60034
61300
|
if (r2.code !== 0) throw new RepoError(`${what}\u6CA1\u6210\uFF08git \u9000\u51FA\u7801 ${r2.code}\uFF09\uFF1A${tail(r2.stderr) || tail(r2.stdout)}`);
|
|
60035
61301
|
return r2.stdout.trim();
|
|
60036
61302
|
}
|
|
61303
|
+
var cloudPersonaIdOf = (originUrl) => personaIdOf(repoSlug(originUrl).split("/").pop() ?? "");
|
|
60037
61304
|
var INLINE_HELPER = '!f() { echo username=x-access-token; echo "password=$CLAWD_GITHUB_TOKEN"; }; f';
|
|
60038
61305
|
async function gitPush(exec, dir, auth) {
|
|
60039
61306
|
if (auth.source === "file") {
|
|
@@ -60058,7 +61325,7 @@ async function samePath(a2, b3) {
|
|
|
60058
61325
|
try {
|
|
60059
61326
|
return await import_promises6.default.realpath(x2);
|
|
60060
61327
|
} catch {
|
|
60061
|
-
return
|
|
61328
|
+
return import_node_path39.default.resolve(x2);
|
|
60062
61329
|
}
|
|
60063
61330
|
};
|
|
60064
61331
|
return await real(a2) === await real(b3);
|
|
@@ -60084,7 +61351,7 @@ async function identityArgs(exec, dir) {
|
|
|
60084
61351
|
return ["-c", "user.name=clawd", "-c", "user.email=clawd@clawos.local"];
|
|
60085
61352
|
}
|
|
60086
61353
|
async function writeGitignore(dir) {
|
|
60087
|
-
const gi =
|
|
61354
|
+
const gi = import_node_path39.default.join(dir, ".gitignore");
|
|
60088
61355
|
let existing;
|
|
60089
61356
|
try {
|
|
60090
61357
|
existing = await import_promises6.default.readFile(gi, "utf8");
|
|
@@ -60095,14 +61362,14 @@ async function writeGitignore(dir) {
|
|
|
60095
61362
|
}
|
|
60096
61363
|
async function writeWorkflow(dir) {
|
|
60097
61364
|
for (const [rel, body] of [[WORKFLOW_PATH, WORKFLOW_TEMPLATE], [UNPUBLISH_WORKFLOW_PATH, UNPUBLISH_WORKFLOW_TEMPLATE]]) {
|
|
60098
|
-
const wf =
|
|
60099
|
-
await import_promises6.default.mkdir(
|
|
61365
|
+
const wf = import_node_path39.default.join(dir, rel);
|
|
61366
|
+
await import_promises6.default.mkdir(import_node_path39.default.dirname(wf), { recursive: true });
|
|
60100
61367
|
await import_promises6.default.writeFile(wf, body);
|
|
60101
61368
|
}
|
|
60102
61369
|
}
|
|
60103
61370
|
async function writeDockerfile(dir, personaId2) {
|
|
60104
|
-
await import_promises6.default.writeFile(
|
|
60105
|
-
await import_promises6.default.writeFile(
|
|
61371
|
+
await import_promises6.default.writeFile(import_node_path39.default.join(dir, DOCKERFILE_PATH), renderDockerfile(personaId2));
|
|
61372
|
+
await import_promises6.default.writeFile(import_node_path39.default.join(dir, DOCKERIGNORE_PATH), DOCKERIGNORE_TEMPLATE);
|
|
60106
61373
|
}
|
|
60107
61374
|
var RepoAlreadyExistsError = class extends RepoError {
|
|
60108
61375
|
constructor(slug) {
|
|
@@ -60139,7 +61406,7 @@ async function ensureRepo(exec, api, args) {
|
|
|
60139
61406
|
const existing = await repoInfo(exec, dir);
|
|
60140
61407
|
if (existing) return { created: false, url: existing.url };
|
|
60141
61408
|
await writeGitignore(dir);
|
|
60142
|
-
await import_promises6.default.rm(
|
|
61409
|
+
await import_promises6.default.rm(import_node_path39.default.join(dir, WORKFLOW_PATH), { force: true });
|
|
60143
61410
|
let created;
|
|
60144
61411
|
try {
|
|
60145
61412
|
created = await createOrgRepo(exec, api, { dir, org, repoName: personaId2, message: "clawd: \u4E0A\u4E91", auth: args.auth });
|
|
@@ -60196,7 +61463,7 @@ async function release(exec, api, args) {
|
|
|
60196
61463
|
await api.enableActions(repoSlug(info.url));
|
|
60197
61464
|
await writeGitignore(args.dir);
|
|
60198
61465
|
await writeWorkflow(args.dir);
|
|
60199
|
-
await writeDockerfile(args.dir,
|
|
61466
|
+
await writeDockerfile(args.dir, cloudPersonaIdOf(info.url));
|
|
60200
61467
|
const identity = await identityArgs(exec, args.dir);
|
|
60201
61468
|
await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
|
|
60202
61469
|
const stamp = new Date(args.now()).toISOString();
|
|
@@ -60679,6 +61946,14 @@ function buildDeployHandlers(deps) {
|
|
|
60679
61946
|
repo2 = await repoInfo(deps.exec, dir);
|
|
60680
61947
|
if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
|
|
60681
61948
|
}
|
|
61949
|
+
const cloudName = cloudPersonaIdOf(repo2.url);
|
|
61950
|
+
let claim;
|
|
61951
|
+
try {
|
|
61952
|
+
claim = await deps.claimPublisher(cloudName);
|
|
61953
|
+
} catch (err) {
|
|
61954
|
+
throw toClawdError(err);
|
|
61955
|
+
}
|
|
61956
|
+
if (claim === "taken") throw invalid(`\u4E91\u4E0A\u7684 ${cloudName} \u767B\u8BB0\u5728\u522B\u4EBA\u540D\u4E0B\uFF0C\u770B\u677F\u53EA\u5F52\u539F\u53D1\u5E03\u8005\uFF1B\u8981\u4E00\u8D77\u7EF4\u62A4\u627E\u7BA1\u7406\u5458`);
|
|
60682
61957
|
if (repo2.branch !== "main") throw invalid(`\u5F53\u524D\u5728\u5206\u652F ${repo2.branch}\uFF0C\u4ED3\u5E93\u91CC\u7684\u6D41\u6C34\u7EBF\u53EA\u8BA4 main\uFF1B\u5207\u56DE main \u518D\u626B\u63CF`);
|
|
60683
61958
|
const live = liveScans.get(personaId2);
|
|
60684
61959
|
if (live && live.sessionId === void 0) return view(personaId2);
|
|
@@ -60856,10 +62131,10 @@ async function probeLocalVersion(exec, pkg) {
|
|
|
60856
62131
|
|
|
60857
62132
|
// src/tools/shell-path.ts
|
|
60858
62133
|
var import_node_child_process10 = require("child_process");
|
|
60859
|
-
var
|
|
62134
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
60860
62135
|
function refreshPathFromLoginShell() {
|
|
60861
62136
|
if (process.platform === "win32") return;
|
|
60862
|
-
const shell = process.env.SHELL ??
|
|
62137
|
+
const shell = process.env.SHELL ?? import_node_os12.default.userInfo().shell ?? "/bin/zsh";
|
|
60863
62138
|
try {
|
|
60864
62139
|
const out = (0, import_node_child_process10.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
|
|
60865
62140
|
encoding: "utf8",
|
|
@@ -61405,15 +62680,15 @@ async function refreshTunnelUrlOnStartup(deps) {
|
|
|
61405
62680
|
}
|
|
61406
62681
|
|
|
61407
62682
|
// src/lark-bot-connector/binding-store.ts
|
|
61408
|
-
var
|
|
61409
|
-
var
|
|
62683
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
62684
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
61410
62685
|
function createLarkBotBindingStore(personaRoot) {
|
|
61411
|
-
const filePath = (personaId2) =>
|
|
62686
|
+
const filePath = (personaId2) => import_node_path40.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
|
|
61412
62687
|
const read = (personaId2) => {
|
|
61413
62688
|
const file = filePath(personaId2);
|
|
61414
|
-
if (!
|
|
62689
|
+
if (!import_node_fs35.default.existsSync(file)) return null;
|
|
61415
62690
|
try {
|
|
61416
|
-
const raw = JSON.parse(
|
|
62691
|
+
const raw = JSON.parse(import_node_fs35.default.readFileSync(file, "utf8"));
|
|
61417
62692
|
if (typeof raw?.appId !== "string" || raw.appId.length === 0) return null;
|
|
61418
62693
|
return raw;
|
|
61419
62694
|
} catch {
|
|
@@ -61422,16 +62697,16 @@ function createLarkBotBindingStore(personaRoot) {
|
|
|
61422
62697
|
};
|
|
61423
62698
|
const write = (personaId2, meta) => {
|
|
61424
62699
|
const file = filePath(personaId2);
|
|
61425
|
-
|
|
62700
|
+
import_node_fs35.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true });
|
|
61426
62701
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
61427
|
-
|
|
61428
|
-
|
|
62702
|
+
import_node_fs35.default.writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
62703
|
+
import_node_fs35.default.renameSync(tmp, file);
|
|
61429
62704
|
};
|
|
61430
62705
|
return {
|
|
61431
62706
|
read,
|
|
61432
62707
|
write,
|
|
61433
62708
|
remove(personaId2) {
|
|
61434
|
-
|
|
62709
|
+
import_node_fs35.default.rmSync(filePath(personaId2), { force: true });
|
|
61435
62710
|
},
|
|
61436
62711
|
addRemovedChat(personaId2, chatId) {
|
|
61437
62712
|
const meta = read(personaId2);
|
|
@@ -61583,8 +62858,8 @@ function createLarkBotCloudClient(opts) {
|
|
|
61583
62858
|
}
|
|
61584
62859
|
|
|
61585
62860
|
// src/lark-bot-connector/chat-router.ts
|
|
61586
|
-
var
|
|
61587
|
-
var
|
|
62861
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
62862
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
61588
62863
|
init_protocol();
|
|
61589
62864
|
var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
|
|
61590
62865
|
var LOCAL_TURN_RETRIES = 2;
|
|
@@ -61662,9 +62937,9 @@ function createLarkChatRouter(deps) {
|
|
|
61662
62937
|
continue;
|
|
61663
62938
|
}
|
|
61664
62939
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61665
|
-
|
|
61666
|
-
const p3 =
|
|
61667
|
-
|
|
62940
|
+
import_node_fs36.default.mkdirSync(dir, { recursive: true });
|
|
62941
|
+
const p3 = import_node_path41.default.join(dir, `${safeMediaName(ownerMessageId)}-${i2}.${ext}`);
|
|
62942
|
+
import_node_fs36.default.writeFileSync(p3, r2.data);
|
|
61668
62943
|
lines.push(`[\u56FE\u7247: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61669
62944
|
} catch (err) {
|
|
61670
62945
|
deps.logger?.warn(`larkBot image fetch failed ${key}: ${err.message}`);
|
|
@@ -61685,9 +62960,9 @@ function createLarkChatRouter(deps) {
|
|
|
61685
62960
|
continue;
|
|
61686
62961
|
}
|
|
61687
62962
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61688
|
-
|
|
61689
|
-
const p3 =
|
|
61690
|
-
|
|
62963
|
+
import_node_fs36.default.mkdirSync(dir, { recursive: true });
|
|
62964
|
+
const p3 = import_node_path41.default.join(dir, `${safeMediaName(ownerMessageId)}-${i2}-${safeAttachmentName(f2.name)}`);
|
|
62965
|
+
import_node_fs36.default.writeFileSync(p3, r2.data);
|
|
61691
62966
|
lines.push(`[\u6587\u4EF6: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61692
62967
|
} catch (err) {
|
|
61693
62968
|
deps.logger?.warn(`larkBot file fetch failed ${f2.key}: ${err.message}`);
|
|
@@ -62335,954 +63610,184 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
62335
63610
|
"personaDispatch:complete": complete,
|
|
62336
63611
|
"personaDispatch:list": list,
|
|
62337
63612
|
"personaDispatch:get": get
|
|
62338
|
-
};
|
|
62339
|
-
}
|
|
62340
|
-
|
|
62341
|
-
// src/dispatch/peer-forward.ts
|
|
62342
|
-
init_protocol();
|
|
62343
|
-
function wsUrlToHttp(url) {
|
|
62344
|
-
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
62345
|
-
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
62346
|
-
return url;
|
|
62347
|
-
}
|
|
62348
|
-
var DISPATCH_POLL_INTERVAL_MS = 5e3;
|
|
62349
|
-
var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
|
|
62350
|
-
var DISPATCH_POLL_TOTAL_MS = 864e5;
|
|
62351
|
-
var defaultSleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
62352
|
-
async function forwardDispatchToPeer(args) {
|
|
62353
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62354
|
-
const sleep2 = args.sleepImpl ?? defaultSleep;
|
|
62355
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62356
|
-
const url = `${base}/rpc/personaDispatch:run`;
|
|
62357
|
-
let res;
|
|
62358
|
-
try {
|
|
62359
|
-
res = await f2(url, {
|
|
62360
|
-
method: "POST",
|
|
62361
|
-
headers: {
|
|
62362
|
-
"content-type": "application/json",
|
|
62363
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62364
|
-
},
|
|
62365
|
-
// 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
|
|
62366
|
-
body: JSON.stringify({
|
|
62367
|
-
targetPersona: args.targetPersona,
|
|
62368
|
-
prompt: args.prompt,
|
|
62369
|
-
// T-20: 条件展开防 undefined 污染 JSON。远端 daemon handler 会再校验一次 model 合法性。
|
|
62370
|
-
...args.model ? { model: args.model } : {}
|
|
62371
|
-
})
|
|
62372
|
-
});
|
|
62373
|
-
} catch (err) {
|
|
62374
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
62375
|
-
return { kind: "failure", reason: `forward to peer failed: ${msg}` };
|
|
62376
|
-
}
|
|
62377
|
-
let json;
|
|
62378
|
-
try {
|
|
62379
|
-
json = await res.json();
|
|
62380
|
-
} catch {
|
|
62381
|
-
return {
|
|
62382
|
-
kind: "failure",
|
|
62383
|
-
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
62384
|
-
};
|
|
62385
|
-
}
|
|
62386
|
-
if (json.ok === false) {
|
|
62387
|
-
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
62388
|
-
}
|
|
62389
|
-
if ("outcome" in json.result) return json.result.outcome;
|
|
62390
|
-
const dispatchId = json.result.dispatchId;
|
|
62391
|
-
let elapsed = 0;
|
|
62392
|
-
let unreachableFor = 0;
|
|
62393
|
-
while (elapsed < DISPATCH_POLL_TOTAL_MS) {
|
|
62394
|
-
await sleep2(DISPATCH_POLL_INTERVAL_MS);
|
|
62395
|
-
elapsed += DISPATCH_POLL_INTERVAL_MS;
|
|
62396
|
-
const markUnreachable = () => {
|
|
62397
|
-
unreachableFor += DISPATCH_POLL_INTERVAL_MS;
|
|
62398
|
-
return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
|
|
62399
|
-
kind: "failure",
|
|
62400
|
-
reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
|
|
62401
|
-
} : null;
|
|
62402
|
-
};
|
|
62403
|
-
let getRes;
|
|
62404
|
-
try {
|
|
62405
|
-
getRes = await f2(`${base}/rpc/personaDispatch:get`, {
|
|
62406
|
-
method: "POST",
|
|
62407
|
-
headers: {
|
|
62408
|
-
"content-type": "application/json",
|
|
62409
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62410
|
-
},
|
|
62411
|
-
body: JSON.stringify({ dispatchId })
|
|
62412
|
-
});
|
|
62413
|
-
} catch {
|
|
62414
|
-
const dead = markUnreachable();
|
|
62415
|
-
if (dead) return dead;
|
|
62416
|
-
continue;
|
|
62417
|
-
}
|
|
62418
|
-
let getJson;
|
|
62419
|
-
try {
|
|
62420
|
-
getJson = await getRes.json();
|
|
62421
|
-
} catch {
|
|
62422
|
-
const dead = markUnreachable();
|
|
62423
|
-
if (dead) return dead;
|
|
62424
|
-
continue;
|
|
62425
|
-
}
|
|
62426
|
-
if (getJson.ok === false) {
|
|
62427
|
-
if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
|
|
62428
|
-
return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
|
|
62429
|
-
}
|
|
62430
|
-
const dead = markUnreachable();
|
|
62431
|
-
if (dead) return dead;
|
|
62432
|
-
continue;
|
|
62433
|
-
}
|
|
62434
|
-
unreachableFor = 0;
|
|
62435
|
-
const { status, outcome } = getJson.result;
|
|
62436
|
-
if (status === "completed" || status === "failed") {
|
|
62437
|
-
return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
|
|
62438
|
-
}
|
|
62439
|
-
}
|
|
62440
|
-
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
62441
|
-
}
|
|
62442
|
-
async function forwardInboxPostToPeer(args) {
|
|
62443
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62444
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62445
|
-
const url = `${base}/rpc/inbox:postMessage`;
|
|
62446
|
-
let res;
|
|
62447
|
-
try {
|
|
62448
|
-
res = await f2(url, {
|
|
62449
|
-
method: "POST",
|
|
62450
|
-
headers: {
|
|
62451
|
-
"content-type": "application/json",
|
|
62452
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62453
|
-
},
|
|
62454
|
-
body: JSON.stringify({
|
|
62455
|
-
id: args.id,
|
|
62456
|
-
text: args.text,
|
|
62457
|
-
createdAt: args.createdAt,
|
|
62458
|
-
...args.origin ? { origin: args.origin } : {}
|
|
62459
|
-
})
|
|
62460
|
-
});
|
|
62461
|
-
} catch (err) {
|
|
62462
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
62463
|
-
}
|
|
62464
|
-
let json = null;
|
|
62465
|
-
try {
|
|
62466
|
-
json = await res.json();
|
|
62467
|
-
} catch {
|
|
62468
|
-
return { ok: false, error: `peer non-JSON (HTTP ${res.status})` };
|
|
62469
|
-
}
|
|
62470
|
-
if (res.status < 200 || res.status >= 300 || json?.ok === false) {
|
|
62471
|
-
return { ok: false, error: json?.error ?? `HTTP ${res.status}` };
|
|
62472
|
-
}
|
|
62473
|
-
return { ok: true };
|
|
62474
|
-
}
|
|
62475
|
-
async function forwardPeerExecToPeer(args) {
|
|
62476
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62477
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62478
|
-
const url = `${base}/rpc/peerExec:run`;
|
|
62479
|
-
const tunnelFail = (msg) => ({
|
|
62480
|
-
type: "peerExec:run:ok",
|
|
62481
|
-
stdout: "",
|
|
62482
|
-
stderr: msg,
|
|
62483
|
-
exitCode: null,
|
|
62484
|
-
timedOut: false,
|
|
62485
|
-
stdoutTruncated: false,
|
|
62486
|
-
stderrTruncated: false
|
|
62487
|
-
});
|
|
62488
|
-
let res;
|
|
62489
|
-
try {
|
|
62490
|
-
res = await f2(url, {
|
|
62491
|
-
method: "POST",
|
|
62492
|
-
headers: {
|
|
62493
|
-
"content-type": "application/json",
|
|
62494
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62495
|
-
},
|
|
62496
|
-
body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
|
|
62497
|
-
});
|
|
62498
|
-
} catch (err) {
|
|
62499
|
-
return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
62500
|
-
}
|
|
62501
|
-
let json;
|
|
62502
|
-
try {
|
|
62503
|
-
json = await res.json();
|
|
62504
|
-
} catch {
|
|
62505
|
-
return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
|
|
62506
|
-
}
|
|
62507
|
-
if (json.ok === false) {
|
|
62508
|
-
throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
|
|
62509
|
-
}
|
|
62510
|
-
return json.result;
|
|
62511
|
-
}
|
|
62512
|
-
|
|
62513
|
-
// src/tools/codex-history.ts
|
|
62514
|
-
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
62515
|
-
var import_node_path40 = __toESM(require("path"), 1);
|
|
62516
|
-
|
|
62517
|
-
// src/tools/transcript-search.ts
|
|
62518
|
-
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
62519
|
-
var import_node_path39 = __toESM(require("path"), 1);
|
|
62520
|
-
var SNIPPET_PAD = 60;
|
|
62521
|
-
var INJECTED_ENVELOPE_RE = /^<(system-reminder|command-name|command-message|command-args|local-command-stdout|local-command-stderr|task-notification|user-prompt-submit-hook|environment_context|user_instructions)\b/;
|
|
62522
|
-
var CODEX_WRAPPER_RE = /<(related-dir|environment_context|user_instructions)>[\s\S]*?<\/\1>/g;
|
|
62523
|
-
function asRecord2(v3) {
|
|
62524
|
-
return typeof v3 === "object" && v3 !== null ? v3 : null;
|
|
62525
|
-
}
|
|
62526
|
-
function textOfBlocks(content, types) {
|
|
62527
|
-
if (!Array.isArray(content)) return null;
|
|
62528
|
-
const parts = [];
|
|
62529
|
-
for (const block of content) {
|
|
62530
|
-
const b3 = asRecord2(block);
|
|
62531
|
-
if (!b3 || typeof b3.type !== "string" || !types.includes(b3.type)) continue;
|
|
62532
|
-
const text = b3.text;
|
|
62533
|
-
if (typeof text === "string" && text !== "") parts.push(text);
|
|
62534
|
-
}
|
|
62535
|
-
return parts.length > 0 ? parts.join("\n") : null;
|
|
62536
|
-
}
|
|
62537
|
-
function lineToSearchableText(raw) {
|
|
62538
|
-
const line = asRecord2(raw);
|
|
62539
|
-
if (!line) return null;
|
|
62540
|
-
if (line.type === "response_item") {
|
|
62541
|
-
const payload = asRecord2(line.payload);
|
|
62542
|
-
if (!payload || payload.type !== "message") return null;
|
|
62543
|
-
const role = payload.role;
|
|
62544
|
-
if (role !== "user" && role !== "assistant") return null;
|
|
62545
|
-
const text2 = textOfBlocks(payload.content, ["input_text", "output_text"]);
|
|
62546
|
-
if (text2 === null) return null;
|
|
62547
|
-
if (role !== "user") return text2;
|
|
62548
|
-
const stripped = text2.replace(CODEX_WRAPPER_RE, "").trim();
|
|
62549
|
-
if (stripped === "") return null;
|
|
62550
|
-
return INJECTED_ENVELOPE_RE.test(stripped) ? null : stripped;
|
|
62551
|
-
}
|
|
62552
|
-
if (line.type !== "user" && line.type !== "assistant") return null;
|
|
62553
|
-
const message = asRecord2(line.message);
|
|
62554
|
-
if (!message) return null;
|
|
62555
|
-
const content = message.content;
|
|
62556
|
-
const text = typeof content === "string" && content !== "" ? content : textOfBlocks(content, ["text"]);
|
|
62557
|
-
if (text === null) return null;
|
|
62558
|
-
if (line.type === "user" && (line.isMeta === true || line.isSynthetic === true)) return null;
|
|
62559
|
-
if (line.type === "user" && INJECTED_ENVELOPE_RE.test(text.trimStart())) return null;
|
|
62560
|
-
return text;
|
|
62561
|
-
}
|
|
62562
|
-
function escapeRegExp(s2) {
|
|
62563
|
-
return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
62564
|
-
}
|
|
62565
|
-
function makeSnippet(text, at3, needleLength) {
|
|
62566
|
-
const from = Math.max(0, at3 - SNIPPET_PAD);
|
|
62567
|
-
const to = Math.min(text.length, at3 + needleLength + SNIPPET_PAD);
|
|
62568
|
-
const body = text.slice(from, to).replace(/\s+/g, " ").trim();
|
|
62569
|
-
return `${from > 0 ? "\u2026 " : ""}${body}${to < text.length ? " \u2026" : ""}`;
|
|
62570
|
-
}
|
|
62571
|
-
function searchTranscriptFile(file, query) {
|
|
62572
|
-
let buf;
|
|
62573
|
-
try {
|
|
62574
|
-
buf = import_node_fs34.default.readFileSync(file);
|
|
62575
|
-
} catch {
|
|
62576
|
-
return null;
|
|
62577
|
-
}
|
|
62578
|
-
const haystack = buf.toString("latin1");
|
|
62579
|
-
const needle = Buffer.from(query, "utf8").toString("latin1");
|
|
62580
|
-
const re2 = new RegExp(escapeRegExp(needle), "gi");
|
|
62581
|
-
let m3;
|
|
62582
|
-
while ((m3 = re2.exec(haystack)) !== null) {
|
|
62583
|
-
const lineStart = haystack.lastIndexOf("\n", m3.index) + 1;
|
|
62584
|
-
let lineEnd = haystack.indexOf("\n", m3.index);
|
|
62585
|
-
if (lineEnd < 0) lineEnd = haystack.length;
|
|
62586
|
-
re2.lastIndex = lineEnd + 1;
|
|
62587
|
-
let parsed;
|
|
62588
|
-
try {
|
|
62589
|
-
parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
|
|
62590
|
-
} catch {
|
|
62591
|
-
continue;
|
|
62592
|
-
}
|
|
62593
|
-
const text = lineToSearchableText(parsed);
|
|
62594
|
-
if (text === null) continue;
|
|
62595
|
-
const at3 = text.toLowerCase().indexOf(query.toLowerCase());
|
|
62596
|
-
if (at3 < 0) continue;
|
|
62597
|
-
return makeSnippet(text, at3, query.length);
|
|
62598
|
-
}
|
|
62599
|
-
return null;
|
|
62600
|
-
}
|
|
62601
|
-
var ROLLOUT_NAME_RE = /-([0-9a-fA-F-]{36})\.jsonl$/;
|
|
62602
|
-
function createCodexRolloutLocator(roots) {
|
|
62603
|
-
let index = null;
|
|
62604
|
-
const walk2 = (dir, into) => {
|
|
62605
|
-
let entries;
|
|
62606
|
-
try {
|
|
62607
|
-
entries = import_node_fs34.default.readdirSync(dir, { withFileTypes: true });
|
|
62608
|
-
} catch {
|
|
62609
|
-
return;
|
|
62610
|
-
}
|
|
62611
|
-
for (const entry of entries) {
|
|
62612
|
-
const full = import_node_path39.default.join(dir, entry.name);
|
|
62613
|
-
if (entry.isDirectory()) {
|
|
62614
|
-
walk2(full, into);
|
|
62615
|
-
continue;
|
|
62616
|
-
}
|
|
62617
|
-
const matched = ROLLOUT_NAME_RE.exec(entry.name);
|
|
62618
|
-
if (matched) into.set(matched[1].toLowerCase(), full);
|
|
62619
|
-
}
|
|
62620
|
-
};
|
|
62621
|
-
return (toolSessionId) => {
|
|
62622
|
-
if (index === null) {
|
|
62623
|
-
index = /* @__PURE__ */ new Map();
|
|
62624
|
-
for (const root of roots) walk2(root, index);
|
|
62625
|
-
}
|
|
62626
|
-
return index.get(toolSessionId.toLowerCase()) ?? null;
|
|
62627
|
-
};
|
|
62628
|
-
}
|
|
62629
|
-
|
|
62630
|
-
// src/tools/codex-rollout.ts
|
|
62631
|
-
function rec2(v3) {
|
|
62632
|
-
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : void 0;
|
|
62633
|
-
}
|
|
62634
|
-
function str2(v3) {
|
|
62635
|
-
return typeof v3 === "string" ? v3 : void 0;
|
|
62636
|
-
}
|
|
62637
|
-
function num2(v3) {
|
|
62638
|
-
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
62639
|
-
}
|
|
62640
|
-
function parseLine(line) {
|
|
62641
|
-
const t2 = line.trim();
|
|
62642
|
-
if (!t2) return void 0;
|
|
62643
|
-
try {
|
|
62644
|
-
return rec2(JSON.parse(t2));
|
|
62645
|
-
} catch {
|
|
62646
|
-
return void 0;
|
|
62647
|
-
}
|
|
62648
|
-
}
|
|
62649
|
-
function metaFrom(obj) {
|
|
62650
|
-
if (obj.type !== "session_meta") return null;
|
|
62651
|
-
const p3 = rec2(obj.payload);
|
|
62652
|
-
const threadId = str2(p3?.id) ?? str2(p3?.session_id);
|
|
62653
|
-
if (!p3 || !threadId) return null;
|
|
62654
|
-
const meta = { threadId };
|
|
62655
|
-
const cwd = str2(p3.cwd);
|
|
62656
|
-
if (cwd) meta.cwd = cwd;
|
|
62657
|
-
const createdAt = str2(p3.timestamp) ?? str2(obj.timestamp);
|
|
62658
|
-
if (createdAt) meta.createdAt = createdAt;
|
|
62659
|
-
const parent = str2(p3.parent_thread_id);
|
|
62660
|
-
if (parent) meta.parentThreadId = parent;
|
|
62661
|
-
const cli = str2(p3.cli_version);
|
|
62662
|
-
if (cli) meta.cliVersion = cli;
|
|
62663
|
-
return meta;
|
|
62664
|
-
}
|
|
62665
|
-
function parseCodexRolloutMeta(firstLine) {
|
|
62666
|
-
const obj = parseLine(firstLine);
|
|
62667
|
-
return obj ? metaFrom(obj) : null;
|
|
62668
|
-
}
|
|
62669
|
-
function firstUserMessageText(text) {
|
|
62670
|
-
for (const line of text.split("\n")) {
|
|
62671
|
-
const obj = parseLine(line);
|
|
62672
|
-
if (!obj || obj.type !== "event_msg") continue;
|
|
62673
|
-
const p3 = rec2(obj.payload);
|
|
62674
|
-
if (p3?.type === "user_message") return str2(p3.message);
|
|
62675
|
-
}
|
|
62676
|
-
return void 0;
|
|
62677
|
-
}
|
|
62678
|
-
function outputText(output) {
|
|
62679
|
-
if (typeof output === "string") return output;
|
|
62680
|
-
if (Array.isArray(output)) {
|
|
62681
|
-
return output.map((part) => typeof part === "string" ? part : str2(rec2(part)?.text) ?? "").join("");
|
|
62682
|
-
}
|
|
62683
|
-
return "";
|
|
62684
|
-
}
|
|
62685
|
-
function functionCallInput(name, rawArgs) {
|
|
62686
|
-
let args = rawArgs;
|
|
62687
|
-
if (typeof rawArgs === "string") {
|
|
62688
|
-
try {
|
|
62689
|
-
args = JSON.parse(rawArgs);
|
|
62690
|
-
} catch {
|
|
62691
|
-
args = { arguments: rawArgs };
|
|
62692
|
-
}
|
|
62693
|
-
}
|
|
62694
|
-
const a2 = rec2(args) ?? {};
|
|
62695
|
-
if (name === "exec_command" || name === "shell" || name === "container.exec") {
|
|
62696
|
-
const cmd = a2.cmd ?? a2.command;
|
|
62697
|
-
const command = Array.isArray(cmd) ? cmd.map(String).join(" ") : str2(cmd) ?? "";
|
|
62698
|
-
return { tool: "commandExecution", input: { command } };
|
|
62699
|
-
}
|
|
62700
|
-
return { tool: name, input: args };
|
|
62701
|
-
}
|
|
62702
|
-
function parseCodexRollout(text) {
|
|
62703
|
-
const out = { meta: null, messages: [], subagents: [], turns: [] };
|
|
62704
|
-
const collabCallIds = /* @__PURE__ */ new Set();
|
|
62705
|
-
for (const line of text.split("\n")) {
|
|
62706
|
-
const obj = parseLine(line);
|
|
62707
|
-
if (!obj) continue;
|
|
62708
|
-
const ts = str2(obj.timestamp);
|
|
62709
|
-
const p3 = rec2(obj.payload);
|
|
62710
|
-
if (!p3) continue;
|
|
62711
|
-
if (obj.type === "session_meta") {
|
|
62712
|
-
if (!out.meta) out.meta = metaFrom(obj);
|
|
62713
|
-
continue;
|
|
62714
|
-
}
|
|
62715
|
-
if (obj.type === "event_msg") {
|
|
62716
|
-
switch (p3.type) {
|
|
62717
|
-
case "user_message": {
|
|
62718
|
-
const t2 = str2(p3.message);
|
|
62719
|
-
if (t2) out.messages.push({ kind: "user-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
62720
|
-
break;
|
|
62721
|
-
}
|
|
62722
|
-
case "agent_message": {
|
|
62723
|
-
const t2 = str2(p3.message);
|
|
62724
|
-
if (t2) out.messages.push({ kind: "assistant-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
62725
|
-
break;
|
|
62726
|
-
}
|
|
62727
|
-
case "sub_agent_activity": {
|
|
62728
|
-
if (p3.kind !== "started") break;
|
|
62729
|
-
const toolUseId = str2(p3.event_id);
|
|
62730
|
-
const agentId = str2(p3.agent_thread_id);
|
|
62731
|
-
const agentPath = str2(p3.agent_path);
|
|
62732
|
-
if (!toolUseId || !agentId || !agentPath) break;
|
|
62733
|
-
const segments = agentPath.split("/").filter(Boolean);
|
|
62734
|
-
const description = segments[segments.length - 1] ?? agentId;
|
|
62735
|
-
out.subagents.push({ toolUseId, agentId, agentPath, description });
|
|
62736
|
-
out.messages.push({
|
|
62737
|
-
kind: "tool_call",
|
|
62738
|
-
tool: "Agent",
|
|
62739
|
-
toolUseId,
|
|
62740
|
-
input: { agentId, description, agentPath },
|
|
62741
|
-
raw: obj,
|
|
62742
|
-
...ts ? { ts } : {}
|
|
62743
|
-
});
|
|
62744
|
-
break;
|
|
62745
|
-
}
|
|
62746
|
-
case "task_complete":
|
|
62747
|
-
case "turn_aborted": {
|
|
62748
|
-
const durationMs = num2(p3.duration_ms);
|
|
62749
|
-
const turnId = str2(p3.turn_id);
|
|
62750
|
-
const reason = str2(p3.reason);
|
|
62751
|
-
out.turns.push({
|
|
62752
|
-
status: p3.type === "task_complete" ? "completed" : "interrupted",
|
|
62753
|
-
...turnId ? { turnId } : {},
|
|
62754
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
62755
|
-
...reason ? { reason } : {}
|
|
62756
|
-
});
|
|
62757
|
-
out.messages.push({
|
|
62758
|
-
kind: "turn-end",
|
|
62759
|
-
raw: obj,
|
|
62760
|
-
...ts ? { ts } : {},
|
|
62761
|
-
...durationMs !== void 0 ? { durationMs } : {}
|
|
62762
|
-
});
|
|
62763
|
-
break;
|
|
62764
|
-
}
|
|
62765
|
-
default:
|
|
62766
|
-
break;
|
|
62767
|
-
}
|
|
62768
|
-
continue;
|
|
62769
|
-
}
|
|
62770
|
-
if (obj.type === "response_item") {
|
|
62771
|
-
switch (p3.type) {
|
|
62772
|
-
case "function_call": {
|
|
62773
|
-
const callId = str2(p3.call_id);
|
|
62774
|
-
const name = str2(p3.name);
|
|
62775
|
-
if (!callId || !name) break;
|
|
62776
|
-
if (p3.namespace === "collaboration") {
|
|
62777
|
-
collabCallIds.add(callId);
|
|
62778
|
-
break;
|
|
62779
|
-
}
|
|
62780
|
-
const { tool, input } = functionCallInput(name, p3.arguments);
|
|
62781
|
-
out.messages.push({ kind: "tool_call", tool, toolUseId: callId, input, raw: obj, ...ts ? { ts } : {} });
|
|
62782
|
-
break;
|
|
62783
|
-
}
|
|
62784
|
-
case "custom_tool_call": {
|
|
62785
|
-
const callId = str2(p3.call_id);
|
|
62786
|
-
const name = str2(p3.name);
|
|
62787
|
-
if (!callId || !name) break;
|
|
62788
|
-
out.messages.push({
|
|
62789
|
-
kind: "tool_call",
|
|
62790
|
-
tool: name,
|
|
62791
|
-
toolUseId: callId,
|
|
62792
|
-
input: { input: str2(p3.input) ?? "" },
|
|
62793
|
-
raw: obj,
|
|
62794
|
-
...ts ? { ts } : {}
|
|
62795
|
-
});
|
|
62796
|
-
break;
|
|
62797
|
-
}
|
|
62798
|
-
case "function_call_output":
|
|
62799
|
-
case "custom_tool_call_output": {
|
|
62800
|
-
const callId = str2(p3.call_id);
|
|
62801
|
-
if (!callId || collabCallIds.has(callId)) break;
|
|
62802
|
-
out.messages.push({
|
|
62803
|
-
kind: "tool_result",
|
|
62804
|
-
toolUseId: callId,
|
|
62805
|
-
output: outputText(p3.output),
|
|
62806
|
-
raw: obj,
|
|
62807
|
-
...ts ? { ts } : {}
|
|
62808
|
-
});
|
|
62809
|
-
break;
|
|
62810
|
-
}
|
|
62811
|
-
default:
|
|
62812
|
-
break;
|
|
62813
|
-
}
|
|
62814
|
-
continue;
|
|
62815
|
-
}
|
|
62816
|
-
}
|
|
62817
|
-
return out;
|
|
62818
|
-
}
|
|
62819
|
-
function subagentTerminalResult(started, child) {
|
|
62820
|
-
const first = child.turns[0];
|
|
62821
|
-
if (!first) return void 0;
|
|
62822
|
-
if (first.status === "completed") return { kind: "tool_result", toolUseId: started.toolUseId };
|
|
62823
|
-
return { kind: "tool_result", toolUseId: started.toolUseId, error: first.reason ?? "Codex subagent interrupted" };
|
|
62824
|
-
}
|
|
62825
|
-
|
|
62826
|
-
// src/tools/codex-history.ts
|
|
62827
|
-
var CODEX_LIST_SESSIONS_LIMIT = 100;
|
|
62828
|
-
var CODEX_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
62829
|
-
var PEEK_BYTES = 8 * 1024;
|
|
62830
|
-
var TITLE_BYTES = 256 * 1024;
|
|
62831
|
-
function readHead(file, bytes) {
|
|
62832
|
-
let fd;
|
|
62833
|
-
try {
|
|
62834
|
-
fd = import_node_fs35.default.openSync(file, "r");
|
|
62835
|
-
} catch {
|
|
62836
|
-
return null;
|
|
62837
|
-
}
|
|
62838
|
-
try {
|
|
62839
|
-
const buf = Buffer.alloc(bytes);
|
|
62840
|
-
const n2 = import_node_fs35.default.readSync(fd, buf, 0, bytes, 0);
|
|
62841
|
-
return buf.subarray(0, n2).toString("utf8");
|
|
62842
|
-
} catch {
|
|
62843
|
-
return null;
|
|
62844
|
-
} finally {
|
|
62845
|
-
import_node_fs35.default.closeSync(fd);
|
|
62846
|
-
}
|
|
62847
|
-
}
|
|
62848
|
-
function peekSessionCwd(head) {
|
|
62849
|
-
if (!head.includes('"session_meta"')) return null;
|
|
62850
|
-
const m3 = /"cwd":"((?:[^"\\]|\\.)*)"/.exec(head);
|
|
62851
|
-
if (!m3) return null;
|
|
62852
|
-
try {
|
|
62853
|
-
return JSON.parse(`"${m3[1]}"`);
|
|
62854
|
-
} catch {
|
|
62855
|
-
return null;
|
|
62856
|
-
}
|
|
63613
|
+
};
|
|
62857
63614
|
}
|
|
62858
|
-
|
|
62859
|
-
|
|
63615
|
+
|
|
63616
|
+
// src/dispatch/peer-forward.ts
|
|
63617
|
+
init_protocol();
|
|
63618
|
+
function wsUrlToHttp(url) {
|
|
63619
|
+
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
63620
|
+
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
63621
|
+
return url;
|
|
63622
|
+
}
|
|
63623
|
+
var DISPATCH_POLL_INTERVAL_MS = 5e3;
|
|
63624
|
+
var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
|
|
63625
|
+
var DISPATCH_POLL_TOTAL_MS = 864e5;
|
|
63626
|
+
var defaultSleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
63627
|
+
async function forwardDispatchToPeer(args) {
|
|
63628
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63629
|
+
const sleep2 = args.sleepImpl ?? defaultSleep;
|
|
63630
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63631
|
+
const url = `${base}/rpc/personaDispatch:run`;
|
|
63632
|
+
let res;
|
|
62860
63633
|
try {
|
|
62861
|
-
|
|
62862
|
-
|
|
62863
|
-
|
|
63634
|
+
res = await f2(url, {
|
|
63635
|
+
method: "POST",
|
|
63636
|
+
headers: {
|
|
63637
|
+
"content-type": "application/json",
|
|
63638
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63639
|
+
},
|
|
63640
|
+
// 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
|
|
63641
|
+
body: JSON.stringify({
|
|
63642
|
+
targetPersona: args.targetPersona,
|
|
63643
|
+
prompt: args.prompt,
|
|
63644
|
+
// T-20: 条件展开防 undefined 污染 JSON。远端 daemon handler 会再校验一次 model 合法性。
|
|
63645
|
+
...args.model ? { model: args.model } : {}
|
|
63646
|
+
})
|
|
63647
|
+
});
|
|
63648
|
+
} catch (err) {
|
|
63649
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63650
|
+
return { kind: "failure", reason: `forward to peer failed: ${msg}` };
|
|
62864
63651
|
}
|
|
63652
|
+
let json;
|
|
62865
63653
|
try {
|
|
62866
|
-
|
|
62867
|
-
const parts = [];
|
|
62868
|
-
let total = 0;
|
|
62869
|
-
while (total < max) {
|
|
62870
|
-
const n2 = import_node_fs35.default.readSync(fd, chunk, 0, chunk.length, total);
|
|
62871
|
-
if (n2 <= 0) break;
|
|
62872
|
-
const nl = chunk.subarray(0, n2).indexOf(10);
|
|
62873
|
-
if (nl >= 0) {
|
|
62874
|
-
parts.push(Buffer.from(chunk.subarray(0, nl)));
|
|
62875
|
-
return Buffer.concat(parts).toString("utf8");
|
|
62876
|
-
}
|
|
62877
|
-
parts.push(Buffer.from(chunk.subarray(0, n2)));
|
|
62878
|
-
total += n2;
|
|
62879
|
-
}
|
|
62880
|
-
return total < max && parts.length > 0 ? Buffer.concat(parts).toString("utf8") : null;
|
|
63654
|
+
json = await res.json();
|
|
62881
63655
|
} catch {
|
|
62882
|
-
return
|
|
62883
|
-
|
|
62884
|
-
|
|
63656
|
+
return {
|
|
63657
|
+
kind: "failure",
|
|
63658
|
+
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
63659
|
+
};
|
|
62885
63660
|
}
|
|
62886
|
-
|
|
62887
|
-
|
|
62888
|
-
const names = /* @__PURE__ */ new Map();
|
|
62889
|
-
if (!sharedHome) return names;
|
|
62890
|
-
let raw;
|
|
62891
|
-
try {
|
|
62892
|
-
raw = import_node_fs35.default.readFileSync(import_node_path40.default.join(sharedHome, "session_index.jsonl"), "utf8");
|
|
62893
|
-
} catch {
|
|
62894
|
-
return names;
|
|
63661
|
+
if (json.ok === false) {
|
|
63662
|
+
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
62895
63663
|
}
|
|
62896
|
-
|
|
62897
|
-
|
|
63664
|
+
if ("outcome" in json.result) return json.result.outcome;
|
|
63665
|
+
const dispatchId = json.result.dispatchId;
|
|
63666
|
+
let elapsed = 0;
|
|
63667
|
+
let unreachableFor = 0;
|
|
63668
|
+
while (elapsed < DISPATCH_POLL_TOTAL_MS) {
|
|
63669
|
+
await sleep2(DISPATCH_POLL_INTERVAL_MS);
|
|
63670
|
+
elapsed += DISPATCH_POLL_INTERVAL_MS;
|
|
63671
|
+
const markUnreachable = () => {
|
|
63672
|
+
unreachableFor += DISPATCH_POLL_INTERVAL_MS;
|
|
63673
|
+
return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
|
|
63674
|
+
kind: "failure",
|
|
63675
|
+
reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
|
|
63676
|
+
} : null;
|
|
63677
|
+
};
|
|
63678
|
+
let getRes;
|
|
62898
63679
|
try {
|
|
62899
|
-
|
|
62900
|
-
|
|
62901
|
-
|
|
62902
|
-
|
|
63680
|
+
getRes = await f2(`${base}/rpc/personaDispatch:get`, {
|
|
63681
|
+
method: "POST",
|
|
63682
|
+
headers: {
|
|
63683
|
+
"content-type": "application/json",
|
|
63684
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63685
|
+
},
|
|
63686
|
+
body: JSON.stringify({ dispatchId })
|
|
63687
|
+
});
|
|
62903
63688
|
} catch {
|
|
63689
|
+
const dead = markUnreachable();
|
|
63690
|
+
if (dead) return dead;
|
|
63691
|
+
continue;
|
|
62904
63692
|
}
|
|
62905
|
-
|
|
62906
|
-
return names;
|
|
62907
|
-
}
|
|
62908
|
-
function walkRollouts(dir, visit) {
|
|
62909
|
-
let entries;
|
|
62910
|
-
try {
|
|
62911
|
-
entries = import_node_fs35.default.readdirSync(dir, { withFileTypes: true });
|
|
62912
|
-
} catch {
|
|
62913
|
-
return true;
|
|
62914
|
-
}
|
|
62915
|
-
entries.sort((a2, b3) => a2.name < b3.name ? 1 : a2.name > b3.name ? -1 : 0);
|
|
62916
|
-
for (const entry of entries) {
|
|
62917
|
-
const full = import_node_path40.default.join(dir, entry.name);
|
|
62918
|
-
if (entry.isDirectory()) {
|
|
62919
|
-
if (!walkRollouts(full, visit)) return false;
|
|
62920
|
-
} else if (ROLLOUT_NAME_RE.test(entry.name)) {
|
|
62921
|
-
if (visit(full) === false) return false;
|
|
62922
|
-
}
|
|
62923
|
-
}
|
|
62924
|
-
return true;
|
|
62925
|
-
}
|
|
62926
|
-
async function withSubagentResults(parent, parentThreadId, loadChild, logger) {
|
|
62927
|
-
const messages = [];
|
|
62928
|
-
const started = new Map(parent.subagents.map((s2) => [s2.toolUseId, s2]));
|
|
62929
|
-
for (const m3 of parent.messages) {
|
|
62930
|
-
messages.push(m3);
|
|
62931
|
-
const sub = m3.kind === "tool_call" && m3.toolUseId ? started.get(m3.toolUseId) : void 0;
|
|
62932
|
-
if (!sub) continue;
|
|
62933
|
-
let text;
|
|
63693
|
+
let getJson;
|
|
62934
63694
|
try {
|
|
62935
|
-
|
|
62936
|
-
} catch
|
|
62937
|
-
|
|
63695
|
+
getJson = await getRes.json();
|
|
63696
|
+
} catch {
|
|
63697
|
+
const dead = markUnreachable();
|
|
63698
|
+
if (dead) return dead;
|
|
62938
63699
|
continue;
|
|
62939
63700
|
}
|
|
62940
|
-
if (
|
|
62941
|
-
|
|
63701
|
+
if (getJson.ok === false) {
|
|
63702
|
+
if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
|
|
63703
|
+
return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
|
|
63704
|
+
}
|
|
63705
|
+
const dead = markUnreachable();
|
|
63706
|
+
if (dead) return dead;
|
|
62942
63707
|
continue;
|
|
62943
63708
|
}
|
|
62944
|
-
|
|
62945
|
-
|
|
62946
|
-
|
|
62947
|
-
|
|
62948
|
-
childThreadId: sub.agentId,
|
|
62949
|
-
actualParent: child.meta?.parentThreadId ?? null
|
|
62950
|
-
});
|
|
62951
|
-
continue;
|
|
63709
|
+
unreachableFor = 0;
|
|
63710
|
+
const { status, outcome } = getJson.result;
|
|
63711
|
+
if (status === "completed" || status === "failed") {
|
|
63712
|
+
return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
|
|
62952
63713
|
}
|
|
62953
|
-
const result = subagentTerminalResult(sub, child);
|
|
62954
|
-
if (result) messages.push(result);
|
|
62955
|
-
}
|
|
62956
|
-
return messages;
|
|
62957
|
-
}
|
|
62958
|
-
function pageMessages(messages, args) {
|
|
62959
|
-
const total = messages.length;
|
|
62960
|
-
const limit = args.limit ?? total;
|
|
62961
|
-
const offset = typeof args.offset === "number" ? Math.min(Math.max(0, args.offset), total) : Math.max(0, total - limit);
|
|
62962
|
-
return { messages: messages.slice(offset, offset + limit), total, offset };
|
|
62963
|
-
}
|
|
62964
|
-
function subagentSummaries(parent) {
|
|
62965
|
-
const seen = /* @__PURE__ */ new Set();
|
|
62966
|
-
const out = [];
|
|
62967
|
-
for (const s2 of parent.subagents) {
|
|
62968
|
-
if (seen.has(s2.agentId)) continue;
|
|
62969
|
-
seen.add(s2.agentId);
|
|
62970
|
-
out.push({ subagentId: s2.agentId, summary: s2.description, messageCount: 0 });
|
|
62971
63714
|
}
|
|
62972
|
-
return
|
|
63715
|
+
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
62973
63716
|
}
|
|
62974
|
-
|
|
62975
|
-
|
|
62976
|
-
|
|
62977
|
-
|
|
62978
|
-
}
|
|
62979
|
-
// 仍返回 []:projects/list 非 session-scoped、不知 tool,跨-tool 项目浏览器合并排后续。
|
|
62980
|
-
// codex 旧会话仍能经 clawd 活跃列表(session:list, tool-agnostic) + read 打开。
|
|
62981
|
-
async listProjects() {
|
|
62982
|
-
return [];
|
|
62983
|
-
}
|
|
62984
|
-
async listSessions(args) {
|
|
62985
|
-
const want = import_node_path40.default.resolve(args.projectPath);
|
|
62986
|
-
const names = readThreadNames(this.deps.sharedHome);
|
|
62987
|
-
const found = [];
|
|
62988
|
-
let scanned = 0;
|
|
62989
|
-
for (const root of this.deps.roots) {
|
|
62990
|
-
const goOn = walkRollouts(root, (file) => {
|
|
62991
|
-
if (++scanned > CODEX_LIST_SESSIONS_SCAN_CAP) return false;
|
|
62992
|
-
const head = readHead(file, PEEK_BYTES);
|
|
62993
|
-
const cwd = head === null ? null : peekSessionCwd(head);
|
|
62994
|
-
if (!cwd || import_node_path40.default.resolve(cwd) !== want) return;
|
|
62995
|
-
const first = readFirstLine(file);
|
|
62996
|
-
const meta = first === null ? null : parseCodexRolloutMeta(first);
|
|
62997
|
-
if (!meta) return;
|
|
62998
|
-
let mtimeMs;
|
|
62999
|
-
try {
|
|
63000
|
-
mtimeMs = import_node_fs35.default.statSync(file).mtimeMs;
|
|
63001
|
-
} catch {
|
|
63002
|
-
return;
|
|
63003
|
-
}
|
|
63004
|
-
found.push({ file, threadId: meta.threadId, mtimeMs });
|
|
63005
|
-
});
|
|
63006
|
-
if (!goOn) break;
|
|
63007
|
-
}
|
|
63008
|
-
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
63009
|
-
return found.slice(0, CODEX_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
63010
|
-
let summary = names.get(f2.threadId.toLowerCase());
|
|
63011
|
-
if (!summary) {
|
|
63012
|
-
const head = readHead(f2.file, TITLE_BYTES);
|
|
63013
|
-
summary = head === null ? void 0 : firstUserMessageText(head);
|
|
63014
|
-
}
|
|
63015
|
-
return {
|
|
63016
|
-
toolSessionId: f2.threadId,
|
|
63017
|
-
summary: summary || "(\u7A7A\u4F1A\u8BDD)",
|
|
63018
|
-
messageCount: 0,
|
|
63019
|
-
// 与此前 thread/list 一致:列表不数条数
|
|
63020
|
-
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
63021
|
-
};
|
|
63022
|
-
});
|
|
63023
|
-
}
|
|
63024
|
-
async read(args) {
|
|
63025
|
-
const locate = createCodexRolloutLocator(this.deps.roots);
|
|
63026
|
-
const parent = this.parseThread(locate, args.toolSessionId);
|
|
63027
|
-
const messages = await withSubagentResults(
|
|
63028
|
-
parent,
|
|
63029
|
-
args.toolSessionId,
|
|
63030
|
-
async (agentId) => {
|
|
63031
|
-
const file = locate(agentId);
|
|
63032
|
-
return file ? import_node_fs35.default.readFileSync(file, "utf8") : null;
|
|
63033
|
-
},
|
|
63034
|
-
this.deps.logger
|
|
63035
|
-
);
|
|
63036
|
-
return pageMessages(messages, args);
|
|
63037
|
-
}
|
|
63038
|
-
async listSubagents(args) {
|
|
63039
|
-
return subagentSummaries(this.parseThread(createCodexRolloutLocator(this.deps.roots), args.toolSessionId));
|
|
63040
|
-
}
|
|
63041
|
-
async readSubagent(args) {
|
|
63042
|
-
const child = this.parseThread(createCodexRolloutLocator(this.deps.roots), args.subagentId);
|
|
63043
|
-
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
63044
|
-
throw new Error(
|
|
63045
|
-
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
63046
|
-
);
|
|
63047
|
-
}
|
|
63048
|
-
return { messages: child.messages };
|
|
63049
|
-
}
|
|
63050
|
-
computeRewindDiff() {
|
|
63051
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
63052
|
-
}
|
|
63053
|
-
listRewindableUserMessageIds() {
|
|
63054
|
-
return [];
|
|
63055
|
-
}
|
|
63056
|
-
parseThread(locate, threadId) {
|
|
63057
|
-
const file = locate(threadId);
|
|
63058
|
-
if (!file) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
63059
|
-
return parseCodexRollout(import_node_fs35.default.readFileSync(file, "utf8"));
|
|
63060
|
-
}
|
|
63061
|
-
};
|
|
63062
|
-
|
|
63063
|
-
// src/cloud/gateway-client.ts
|
|
63064
|
-
init_protocol();
|
|
63065
|
-
init_protocol();
|
|
63066
|
-
|
|
63067
|
-
// src/feishu-auth/central-client.ts
|
|
63068
|
-
var CentralClientError = class extends Error {
|
|
63069
|
-
constructor(code, message, cause) {
|
|
63070
|
-
super(message);
|
|
63071
|
-
this.code = code;
|
|
63072
|
-
this.cause = cause;
|
|
63073
|
-
this.name = "CentralClientError";
|
|
63074
|
-
}
|
|
63075
|
-
code;
|
|
63076
|
-
cause;
|
|
63077
|
-
};
|
|
63078
|
-
async function centralRequest(opts, path102, init) {
|
|
63079
|
-
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
63080
|
-
const url = `${opts.api.replace(/\/+$/, "")}${path102}`;
|
|
63081
|
-
const ctrl = new AbortController();
|
|
63082
|
-
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
63717
|
+
async function forwardInboxPostToPeer(args) {
|
|
63718
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63719
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63720
|
+
const url = `${base}/rpc/inbox:postMessage`;
|
|
63083
63721
|
let res;
|
|
63084
63722
|
try {
|
|
63085
63723
|
res = await f2(url, {
|
|
63086
|
-
method:
|
|
63724
|
+
method: "POST",
|
|
63087
63725
|
headers: {
|
|
63088
63726
|
"content-type": "application/json",
|
|
63089
|
-
|
|
63727
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63090
63728
|
},
|
|
63091
|
-
|
|
63092
|
-
|
|
63729
|
+
body: JSON.stringify({
|
|
63730
|
+
id: args.id,
|
|
63731
|
+
text: args.text,
|
|
63732
|
+
createdAt: args.createdAt,
|
|
63733
|
+
...args.origin ? { origin: args.origin } : {}
|
|
63734
|
+
})
|
|
63093
63735
|
});
|
|
63094
63736
|
} catch (err) {
|
|
63095
|
-
|
|
63096
|
-
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
63097
|
-
}
|
|
63098
|
-
clearTimeout(timer);
|
|
63099
|
-
if (res.status === 401) {
|
|
63100
|
-
throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
63101
|
-
}
|
|
63102
|
-
if (!res.ok) {
|
|
63103
|
-
let detail = "";
|
|
63104
|
-
try {
|
|
63105
|
-
const body = await res.json();
|
|
63106
|
-
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
63107
|
-
} catch {
|
|
63108
|
-
}
|
|
63109
|
-
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`);
|
|
63737
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
63110
63738
|
}
|
|
63739
|
+
let json = null;
|
|
63111
63740
|
try {
|
|
63112
|
-
|
|
63113
|
-
} catch
|
|
63114
|
-
|
|
63115
|
-
}
|
|
63116
|
-
}
|
|
63117
|
-
async function centralExchange(opts) {
|
|
63118
|
-
const body = await centralRequest(opts, "/api/clawd-identity/exchange", {
|
|
63119
|
-
method: "POST",
|
|
63120
|
-
bearer: opts.loginPass,
|
|
63121
|
-
body: { deviceId: opts.deviceId }
|
|
63122
|
-
});
|
|
63123
|
-
if (typeof body.token !== "string" || body.token.length === 0 || typeof body.ownerId !== "string" || typeof body.provider !== "string" || typeof body.displayName !== "string") {
|
|
63124
|
-
throw new CentralClientError("BAD_RESPONSE", "exchange: missing token/ownerId/provider/displayName");
|
|
63125
|
-
}
|
|
63126
|
-
const deviceUrl = typeof body.deviceUrl === "string" && body.deviceUrl.length > 0 ? body.deviceUrl : null;
|
|
63127
|
-
return { token: body.token, ownerId: body.ownerId, provider: body.provider, displayName: body.displayName, deviceUrl };
|
|
63128
|
-
}
|
|
63129
|
-
async function centralGetPublicKey(opts) {
|
|
63130
|
-
const body = await centralRequest(opts, "/api/clawd-identity/public-key", {
|
|
63131
|
-
method: "GET"
|
|
63132
|
-
});
|
|
63133
|
-
if (typeof body.publicKeyPem !== "string" || !body.publicKeyPem.includes("PUBLIC KEY")) {
|
|
63134
|
-
throw new CentralClientError("BAD_RESPONSE", "public-key: missing or malformed publicKeyPem");
|
|
63741
|
+
json = await res.json();
|
|
63742
|
+
} catch {
|
|
63743
|
+
return { ok: false, error: `peer non-JSON (HTTP ${res.status})` };
|
|
63135
63744
|
}
|
|
63136
|
-
|
|
63137
|
-
}
|
|
63138
|
-
async function centralUpsertDevice(opts) {
|
|
63139
|
-
const body = await centralRequest(opts, "/api/clawd-identity/devices/self", {
|
|
63140
|
-
method: "PUT",
|
|
63141
|
-
bearer: opts.loginPass,
|
|
63142
|
-
body: {
|
|
63143
|
-
...opts.url ? { url: opts.url } : {},
|
|
63144
|
-
...opts.name ? { name: opts.name } : {}
|
|
63145
|
-
}
|
|
63146
|
-
});
|
|
63147
|
-
if (body.ok !== true) {
|
|
63148
|
-
throw new CentralClientError("BAD_RESPONSE", "upsertDevice: server did not ack ok");
|
|
63745
|
+
if (res.status < 200 || res.status >= 300 || json?.ok === false) {
|
|
63746
|
+
return { ok: false, error: json?.error ?? `HTTP ${res.status}` };
|
|
63149
63747
|
}
|
|
63150
63748
|
return { ok: true };
|
|
63151
63749
|
}
|
|
63152
|
-
async function
|
|
63153
|
-
const
|
|
63154
|
-
const
|
|
63155
|
-
|
|
63156
|
-
|
|
63157
|
-
|
|
63158
|
-
|
|
63159
|
-
|
|
63160
|
-
|
|
63161
|
-
|
|
63162
|
-
|
|
63163
|
-
|
|
63164
|
-
method: "GET",
|
|
63165
|
-
bearer: opts.loginPass
|
|
63750
|
+
async function forwardPeerExecToPeer(args) {
|
|
63751
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63752
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63753
|
+
const url = `${base}/rpc/peerExec:run`;
|
|
63754
|
+
const tunnelFail = (msg) => ({
|
|
63755
|
+
type: "peerExec:run:ok",
|
|
63756
|
+
stdout: "",
|
|
63757
|
+
stderr: msg,
|
|
63758
|
+
exitCode: null,
|
|
63759
|
+
timedOut: false,
|
|
63760
|
+
stdoutTruncated: false,
|
|
63761
|
+
stderrTruncated: false
|
|
63166
63762
|
});
|
|
63167
|
-
if (body.apiKey !== null && typeof body.apiKey !== "string") {
|
|
63168
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: apiKey must be string or null");
|
|
63169
|
-
}
|
|
63170
|
-
return {
|
|
63171
|
-
apiKey: body.apiKey,
|
|
63172
|
-
createdAt: typeof body.createdAt === "string" ? body.createdAt : null,
|
|
63173
|
-
// 中心没配就是 null —— 不当成错误,key 本身照样有效
|
|
63174
|
-
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
63175
|
-
};
|
|
63176
|
-
}
|
|
63177
|
-
async function centralGetThread(opts) {
|
|
63178
|
-
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
63179
|
-
const url = `${opts.api.replace(/\/+$/, "")}/api/persona-cloud/threads/${encodeURIComponent(opts.threadId)}`;
|
|
63180
|
-
const ctrl = new AbortController();
|
|
63181
|
-
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
63182
63763
|
let res;
|
|
63183
63764
|
try {
|
|
63184
|
-
res = await f2(url, {
|
|
63765
|
+
res = await f2(url, {
|
|
63766
|
+
method: "POST",
|
|
63767
|
+
headers: {
|
|
63768
|
+
"content-type": "application/json",
|
|
63769
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63770
|
+
},
|
|
63771
|
+
body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
|
|
63772
|
+
});
|
|
63185
63773
|
} catch (err) {
|
|
63186
|
-
|
|
63187
|
-
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
63188
|
-
}
|
|
63189
|
-
clearTimeout(timer);
|
|
63190
|
-
if (res.status === 404) return null;
|
|
63191
|
-
if (res.status === 401) throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
63192
|
-
if (!res.ok) {
|
|
63193
|
-
let detail = "";
|
|
63194
|
-
try {
|
|
63195
|
-
const body = await res.json();
|
|
63196
|
-
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
63197
|
-
} catch {
|
|
63198
|
-
}
|
|
63199
|
-
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`);
|
|
63200
|
-
}
|
|
63201
|
-
return await res.text();
|
|
63202
|
-
}
|
|
63203
|
-
async function centralIssueApiKey(opts) {
|
|
63204
|
-
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
63205
|
-
method: "POST",
|
|
63206
|
-
bearer: opts.loginPass
|
|
63207
|
-
});
|
|
63208
|
-
if (typeof body.apiKey !== "string" || body.apiKey.length === 0) {
|
|
63209
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: missing apiKey");
|
|
63210
|
-
}
|
|
63211
|
-
if (typeof body.createdAt !== "string" || body.createdAt.length === 0) {
|
|
63212
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: missing createdAt");
|
|
63213
|
-
}
|
|
63214
|
-
return {
|
|
63215
|
-
apiKey: body.apiKey,
|
|
63216
|
-
createdAt: body.createdAt,
|
|
63217
|
-
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
63218
|
-
};
|
|
63219
|
-
}
|
|
63220
|
-
async function centralRevokeApiKey(opts) {
|
|
63221
|
-
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
63222
|
-
method: "DELETE",
|
|
63223
|
-
bearer: opts.loginPass
|
|
63224
|
-
});
|
|
63225
|
-
if (typeof body.revoked !== "boolean") {
|
|
63226
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: revoked must be boolean");
|
|
63774
|
+
return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
63227
63775
|
}
|
|
63228
|
-
|
|
63229
|
-
|
|
63230
|
-
|
|
63231
|
-
|
|
63232
|
-
|
|
63233
|
-
bearer: opts.loginPass
|
|
63234
|
-
});
|
|
63235
|
-
if (!Array.isArray(body.devices)) {
|
|
63236
|
-
throw new CentralClientError("BAD_RESPONSE", "devices: missing devices array");
|
|
63776
|
+
let json;
|
|
63777
|
+
try {
|
|
63778
|
+
json = await res.json();
|
|
63779
|
+
} catch {
|
|
63780
|
+
return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
|
|
63237
63781
|
}
|
|
63238
|
-
|
|
63239
|
-
|
|
63240
|
-
const d2 = raw;
|
|
63241
|
-
if (typeof d2.deviceId !== "string" || typeof d2.name !== "string" || typeof d2.url !== "string") {
|
|
63242
|
-
throw new CentralClientError("BAD_RESPONSE", "devices: malformed device entry");
|
|
63243
|
-
}
|
|
63244
|
-
out.push({ deviceId: d2.deviceId, name: d2.name, url: d2.url });
|
|
63782
|
+
if (json.ok === false) {
|
|
63783
|
+
throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
|
|
63245
63784
|
}
|
|
63246
|
-
return
|
|
63247
|
-
}
|
|
63248
|
-
async function centralConnectorsMine(opts) {
|
|
63249
|
-
const body = await centralRequest(opts, "/api/persona-cloud/connectors/mine", {
|
|
63250
|
-
method: "POST",
|
|
63251
|
-
bearer: opts.loginPass,
|
|
63252
|
-
body: { personaId: opts.personaId, connectors: opts.connectors }
|
|
63253
|
-
});
|
|
63254
|
-
if (!Array.isArray(body.connectors)) throw new CentralClientError("BAD_RESPONSE", "connectors/mine: missing connectors array");
|
|
63255
|
-
return { connectors: body.connectors };
|
|
63256
|
-
}
|
|
63257
|
-
async function centralConnectorFill(opts) {
|
|
63258
|
-
const body = await centralRequest(opts, "/api/persona-cloud/connectors/fields", {
|
|
63259
|
-
method: "PUT",
|
|
63260
|
-
bearer: opts.loginPass,
|
|
63261
|
-
body: { connector: opts.connector, values: opts.values }
|
|
63262
|
-
});
|
|
63263
|
-
if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
|
|
63264
|
-
}
|
|
63265
|
-
async function centralGithubAuthOauthStart(opts) {
|
|
63266
|
-
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
|
|
63267
|
-
method: "POST",
|
|
63268
|
-
bearer: opts.loginPass,
|
|
63269
|
-
body: { ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
|
|
63270
|
-
});
|
|
63271
|
-
if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
|
|
63272
|
-
return { authUrl: body.authUrl };
|
|
63273
|
-
}
|
|
63274
|
-
async function centralGithubAuthToken(opts) {
|
|
63275
|
-
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
|
|
63276
|
-
method: "POST",
|
|
63277
|
-
bearer: opts.loginPass,
|
|
63278
|
-
body: {}
|
|
63279
|
-
});
|
|
63280
|
-
if (body.token === null || body.token === void 0) return void 0;
|
|
63281
|
-
if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
|
|
63282
|
-
return { token: body.token, login: typeof body.login === "string" ? body.login : "" };
|
|
63785
|
+
return json.result;
|
|
63283
63786
|
}
|
|
63284
63787
|
|
|
63285
63788
|
// src/cloud/gateway-client.ts
|
|
63789
|
+
init_protocol();
|
|
63790
|
+
init_protocol();
|
|
63286
63791
|
var CLOUD_WORKSPACE_SLOT = "{{WORKSPACE}}";
|
|
63287
63792
|
var CLOUD_TURN_HEADER = "x-clawd-turn";
|
|
63288
63793
|
var CLOUD_TURN_HEADER_START = "start";
|
|
@@ -63293,7 +63798,7 @@ var GATEWAY_ROUTES = {
|
|
|
63293
63798
|
sessionMirror: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}/mirror`,
|
|
63294
63799
|
// 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
|
|
63295
63800
|
volume: () => "/me/volume",
|
|
63296
|
-
// 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6
|
|
63801
|
+
// 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6):只给发布者与管理员(gateway 问中心,design 2026-09-24-cloud-persona-conversations §3.2)
|
|
63297
63802
|
metrics: (personaId2) => `/metrics/${encodeURIComponent(personaId2)}`
|
|
63298
63803
|
};
|
|
63299
63804
|
var METRICS_TIMEOUT_MS = 3e4;
|
|
@@ -63524,7 +64029,7 @@ function openHttpLineStream(opts) {
|
|
|
63524
64029
|
if (closed || !sessionId) return;
|
|
63525
64030
|
const url = gatewayUrl + GATEWAY_ROUTES.sessionMirror(personaId2, sessionId);
|
|
63526
64031
|
try {
|
|
63527
|
-
const res = await fetchImpl(url, { method: "POST", headers, body: turn ? JSON.stringify({ turn: { outcome: turn.outcome } }) : "" });
|
|
64032
|
+
const res = await fetchImpl(url, { method: "POST", headers, body: turn ? JSON.stringify({ turn: { outcome: turn.outcome, ...turn.afterFirstByteMs !== void 0 ? { afterFirstByteMs: turn.afterFirstByteMs } : {} } }) : "" });
|
|
63528
64033
|
if (!res.ok) logger?.warn("cloud session mirror failed", { status: res.status });
|
|
63529
64034
|
} catch (e) {
|
|
63530
64035
|
if (!closed) logger?.warn("cloud session mirror error", { message: e.message });
|
|
@@ -63653,68 +64158,7 @@ function openHttpLineStream(opts) {
|
|
|
63653
64158
|
init_protocol();
|
|
63654
64159
|
|
|
63655
64160
|
// src/tools/codex-cloud-session.ts
|
|
63656
|
-
var
|
|
63657
|
-
|
|
63658
|
-
// src/tools/codex-cloud-history.ts
|
|
63659
|
-
function personaIdFromCloudCwd(cwd) {
|
|
63660
|
-
const m3 = /\/p\/([^/?#]+)(?:[/?#]|$)/.exec(cwd);
|
|
63661
|
-
if (!m3) return void 0;
|
|
63662
|
-
try {
|
|
63663
|
-
return decodeURIComponent(m3[1]);
|
|
63664
|
-
} catch {
|
|
63665
|
-
return void 0;
|
|
63666
|
-
}
|
|
63667
|
-
}
|
|
63668
|
-
var CloudCodexHistoryReader = class {
|
|
63669
|
-
constructor(client, logger) {
|
|
63670
|
-
this.client = client;
|
|
63671
|
-
this.logger = logger;
|
|
63672
|
-
}
|
|
63673
|
-
client;
|
|
63674
|
-
logger;
|
|
63675
|
-
async listProjects() {
|
|
63676
|
-
return [];
|
|
63677
|
-
}
|
|
63678
|
-
/** 云 persona 没有「关联已有 thread」的动线:会话列表就是本机的会话记录 */
|
|
63679
|
-
async listSessions() {
|
|
63680
|
-
return [];
|
|
63681
|
-
}
|
|
63682
|
-
async read(args) {
|
|
63683
|
-
const parent = await this.fetchThread(args.toolSessionId);
|
|
63684
|
-
const messages = await withSubagentResults(
|
|
63685
|
-
parent,
|
|
63686
|
-
args.toolSessionId,
|
|
63687
|
-
(agentId) => this.client.fetchRollout(agentId),
|
|
63688
|
-
this.logger
|
|
63689
|
-
);
|
|
63690
|
-
return pageMessages(messages, args);
|
|
63691
|
-
}
|
|
63692
|
-
async listSubagents(args) {
|
|
63693
|
-
return subagentSummaries(await this.fetchThread(args.toolSessionId));
|
|
63694
|
-
}
|
|
63695
|
-
async readSubagent(args) {
|
|
63696
|
-
const child = await this.fetchThread(args.subagentId);
|
|
63697
|
-
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
63698
|
-
throw new Error(
|
|
63699
|
-
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
63700
|
-
);
|
|
63701
|
-
}
|
|
63702
|
-
return { messages: child.messages };
|
|
63703
|
-
}
|
|
63704
|
-
computeRewindDiff() {
|
|
63705
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
63706
|
-
}
|
|
63707
|
-
listRewindableUserMessageIds() {
|
|
63708
|
-
return [];
|
|
63709
|
-
}
|
|
63710
|
-
async fetchThread(threadId) {
|
|
63711
|
-
const text = await this.client.fetchRollout(threadId);
|
|
63712
|
-
if (text === null) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
63713
|
-
return parseCodexRollout(text);
|
|
63714
|
-
}
|
|
63715
|
-
};
|
|
63716
|
-
|
|
63717
|
-
// src/tools/codex-cloud-session.ts
|
|
64161
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
63718
64162
|
function cloudThreadParams(ctx, paths) {
|
|
63719
64163
|
const { model: _model, ...base } = threadStartParams(ctx);
|
|
63720
64164
|
const developerInstructions = [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
|
|
@@ -63771,7 +64215,7 @@ function cloudImageInput(deps) {
|
|
|
63771
64215
|
const mime = lookupMime(p3);
|
|
63772
64216
|
if (!mime.startsWith("image/")) return null;
|
|
63773
64217
|
try {
|
|
63774
|
-
const bytes = (deps.readFile ??
|
|
64218
|
+
const bytes = (deps.readFile ?? import_node_fs37.default.readFileSync)(p3);
|
|
63775
64219
|
return { type: "image", url: `data:${mime};base64,${bytes.toString("base64")}` };
|
|
63776
64220
|
} catch (e) {
|
|
63777
64221
|
deps.logger?.warn("cloud image attachment unreadable", { path: p3, message: e.message });
|
|
@@ -63804,7 +64248,9 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
63804
64248
|
kill: () => stream.close(),
|
|
63805
64249
|
// 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
|
|
63806
64250
|
onTurnStarting: () => channel?.markNextLineTurnStart(),
|
|
63807
|
-
|
|
64251
|
+
// 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
|
|
64252
|
+
onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
|
|
64253
|
+
now: deps.now
|
|
63808
64254
|
});
|
|
63809
64255
|
if (!personaId2) {
|
|
63810
64256
|
queueMicrotask(() => {
|
|
@@ -64004,37 +64450,11 @@ var PiRpcClient = class {
|
|
|
64004
64450
|
// src/tools/pi-session.ts
|
|
64005
64451
|
var import_node_child_process11 = require("child_process");
|
|
64006
64452
|
var import_node_crypto12 = require("crypto");
|
|
64007
|
-
var
|
|
64008
|
-
var
|
|
64453
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64454
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
64009
64455
|
|
|
64010
64456
|
// src/tools/pi-events.ts
|
|
64011
64457
|
init_protocol();
|
|
64012
|
-
|
|
64013
|
-
// src/tools/pi-json.ts
|
|
64014
|
-
function rec3(v3) {
|
|
64015
|
-
return typeof v3 === "object" && v3 !== null && !Array.isArray(v3) ? v3 : void 0;
|
|
64016
|
-
}
|
|
64017
|
-
function str3(v3) {
|
|
64018
|
-
return typeof v3 === "string" ? v3 : void 0;
|
|
64019
|
-
}
|
|
64020
|
-
function num3(v3) {
|
|
64021
|
-
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
64022
|
-
}
|
|
64023
|
-
function arr(v3) {
|
|
64024
|
-
return Array.isArray(v3) ? v3 : [];
|
|
64025
|
-
}
|
|
64026
|
-
function contentText(v3, imagePlaceholder = "") {
|
|
64027
|
-
if (typeof v3 === "string") return v3;
|
|
64028
|
-
return arr(v3).map((b3) => {
|
|
64029
|
-
const r2 = rec3(b3);
|
|
64030
|
-
if (!r2) return "";
|
|
64031
|
-
if (r2.type === "text") return str3(r2.text) ?? "";
|
|
64032
|
-
if (r2.type === "image") return imagePlaceholder;
|
|
64033
|
-
return "";
|
|
64034
|
-
}).filter(Boolean).join("\n");
|
|
64035
|
-
}
|
|
64036
|
-
|
|
64037
|
-
// src/tools/pi-events.ts
|
|
64038
64458
|
function createPiTranslatorState() {
|
|
64039
64459
|
return { assistantSeq: 0, aborted: false };
|
|
64040
64460
|
}
|
|
@@ -64045,23 +64465,23 @@ function translatePiEvent(ev, st2) {
|
|
|
64045
64465
|
return [];
|
|
64046
64466
|
}
|
|
64047
64467
|
case "message_start": {
|
|
64048
|
-
if (
|
|
64468
|
+
if (rec2(ev.message)?.role === "assistant") st2.assistantSeq += 1;
|
|
64049
64469
|
return [];
|
|
64050
64470
|
}
|
|
64051
64471
|
case "message_update":
|
|
64052
|
-
return messageUpdate(
|
|
64472
|
+
return messageUpdate(rec2(ev.assistantMessageEvent), st2);
|
|
64053
64473
|
case "message_end":
|
|
64054
|
-
return messageEnd(
|
|
64474
|
+
return messageEnd(rec2(ev.message), st2);
|
|
64055
64475
|
case "tool_execution_start": {
|
|
64056
|
-
const toolUseId =
|
|
64057
|
-
const tool =
|
|
64476
|
+
const toolUseId = str2(ev.toolCallId);
|
|
64477
|
+
const tool = str2(ev.toolName);
|
|
64058
64478
|
if (!toolUseId || !tool) return [];
|
|
64059
64479
|
return [{ kind: "tool_call", toolUseId, tool, toolKind: PI_TOOL_ID, input: ev.args ?? {} }];
|
|
64060
64480
|
}
|
|
64061
64481
|
case "tool_execution_end": {
|
|
64062
|
-
const toolUseId =
|
|
64482
|
+
const toolUseId = str2(ev.toolCallId);
|
|
64063
64483
|
if (!toolUseId) return [];
|
|
64064
|
-
const text = contentText(
|
|
64484
|
+
const text = contentText(rec2(ev.result)?.content);
|
|
64065
64485
|
const out = { kind: "tool_result", toolUseId, output: text };
|
|
64066
64486
|
if (ev.isError === true) out.error = text || "tool failed";
|
|
64067
64487
|
return [out];
|
|
@@ -64077,9 +64497,9 @@ function translatePiEvent(ev, st2) {
|
|
|
64077
64497
|
}
|
|
64078
64498
|
function messageUpdate(e, st2) {
|
|
64079
64499
|
if (!e) return [];
|
|
64080
|
-
const delta =
|
|
64500
|
+
const delta = str2(e.delta);
|
|
64081
64501
|
if (!delta) return [];
|
|
64082
|
-
const partialId = `${st2.assistantSeq}-${
|
|
64502
|
+
const partialId = `${st2.assistantSeq}-${num2(e.contentIndex) ?? 0}`;
|
|
64083
64503
|
if (e.type === "text_delta") return [{ kind: "text", text: delta, partialId }];
|
|
64084
64504
|
if (e.type === "thinking_delta") return [{ kind: "thinking", text: delta, partialId }];
|
|
64085
64505
|
return [];
|
|
@@ -64088,32 +64508,32 @@ function messageEnd(m3, st2) {
|
|
|
64088
64508
|
if (!m3 || m3.role !== "assistant") return [];
|
|
64089
64509
|
const out = [];
|
|
64090
64510
|
for (const b3 of arr(m3.content)) {
|
|
64091
|
-
const block =
|
|
64511
|
+
const block = rec2(b3);
|
|
64092
64512
|
if (!block) continue;
|
|
64093
64513
|
if (block.type === "thinking") {
|
|
64094
|
-
const t2 =
|
|
64514
|
+
const t2 = str2(block.thinking);
|
|
64095
64515
|
if (t2) out.push({ kind: "thinking", text: t2 });
|
|
64096
64516
|
} else if (block.type === "text") {
|
|
64097
|
-
const t2 =
|
|
64517
|
+
const t2 = str2(block.text);
|
|
64098
64518
|
if (t2) out.push({ kind: "text", text: t2 });
|
|
64099
64519
|
}
|
|
64100
64520
|
}
|
|
64101
64521
|
const patch = {};
|
|
64102
|
-
const model =
|
|
64103
|
-
const provider =
|
|
64522
|
+
const model = str2(m3.model);
|
|
64523
|
+
const provider = str2(m3.provider);
|
|
64104
64524
|
if (model) patch.resolvedModel = provider ? `${provider}/${model}` : model;
|
|
64105
|
-
const u2 =
|
|
64106
|
-
if (u2 && (
|
|
64525
|
+
const u2 = rec2(m3.usage);
|
|
64526
|
+
if (u2 && (num2(u2.totalTokens) ?? 0) > 0) {
|
|
64107
64527
|
patch.contextUsage = {
|
|
64108
|
-
inputTokens:
|
|
64109
|
-
outputTokens:
|
|
64110
|
-
...
|
|
64111
|
-
...
|
|
64528
|
+
inputTokens: num2(u2.input) ?? 0,
|
|
64529
|
+
outputTokens: num2(u2.output) ?? 0,
|
|
64530
|
+
...num2(u2.cacheRead) !== void 0 ? { cacheReadTokens: num2(u2.cacheRead) } : {},
|
|
64531
|
+
...num2(u2.cacheWrite) !== void 0 ? { cacheCreateTokens: num2(u2.cacheWrite) } : {}
|
|
64112
64532
|
};
|
|
64113
64533
|
}
|
|
64114
64534
|
if (patch.resolvedModel || patch.contextUsage) out.push({ kind: "meta_update", patch });
|
|
64115
|
-
const stop =
|
|
64116
|
-
const err =
|
|
64535
|
+
const stop = str2(m3.stopReason);
|
|
64536
|
+
const err = str2(m3.errorMessage);
|
|
64117
64537
|
if (stop === "aborted") {
|
|
64118
64538
|
st2.aborted = true;
|
|
64119
64539
|
} else if (stop === "error" || err) {
|
|
@@ -64167,6 +64587,7 @@ function drivePiSession(opts) {
|
|
|
64167
64587
|
let turnErrored = false;
|
|
64168
64588
|
let turnInterrupted = false;
|
|
64169
64589
|
let turnOpen = false;
|
|
64590
|
+
const firstByte = createFirstByteClock(opts.now);
|
|
64170
64591
|
let handshakeFailed = false;
|
|
64171
64592
|
let exited = false;
|
|
64172
64593
|
const finish = (code) => {
|
|
@@ -64178,11 +64599,11 @@ function drivePiSession(opts) {
|
|
|
64178
64599
|
sink.pushEvents([{ kind: "error", message }, { kind: "turn_end", reason: "completed" }]);
|
|
64179
64600
|
if (!opened) return;
|
|
64180
64601
|
turnOpen = false;
|
|
64181
|
-
opts.onTurnCompleted?.("failure");
|
|
64602
|
+
opts.onTurnCompleted?.("failure", firstByte.afterFirstByteMs());
|
|
64182
64603
|
};
|
|
64183
64604
|
const handleUiRequest = (ev) => {
|
|
64184
|
-
const method =
|
|
64185
|
-
const id =
|
|
64605
|
+
const method = str2(ev.method);
|
|
64606
|
+
const id = str2(ev.id);
|
|
64186
64607
|
logger?.warn("pi extension_ui_request unexpected in clawd session", { method });
|
|
64187
64608
|
if (id && (method === "select" || method === "confirm" || method === "input" || method === "editor")) {
|
|
64188
64609
|
client.send({ type: "extension_ui_response", id, cancelled: true });
|
|
@@ -64197,7 +64618,7 @@ function drivePiSession(opts) {
|
|
|
64197
64618
|
}
|
|
64198
64619
|
if (ev.type === "agent_settled") busy = false;
|
|
64199
64620
|
if (ev.type === "response") {
|
|
64200
|
-
logger?.warn("pi response without pending id", { command:
|
|
64621
|
+
logger?.warn("pi response without pending id", { command: str2(ev.command), error: str2(ev.error) });
|
|
64201
64622
|
return;
|
|
64202
64623
|
}
|
|
64203
64624
|
if (ev.type === "extension_ui_request") {
|
|
@@ -64205,29 +64626,32 @@ function drivePiSession(opts) {
|
|
|
64205
64626
|
return;
|
|
64206
64627
|
}
|
|
64207
64628
|
const events = translatePiEvent(ev, st2);
|
|
64208
|
-
if (events.length)
|
|
64629
|
+
if (events.length) {
|
|
64630
|
+
if (turnOpen) firstByte.see(events);
|
|
64631
|
+
sink.pushEvents(events);
|
|
64632
|
+
}
|
|
64209
64633
|
for (const e of events) {
|
|
64210
64634
|
if (e.kind === "error") turnErrored = true;
|
|
64211
64635
|
if (e.kind === "turn_end" && e.reason === "interrupted") turnInterrupted = true;
|
|
64212
64636
|
}
|
|
64213
64637
|
if (ev.type === "agent_settled") {
|
|
64214
64638
|
turnOpen = false;
|
|
64215
|
-
opts.onTurnCompleted?.(turnInterrupted ? "interrupted" : turnErrored ? "failure" : "success");
|
|
64639
|
+
opts.onTurnCompleted?.(turnInterrupted ? "interrupted" : turnErrored ? "failure" : "success", firstByte.afterFirstByteMs());
|
|
64216
64640
|
}
|
|
64217
64641
|
},
|
|
64218
64642
|
onClose: () => finish(opts.exitCodeOnClose?.() ?? null)
|
|
64219
64643
|
});
|
|
64220
64644
|
const ready = client.request({ type: "get_state" }).then((r2) => {
|
|
64221
64645
|
if (!r2.success) throw new Error(r2.error ?? "get_state failed");
|
|
64222
|
-
const data =
|
|
64223
|
-
const sid =
|
|
64646
|
+
const data = rec2(r2.data);
|
|
64647
|
+
const sid = str2(data?.sessionId) ?? sessionId;
|
|
64224
64648
|
if (sid !== sessionId) logger?.error("pi sessionId mismatch", { expected: sessionId, actual: sid });
|
|
64225
|
-
const model =
|
|
64649
|
+
const model = rec2(data?.model);
|
|
64226
64650
|
const patch = {};
|
|
64227
|
-
const mid =
|
|
64228
|
-
const prov =
|
|
64651
|
+
const mid = str2(model?.id);
|
|
64652
|
+
const prov = str2(model?.provider);
|
|
64229
64653
|
if (mid && mid !== "unknown") patch.resolvedModel = prov ? `${prov}/${mid}` : mid;
|
|
64230
|
-
const cw =
|
|
64654
|
+
const cw = num2(model?.contextWindow);
|
|
64231
64655
|
if (cw && cw > 0) patch.contextWindowSize = cw;
|
|
64232
64656
|
const events = [{ kind: "session_init", toolSessionId: sid }];
|
|
64233
64657
|
if (patch.resolvedModel || patch.contextWindowSize) events.push({ kind: "meta_update", patch });
|
|
@@ -64248,6 +64672,7 @@ function drivePiSession(opts) {
|
|
|
64248
64672
|
opened = !turnOpen;
|
|
64249
64673
|
if (opened) {
|
|
64250
64674
|
turnOpen = true;
|
|
64675
|
+
firstByte.reset();
|
|
64251
64676
|
opts.onTurnStarting?.();
|
|
64252
64677
|
}
|
|
64253
64678
|
const r2 = await client.request({
|
|
@@ -64298,8 +64723,8 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
|
|
|
64298
64723
|
function createPiSession(ctx, sink, deps = {}) {
|
|
64299
64724
|
const cmd = process.env.PI_BIN ?? "pi";
|
|
64300
64725
|
const sessionId = ctx.toolSessionId ?? (0, import_node_crypto12.randomUUID)();
|
|
64301
|
-
const skillsDir =
|
|
64302
|
-
const exists = deps.skillsDirExists ?? ((d2) =>
|
|
64726
|
+
const skillsDir = import_node_path42.default.join(ctx.cwd, ".claude", "skills");
|
|
64727
|
+
const exists = deps.skillsDirExists ?? ((d2) => import_node_fs38.default.existsSync(d2));
|
|
64303
64728
|
const args = buildPiArgs(ctx, {
|
|
64304
64729
|
sessionId,
|
|
64305
64730
|
skillsDir: exists(skillsDir) ? skillsDir : void 0,
|
|
@@ -64342,201 +64767,6 @@ function createPiSession(ctx, sink, deps = {}) {
|
|
|
64342
64767
|
return driver.session;
|
|
64343
64768
|
}
|
|
64344
64769
|
|
|
64345
|
-
// src/tools/pi-history.ts
|
|
64346
|
-
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64347
|
-
var import_node_os12 = __toESM(require("os"), 1);
|
|
64348
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
64349
|
-
var PI_LIST_SESSIONS_LIMIT = 100;
|
|
64350
|
-
var PI_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
64351
|
-
function piAgentDir(env = process.env) {
|
|
64352
|
-
return env.PI_CODING_AGENT_DIR || import_node_path42.default.join(import_node_os12.default.homedir(), ".pi", "agent");
|
|
64353
|
-
}
|
|
64354
|
-
function piSessionsRoot(env = process.env) {
|
|
64355
|
-
return import_node_path42.default.join(piAgentDir(env), "sessions");
|
|
64356
|
-
}
|
|
64357
|
-
function encodePiCwd(cwd) {
|
|
64358
|
-
return `--${cwd.replace(/^[\\/]+/, "").replace(/[\\/:]/g, "-")}--`;
|
|
64359
|
-
}
|
|
64360
|
-
function piProjectSessionDir(sessionsRoot, cwd) {
|
|
64361
|
-
return import_node_path42.default.join(sessionsRoot, encodePiCwd(cwd));
|
|
64362
|
-
}
|
|
64363
|
-
function findInDir(dir, toolSessionId) {
|
|
64364
|
-
let names;
|
|
64365
|
-
try {
|
|
64366
|
-
names = import_node_fs38.default.readdirSync(dir);
|
|
64367
|
-
} catch {
|
|
64368
|
-
return null;
|
|
64369
|
-
}
|
|
64370
|
-
const suffix = `_${toolSessionId}.jsonl`;
|
|
64371
|
-
const hit = names.filter((n2) => n2.endsWith(suffix)).sort().at(-1);
|
|
64372
|
-
return hit ? import_node_path42.default.join(dir, hit) : null;
|
|
64373
|
-
}
|
|
64374
|
-
function locatePiSessionFile(sessionsRoot, cwd, toolSessionId) {
|
|
64375
|
-
const direct = findInDir(piProjectSessionDir(sessionsRoot, cwd), toolSessionId);
|
|
64376
|
-
if (direct) return direct;
|
|
64377
|
-
let dirs;
|
|
64378
|
-
try {
|
|
64379
|
-
dirs = import_node_fs38.default.readdirSync(sessionsRoot, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
|
|
64380
|
-
} catch {
|
|
64381
|
-
return null;
|
|
64382
|
-
}
|
|
64383
|
-
for (const d2 of dirs) {
|
|
64384
|
-
const hit = findInDir(import_node_path42.default.join(sessionsRoot, d2), toolSessionId);
|
|
64385
|
-
if (hit) return hit;
|
|
64386
|
-
}
|
|
64387
|
-
return null;
|
|
64388
|
-
}
|
|
64389
|
-
function parseLine2(line) {
|
|
64390
|
-
const t2 = line.trim();
|
|
64391
|
-
if (!t2) return null;
|
|
64392
|
-
try {
|
|
64393
|
-
return rec3(JSON.parse(t2)) ?? null;
|
|
64394
|
-
} catch {
|
|
64395
|
-
return null;
|
|
64396
|
-
}
|
|
64397
|
-
}
|
|
64398
|
-
function parsePiSession(text) {
|
|
64399
|
-
let meta = null;
|
|
64400
|
-
const entries = /* @__PURE__ */ new Map();
|
|
64401
|
-
let last = null;
|
|
64402
|
-
for (const line of text.split("\n")) {
|
|
64403
|
-
const o2 = parseLine2(line);
|
|
64404
|
-
if (!o2) continue;
|
|
64405
|
-
if (o2.type === "session") {
|
|
64406
|
-
if (!meta) {
|
|
64407
|
-
const timestamp = str3(o2.timestamp);
|
|
64408
|
-
meta = { id: str3(o2.id) ?? "", cwd: str3(o2.cwd) ?? "", ...timestamp ? { timestamp } : {} };
|
|
64409
|
-
}
|
|
64410
|
-
continue;
|
|
64411
|
-
}
|
|
64412
|
-
const id = str3(o2.id);
|
|
64413
|
-
if (!id) continue;
|
|
64414
|
-
entries.set(id, o2);
|
|
64415
|
-
last = o2;
|
|
64416
|
-
}
|
|
64417
|
-
const chain = [];
|
|
64418
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64419
|
-
let cur = last;
|
|
64420
|
-
while (cur) {
|
|
64421
|
-
const id = str3(cur.id);
|
|
64422
|
-
if (seen.has(id)) break;
|
|
64423
|
-
seen.add(id);
|
|
64424
|
-
chain.push(cur);
|
|
64425
|
-
const pid = str3(cur.parentId);
|
|
64426
|
-
cur = pid ? entries.get(pid) ?? null : null;
|
|
64427
|
-
}
|
|
64428
|
-
chain.reverse();
|
|
64429
|
-
const messages = [];
|
|
64430
|
-
for (const e of chain) {
|
|
64431
|
-
if (e.type !== "message") continue;
|
|
64432
|
-
const m3 = rec3(e.message);
|
|
64433
|
-
if (!m3) continue;
|
|
64434
|
-
const ts = str3(e.timestamp);
|
|
64435
|
-
const base = { raw: e, uuid: str3(e.id), ...ts ? { ts } : {} };
|
|
64436
|
-
switch (m3.role) {
|
|
64437
|
-
case "user": {
|
|
64438
|
-
const t2 = contentText(m3.content, "[image]");
|
|
64439
|
-
if (t2) messages.push({ kind: "user-text", text: t2, ...base });
|
|
64440
|
-
break;
|
|
64441
|
-
}
|
|
64442
|
-
case "assistant": {
|
|
64443
|
-
for (const b3 of arr(m3.content)) {
|
|
64444
|
-
const block = rec3(b3);
|
|
64445
|
-
if (!block) continue;
|
|
64446
|
-
if (block.type === "text") {
|
|
64447
|
-
const t2 = str3(block.text);
|
|
64448
|
-
if (t2) messages.push({ kind: "assistant-text", text: t2, ...base });
|
|
64449
|
-
} else if (block.type === "toolCall") {
|
|
64450
|
-
const toolUseId = str3(block.id);
|
|
64451
|
-
const tool = str3(block.name);
|
|
64452
|
-
if (toolUseId && tool) messages.push({ kind: "tool_call", tool, toolUseId, input: block.arguments ?? {}, ...base });
|
|
64453
|
-
}
|
|
64454
|
-
}
|
|
64455
|
-
if (str3(m3.stopReason) !== "toolUse") messages.push({ kind: "turn-end", ...base });
|
|
64456
|
-
break;
|
|
64457
|
-
}
|
|
64458
|
-
case "toolResult": {
|
|
64459
|
-
const toolUseId = str3(m3.toolCallId);
|
|
64460
|
-
if (!toolUseId) break;
|
|
64461
|
-
const t2 = contentText(m3.content);
|
|
64462
|
-
const msg = { kind: "tool_result", toolUseId, output: t2, ...base };
|
|
64463
|
-
if (m3.isError === true) msg.error = t2 || "tool failed";
|
|
64464
|
-
messages.push(msg);
|
|
64465
|
-
break;
|
|
64466
|
-
}
|
|
64467
|
-
default:
|
|
64468
|
-
break;
|
|
64469
|
-
}
|
|
64470
|
-
}
|
|
64471
|
-
return { meta, messages };
|
|
64472
|
-
}
|
|
64473
|
-
var PiHistoryReader = class {
|
|
64474
|
-
constructor(deps) {
|
|
64475
|
-
this.deps = deps;
|
|
64476
|
-
}
|
|
64477
|
-
deps;
|
|
64478
|
-
// projects/list 非 session-scoped、不知 tool(同 codex 的取舍)
|
|
64479
|
-
async listProjects() {
|
|
64480
|
-
return [];
|
|
64481
|
-
}
|
|
64482
|
-
async listSessions(args) {
|
|
64483
|
-
const want = import_node_path42.default.resolve(args.projectPath);
|
|
64484
|
-
const dir = piProjectSessionDir(this.deps.sessionsRoot, want);
|
|
64485
|
-
let names;
|
|
64486
|
-
try {
|
|
64487
|
-
names = import_node_fs38.default.readdirSync(dir).filter((n2) => n2.endsWith(".jsonl"));
|
|
64488
|
-
} catch {
|
|
64489
|
-
return [];
|
|
64490
|
-
}
|
|
64491
|
-
const found = [];
|
|
64492
|
-
let scanned = 0;
|
|
64493
|
-
for (const n2 of names) {
|
|
64494
|
-
if (++scanned > PI_LIST_SESSIONS_SCAN_CAP) break;
|
|
64495
|
-
const file = import_node_path42.default.join(dir, n2);
|
|
64496
|
-
const head = parseLine2(readFirstLine(file) ?? "");
|
|
64497
|
-
if (!head || head.type !== "session") continue;
|
|
64498
|
-
const cwd = str3(head.cwd);
|
|
64499
|
-
const id = str3(head.id);
|
|
64500
|
-
if (!cwd || !id || import_node_path42.default.resolve(cwd) !== want) continue;
|
|
64501
|
-
let mtimeMs;
|
|
64502
|
-
try {
|
|
64503
|
-
mtimeMs = import_node_fs38.default.statSync(file).mtimeMs;
|
|
64504
|
-
} catch {
|
|
64505
|
-
continue;
|
|
64506
|
-
}
|
|
64507
|
-
found.push({ file, id, mtimeMs });
|
|
64508
|
-
}
|
|
64509
|
-
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
64510
|
-
return found.slice(0, PI_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
64511
|
-
const parsed = parsePiSession(import_node_fs38.default.readFileSync(f2.file, "utf8"));
|
|
64512
|
-
const firstUser = parsed.messages.find((m3) => m3.kind === "user-text");
|
|
64513
|
-
return {
|
|
64514
|
-
toolSessionId: f2.id,
|
|
64515
|
-
summary: firstUser?.text?.slice(0, 80) || "(\u7A7A\u4F1A\u8BDD)",
|
|
64516
|
-
messageCount: parsed.messages.length,
|
|
64517
|
-
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
64518
|
-
};
|
|
64519
|
-
});
|
|
64520
|
-
}
|
|
64521
|
-
async read(args) {
|
|
64522
|
-
const file = locatePiSessionFile(this.deps.sessionsRoot, args.cwd, args.toolSessionId);
|
|
64523
|
-
if (!file) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
64524
|
-
return pageMessages(parsePiSession(import_node_fs38.default.readFileSync(file, "utf8")).messages, args);
|
|
64525
|
-
}
|
|
64526
|
-
async listSubagents(_args) {
|
|
64527
|
-
return [];
|
|
64528
|
-
}
|
|
64529
|
-
async readSubagent(args) {
|
|
64530
|
-
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
64531
|
-
}
|
|
64532
|
-
computeRewindDiff() {
|
|
64533
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
64534
|
-
}
|
|
64535
|
-
listRewindableUserMessageIds() {
|
|
64536
|
-
return [];
|
|
64537
|
-
}
|
|
64538
|
-
};
|
|
64539
|
-
|
|
64540
64770
|
// src/tools/pi.ts
|
|
64541
64771
|
var PI_DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
64542
64772
|
var PI_THINKING_EFFORTS = [
|
|
@@ -64565,20 +64795,20 @@ async function probePi(env = process.env) {
|
|
|
64565
64795
|
return { available: false };
|
|
64566
64796
|
}
|
|
64567
64797
|
function mapPiModels(raw) {
|
|
64568
|
-
const models = arr(
|
|
64569
|
-
const providers = new Set(models.map((m3) =>
|
|
64798
|
+
const models = arr(rec2(raw)?.models).map(rec2).filter((m3) => m3 !== void 0);
|
|
64799
|
+
const providers = new Set(models.map((m3) => str2(m3.provider)).filter(Boolean));
|
|
64570
64800
|
const out = [];
|
|
64571
64801
|
for (const m3 of models) {
|
|
64572
|
-
const id =
|
|
64573
|
-
const provider =
|
|
64802
|
+
const id = str2(m3.id);
|
|
64803
|
+
const provider = str2(m3.provider);
|
|
64574
64804
|
if (!id || !provider) continue;
|
|
64575
|
-
const cw =
|
|
64805
|
+
const cw = num2(m3.contextWindow);
|
|
64576
64806
|
const levels = arr(m3.thinkingLevels).filter((v3) => typeof v3 === "string");
|
|
64577
64807
|
const efforts = levels.length ? levels.map((v3) => PI_THINKING_EFFORTS.find((e) => e.value === v3) ?? { value: v3, label: v3 }) : m3.reasoning === true ? PI_THINKING_EFFORTS : [PI_THINKING_EFFORTS[0]];
|
|
64578
64808
|
out.push({
|
|
64579
64809
|
id: `${provider}/${id}`,
|
|
64580
64810
|
// 与 --model 同一串
|
|
64581
|
-
label:
|
|
64811
|
+
label: str2(m3.name) || id,
|
|
64582
64812
|
contextWindowSize: cw && cw > 0 ? cw : PI_DEFAULT_CONTEXT_WINDOW,
|
|
64583
64813
|
efforts,
|
|
64584
64814
|
// 分组按 provider;只有一个 provider 时不分组(一个横跨全表的标题没信息量,同 codex)
|
|
@@ -64644,18 +64874,18 @@ async function queryPiModels(deps = {}) {
|
|
|
64644
64874
|
try {
|
|
64645
64875
|
const r2 = await race(client.request({ type: "get_available_models" }));
|
|
64646
64876
|
if (!r2.success) throw new Error(r2.error ?? "get_available_models failed");
|
|
64647
|
-
const models = arr(
|
|
64877
|
+
const models = arr(rec2(r2.data)?.models).map(rec2).filter((m3) => m3 !== void 0);
|
|
64648
64878
|
const enriched = [];
|
|
64649
64879
|
for (const m3 of models) {
|
|
64650
|
-
const provider =
|
|
64651
|
-
const id =
|
|
64880
|
+
const provider = str2(m3.provider);
|
|
64881
|
+
const id = str2(m3.id);
|
|
64652
64882
|
let thinkingLevels;
|
|
64653
64883
|
if (provider && id && !timedOut) {
|
|
64654
64884
|
try {
|
|
64655
64885
|
const set = await race(client.request({ type: "set_model", provider, modelId: id }));
|
|
64656
64886
|
if (set.success) {
|
|
64657
64887
|
const lv = await race(client.request({ type: "get_available_thinking_levels" }));
|
|
64658
|
-
const levels = arr(
|
|
64888
|
+
const levels = arr(rec2(lv.data)?.levels).filter((v3) => typeof v3 === "string");
|
|
64659
64889
|
if (lv.success && levels.length > 0) thinkingLevels = levels;
|
|
64660
64890
|
}
|
|
64661
64891
|
} catch {
|
|
@@ -64748,7 +64978,9 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
64748
64978
|
kill: () => stream.close(),
|
|
64749
64979
|
// 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
|
|
64750
64980
|
onTurnStarting: () => channel?.markNextLineTurnStart(),
|
|
64751
|
-
|
|
64981
|
+
// 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
|
|
64982
|
+
onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
|
|
64983
|
+
now: deps.now
|
|
64752
64984
|
});
|
|
64753
64985
|
if (!personaId2) {
|
|
64754
64986
|
queueMicrotask(() => {
|
|
@@ -64777,42 +65009,6 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
64777
65009
|
return driver.session;
|
|
64778
65010
|
}
|
|
64779
65011
|
|
|
64780
|
-
// src/tools/pi-cloud-history.ts
|
|
64781
|
-
var CloudPiHistoryReader = class {
|
|
64782
|
-
constructor(client, logger) {
|
|
64783
|
-
this.client = client;
|
|
64784
|
-
this.logger = logger;
|
|
64785
|
-
}
|
|
64786
|
-
client;
|
|
64787
|
-
logger;
|
|
64788
|
-
async listProjects() {
|
|
64789
|
-
return [];
|
|
64790
|
-
}
|
|
64791
|
-
/** 云 persona 没有「关联已有会话」的动线:会话列表就是本机的会话记录 */
|
|
64792
|
-
async listSessions() {
|
|
64793
|
-
return [];
|
|
64794
|
-
}
|
|
64795
|
-
async read(args) {
|
|
64796
|
-
const text = await this.client.fetchRollout(args.toolSessionId);
|
|
64797
|
-
if (text === null) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
64798
|
-
const parsed = parsePiSession(text);
|
|
64799
|
-
if (!parsed.meta) this.logger?.warn("cloud pi session has no header line", { toolSessionId: args.toolSessionId });
|
|
64800
|
-
return pageMessages(parsed.messages, args);
|
|
64801
|
-
}
|
|
64802
|
-
async listSubagents(_args) {
|
|
64803
|
-
return [];
|
|
64804
|
-
}
|
|
64805
|
-
async readSubagent(args) {
|
|
64806
|
-
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
64807
|
-
}
|
|
64808
|
-
computeRewindDiff() {
|
|
64809
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
64810
|
-
}
|
|
64811
|
-
listRewindableUserMessageIds() {
|
|
64812
|
-
return [];
|
|
64813
|
-
}
|
|
64814
|
-
};
|
|
64815
|
-
|
|
64816
65012
|
// src/tools/pi-cloud.ts
|
|
64817
65013
|
var CLOUD_APPROVAL2 = { id: "never", label: "Full Access", description: "\u4E91\u4E0A\u65E0\u4EBA\u503C\u5B88\uFF1A\u4ECE\u4E0D\u8BE2\u95EE\uFF0C\u81EA\u4E3B\u6267\u884C" };
|
|
64818
65014
|
var CLOUD_PI_CAPABILITIES = {
|
|
@@ -72088,7 +72284,7 @@ function computeMethodAccess(args) {
|
|
|
72088
72284
|
}
|
|
72089
72285
|
|
|
72090
72286
|
// src/version.ts
|
|
72091
|
-
var version = "0.2.
|
|
72287
|
+
var version = "0.2.584".length > 0 ? "0.2.584" : "dev";
|
|
72092
72288
|
|
|
72093
72289
|
// src/cli-probe/probe.ts
|
|
72094
72290
|
var fs80 = __toESM(require("fs"), 1);
|
|
@@ -75769,6 +75965,18 @@ async function startDaemon(config) {
|
|
|
75769
75965
|
githubIdentity,
|
|
75770
75966
|
githubApi: (token) => createGithubApi({ token }),
|
|
75771
75967
|
org: PERSONA_REPO_ORG,
|
|
75968
|
+
// 发布者认领(design 2026-09-24-cloud-persona-conversations §3.1):身份由中心从登录票里取;409 = 名字是别人的
|
|
75969
|
+
claimPublisher: async (personaId2) => {
|
|
75970
|
+
const loginPass = ownerIdentityStore.read()?.loginPass;
|
|
75971
|
+
if (!loginPass) throw new Error("\u8FD8\u6CA1\u767B\u5F55 clawd\uFF1A\u5148\u5728\u8BBE\u7F6E\u91CC\u767B\u5F55\u98DE\u4E66\uFF0C\u518D\u53D1\u5E03");
|
|
75972
|
+
try {
|
|
75973
|
+
await centralClaimPublisher({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass, personaId: personaId2 });
|
|
75974
|
+
return "ok";
|
|
75975
|
+
} catch (err) {
|
|
75976
|
+
if (err instanceof CentralClientError && err.status === 409) return "taken";
|
|
75977
|
+
throw new Error(`\u5411\u4E2D\u5FC3\u767B\u8BB0\u53D1\u5E03\u8005\u5931\u8D25\uFF1A${err.message}`);
|
|
75978
|
+
}
|
|
75979
|
+
},
|
|
75772
75980
|
now: () => Date.now(),
|
|
75773
75981
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
75774
75982
|
// 在不在线上 = gateway 正式登记表里有没有它(design 2026-09-24-deploy-unpublish §3.3):跟 persona 列表的云卡片同一个来源。
|
|
@@ -75909,13 +76117,19 @@ async function startDaemon(config) {
|
|
|
75909
76117
|
const viewerAssetLoader = tryLoadViewerAssets(logger);
|
|
75910
76118
|
const shareUiAssetLoader = tryLoadShareUi(logger);
|
|
75911
76119
|
const dashboardPages = loadDashboardPages(logger);
|
|
76120
|
+
const dashboardApi = () => config.clawosApi ?? DEFAULT_CLAWOS_API;
|
|
75912
76121
|
const dashboardRoutes = createDashboardRoutes({
|
|
75913
76122
|
getSecret: () => authFile.token,
|
|
75914
76123
|
loadPageHtml: (page) => dashboardPages.get(page) ?? null,
|
|
75915
|
-
|
|
75916
|
-
|
|
75917
|
-
|
|
75918
|
-
|
|
76124
|
+
data: createDashboardData({
|
|
76125
|
+
loginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
|
|
76126
|
+
registeredPersonas: async () => (await cloudGateway.listPersonas()).map((p3) => p3.personaId),
|
|
76127
|
+
visiblePersonas: (loginPass) => centralVisiblePersonas({ api: dashboardApi(), loginPass }),
|
|
76128
|
+
metrics: (q3) => cloudGateway.fetchMetrics(q3),
|
|
76129
|
+
threads: (loginPass, q3) => centralPersonaThreads({ api: dashboardApi(), loginPass, ...q3 }),
|
|
76130
|
+
thread: (loginPass, q3) => centralPersonaThread({ api: dashboardApi(), loginPass, ...q3 }),
|
|
76131
|
+
logger
|
|
76132
|
+
})
|
|
75919
76133
|
});
|
|
75920
76134
|
const httpRouter = createHttpRouter({
|
|
75921
76135
|
authResolver,
|