@clawos-dev/clawd 0.2.582 → 0.2.583
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 +1871 -1688
- package/dist/cloud-persona-conversations.html +312 -0
- package/dist/cloud-persona-metrics.html +19 -19
- 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
|
|
|
@@ -52340,17 +53584,17 @@ function stringValue2(value) {
|
|
|
52340
53584
|
}
|
|
52341
53585
|
|
|
52342
53586
|
// src/tools/mcp-server-defs.ts
|
|
52343
|
-
var
|
|
52344
|
-
var
|
|
53587
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
53588
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
52345
53589
|
var HTTP_TYPES = /* @__PURE__ */ new Set(["http", "sse", "streamable_http", "streamable-http"]);
|
|
52346
53590
|
function loadMcpServerDefs(args) {
|
|
52347
53591
|
const out = [];
|
|
52348
53592
|
const seen = /* @__PURE__ */ new Set();
|
|
52349
|
-
const sources = [...args.mcpConfigPaths,
|
|
53593
|
+
const sources = [...args.mcpConfigPaths, import_node_path19.default.join(args.cwd, ".mcp.json")];
|
|
52350
53594
|
for (const file of sources) {
|
|
52351
53595
|
let parsed;
|
|
52352
53596
|
try {
|
|
52353
|
-
parsed = JSON.parse(
|
|
53597
|
+
parsed = JSON.parse(import_node_fs21.default.readFileSync(file, "utf8"));
|
|
52354
53598
|
} catch (e) {
|
|
52355
53599
|
const code = e.code;
|
|
52356
53600
|
if (code !== "ENOENT") {
|
|
@@ -52414,18 +53658,18 @@ function loadMcpServerDefs(args) {
|
|
|
52414
53658
|
}
|
|
52415
53659
|
|
|
52416
53660
|
// src/tools/codex-agents-config.ts
|
|
52417
|
-
var
|
|
52418
|
-
var
|
|
53661
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
53662
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
52419
53663
|
var AGENTS_SECTION = /^\[agents\][^\n]*\n([\s\S]*?)(?=^\[|$(?![\s\S]))/m;
|
|
52420
53664
|
function readKey(section, key) {
|
|
52421
53665
|
const m3 = new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*(["'])(.*?)\\1`, "m").exec(section);
|
|
52422
53666
|
return m3?.[2].trim() || void 0;
|
|
52423
53667
|
}
|
|
52424
53668
|
function loadCodexAgentsConfig(args) {
|
|
52425
|
-
const file =
|
|
53669
|
+
const file = import_node_path20.default.join(args.cwd, ".codex", "config.toml");
|
|
52426
53670
|
let text;
|
|
52427
53671
|
try {
|
|
52428
|
-
text =
|
|
53672
|
+
text = import_node_fs22.default.readFileSync(file, "utf8");
|
|
52429
53673
|
} catch (e) {
|
|
52430
53674
|
const code = e.code;
|
|
52431
53675
|
if (code !== "ENOENT") {
|
|
@@ -52481,7 +53725,7 @@ function warmCodexFeatureCache() {
|
|
|
52481
53725
|
}
|
|
52482
53726
|
|
|
52483
53727
|
// src/attachment/mime.ts
|
|
52484
|
-
var
|
|
53728
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
52485
53729
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
52486
53730
|
var EXT_TO_NATIVE_MIME = {
|
|
52487
53731
|
// 图片
|
|
@@ -52588,7 +53832,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
52588
53832
|
".mk"
|
|
52589
53833
|
]);
|
|
52590
53834
|
function lookupMime(filePathOrName) {
|
|
52591
|
-
const ext =
|
|
53835
|
+
const ext = import_node_path21.default.extname(filePathOrName).toLowerCase();
|
|
52592
53836
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
52593
53837
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
52594
53838
|
return "application/octet-stream";
|
|
@@ -52637,7 +53881,7 @@ function createCodexSession(ctx, sink, deps = {}) {
|
|
|
52637
53881
|
threadParams: threadStartParams(ctx),
|
|
52638
53882
|
// persona 惯例装在 <cwd>/.claude/skills/,经 extraRoots 注入 discovery
|
|
52639
53883
|
// (skills:list RPC 侧 codex-skills.ts 同款注入,两处保持一致)。
|
|
52640
|
-
skillsExtraRoots: [
|
|
53884
|
+
skillsExtraRoots: [import_node_path22.default.join(ctx.cwd, ".claude", "skills")],
|
|
52641
53885
|
// 附件图片:验签认出本机 absPath → localImage,codex 自己读文件(TUI 拖图 / SDK local_image 同一条路)。
|
|
52642
53886
|
// 0.145 起 http(s) URL 在 turn/start 入口被拒,不能再按 url 传(codex-app-server-params.ts 头注释)。
|
|
52643
53887
|
// 与云上 cloudImageInput 同一道 mime 门:非图片(签过的 .md 之类)留文本,别让 codex 读到一半才炸。
|
|
@@ -52791,18 +54035,18 @@ function mergeProviderModels(native, provider) {
|
|
|
52791
54035
|
|
|
52792
54036
|
// src/tools/codex-provider-models.ts
|
|
52793
54037
|
var import_node_child_process6 = require("child_process");
|
|
52794
|
-
var
|
|
52795
|
-
var
|
|
52796
|
-
var
|
|
54038
|
+
var import_node_fs23 = __toESM(require("fs"), 1);
|
|
54039
|
+
var import_node_os9 = __toESM(require("os"), 1);
|
|
54040
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
52797
54041
|
var import_node_util = require("util");
|
|
52798
54042
|
var PROVIDER_FETCH_TIMEOUT_MS = 3e3;
|
|
52799
|
-
function
|
|
54043
|
+
function asRecord2(v3) {
|
|
52800
54044
|
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : null;
|
|
52801
54045
|
}
|
|
52802
54046
|
function readAuthKey(codexHome) {
|
|
52803
54047
|
try {
|
|
52804
|
-
const raw = JSON.parse(
|
|
52805
|
-
const key =
|
|
54048
|
+
const raw = JSON.parse(import_node_fs23.default.readFileSync(import_node_path23.default.join(codexHome, "auth.json"), "utf8"));
|
|
54049
|
+
const key = asRecord2(raw)?.OPENAI_API_KEY;
|
|
52806
54050
|
return typeof key === "string" && key ? key : void 0;
|
|
52807
54051
|
} catch {
|
|
52808
54052
|
return void 0;
|
|
@@ -52829,19 +54073,19 @@ async function runAuthCommand(name, auth, deps) {
|
|
|
52829
54073
|
return void 0;
|
|
52830
54074
|
}
|
|
52831
54075
|
async function resolveProviderEndpoint(rawConfig, deps = {}) {
|
|
52832
|
-
const config =
|
|
54076
|
+
const config = asRecord2(asRecord2(rawConfig)?.config);
|
|
52833
54077
|
if (!config) return null;
|
|
52834
54078
|
const name = config.model_provider;
|
|
52835
54079
|
if (typeof name !== "string" || !name) return null;
|
|
52836
|
-
const entry =
|
|
54080
|
+
const entry = asRecord2(asRecord2(config.model_providers)?.[name]);
|
|
52837
54081
|
if (!entry) return null;
|
|
52838
54082
|
const baseUrl = entry.base_url;
|
|
52839
54083
|
if (typeof baseUrl !== "string" || !baseUrl) return null;
|
|
52840
54084
|
const env = deps.env ?? process.env;
|
|
52841
|
-
const codexHome = deps.codexHome ?? env.CODEX_HOME ??
|
|
54085
|
+
const codexHome = deps.codexHome ?? env.CODEX_HOME ?? import_node_path23.default.join(import_node_os9.default.homedir(), ".codex");
|
|
52842
54086
|
const envKey = typeof entry.env_key === "string" && entry.env_key ? entry.env_key : null;
|
|
52843
54087
|
const inlineToken = typeof entry.experimental_bearer_token === "string" && entry.experimental_bearer_token ? entry.experimental_bearer_token : void 0;
|
|
52844
|
-
const auth =
|
|
54088
|
+
const auth = asRecord2(entry.auth);
|
|
52845
54089
|
let token;
|
|
52846
54090
|
if (envKey) token = env[envKey];
|
|
52847
54091
|
else if (inlineToken) token = inlineToken;
|
|
@@ -52869,11 +54113,11 @@ async function fetchProviderModels(endpoint, deps = {}) {
|
|
|
52869
54113
|
try {
|
|
52870
54114
|
const res = await fetchImpl(url, { headers: endpoint.headers, signal: controller.signal });
|
|
52871
54115
|
if (!res.ok) throw new Error(`provider model list failed: HTTP ${res.status} from ${url}`);
|
|
52872
|
-
const data =
|
|
54116
|
+
const data = asRecord2(await res.json())?.data;
|
|
52873
54117
|
if (!Array.isArray(data)) throw new Error(`provider model list: \`data\` is not an array (${url})`);
|
|
52874
54118
|
const out = [];
|
|
52875
54119
|
for (const item of data) {
|
|
52876
|
-
const e =
|
|
54120
|
+
const e = asRecord2(item);
|
|
52877
54121
|
if (!e || typeof e.id !== "string" || !e.id) continue;
|
|
52878
54122
|
out.push({ id: e.id, ...typeof e.owned_by === "string" ? { ownedBy: e.owned_by } : {} });
|
|
52879
54123
|
}
|
|
@@ -52953,10 +54197,10 @@ function sanitizeCodexModel(model) {
|
|
|
52953
54197
|
return model;
|
|
52954
54198
|
}
|
|
52955
54199
|
async function probeCodex(env = process.env) {
|
|
52956
|
-
if (env.CODEX_BIN &&
|
|
54200
|
+
if (env.CODEX_BIN && import_node_fs24.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
|
|
52957
54201
|
try {
|
|
52958
54202
|
const out = (0, import_node_child_process7.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
|
|
52959
|
-
if (out &&
|
|
54203
|
+
if (out && import_node_fs24.default.existsSync(out)) return { available: true, path: out };
|
|
52960
54204
|
} catch {
|
|
52961
54205
|
}
|
|
52962
54206
|
return { available: false };
|
|
@@ -53076,9 +54320,9 @@ var CodexAdapter = class {
|
|
|
53076
54320
|
|
|
53077
54321
|
// src/tools/claude-tui.ts
|
|
53078
54322
|
var import_node_crypto7 = require("crypto");
|
|
53079
|
-
var
|
|
53080
|
-
var
|
|
53081
|
-
var
|
|
54323
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
54324
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
54325
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
53082
54326
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
53083
54327
|
|
|
53084
54328
|
// ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
|
|
@@ -54303,10 +55547,10 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
|
|
|
54303
55547
|
}
|
|
54304
55548
|
function jsonlExistsForCtx(ctx) {
|
|
54305
55549
|
if (!ctx.toolSessionId) return false;
|
|
54306
|
-
const home =
|
|
54307
|
-
const file =
|
|
55550
|
+
const home = import_node_os10.default.homedir();
|
|
55551
|
+
const file = import_node_path24.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
|
|
54308
55552
|
try {
|
|
54309
|
-
return
|
|
55553
|
+
return import_node_fs25.default.statSync(file).isFile();
|
|
54310
55554
|
} catch {
|
|
54311
55555
|
return false;
|
|
54312
55556
|
}
|
|
@@ -54391,7 +55635,7 @@ var PersonaDispatchManager = class {
|
|
|
54391
55635
|
|
|
54392
55636
|
// src/dispatch/store.ts
|
|
54393
55637
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
54394
|
-
var
|
|
55638
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
54395
55639
|
|
|
54396
55640
|
// src/shift/constants.ts
|
|
54397
55641
|
var MAX_RUNS_PER_SHIFT = 30;
|
|
@@ -54429,7 +55673,7 @@ function createDispatchStore(deps) {
|
|
|
54429
55673
|
flushTimer = null;
|
|
54430
55674
|
}
|
|
54431
55675
|
const content = { version: 1, records };
|
|
54432
|
-
await import_promises.default.mkdir(
|
|
55676
|
+
await import_promises.default.mkdir(import_node_path25.default.dirname(deps.filePath), { recursive: true });
|
|
54433
55677
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54434
55678
|
await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54435
55679
|
await import_promises.default.rename(tmp, deps.filePath);
|
|
@@ -54498,7 +55742,7 @@ function createDispatchStore(deps) {
|
|
|
54498
55742
|
|
|
54499
55743
|
// src/channel/json-store.ts
|
|
54500
55744
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
54501
|
-
var
|
|
55745
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
54502
55746
|
function createJsonStore(deps) {
|
|
54503
55747
|
let items = [];
|
|
54504
55748
|
let flushTimer = null;
|
|
@@ -54509,7 +55753,7 @@ function createJsonStore(deps) {
|
|
|
54509
55753
|
flushTimer = null;
|
|
54510
55754
|
}
|
|
54511
55755
|
const content = { version: version2, [deps.key]: items };
|
|
54512
|
-
await import_promises2.default.mkdir(
|
|
55756
|
+
await import_promises2.default.mkdir(import_node_path26.default.dirname(deps.filePath), { recursive: true });
|
|
54513
55757
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54514
55758
|
await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54515
55759
|
await import_promises2.default.rename(tmp, deps.filePath);
|
|
@@ -54801,10 +56045,10 @@ function createTopicStore(deps) {
|
|
|
54801
56045
|
}
|
|
54802
56046
|
|
|
54803
56047
|
// src/dispatch/mcp-config.ts
|
|
54804
|
-
var
|
|
54805
|
-
var
|
|
56048
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
56049
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
54806
56050
|
function dispatchMcpConfigPath(dataDir) {
|
|
54807
|
-
return
|
|
56051
|
+
return import_node_path27.default.join(dataDir, "dispatch.mcp.json");
|
|
54808
56052
|
}
|
|
54809
56053
|
function writeDispatchMcpConfig(args) {
|
|
54810
56054
|
const cfgPath = dispatchMcpConfigPath(args.dataDir);
|
|
@@ -54823,16 +56067,16 @@ function writeDispatchMcpConfig(args) {
|
|
|
54823
56067
|
}
|
|
54824
56068
|
}
|
|
54825
56069
|
};
|
|
54826
|
-
|
|
54827
|
-
|
|
56070
|
+
import_node_fs26.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56071
|
+
import_node_fs26.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54828
56072
|
return cfgPath;
|
|
54829
56073
|
}
|
|
54830
56074
|
|
|
54831
56075
|
// src/ticket/mcp-config.ts
|
|
54832
|
-
var
|
|
54833
|
-
var
|
|
56076
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
56077
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
54834
56078
|
function ticketMcpConfigPath(dataDir) {
|
|
54835
|
-
return
|
|
56079
|
+
return import_node_path28.default.join(dataDir, "ticket.mcp.json");
|
|
54836
56080
|
}
|
|
54837
56081
|
function writeTicketMcpConfig(args) {
|
|
54838
56082
|
const cfgPath = ticketMcpConfigPath(args.dataDir);
|
|
@@ -54854,16 +56098,16 @@ function writeTicketMcpConfig(args) {
|
|
|
54854
56098
|
}
|
|
54855
56099
|
}
|
|
54856
56100
|
};
|
|
54857
|
-
|
|
54858
|
-
|
|
56101
|
+
import_node_fs27.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56102
|
+
import_node_fs27.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54859
56103
|
return cfgPath;
|
|
54860
56104
|
}
|
|
54861
56105
|
|
|
54862
56106
|
// src/shift/mcp-config.ts
|
|
54863
|
-
var
|
|
54864
|
-
var
|
|
56107
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
56108
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
54865
56109
|
function shiftMcpConfigPath(dataDir) {
|
|
54866
|
-
return
|
|
56110
|
+
return import_node_path29.default.join(dataDir, "shift.mcp.json");
|
|
54867
56111
|
}
|
|
54868
56112
|
async function writeShiftMcpConfig(args) {
|
|
54869
56113
|
const cfgPath = shiftMcpConfigPath(args.dataDir);
|
|
@@ -54882,16 +56126,16 @@ async function writeShiftMcpConfig(args) {
|
|
|
54882
56126
|
}
|
|
54883
56127
|
}
|
|
54884
56128
|
};
|
|
54885
|
-
await
|
|
54886
|
-
await
|
|
56129
|
+
await import_node_fs28.default.promises.mkdir(args.dataDir, { recursive: true });
|
|
56130
|
+
await import_node_fs28.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54887
56131
|
return cfgPath;
|
|
54888
56132
|
}
|
|
54889
56133
|
|
|
54890
56134
|
// src/inbox/mcp-config.ts
|
|
54891
|
-
var
|
|
54892
|
-
var
|
|
56135
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
56136
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
54893
56137
|
function inboxMcpConfigPath(dataDir) {
|
|
54894
|
-
return
|
|
56138
|
+
return import_node_path30.default.join(dataDir, "inbox.mcp.json");
|
|
54895
56139
|
}
|
|
54896
56140
|
async function writeInboxMcpConfig(args) {
|
|
54897
56141
|
const cfgPath = inboxMcpConfigPath(args.dataDir);
|
|
@@ -54910,16 +56154,16 @@ async function writeInboxMcpConfig(args) {
|
|
|
54910
56154
|
}
|
|
54911
56155
|
}
|
|
54912
56156
|
};
|
|
54913
|
-
await
|
|
54914
|
-
await
|
|
56157
|
+
await import_node_fs29.default.promises.mkdir(args.dataDir, { recursive: true });
|
|
56158
|
+
await import_node_fs29.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54915
56159
|
return cfgPath;
|
|
54916
56160
|
}
|
|
54917
56161
|
|
|
54918
56162
|
// src/peer-ops/mcp-config.ts
|
|
54919
|
-
var
|
|
54920
|
-
var
|
|
56163
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
56164
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
54921
56165
|
function peerOpsMcpConfigPath(dataDir) {
|
|
54922
|
-
return
|
|
56166
|
+
return import_node_path31.default.join(dataDir, "peer-ops.mcp.json");
|
|
54923
56167
|
}
|
|
54924
56168
|
function writePeerOpsMcpConfig(args) {
|
|
54925
56169
|
const cfgPath = peerOpsMcpConfigPath(args.dataDir);
|
|
@@ -54936,16 +56180,16 @@ function writePeerOpsMcpConfig(args) {
|
|
|
54936
56180
|
}
|
|
54937
56181
|
}
|
|
54938
56182
|
};
|
|
54939
|
-
|
|
54940
|
-
|
|
56183
|
+
import_node_fs30.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56184
|
+
import_node_fs30.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
54941
56185
|
return cfgPath;
|
|
54942
56186
|
}
|
|
54943
56187
|
|
|
54944
56188
|
// src/rpc-tool/mcp-config.ts
|
|
54945
|
-
var
|
|
54946
|
-
var
|
|
56189
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
56190
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
54947
56191
|
function rpcToolMcpConfigPath(dataDir) {
|
|
54948
|
-
return
|
|
56192
|
+
return import_node_path32.default.join(dataDir, "rpc-tool.mcp.json");
|
|
54949
56193
|
}
|
|
54950
56194
|
function writeRpcToolMcpConfig(args) {
|
|
54951
56195
|
const cfgPath = rpcToolMcpConfigPath(args.dataDir);
|
|
@@ -54964,29 +56208,29 @@ function writeRpcToolMcpConfig(args) {
|
|
|
54964
56208
|
}
|
|
54965
56209
|
}
|
|
54966
56210
|
};
|
|
54967
|
-
|
|
54968
|
-
|
|
56211
|
+
import_node_fs31.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56212
|
+
import_node_fs31.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54969
56213
|
return cfgPath;
|
|
54970
56214
|
}
|
|
54971
56215
|
|
|
54972
56216
|
// src/deploy/secret-table.ts
|
|
54973
|
-
var
|
|
54974
|
-
var
|
|
56217
|
+
var import_node_fs32 = __toESM(require("fs"), 1);
|
|
56218
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
54975
56219
|
var import_node_url5 = require("url");
|
|
54976
56220
|
var import_meta5 = {};
|
|
54977
56221
|
var LOCAL_SECRETS_DIRNAME = "secrets";
|
|
54978
56222
|
function findSecretsBundleRoot(logger) {
|
|
54979
56223
|
const candidates = [];
|
|
54980
56224
|
try {
|
|
54981
|
-
const here =
|
|
54982
|
-
candidates.push(
|
|
56225
|
+
const here = import_node_path33.default.dirname((0, import_node_url5.fileURLToPath)(import_meta5.url));
|
|
56226
|
+
candidates.push(import_node_path33.default.resolve(here, "..", "secrets"));
|
|
54983
56227
|
} catch {
|
|
54984
56228
|
}
|
|
54985
56229
|
const fromArgv = bundleSiblingFromArgv(process.argv[1], "secrets");
|
|
54986
56230
|
if (fromArgv) candidates.push(fromArgv);
|
|
54987
56231
|
for (const c2 of candidates) {
|
|
54988
56232
|
try {
|
|
54989
|
-
if (
|
|
56233
|
+
if (import_node_fs32.default.statSync(c2).isDirectory()) return c2;
|
|
54990
56234
|
} catch {
|
|
54991
56235
|
}
|
|
54992
56236
|
}
|
|
@@ -55012,7 +56256,7 @@ function readDir(dir, logger) {
|
|
|
55012
56256
|
const out = {};
|
|
55013
56257
|
let files;
|
|
55014
56258
|
try {
|
|
55015
|
-
files =
|
|
56259
|
+
files = import_node_fs32.default.readdirSync(dir).filter((f2) => f2.endsWith(".env")).sort();
|
|
55016
56260
|
} catch (e) {
|
|
55017
56261
|
const code = e.code;
|
|
55018
56262
|
if (code !== "ENOENT") logger?.warn("secrets.dir-unreadable", { dir, code: code ?? "unknown" });
|
|
@@ -55020,19 +56264,19 @@ function readDir(dir, logger) {
|
|
|
55020
56264
|
}
|
|
55021
56265
|
for (const f2 of files) {
|
|
55022
56266
|
try {
|
|
55023
|
-
Object.assign(out, parseEnvFile(
|
|
56267
|
+
Object.assign(out, parseEnvFile(import_node_fs32.default.readFileSync(import_node_path33.default.join(dir, f2), "utf8")));
|
|
55024
56268
|
} catch (e) {
|
|
55025
|
-
logger?.warn("secrets.file-unreadable", { file:
|
|
56269
|
+
logger?.warn("secrets.file-unreadable", { file: import_node_path33.default.join(dir, f2), error: e.message });
|
|
55026
56270
|
}
|
|
55027
56271
|
}
|
|
55028
56272
|
return out;
|
|
55029
56273
|
}
|
|
55030
56274
|
function upsertEnvFileVar(file, key, value) {
|
|
55031
56275
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55032
|
-
|
|
56276
|
+
import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(file), { recursive: true, mode: 448 });
|
|
55033
56277
|
let lines = [];
|
|
55034
56278
|
try {
|
|
55035
|
-
lines =
|
|
56279
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55036
56280
|
if (lines[lines.length - 1] === "") lines.pop();
|
|
55037
56281
|
} catch {
|
|
55038
56282
|
}
|
|
@@ -55040,12 +56284,12 @@ function upsertEnvFileVar(file, key, value) {
|
|
|
55040
56284
|
let replaced = false;
|
|
55041
56285
|
const out = lines.map((l2) => re2.test(l2) ? (replaced = true, `${key}=${value}`) : l2);
|
|
55042
56286
|
if (!replaced) out.push(`${key}=${value}`);
|
|
55043
|
-
|
|
56287
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55044
56288
|
`, { encoding: "utf8", mode: 384 });
|
|
55045
56289
|
}
|
|
55046
56290
|
function readEnvFileVar(file, key) {
|
|
55047
56291
|
try {
|
|
55048
|
-
return parseEnvFile(
|
|
56292
|
+
return parseEnvFile(import_node_fs32.default.readFileSync(file, "utf8"))[key];
|
|
55049
56293
|
} catch {
|
|
55050
56294
|
return void 0;
|
|
55051
56295
|
}
|
|
@@ -55054,7 +56298,7 @@ function removeEnvFileVar(file, key) {
|
|
|
55054
56298
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55055
56299
|
let lines;
|
|
55056
56300
|
try {
|
|
55057
|
-
lines =
|
|
56301
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55058
56302
|
} catch {
|
|
55059
56303
|
return;
|
|
55060
56304
|
}
|
|
@@ -55063,10 +56307,10 @@ function removeEnvFileVar(file, key) {
|
|
|
55063
56307
|
const out = lines.filter((l2) => !re2.test(l2));
|
|
55064
56308
|
if (out.length === lines.length) return;
|
|
55065
56309
|
if (out.every((l2) => l2.trim() === "" || l2.trim().startsWith("#"))) {
|
|
55066
|
-
|
|
56310
|
+
import_node_fs32.default.rmSync(file, { force: true });
|
|
55067
56311
|
return;
|
|
55068
56312
|
}
|
|
55069
|
-
|
|
56313
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55070
56314
|
`, { encoding: "utf8", mode: 384 });
|
|
55071
56315
|
}
|
|
55072
56316
|
function readSecretTable(args) {
|
|
@@ -55077,12 +56321,12 @@ function readSecretTable(args) {
|
|
|
55077
56321
|
}
|
|
55078
56322
|
|
|
55079
56323
|
// src/dispatch/source-transcript.ts
|
|
55080
|
-
var
|
|
56324
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
55081
56325
|
init_claude_history();
|
|
55082
56326
|
var NO_SOURCE_TRANSCRIPT = "(no transcript yet \u2014 operate from the task description alone)";
|
|
55083
56327
|
function resolveSourceJsonlPath(sourceFile, home) {
|
|
55084
56328
|
if (!sourceFile?.toolSessionId) return NO_SOURCE_TRANSCRIPT;
|
|
55085
|
-
return
|
|
56329
|
+
return import_node_path34.default.join(
|
|
55086
56330
|
home,
|
|
55087
56331
|
".claude",
|
|
55088
56332
|
"projects",
|
|
@@ -55093,7 +56337,7 @@ function resolveSourceJsonlPath(sourceFile, home) {
|
|
|
55093
56337
|
|
|
55094
56338
|
// src/shift/store.ts
|
|
55095
56339
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
55096
|
-
var
|
|
56340
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
55097
56341
|
var import_node_crypto8 = require("crypto");
|
|
55098
56342
|
|
|
55099
56343
|
// src/shift/schedule.ts
|
|
@@ -55168,7 +56412,7 @@ function createShiftStore(deps) {
|
|
|
55168
56412
|
flushTimer = null;
|
|
55169
56413
|
}
|
|
55170
56414
|
const content = { version: 1, shifts };
|
|
55171
|
-
await import_promises3.default.mkdir(
|
|
56415
|
+
await import_promises3.default.mkdir(import_node_path35.default.dirname(deps.filePath), { recursive: true });
|
|
55172
56416
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
55173
56417
|
await import_promises3.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
55174
56418
|
await import_promises3.default.rename(tmp, deps.filePath);
|
|
@@ -55622,14 +56866,14 @@ function redactEvidence(evidence) {
|
|
|
55622
56866
|
|
|
55623
56867
|
// src/deploy/deploy-file.ts
|
|
55624
56868
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
55625
|
-
var
|
|
56869
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
55626
56870
|
var import_node_crypto9 = require("crypto");
|
|
55627
56871
|
init_zod();
|
|
55628
56872
|
init_protocol();
|
|
55629
56873
|
|
|
55630
56874
|
// src/deploy/connectors.ts
|
|
55631
|
-
var
|
|
55632
|
-
var
|
|
56875
|
+
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
56876
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
55633
56877
|
init_zod();
|
|
55634
56878
|
init_protocol();
|
|
55635
56879
|
var PLACEHOLDER2 = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -55679,11 +56923,11 @@ var PACKAGE_RUNNERS = /* @__PURE__ */ new Set(["npx", "uvx", "pnpx", "bunx"]);
|
|
|
55679
56923
|
var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["node", "bun", "deno", "python", "python3"]);
|
|
55680
56924
|
var HOST_ARG_PLACEHOLDER = "CLAWD_PERSONA_DIR";
|
|
55681
56925
|
function scanMcpConnectors(personaDir) {
|
|
55682
|
-
const file =
|
|
56926
|
+
const file = import_node_path36.default.join(personaDir, ".mcp.json");
|
|
55683
56927
|
const out = { readable: true, connectors: [], evidence: [], warnings: [] };
|
|
55684
56928
|
let parsed;
|
|
55685
56929
|
try {
|
|
55686
|
-
parsed = JSON.parse(
|
|
56930
|
+
parsed = JSON.parse(import_node_fs33.default.readFileSync(file, "utf8"));
|
|
55687
56931
|
} catch (e) {
|
|
55688
56932
|
if (e.code === "ENOENT") return out;
|
|
55689
56933
|
out.readable = false;
|
|
@@ -55778,8 +57022,8 @@ function mergeScannedConnectors(existing, scanned) {
|
|
|
55778
57022
|
}
|
|
55779
57023
|
|
|
55780
57024
|
// src/deploy/deploy-file.ts
|
|
55781
|
-
var DEPLOY_FILE =
|
|
55782
|
-
var SCAN_FILE =
|
|
57025
|
+
var DEPLOY_FILE = import_node_path37.default.join(".clawd", "deploy.json");
|
|
57026
|
+
var SCAN_FILE = import_node_path37.default.join(".clawd", "deploy-scan.json");
|
|
55783
57027
|
async function readJson(file) {
|
|
55784
57028
|
let raw;
|
|
55785
57029
|
try {
|
|
@@ -55795,14 +57039,14 @@ async function readJson(file) {
|
|
|
55795
57039
|
}
|
|
55796
57040
|
}
|
|
55797
57041
|
async function writeJson(file, value) {
|
|
55798
|
-
await import_promises4.default.mkdir(
|
|
57042
|
+
await import_promises4.default.mkdir(import_node_path37.default.dirname(file), { recursive: true });
|
|
55799
57043
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
55800
57044
|
await import_promises4.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
|
|
55801
57045
|
`);
|
|
55802
57046
|
await import_promises4.default.rename(tmp, file);
|
|
55803
57047
|
}
|
|
55804
57048
|
async function readDeployFile(dir) {
|
|
55805
|
-
const raw = await readJson(
|
|
57049
|
+
const raw = await readJson(import_node_path37.default.join(dir, DEPLOY_FILE));
|
|
55806
57050
|
if (raw === void 0) return void 0;
|
|
55807
57051
|
const parsed = DeployFileSchema.safeParse(raw);
|
|
55808
57052
|
if (!parsed.success) {
|
|
@@ -55812,18 +57056,18 @@ async function readDeployFile(dir) {
|
|
|
55812
57056
|
return parsed.data;
|
|
55813
57057
|
}
|
|
55814
57058
|
async function writeDeployFile(dir, file) {
|
|
55815
|
-
await writeJson(
|
|
57059
|
+
await writeJson(import_node_path37.default.join(dir, DEPLOY_FILE), DeployFileSchema.parse(file));
|
|
55816
57060
|
}
|
|
55817
57061
|
async function readDeployFileRaw(dir) {
|
|
55818
57062
|
try {
|
|
55819
|
-
return await import_promises4.default.readFile(
|
|
57063
|
+
return await import_promises4.default.readFile(import_node_path37.default.join(dir, DEPLOY_FILE), "utf8");
|
|
55820
57064
|
} catch (err) {
|
|
55821
57065
|
if (err.code === "ENOENT") return void 0;
|
|
55822
57066
|
throw err;
|
|
55823
57067
|
}
|
|
55824
57068
|
}
|
|
55825
57069
|
async function restoreDeployFileRaw(dir, raw) {
|
|
55826
|
-
const file =
|
|
57070
|
+
const file = import_node_path37.default.join(dir, DEPLOY_FILE);
|
|
55827
57071
|
if (raw === void 0) await import_promises4.default.rm(file, { force: true });
|
|
55828
57072
|
else await import_promises4.default.writeFile(file, raw);
|
|
55829
57073
|
}
|
|
@@ -55864,7 +57108,7 @@ function normalizeLegacyScan(raw) {
|
|
|
55864
57108
|
return fix(raw);
|
|
55865
57109
|
}
|
|
55866
57110
|
async function readScanFile(dir) {
|
|
55867
|
-
const raw = normalizeLegacyScan(await readJson(
|
|
57111
|
+
const raw = normalizeLegacyScan(await readJson(import_node_path37.default.join(dir, SCAN_FILE)));
|
|
55868
57112
|
if (raw === void 0) return void 0;
|
|
55869
57113
|
const badShape = (issues) => {
|
|
55870
57114
|
const first = issues[0];
|
|
@@ -55885,7 +57129,7 @@ async function readScanFile(dir) {
|
|
|
55885
57129
|
return parsed.data;
|
|
55886
57130
|
}
|
|
55887
57131
|
async function writeScanFile(dir, file) {
|
|
55888
|
-
await writeJson(
|
|
57132
|
+
await writeJson(import_node_path37.default.join(dir, SCAN_FILE), DeployScanFileSchema.parse(file));
|
|
55889
57133
|
}
|
|
55890
57134
|
function replaceScan(file, entry) {
|
|
55891
57135
|
const idx = file.scans.findIndex((s2) => s2.id === entry.id);
|
|
@@ -55968,9 +57212,9 @@ function requiredEnvNames(manifest) {
|
|
|
55968
57212
|
|
|
55969
57213
|
// src/deploy/artifact.ts
|
|
55970
57214
|
var import_node_crypto10 = require("crypto");
|
|
55971
|
-
var
|
|
55972
|
-
var
|
|
55973
|
-
var
|
|
57215
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
57216
|
+
var import_node_os11 = __toESM(require("os"), 1);
|
|
57217
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
55974
57218
|
var import_node_stream3 = require("stream");
|
|
55975
57219
|
var import_promises5 = require("stream/promises");
|
|
55976
57220
|
var ARTIFACT_MAX_BYTES = 200 * 1024 * 1024;
|
|
@@ -56004,8 +57248,8 @@ function downloadArtifact(exec) {
|
|
|
56004
57248
|
if (!res.ok || !res.body) throw new Error(`\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}\uFF1A${url}`);
|
|
56005
57249
|
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
56006
57250
|
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 =
|
|
57251
|
+
const dir = await import_node_fs34.default.promises.mkdtemp(import_node_path38.default.join(import_node_os11.default.tmpdir(), "clawd-artifact-"));
|
|
57252
|
+
const file = import_node_path38.default.join(dir, "pkg");
|
|
56009
57253
|
const hash = (0, import_node_crypto10.createHash)("sha256");
|
|
56010
57254
|
let size = 0;
|
|
56011
57255
|
const meter = new import_node_stream3.Transform({
|
|
@@ -56017,7 +57261,7 @@ function downloadArtifact(exec) {
|
|
|
56017
57261
|
}
|
|
56018
57262
|
});
|
|
56019
57263
|
try {
|
|
56020
|
-
await (0, import_promises5.pipeline)(import_node_stream3.Readable.fromWeb(res.body), meter,
|
|
57264
|
+
await (0, import_promises5.pipeline)(import_node_stream3.Readable.fromWeb(res.body), meter, import_node_fs34.default.createWriteStream(file));
|
|
56021
57265
|
let entries;
|
|
56022
57266
|
if (listEntries) {
|
|
56023
57267
|
const r2 = await exec("tar", ["-tzf", file], { timeoutMs: 3e4 });
|
|
@@ -56026,7 +57270,7 @@ function downloadArtifact(exec) {
|
|
|
56026
57270
|
}
|
|
56027
57271
|
return { sha256: hash.digest("hex"), size, ...entries ? { entries } : {} };
|
|
56028
57272
|
} finally {
|
|
56029
|
-
await
|
|
57273
|
+
await import_node_fs34.default.promises.rm(dir, { recursive: true, force: true });
|
|
56030
57274
|
}
|
|
56031
57275
|
};
|
|
56032
57276
|
}
|
|
@@ -56077,7 +57321,7 @@ init_protocol();
|
|
|
56077
57321
|
|
|
56078
57322
|
// src/deploy/repo.ts
|
|
56079
57323
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56080
|
-
var
|
|
57324
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
56081
57325
|
init_protocol();
|
|
56082
57326
|
|
|
56083
57327
|
// ../node_modules/.pnpm/libsodium@0.8.4/node_modules/libsodium/dist/modules-esm/libsodium.mjs
|
|
@@ -60019,7 +61263,7 @@ var runCommand = (cmd, args, opts = {}) => new Promise((resolve9) => {
|
|
|
60019
61263
|
});
|
|
60020
61264
|
|
|
60021
61265
|
// src/deploy/repo.ts
|
|
60022
|
-
var DEPLOY_FILE_REL = DEPLOY_FILE.split(
|
|
61266
|
+
var DEPLOY_FILE_REL = DEPLOY_FILE.split(import_node_path39.default.sep).join("/");
|
|
60023
61267
|
var PERSONA_FILE_REL = ".clawd/persona.json";
|
|
60024
61268
|
var DIRTY_IGNORED = [DEPLOY_FILE_REL, PERSONA_FILE_REL];
|
|
60025
61269
|
var RepoError = class extends Error {
|
|
@@ -60034,6 +61278,7 @@ async function mustGit(exec, dir, args, what, opts) {
|
|
|
60034
61278
|
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
61279
|
return r2.stdout.trim();
|
|
60036
61280
|
}
|
|
61281
|
+
var cloudPersonaIdOf = (originUrl) => personaIdOf(repoSlug(originUrl).split("/").pop() ?? "");
|
|
60037
61282
|
var INLINE_HELPER = '!f() { echo username=x-access-token; echo "password=$CLAWD_GITHUB_TOKEN"; }; f';
|
|
60038
61283
|
async function gitPush(exec, dir, auth) {
|
|
60039
61284
|
if (auth.source === "file") {
|
|
@@ -60058,7 +61303,7 @@ async function samePath(a2, b3) {
|
|
|
60058
61303
|
try {
|
|
60059
61304
|
return await import_promises6.default.realpath(x2);
|
|
60060
61305
|
} catch {
|
|
60061
|
-
return
|
|
61306
|
+
return import_node_path39.default.resolve(x2);
|
|
60062
61307
|
}
|
|
60063
61308
|
};
|
|
60064
61309
|
return await real(a2) === await real(b3);
|
|
@@ -60084,7 +61329,7 @@ async function identityArgs(exec, dir) {
|
|
|
60084
61329
|
return ["-c", "user.name=clawd", "-c", "user.email=clawd@clawos.local"];
|
|
60085
61330
|
}
|
|
60086
61331
|
async function writeGitignore(dir) {
|
|
60087
|
-
const gi =
|
|
61332
|
+
const gi = import_node_path39.default.join(dir, ".gitignore");
|
|
60088
61333
|
let existing;
|
|
60089
61334
|
try {
|
|
60090
61335
|
existing = await import_promises6.default.readFile(gi, "utf8");
|
|
@@ -60095,14 +61340,14 @@ async function writeGitignore(dir) {
|
|
|
60095
61340
|
}
|
|
60096
61341
|
async function writeWorkflow(dir) {
|
|
60097
61342
|
for (const [rel, body] of [[WORKFLOW_PATH, WORKFLOW_TEMPLATE], [UNPUBLISH_WORKFLOW_PATH, UNPUBLISH_WORKFLOW_TEMPLATE]]) {
|
|
60098
|
-
const wf =
|
|
60099
|
-
await import_promises6.default.mkdir(
|
|
61343
|
+
const wf = import_node_path39.default.join(dir, rel);
|
|
61344
|
+
await import_promises6.default.mkdir(import_node_path39.default.dirname(wf), { recursive: true });
|
|
60100
61345
|
await import_promises6.default.writeFile(wf, body);
|
|
60101
61346
|
}
|
|
60102
61347
|
}
|
|
60103
61348
|
async function writeDockerfile(dir, personaId2) {
|
|
60104
|
-
await import_promises6.default.writeFile(
|
|
60105
|
-
await import_promises6.default.writeFile(
|
|
61349
|
+
await import_promises6.default.writeFile(import_node_path39.default.join(dir, DOCKERFILE_PATH), renderDockerfile(personaId2));
|
|
61350
|
+
await import_promises6.default.writeFile(import_node_path39.default.join(dir, DOCKERIGNORE_PATH), DOCKERIGNORE_TEMPLATE);
|
|
60106
61351
|
}
|
|
60107
61352
|
var RepoAlreadyExistsError = class extends RepoError {
|
|
60108
61353
|
constructor(slug) {
|
|
@@ -60139,7 +61384,7 @@ async function ensureRepo(exec, api, args) {
|
|
|
60139
61384
|
const existing = await repoInfo(exec, dir);
|
|
60140
61385
|
if (existing) return { created: false, url: existing.url };
|
|
60141
61386
|
await writeGitignore(dir);
|
|
60142
|
-
await import_promises6.default.rm(
|
|
61387
|
+
await import_promises6.default.rm(import_node_path39.default.join(dir, WORKFLOW_PATH), { force: true });
|
|
60143
61388
|
let created;
|
|
60144
61389
|
try {
|
|
60145
61390
|
created = await createOrgRepo(exec, api, { dir, org, repoName: personaId2, message: "clawd: \u4E0A\u4E91", auth: args.auth });
|
|
@@ -60196,7 +61441,7 @@ async function release(exec, api, args) {
|
|
|
60196
61441
|
await api.enableActions(repoSlug(info.url));
|
|
60197
61442
|
await writeGitignore(args.dir);
|
|
60198
61443
|
await writeWorkflow(args.dir);
|
|
60199
|
-
await writeDockerfile(args.dir,
|
|
61444
|
+
await writeDockerfile(args.dir, cloudPersonaIdOf(info.url));
|
|
60200
61445
|
const identity = await identityArgs(exec, args.dir);
|
|
60201
61446
|
await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
|
|
60202
61447
|
const stamp = new Date(args.now()).toISOString();
|
|
@@ -60679,6 +61924,14 @@ function buildDeployHandlers(deps) {
|
|
|
60679
61924
|
repo2 = await repoInfo(deps.exec, dir);
|
|
60680
61925
|
if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
|
|
60681
61926
|
}
|
|
61927
|
+
const cloudName = cloudPersonaIdOf(repo2.url);
|
|
61928
|
+
let claim;
|
|
61929
|
+
try {
|
|
61930
|
+
claim = await deps.claimPublisher(cloudName);
|
|
61931
|
+
} catch (err) {
|
|
61932
|
+
throw toClawdError(err);
|
|
61933
|
+
}
|
|
61934
|
+
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
61935
|
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
61936
|
const live = liveScans.get(personaId2);
|
|
60684
61937
|
if (live && live.sessionId === void 0) return view(personaId2);
|
|
@@ -60856,10 +62109,10 @@ async function probeLocalVersion(exec, pkg) {
|
|
|
60856
62109
|
|
|
60857
62110
|
// src/tools/shell-path.ts
|
|
60858
62111
|
var import_node_child_process10 = require("child_process");
|
|
60859
|
-
var
|
|
62112
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
60860
62113
|
function refreshPathFromLoginShell() {
|
|
60861
62114
|
if (process.platform === "win32") return;
|
|
60862
|
-
const shell = process.env.SHELL ??
|
|
62115
|
+
const shell = process.env.SHELL ?? import_node_os12.default.userInfo().shell ?? "/bin/zsh";
|
|
60863
62116
|
try {
|
|
60864
62117
|
const out = (0, import_node_child_process10.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
|
|
60865
62118
|
encoding: "utf8",
|
|
@@ -61405,15 +62658,15 @@ async function refreshTunnelUrlOnStartup(deps) {
|
|
|
61405
62658
|
}
|
|
61406
62659
|
|
|
61407
62660
|
// src/lark-bot-connector/binding-store.ts
|
|
61408
|
-
var
|
|
61409
|
-
var
|
|
62661
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
62662
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
61410
62663
|
function createLarkBotBindingStore(personaRoot) {
|
|
61411
|
-
const filePath = (personaId2) =>
|
|
62664
|
+
const filePath = (personaId2) => import_node_path40.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
|
|
61412
62665
|
const read = (personaId2) => {
|
|
61413
62666
|
const file = filePath(personaId2);
|
|
61414
|
-
if (!
|
|
62667
|
+
if (!import_node_fs35.default.existsSync(file)) return null;
|
|
61415
62668
|
try {
|
|
61416
|
-
const raw = JSON.parse(
|
|
62669
|
+
const raw = JSON.parse(import_node_fs35.default.readFileSync(file, "utf8"));
|
|
61417
62670
|
if (typeof raw?.appId !== "string" || raw.appId.length === 0) return null;
|
|
61418
62671
|
return raw;
|
|
61419
62672
|
} catch {
|
|
@@ -61422,16 +62675,16 @@ function createLarkBotBindingStore(personaRoot) {
|
|
|
61422
62675
|
};
|
|
61423
62676
|
const write = (personaId2, meta) => {
|
|
61424
62677
|
const file = filePath(personaId2);
|
|
61425
|
-
|
|
62678
|
+
import_node_fs35.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true });
|
|
61426
62679
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
61427
|
-
|
|
61428
|
-
|
|
62680
|
+
import_node_fs35.default.writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
62681
|
+
import_node_fs35.default.renameSync(tmp, file);
|
|
61429
62682
|
};
|
|
61430
62683
|
return {
|
|
61431
62684
|
read,
|
|
61432
62685
|
write,
|
|
61433
62686
|
remove(personaId2) {
|
|
61434
|
-
|
|
62687
|
+
import_node_fs35.default.rmSync(filePath(personaId2), { force: true });
|
|
61435
62688
|
},
|
|
61436
62689
|
addRemovedChat(personaId2, chatId) {
|
|
61437
62690
|
const meta = read(personaId2);
|
|
@@ -61583,8 +62836,8 @@ function createLarkBotCloudClient(opts) {
|
|
|
61583
62836
|
}
|
|
61584
62837
|
|
|
61585
62838
|
// src/lark-bot-connector/chat-router.ts
|
|
61586
|
-
var
|
|
61587
|
-
var
|
|
62839
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
62840
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
61588
62841
|
init_protocol();
|
|
61589
62842
|
var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
|
|
61590
62843
|
var LOCAL_TURN_RETRIES = 2;
|
|
@@ -61662,9 +62915,9 @@ function createLarkChatRouter(deps) {
|
|
|
61662
62915
|
continue;
|
|
61663
62916
|
}
|
|
61664
62917
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61665
|
-
|
|
61666
|
-
const p3 =
|
|
61667
|
-
|
|
62918
|
+
import_node_fs36.default.mkdirSync(dir, { recursive: true });
|
|
62919
|
+
const p3 = import_node_path41.default.join(dir, `${safeMediaName(ownerMessageId)}-${i2}.${ext}`);
|
|
62920
|
+
import_node_fs36.default.writeFileSync(p3, r2.data);
|
|
61668
62921
|
lines.push(`[\u56FE\u7247: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61669
62922
|
} catch (err) {
|
|
61670
62923
|
deps.logger?.warn(`larkBot image fetch failed ${key}: ${err.message}`);
|
|
@@ -61685,9 +62938,9 @@ function createLarkChatRouter(deps) {
|
|
|
61685
62938
|
continue;
|
|
61686
62939
|
}
|
|
61687
62940
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61688
|
-
|
|
61689
|
-
const p3 =
|
|
61690
|
-
|
|
62941
|
+
import_node_fs36.default.mkdirSync(dir, { recursive: true });
|
|
62942
|
+
const p3 = import_node_path41.default.join(dir, `${safeMediaName(ownerMessageId)}-${i2}-${safeAttachmentName(f2.name)}`);
|
|
62943
|
+
import_node_fs36.default.writeFileSync(p3, r2.data);
|
|
61691
62944
|
lines.push(`[\u6587\u4EF6: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61692
62945
|
} catch (err) {
|
|
61693
62946
|
deps.logger?.warn(`larkBot file fetch failed ${f2.key}: ${err.message}`);
|
|
@@ -62335,954 +63588,184 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
62335
63588
|
"personaDispatch:complete": complete,
|
|
62336
63589
|
"personaDispatch:list": list,
|
|
62337
63590
|
"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
|
-
}
|
|
63591
|
+
};
|
|
62857
63592
|
}
|
|
62858
|
-
|
|
62859
|
-
|
|
63593
|
+
|
|
63594
|
+
// src/dispatch/peer-forward.ts
|
|
63595
|
+
init_protocol();
|
|
63596
|
+
function wsUrlToHttp(url) {
|
|
63597
|
+
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
63598
|
+
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
63599
|
+
return url;
|
|
63600
|
+
}
|
|
63601
|
+
var DISPATCH_POLL_INTERVAL_MS = 5e3;
|
|
63602
|
+
var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
|
|
63603
|
+
var DISPATCH_POLL_TOTAL_MS = 864e5;
|
|
63604
|
+
var defaultSleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
63605
|
+
async function forwardDispatchToPeer(args) {
|
|
63606
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63607
|
+
const sleep2 = args.sleepImpl ?? defaultSleep;
|
|
63608
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63609
|
+
const url = `${base}/rpc/personaDispatch:run`;
|
|
63610
|
+
let res;
|
|
62860
63611
|
try {
|
|
62861
|
-
|
|
62862
|
-
|
|
62863
|
-
|
|
63612
|
+
res = await f2(url, {
|
|
63613
|
+
method: "POST",
|
|
63614
|
+
headers: {
|
|
63615
|
+
"content-type": "application/json",
|
|
63616
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63617
|
+
},
|
|
63618
|
+
// 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
|
|
63619
|
+
body: JSON.stringify({
|
|
63620
|
+
targetPersona: args.targetPersona,
|
|
63621
|
+
prompt: args.prompt,
|
|
63622
|
+
// T-20: 条件展开防 undefined 污染 JSON。远端 daemon handler 会再校验一次 model 合法性。
|
|
63623
|
+
...args.model ? { model: args.model } : {}
|
|
63624
|
+
})
|
|
63625
|
+
});
|
|
63626
|
+
} catch (err) {
|
|
63627
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63628
|
+
return { kind: "failure", reason: `forward to peer failed: ${msg}` };
|
|
62864
63629
|
}
|
|
63630
|
+
let json;
|
|
62865
63631
|
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;
|
|
63632
|
+
json = await res.json();
|
|
62881
63633
|
} catch {
|
|
62882
|
-
return
|
|
62883
|
-
|
|
62884
|
-
|
|
63634
|
+
return {
|
|
63635
|
+
kind: "failure",
|
|
63636
|
+
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
63637
|
+
};
|
|
62885
63638
|
}
|
|
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;
|
|
63639
|
+
if (json.ok === false) {
|
|
63640
|
+
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
62895
63641
|
}
|
|
62896
|
-
|
|
62897
|
-
|
|
63642
|
+
if ("outcome" in json.result) return json.result.outcome;
|
|
63643
|
+
const dispatchId = json.result.dispatchId;
|
|
63644
|
+
let elapsed = 0;
|
|
63645
|
+
let unreachableFor = 0;
|
|
63646
|
+
while (elapsed < DISPATCH_POLL_TOTAL_MS) {
|
|
63647
|
+
await sleep2(DISPATCH_POLL_INTERVAL_MS);
|
|
63648
|
+
elapsed += DISPATCH_POLL_INTERVAL_MS;
|
|
63649
|
+
const markUnreachable = () => {
|
|
63650
|
+
unreachableFor += DISPATCH_POLL_INTERVAL_MS;
|
|
63651
|
+
return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
|
|
63652
|
+
kind: "failure",
|
|
63653
|
+
reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
|
|
63654
|
+
} : null;
|
|
63655
|
+
};
|
|
63656
|
+
let getRes;
|
|
62898
63657
|
try {
|
|
62899
|
-
|
|
62900
|
-
|
|
62901
|
-
|
|
62902
|
-
|
|
63658
|
+
getRes = await f2(`${base}/rpc/personaDispatch:get`, {
|
|
63659
|
+
method: "POST",
|
|
63660
|
+
headers: {
|
|
63661
|
+
"content-type": "application/json",
|
|
63662
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63663
|
+
},
|
|
63664
|
+
body: JSON.stringify({ dispatchId })
|
|
63665
|
+
});
|
|
62903
63666
|
} catch {
|
|
63667
|
+
const dead = markUnreachable();
|
|
63668
|
+
if (dead) return dead;
|
|
63669
|
+
continue;
|
|
62904
63670
|
}
|
|
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;
|
|
63671
|
+
let getJson;
|
|
62934
63672
|
try {
|
|
62935
|
-
|
|
62936
|
-
} catch
|
|
62937
|
-
|
|
63673
|
+
getJson = await getRes.json();
|
|
63674
|
+
} catch {
|
|
63675
|
+
const dead = markUnreachable();
|
|
63676
|
+
if (dead) return dead;
|
|
62938
63677
|
continue;
|
|
62939
63678
|
}
|
|
62940
|
-
if (
|
|
62941
|
-
|
|
63679
|
+
if (getJson.ok === false) {
|
|
63680
|
+
if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
|
|
63681
|
+
return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
|
|
63682
|
+
}
|
|
63683
|
+
const dead = markUnreachable();
|
|
63684
|
+
if (dead) return dead;
|
|
62942
63685
|
continue;
|
|
62943
63686
|
}
|
|
62944
|
-
|
|
62945
|
-
|
|
62946
|
-
|
|
62947
|
-
|
|
62948
|
-
childThreadId: sub.agentId,
|
|
62949
|
-
actualParent: child.meta?.parentThreadId ?? null
|
|
62950
|
-
});
|
|
62951
|
-
continue;
|
|
63687
|
+
unreachableFor = 0;
|
|
63688
|
+
const { status, outcome } = getJson.result;
|
|
63689
|
+
if (status === "completed" || status === "failed") {
|
|
63690
|
+
return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
|
|
62952
63691
|
}
|
|
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
63692
|
}
|
|
62972
|
-
return
|
|
63693
|
+
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
62973
63694
|
}
|
|
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);
|
|
63695
|
+
async function forwardInboxPostToPeer(args) {
|
|
63696
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63697
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63698
|
+
const url = `${base}/rpc/inbox:postMessage`;
|
|
63083
63699
|
let res;
|
|
63084
63700
|
try {
|
|
63085
63701
|
res = await f2(url, {
|
|
63086
|
-
method:
|
|
63702
|
+
method: "POST",
|
|
63087
63703
|
headers: {
|
|
63088
63704
|
"content-type": "application/json",
|
|
63089
|
-
|
|
63705
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63090
63706
|
},
|
|
63091
|
-
|
|
63092
|
-
|
|
63707
|
+
body: JSON.stringify({
|
|
63708
|
+
id: args.id,
|
|
63709
|
+
text: args.text,
|
|
63710
|
+
createdAt: args.createdAt,
|
|
63711
|
+
...args.origin ? { origin: args.origin } : {}
|
|
63712
|
+
})
|
|
63093
63713
|
});
|
|
63094
63714
|
} 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}`);
|
|
63715
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
63110
63716
|
}
|
|
63717
|
+
let json = null;
|
|
63111
63718
|
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");
|
|
63719
|
+
json = await res.json();
|
|
63720
|
+
} catch {
|
|
63721
|
+
return { ok: false, error: `peer non-JSON (HTTP ${res.status})` };
|
|
63135
63722
|
}
|
|
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");
|
|
63723
|
+
if (res.status < 200 || res.status >= 300 || json?.ok === false) {
|
|
63724
|
+
return { ok: false, error: json?.error ?? `HTTP ${res.status}` };
|
|
63149
63725
|
}
|
|
63150
63726
|
return { ok: true };
|
|
63151
63727
|
}
|
|
63152
|
-
async function
|
|
63153
|
-
const
|
|
63154
|
-
const
|
|
63155
|
-
|
|
63156
|
-
|
|
63157
|
-
|
|
63158
|
-
|
|
63159
|
-
|
|
63160
|
-
|
|
63161
|
-
|
|
63162
|
-
|
|
63163
|
-
|
|
63164
|
-
method: "GET",
|
|
63165
|
-
bearer: opts.loginPass
|
|
63728
|
+
async function forwardPeerExecToPeer(args) {
|
|
63729
|
+
const f2 = args.fetchImpl ?? fetch;
|
|
63730
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
63731
|
+
const url = `${base}/rpc/peerExec:run`;
|
|
63732
|
+
const tunnelFail = (msg) => ({
|
|
63733
|
+
type: "peerExec:run:ok",
|
|
63734
|
+
stdout: "",
|
|
63735
|
+
stderr: msg,
|
|
63736
|
+
exitCode: null,
|
|
63737
|
+
timedOut: false,
|
|
63738
|
+
stdoutTruncated: false,
|
|
63739
|
+
stderrTruncated: false
|
|
63166
63740
|
});
|
|
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
63741
|
let res;
|
|
63183
63742
|
try {
|
|
63184
|
-
res = await f2(url, {
|
|
63743
|
+
res = await f2(url, {
|
|
63744
|
+
method: "POST",
|
|
63745
|
+
headers: {
|
|
63746
|
+
"content-type": "application/json",
|
|
63747
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
63748
|
+
},
|
|
63749
|
+
body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
|
|
63750
|
+
});
|
|
63185
63751
|
} 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");
|
|
63752
|
+
return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
63227
63753
|
}
|
|
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");
|
|
63754
|
+
let json;
|
|
63755
|
+
try {
|
|
63756
|
+
json = await res.json();
|
|
63757
|
+
} catch {
|
|
63758
|
+
return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
|
|
63237
63759
|
}
|
|
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 });
|
|
63760
|
+
if (json.ok === false) {
|
|
63761
|
+
throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
|
|
63245
63762
|
}
|
|
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 : "" };
|
|
63763
|
+
return json.result;
|
|
63283
63764
|
}
|
|
63284
63765
|
|
|
63285
63766
|
// src/cloud/gateway-client.ts
|
|
63767
|
+
init_protocol();
|
|
63768
|
+
init_protocol();
|
|
63286
63769
|
var CLOUD_WORKSPACE_SLOT = "{{WORKSPACE}}";
|
|
63287
63770
|
var CLOUD_TURN_HEADER = "x-clawd-turn";
|
|
63288
63771
|
var CLOUD_TURN_HEADER_START = "start";
|
|
@@ -63293,7 +63776,7 @@ var GATEWAY_ROUTES = {
|
|
|
63293
63776
|
sessionMirror: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}/mirror`,
|
|
63294
63777
|
// 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
|
|
63295
63778
|
volume: () => "/me/volume",
|
|
63296
|
-
// 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6
|
|
63779
|
+
// 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6):只给发布者与管理员(gateway 问中心,design 2026-09-24-cloud-persona-conversations §3.2)
|
|
63297
63780
|
metrics: (personaId2) => `/metrics/${encodeURIComponent(personaId2)}`
|
|
63298
63781
|
};
|
|
63299
63782
|
var METRICS_TIMEOUT_MS = 3e4;
|
|
@@ -63653,68 +64136,7 @@ function openHttpLineStream(opts) {
|
|
|
63653
64136
|
init_protocol();
|
|
63654
64137
|
|
|
63655
64138
|
// 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
|
|
64139
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
63718
64140
|
function cloudThreadParams(ctx, paths) {
|
|
63719
64141
|
const { model: _model, ...base } = threadStartParams(ctx);
|
|
63720
64142
|
const developerInstructions = [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
|
|
@@ -63771,7 +64193,7 @@ function cloudImageInput(deps) {
|
|
|
63771
64193
|
const mime = lookupMime(p3);
|
|
63772
64194
|
if (!mime.startsWith("image/")) return null;
|
|
63773
64195
|
try {
|
|
63774
|
-
const bytes = (deps.readFile ??
|
|
64196
|
+
const bytes = (deps.readFile ?? import_node_fs37.default.readFileSync)(p3);
|
|
63775
64197
|
return { type: "image", url: `data:${mime};base64,${bytes.toString("base64")}` };
|
|
63776
64198
|
} catch (e) {
|
|
63777
64199
|
deps.logger?.warn("cloud image attachment unreadable", { path: p3, message: e.message });
|
|
@@ -64004,37 +64426,11 @@ var PiRpcClient = class {
|
|
|
64004
64426
|
// src/tools/pi-session.ts
|
|
64005
64427
|
var import_node_child_process11 = require("child_process");
|
|
64006
64428
|
var import_node_crypto12 = require("crypto");
|
|
64007
|
-
var
|
|
64008
|
-
var
|
|
64429
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64430
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
64009
64431
|
|
|
64010
64432
|
// src/tools/pi-events.ts
|
|
64011
64433
|
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
64434
|
function createPiTranslatorState() {
|
|
64039
64435
|
return { assistantSeq: 0, aborted: false };
|
|
64040
64436
|
}
|
|
@@ -64045,23 +64441,23 @@ function translatePiEvent(ev, st2) {
|
|
|
64045
64441
|
return [];
|
|
64046
64442
|
}
|
|
64047
64443
|
case "message_start": {
|
|
64048
|
-
if (
|
|
64444
|
+
if (rec2(ev.message)?.role === "assistant") st2.assistantSeq += 1;
|
|
64049
64445
|
return [];
|
|
64050
64446
|
}
|
|
64051
64447
|
case "message_update":
|
|
64052
|
-
return messageUpdate(
|
|
64448
|
+
return messageUpdate(rec2(ev.assistantMessageEvent), st2);
|
|
64053
64449
|
case "message_end":
|
|
64054
|
-
return messageEnd(
|
|
64450
|
+
return messageEnd(rec2(ev.message), st2);
|
|
64055
64451
|
case "tool_execution_start": {
|
|
64056
|
-
const toolUseId =
|
|
64057
|
-
const tool =
|
|
64452
|
+
const toolUseId = str2(ev.toolCallId);
|
|
64453
|
+
const tool = str2(ev.toolName);
|
|
64058
64454
|
if (!toolUseId || !tool) return [];
|
|
64059
64455
|
return [{ kind: "tool_call", toolUseId, tool, toolKind: PI_TOOL_ID, input: ev.args ?? {} }];
|
|
64060
64456
|
}
|
|
64061
64457
|
case "tool_execution_end": {
|
|
64062
|
-
const toolUseId =
|
|
64458
|
+
const toolUseId = str2(ev.toolCallId);
|
|
64063
64459
|
if (!toolUseId) return [];
|
|
64064
|
-
const text = contentText(
|
|
64460
|
+
const text = contentText(rec2(ev.result)?.content);
|
|
64065
64461
|
const out = { kind: "tool_result", toolUseId, output: text };
|
|
64066
64462
|
if (ev.isError === true) out.error = text || "tool failed";
|
|
64067
64463
|
return [out];
|
|
@@ -64077,9 +64473,9 @@ function translatePiEvent(ev, st2) {
|
|
|
64077
64473
|
}
|
|
64078
64474
|
function messageUpdate(e, st2) {
|
|
64079
64475
|
if (!e) return [];
|
|
64080
|
-
const delta =
|
|
64476
|
+
const delta = str2(e.delta);
|
|
64081
64477
|
if (!delta) return [];
|
|
64082
|
-
const partialId = `${st2.assistantSeq}-${
|
|
64478
|
+
const partialId = `${st2.assistantSeq}-${num2(e.contentIndex) ?? 0}`;
|
|
64083
64479
|
if (e.type === "text_delta") return [{ kind: "text", text: delta, partialId }];
|
|
64084
64480
|
if (e.type === "thinking_delta") return [{ kind: "thinking", text: delta, partialId }];
|
|
64085
64481
|
return [];
|
|
@@ -64088,32 +64484,32 @@ function messageEnd(m3, st2) {
|
|
|
64088
64484
|
if (!m3 || m3.role !== "assistant") return [];
|
|
64089
64485
|
const out = [];
|
|
64090
64486
|
for (const b3 of arr(m3.content)) {
|
|
64091
|
-
const block =
|
|
64487
|
+
const block = rec2(b3);
|
|
64092
64488
|
if (!block) continue;
|
|
64093
64489
|
if (block.type === "thinking") {
|
|
64094
|
-
const t2 =
|
|
64490
|
+
const t2 = str2(block.thinking);
|
|
64095
64491
|
if (t2) out.push({ kind: "thinking", text: t2 });
|
|
64096
64492
|
} else if (block.type === "text") {
|
|
64097
|
-
const t2 =
|
|
64493
|
+
const t2 = str2(block.text);
|
|
64098
64494
|
if (t2) out.push({ kind: "text", text: t2 });
|
|
64099
64495
|
}
|
|
64100
64496
|
}
|
|
64101
64497
|
const patch = {};
|
|
64102
|
-
const model =
|
|
64103
|
-
const provider =
|
|
64498
|
+
const model = str2(m3.model);
|
|
64499
|
+
const provider = str2(m3.provider);
|
|
64104
64500
|
if (model) patch.resolvedModel = provider ? `${provider}/${model}` : model;
|
|
64105
|
-
const u2 =
|
|
64106
|
-
if (u2 && (
|
|
64501
|
+
const u2 = rec2(m3.usage);
|
|
64502
|
+
if (u2 && (num2(u2.totalTokens) ?? 0) > 0) {
|
|
64107
64503
|
patch.contextUsage = {
|
|
64108
|
-
inputTokens:
|
|
64109
|
-
outputTokens:
|
|
64110
|
-
...
|
|
64111
|
-
...
|
|
64504
|
+
inputTokens: num2(u2.input) ?? 0,
|
|
64505
|
+
outputTokens: num2(u2.output) ?? 0,
|
|
64506
|
+
...num2(u2.cacheRead) !== void 0 ? { cacheReadTokens: num2(u2.cacheRead) } : {},
|
|
64507
|
+
...num2(u2.cacheWrite) !== void 0 ? { cacheCreateTokens: num2(u2.cacheWrite) } : {}
|
|
64112
64508
|
};
|
|
64113
64509
|
}
|
|
64114
64510
|
if (patch.resolvedModel || patch.contextUsage) out.push({ kind: "meta_update", patch });
|
|
64115
|
-
const stop =
|
|
64116
|
-
const err =
|
|
64511
|
+
const stop = str2(m3.stopReason);
|
|
64512
|
+
const err = str2(m3.errorMessage);
|
|
64117
64513
|
if (stop === "aborted") {
|
|
64118
64514
|
st2.aborted = true;
|
|
64119
64515
|
} else if (stop === "error" || err) {
|
|
@@ -64181,8 +64577,8 @@ function drivePiSession(opts) {
|
|
|
64181
64577
|
opts.onTurnCompleted?.("failure");
|
|
64182
64578
|
};
|
|
64183
64579
|
const handleUiRequest = (ev) => {
|
|
64184
|
-
const method =
|
|
64185
|
-
const id =
|
|
64580
|
+
const method = str2(ev.method);
|
|
64581
|
+
const id = str2(ev.id);
|
|
64186
64582
|
logger?.warn("pi extension_ui_request unexpected in clawd session", { method });
|
|
64187
64583
|
if (id && (method === "select" || method === "confirm" || method === "input" || method === "editor")) {
|
|
64188
64584
|
client.send({ type: "extension_ui_response", id, cancelled: true });
|
|
@@ -64197,7 +64593,7 @@ function drivePiSession(opts) {
|
|
|
64197
64593
|
}
|
|
64198
64594
|
if (ev.type === "agent_settled") busy = false;
|
|
64199
64595
|
if (ev.type === "response") {
|
|
64200
|
-
logger?.warn("pi response without pending id", { command:
|
|
64596
|
+
logger?.warn("pi response without pending id", { command: str2(ev.command), error: str2(ev.error) });
|
|
64201
64597
|
return;
|
|
64202
64598
|
}
|
|
64203
64599
|
if (ev.type === "extension_ui_request") {
|
|
@@ -64219,15 +64615,15 @@ function drivePiSession(opts) {
|
|
|
64219
64615
|
});
|
|
64220
64616
|
const ready = client.request({ type: "get_state" }).then((r2) => {
|
|
64221
64617
|
if (!r2.success) throw new Error(r2.error ?? "get_state failed");
|
|
64222
|
-
const data =
|
|
64223
|
-
const sid =
|
|
64618
|
+
const data = rec2(r2.data);
|
|
64619
|
+
const sid = str2(data?.sessionId) ?? sessionId;
|
|
64224
64620
|
if (sid !== sessionId) logger?.error("pi sessionId mismatch", { expected: sessionId, actual: sid });
|
|
64225
|
-
const model =
|
|
64621
|
+
const model = rec2(data?.model);
|
|
64226
64622
|
const patch = {};
|
|
64227
|
-
const mid =
|
|
64228
|
-
const prov =
|
|
64623
|
+
const mid = str2(model?.id);
|
|
64624
|
+
const prov = str2(model?.provider);
|
|
64229
64625
|
if (mid && mid !== "unknown") patch.resolvedModel = prov ? `${prov}/${mid}` : mid;
|
|
64230
|
-
const cw =
|
|
64626
|
+
const cw = num2(model?.contextWindow);
|
|
64231
64627
|
if (cw && cw > 0) patch.contextWindowSize = cw;
|
|
64232
64628
|
const events = [{ kind: "session_init", toolSessionId: sid }];
|
|
64233
64629
|
if (patch.resolvedModel || patch.contextWindowSize) events.push({ kind: "meta_update", patch });
|
|
@@ -64298,8 +64694,8 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
|
|
|
64298
64694
|
function createPiSession(ctx, sink, deps = {}) {
|
|
64299
64695
|
const cmd = process.env.PI_BIN ?? "pi";
|
|
64300
64696
|
const sessionId = ctx.toolSessionId ?? (0, import_node_crypto12.randomUUID)();
|
|
64301
|
-
const skillsDir =
|
|
64302
|
-
const exists = deps.skillsDirExists ?? ((d2) =>
|
|
64697
|
+
const skillsDir = import_node_path42.default.join(ctx.cwd, ".claude", "skills");
|
|
64698
|
+
const exists = deps.skillsDirExists ?? ((d2) => import_node_fs38.default.existsSync(d2));
|
|
64303
64699
|
const args = buildPiArgs(ctx, {
|
|
64304
64700
|
sessionId,
|
|
64305
64701
|
skillsDir: exists(skillsDir) ? skillsDir : void 0,
|
|
@@ -64342,201 +64738,6 @@ function createPiSession(ctx, sink, deps = {}) {
|
|
|
64342
64738
|
return driver.session;
|
|
64343
64739
|
}
|
|
64344
64740
|
|
|
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
64741
|
// src/tools/pi.ts
|
|
64541
64742
|
var PI_DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
64542
64743
|
var PI_THINKING_EFFORTS = [
|
|
@@ -64565,20 +64766,20 @@ async function probePi(env = process.env) {
|
|
|
64565
64766
|
return { available: false };
|
|
64566
64767
|
}
|
|
64567
64768
|
function mapPiModels(raw) {
|
|
64568
|
-
const models = arr(
|
|
64569
|
-
const providers = new Set(models.map((m3) =>
|
|
64769
|
+
const models = arr(rec2(raw)?.models).map(rec2).filter((m3) => m3 !== void 0);
|
|
64770
|
+
const providers = new Set(models.map((m3) => str2(m3.provider)).filter(Boolean));
|
|
64570
64771
|
const out = [];
|
|
64571
64772
|
for (const m3 of models) {
|
|
64572
|
-
const id =
|
|
64573
|
-
const provider =
|
|
64773
|
+
const id = str2(m3.id);
|
|
64774
|
+
const provider = str2(m3.provider);
|
|
64574
64775
|
if (!id || !provider) continue;
|
|
64575
|
-
const cw =
|
|
64776
|
+
const cw = num2(m3.contextWindow);
|
|
64576
64777
|
const levels = arr(m3.thinkingLevels).filter((v3) => typeof v3 === "string");
|
|
64577
64778
|
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
64779
|
out.push({
|
|
64579
64780
|
id: `${provider}/${id}`,
|
|
64580
64781
|
// 与 --model 同一串
|
|
64581
|
-
label:
|
|
64782
|
+
label: str2(m3.name) || id,
|
|
64582
64783
|
contextWindowSize: cw && cw > 0 ? cw : PI_DEFAULT_CONTEXT_WINDOW,
|
|
64583
64784
|
efforts,
|
|
64584
64785
|
// 分组按 provider;只有一个 provider 时不分组(一个横跨全表的标题没信息量,同 codex)
|
|
@@ -64644,18 +64845,18 @@ async function queryPiModels(deps = {}) {
|
|
|
64644
64845
|
try {
|
|
64645
64846
|
const r2 = await race(client.request({ type: "get_available_models" }));
|
|
64646
64847
|
if (!r2.success) throw new Error(r2.error ?? "get_available_models failed");
|
|
64647
|
-
const models = arr(
|
|
64848
|
+
const models = arr(rec2(r2.data)?.models).map(rec2).filter((m3) => m3 !== void 0);
|
|
64648
64849
|
const enriched = [];
|
|
64649
64850
|
for (const m3 of models) {
|
|
64650
|
-
const provider =
|
|
64651
|
-
const id =
|
|
64851
|
+
const provider = str2(m3.provider);
|
|
64852
|
+
const id = str2(m3.id);
|
|
64652
64853
|
let thinkingLevels;
|
|
64653
64854
|
if (provider && id && !timedOut) {
|
|
64654
64855
|
try {
|
|
64655
64856
|
const set = await race(client.request({ type: "set_model", provider, modelId: id }));
|
|
64656
64857
|
if (set.success) {
|
|
64657
64858
|
const lv = await race(client.request({ type: "get_available_thinking_levels" }));
|
|
64658
|
-
const levels = arr(
|
|
64859
|
+
const levels = arr(rec2(lv.data)?.levels).filter((v3) => typeof v3 === "string");
|
|
64659
64860
|
if (lv.success && levels.length > 0) thinkingLevels = levels;
|
|
64660
64861
|
}
|
|
64661
64862
|
} catch {
|
|
@@ -64777,42 +64978,6 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
64777
64978
|
return driver.session;
|
|
64778
64979
|
}
|
|
64779
64980
|
|
|
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
64981
|
// src/tools/pi-cloud.ts
|
|
64817
64982
|
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
64983
|
var CLOUD_PI_CAPABILITIES = {
|
|
@@ -72088,7 +72253,7 @@ function computeMethodAccess(args) {
|
|
|
72088
72253
|
}
|
|
72089
72254
|
|
|
72090
72255
|
// src/version.ts
|
|
72091
|
-
var version = "0.2.
|
|
72256
|
+
var version = "0.2.583".length > 0 ? "0.2.583" : "dev";
|
|
72092
72257
|
|
|
72093
72258
|
// src/cli-probe/probe.ts
|
|
72094
72259
|
var fs80 = __toESM(require("fs"), 1);
|
|
@@ -75769,6 +75934,18 @@ async function startDaemon(config) {
|
|
|
75769
75934
|
githubIdentity,
|
|
75770
75935
|
githubApi: (token) => createGithubApi({ token }),
|
|
75771
75936
|
org: PERSONA_REPO_ORG,
|
|
75937
|
+
// 发布者认领(design 2026-09-24-cloud-persona-conversations §3.1):身份由中心从登录票里取;409 = 名字是别人的
|
|
75938
|
+
claimPublisher: async (personaId2) => {
|
|
75939
|
+
const loginPass = ownerIdentityStore.read()?.loginPass;
|
|
75940
|
+
if (!loginPass) throw new Error("\u8FD8\u6CA1\u767B\u5F55 clawd\uFF1A\u5148\u5728\u8BBE\u7F6E\u91CC\u767B\u5F55\u98DE\u4E66\uFF0C\u518D\u53D1\u5E03");
|
|
75941
|
+
try {
|
|
75942
|
+
await centralClaimPublisher({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass, personaId: personaId2 });
|
|
75943
|
+
return "ok";
|
|
75944
|
+
} catch (err) {
|
|
75945
|
+
if (err instanceof CentralClientError && err.status === 409) return "taken";
|
|
75946
|
+
throw new Error(`\u5411\u4E2D\u5FC3\u767B\u8BB0\u53D1\u5E03\u8005\u5931\u8D25\uFF1A${err.message}`);
|
|
75947
|
+
}
|
|
75948
|
+
},
|
|
75772
75949
|
now: () => Date.now(),
|
|
75773
75950
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
75774
75951
|
// 在不在线上 = gateway 正式登记表里有没有它(design 2026-09-24-deploy-unpublish §3.3):跟 persona 列表的云卡片同一个来源。
|
|
@@ -75909,13 +76086,19 @@ async function startDaemon(config) {
|
|
|
75909
76086
|
const viewerAssetLoader = tryLoadViewerAssets(logger);
|
|
75910
76087
|
const shareUiAssetLoader = tryLoadShareUi(logger);
|
|
75911
76088
|
const dashboardPages = loadDashboardPages(logger);
|
|
76089
|
+
const dashboardApi = () => config.clawosApi ?? DEFAULT_CLAWOS_API;
|
|
75912
76090
|
const dashboardRoutes = createDashboardRoutes({
|
|
75913
76091
|
getSecret: () => authFile.token,
|
|
75914
76092
|
loadPageHtml: (page) => dashboardPages.get(page) ?? null,
|
|
75915
|
-
|
|
75916
|
-
|
|
75917
|
-
|
|
75918
|
-
|
|
76093
|
+
data: createDashboardData({
|
|
76094
|
+
loginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
|
|
76095
|
+
registeredPersonas: async () => (await cloudGateway.listPersonas()).map((p3) => p3.personaId),
|
|
76096
|
+
visiblePersonas: (loginPass) => centralVisiblePersonas({ api: dashboardApi(), loginPass }),
|
|
76097
|
+
metrics: (q3) => cloudGateway.fetchMetrics(q3),
|
|
76098
|
+
threads: (loginPass, q3) => centralPersonaThreads({ api: dashboardApi(), loginPass, ...q3 }),
|
|
76099
|
+
thread: (loginPass, q3) => centralPersonaThread({ api: dashboardApi(), loginPass, ...q3 }),
|
|
76100
|
+
logger
|
|
76101
|
+
})
|
|
75919
76102
|
});
|
|
75920
76103
|
const httpRouter = createHttpRouter({
|
|
75921
76104
|
authResolver,
|