@clawos-dev/clawd 0.2.581 → 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 +66 -2
- package/dist/cli.cjs +2028 -1693
- package/dist/cloud-persona-conversations.html +312 -0
- package/dist/cloud-persona-metrics.html +19 -19
- package/dist/dispatch/mcp-server.cjs +22 -1
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/mcp-server.cjs +66 -2
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/mcp-server.cjs +66 -2
- 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-BfijurRS.js → guest-DVxTImMk.js} +132 -132
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-D8Om2vaT.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -6168,7 +6168,7 @@ function cloudEngineOk(tool) {
|
|
|
6168
6168
|
function repoSlug(url) {
|
|
6169
6169
|
return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
|
|
6170
6170
|
}
|
|
6171
|
-
var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
|
|
6171
|
+
var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
|
|
6172
6172
|
var init_deploy_schemas = __esm({
|
|
6173
6173
|
"../protocol/src/deploy-schemas.ts"() {
|
|
6174
6174
|
"use strict";
|
|
@@ -6277,6 +6277,12 @@ var init_deploy_schemas = __esm({
|
|
|
6277
6277
|
jobs: external_exports.array(DeployJobSchema)
|
|
6278
6278
|
}).strict();
|
|
6279
6279
|
CLOUD_ENGINES = ["codex", "pi"];
|
|
6280
|
+
DeployUnpublishRunSchema = external_exports.object({
|
|
6281
|
+
runId: external_exports.number(),
|
|
6282
|
+
status: DeployRunSchema.shape.status,
|
|
6283
|
+
conclusion: external_exports.enum(["success", "failure"]).optional(),
|
|
6284
|
+
htmlUrl: external_exports.string().min(1)
|
|
6285
|
+
}).strict();
|
|
6280
6286
|
DeployStateSchema = external_exports.object({
|
|
6281
6287
|
personaId: external_exports.string().min(1),
|
|
6282
6288
|
/** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
|
|
@@ -6293,12 +6299,23 @@ var init_deploy_schemas = __esm({
|
|
|
6293
6299
|
* **缺席是常态**:没推过、没授权 GitHub、GitHub 查不到、仓库还没有 run,都不该让整页打不开——同 `secrets` 的处理。
|
|
6294
6300
|
*/
|
|
6295
6301
|
run: DeployRunSchema.optional(),
|
|
6302
|
+
/**
|
|
6303
|
+
* 现在在不在线上 = gateway 的正式登记表里有没有它(`/healthz`,跟 persona 列表里云卡片同一个来源)。
|
|
6304
|
+
* **缺席 = 查不到**(没登录飞书 / 连不上云上服务),不是「不在」——页面据此不给下线按钮,也不撤「线上」标。
|
|
6305
|
+
* 不看 `releases`:发布过不等于还在线上(下线过、或者被人用脚本撤了)
|
|
6306
|
+
*/
|
|
6307
|
+
online: external_exports.boolean().optional(),
|
|
6308
|
+
/** 最新那次下线流水线。没下线过 / 没仓库 / 没 GitHub 身份 / 查不到都缺席 */
|
|
6309
|
+
unpublish: DeployUnpublishRunSchema.optional(),
|
|
6296
6310
|
/**
|
|
6297
6311
|
* `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
|
|
6298
6312
|
* 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
|
|
6299
6313
|
*/
|
|
6300
6314
|
historyError: external_exports.string().optional()
|
|
6301
6315
|
}).strict();
|
|
6316
|
+
DeployUnpublishResultSchema = external_exports.object({
|
|
6317
|
+
before: external_exports.number().nullable()
|
|
6318
|
+
}).strict();
|
|
6302
6319
|
DeployReleaseResultSchema = external_exports.object({
|
|
6303
6320
|
sha: external_exports.string().min(1),
|
|
6304
6321
|
actionsUrl: external_exports.string().min(1)
|
|
@@ -7674,7 +7691,7 @@ var init_dashboard_schemas = __esm({
|
|
|
7674
7691
|
"../protocol/src/dashboard-schemas.ts"() {
|
|
7675
7692
|
"use strict";
|
|
7676
7693
|
init_zod();
|
|
7677
|
-
DASHBOARD_PAGES = ["cloud-persona-metrics"];
|
|
7694
|
+
DASHBOARD_PAGES = ["cloud-persona-metrics", "cloud-persona-conversations"];
|
|
7678
7695
|
DashboardPageSchema = external_exports.enum(DASHBOARD_PAGES);
|
|
7679
7696
|
DASHBOARD_LINK_TTL_SECONDS = 24 * 3600;
|
|
7680
7697
|
DashboardOpenUrlArgs = external_exports.object({ page: DashboardPageSchema }).strict();
|
|
@@ -9139,6 +9156,10 @@ var init_methods = __esm({
|
|
|
9139
9156
|
summary: "\u539F\u6837\u91CD\u8DD1\u6700\u65B0\u90A3\u6761\u6D41\u6C34\u7EBF\u91CC\u5931\u8D25\u7684\u67D0\u4E00\u6BB5\uFF08GitHub REST rerun job\uFF09\u3002**\u540C\u4E00\u4E2A commit\u3001\u540C\u4E00\u4EFD\u8F93\u5165**\uFF0C\u4E0D\u4EA7\u751F\u65B0\u63D0\u4EA4\u2014\u2014\u6539\u4E86 persona \u5185\u5BB9\u8981 deploy:scan \u518D deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
|
|
9140
9157
|
args: DeployRerunJobArgsSchema
|
|
9141
9158
|
},
|
|
9159
|
+
"deploy:unpublish": {
|
|
9160
|
+
summary: "\u4E0B\u7EBF\uFF1A\u89E6\u53D1 persona \u4ED3\u5E93\u91CC\u7684 clawd-unpublish.yml\uFF08workflow_dispatch\uFF09\uFF0C\u7531\u6D41\u6C34\u7EBF\u5728 gateway \u64A4\u6389\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93 / \u955C\u50CF / \u672C\u673A\u4E0A\u4E91\u8BB0\u5F55\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002\u6700\u540E\u4E00\u6B21\u53D1\u5E03\u7684 commit \u91CC\u6CA1\u6709\u8FD9\u4EFD workflow\uFF08\u65E7\u6A21\u677F\uFF09\u65F6\u62D2\uFF0C\u8981\u5148\u91CD\u65B0\u53D1\u5E03\u4E00\u6B21\u3002\u56DE { before }\uFF1A\u89E6\u53D1\u4E4B\u524D\u73B0\u67E5\u7684\u6700\u65B0\u4E0B\u7EBF run id\uFF08\u6CA1\u4E0B\u7EBF\u8FC7 = null\uFF09\uFF0C\u9875\u9762\u62FF\u5B83\u8BA4\u65B0 run\uFF1Bowner-only\u3001human-only",
|
|
9161
|
+
args: DeployPersonaArgsSchema
|
|
9162
|
+
},
|
|
9142
9163
|
// ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
|
|
9143
9164
|
"githubAuth:get": {
|
|
9144
9165
|
summary: "\u672C\u673A\u7684 GitHub \u8EAB\u4EFD\uFF1Aauthorized\uFF08@login\uFF0C\u6765\u81EA git \u51ED\u636E\u5B58\u50A8 / \u672C\u673A\u51ED\u636E\u8868\uFF09/ missing / invalid\uFF08GitHub \u4E0D\u8BA4\uFF09/ pending\uFF08\u7B49 GitHub \u8DF3\u56DE\u4E2D\u5FC3\uFF09\u3002\u8BBE\u7F6E\u9875\u300C\u8D26\u53F7\u300D\u3001\u4E0A\u4E91\u9875\u9876\u4E0A\u90A3\u5F20\u5361\u3001PR \u6536\u4EF6\u7BB1\u90FD\u770B\u5B83\uFF1Bpending \u671F\u9875\u9762\u6BCF 2 \u79D2\u8F6E\u8BE2\uFF0Cdaemon \u987A\u624B\u5411\u4E2D\u5FC3\u9886 token\u3002\u65E0\u53C2\u6570",
|
|
@@ -15890,13 +15911,13 @@ var init_claude_history = __esm({
|
|
|
15890
15911
|
function probeViaWhich() {
|
|
15891
15912
|
try {
|
|
15892
15913
|
const out = (0, import_node_child_process2.execFileSync)("which", ["claude"], { encoding: "utf8" }).trim();
|
|
15893
|
-
if (out &&
|
|
15914
|
+
if (out && import_node_fs20.default.existsSync(out)) return out;
|
|
15894
15915
|
} catch {
|
|
15895
15916
|
}
|
|
15896
15917
|
return null;
|
|
15897
15918
|
}
|
|
15898
15919
|
async function probeClaude(env = process.env) {
|
|
15899
|
-
if (env.CLAUDE_BIN &&
|
|
15920
|
+
if (env.CLAUDE_BIN && import_node_fs20.default.existsSync(env.CLAUDE_BIN)) {
|
|
15900
15921
|
return { available: true, path: env.CLAUDE_BIN };
|
|
15901
15922
|
}
|
|
15902
15923
|
const w3 = probeViaWhich();
|
|
@@ -16362,13 +16383,13 @@ function encodeClaudeStdin(text) {
|
|
|
16362
16383
|
};
|
|
16363
16384
|
return JSON.stringify(frame) + "\n";
|
|
16364
16385
|
}
|
|
16365
|
-
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;
|
|
16366
16387
|
var init_claude = __esm({
|
|
16367
16388
|
"src/tools/claude.ts"() {
|
|
16368
16389
|
"use strict";
|
|
16369
16390
|
import_node_child_process = require("child_process");
|
|
16370
16391
|
import_node_child_process2 = require("child_process");
|
|
16371
|
-
|
|
16392
|
+
import_node_fs20 = __toESM(require("fs"), 1);
|
|
16372
16393
|
init_protocol();
|
|
16373
16394
|
init_claude_history();
|
|
16374
16395
|
init_tool_result_extra();
|
|
@@ -51402,19 +51423,37 @@ function verifyDashboardLink(secret, page, eRaw, s2, now = Date.now) {
|
|
|
51402
51423
|
}
|
|
51403
51424
|
|
|
51404
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" };
|
|
51405
51441
|
var NO_STORE = { "cache-control": "no-store" };
|
|
51406
51442
|
function send(res, status, contentType, body) {
|
|
51407
51443
|
res.writeHead(status, { "content-type": contentType, ...NO_STORE });
|
|
51408
51444
|
res.end(body);
|
|
51409
51445
|
}
|
|
51410
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);
|
|
51411
51448
|
var PERSONA_RE = /^[a-z0-9-]+$/;
|
|
51449
|
+
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
51412
51450
|
function createDashboardRoutes(deps) {
|
|
51413
51451
|
const now = deps.now ?? Date.now;
|
|
51414
51452
|
return async (req, res, url) => {
|
|
51415
|
-
const m3 = /^\/dashboard\/([^/]+)(?:\/(
|
|
51453
|
+
const m3 = /^\/dashboard\/([^/]+)(?:\/([a-z]+))?$/.exec(url.pathname);
|
|
51416
51454
|
const page = m3 && DASHBOARD_PAGES.includes(m3[1]) ? m3[1] : null;
|
|
51417
|
-
|
|
51455
|
+
const sub = m3?.[2];
|
|
51456
|
+
if (!m3 || !page || req.method !== "GET" || sub !== void 0 && !SUBROUTES[page].includes(sub)) {
|
|
51418
51457
|
sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
|
|
51419
51458
|
return true;
|
|
51420
51459
|
}
|
|
@@ -51424,8 +51463,7 @@ function createDashboardRoutes(deps) {
|
|
|
51424
51463
|
return true;
|
|
51425
51464
|
}
|
|
51426
51465
|
const v3 = verifyDashboardLink(secret, page, url.searchParams.get("e"), url.searchParams.get("s"), now);
|
|
51427
|
-
|
|
51428
|
-
if (!sub) {
|
|
51466
|
+
if (sub === void 0) {
|
|
51429
51467
|
if (!v3.ok && v3.code !== "EXPIRED") {
|
|
51430
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");
|
|
51431
51469
|
return true;
|
|
@@ -51442,290 +51480,1517 @@ function createDashboardRoutes(deps) {
|
|
|
51442
51480
|
sendJson(res, 403, v3.code === "EXPIRED" ? { code: "LINK_EXPIRED", message: "\u94FE\u63A5\u5DF2\u8FC7\u671F" } : { code: "LINK_INVALID", message: "\u94FE\u63A5\u65E0\u6548" });
|
|
51443
51481
|
return true;
|
|
51444
51482
|
}
|
|
51483
|
+
const q3 = url.searchParams;
|
|
51445
51484
|
try {
|
|
51446
51485
|
if (sub === "personas") {
|
|
51447
|
-
sendJson(res, 200, { personas: await deps.
|
|
51486
|
+
sendJson(res, 200, { personas: await deps.data.personas() });
|
|
51448
51487
|
return true;
|
|
51449
51488
|
}
|
|
51450
|
-
const personaId2 =
|
|
51489
|
+
const personaId2 = q3.get("persona") ?? "";
|
|
51451
51490
|
if (!PERSONA_RE.test(personaId2)) {
|
|
51452
51491
|
sendJson(res, 400, { code: "INVALID_PARAM", message: "persona \u8981\u662F\u4E91 persona \u540D\uFF08[a-z0-9-]\uFF09" });
|
|
51453
51492
|
return true;
|
|
51454
51493
|
}
|
|
51455
|
-
|
|
51456
|
-
|
|
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 }));
|
|
51457
51514
|
} catch (e) {
|
|
51458
|
-
|
|
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 });
|
|
51459
51520
|
}
|
|
51460
51521
|
return true;
|
|
51461
51522
|
};
|
|
51462
51523
|
}
|
|
51463
51524
|
|
|
51464
|
-
// src/
|
|
51465
|
-
|
|
51466
|
-
|
|
51467
|
-
var import_node_url4 = require("url");
|
|
51468
|
-
|
|
51469
|
-
// src/share-ui/asset-loader.ts
|
|
51470
|
-
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
51471
|
-
var import_node_path13 = __toESM(require("path"), 1);
|
|
51472
|
-
function createShareUiAssetLoader(opts) {
|
|
51473
|
-
let indexHtml;
|
|
51474
|
-
try {
|
|
51475
|
-
indexHtml = import_node_fs14.default.readFileSync(opts.indexHtmlPath, "utf8");
|
|
51476
|
-
} catch (err) {
|
|
51477
|
-
throw new Error(`share-ui index.html not found at ${opts.indexHtmlPath}: ${err.message}`);
|
|
51478
|
-
}
|
|
51479
|
-
const assetsDir = import_node_path13.default.resolve(opts.assetsDir);
|
|
51480
|
-
return {
|
|
51481
|
-
renderIndexHtml() {
|
|
51482
|
-
return indexHtml;
|
|
51483
|
-
},
|
|
51484
|
-
resolveAssetPath(rel) {
|
|
51485
|
-
const resolved = import_node_path13.default.resolve(assetsDir, rel);
|
|
51486
|
-
if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path13.default.sep)) return null;
|
|
51487
|
-
try {
|
|
51488
|
-
if (import_node_fs14.default.statSync(resolved).isFile()) return resolved;
|
|
51489
|
-
} catch {
|
|
51490
|
-
}
|
|
51491
|
-
return null;
|
|
51492
|
-
}
|
|
51493
|
-
};
|
|
51525
|
+
// src/tools/codex-rollout.ts
|
|
51526
|
+
function rec(v3) {
|
|
51527
|
+
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : void 0;
|
|
51494
51528
|
}
|
|
51495
|
-
|
|
51496
|
-
|
|
51497
|
-
|
|
51498
|
-
function
|
|
51499
|
-
|
|
51500
|
-
|
|
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;
|
|
51501
51538
|
try {
|
|
51502
|
-
|
|
51539
|
+
return rec(JSON.parse(t2));
|
|
51503
51540
|
} catch {
|
|
51541
|
+
return void 0;
|
|
51504
51542
|
}
|
|
51505
|
-
return import_node_path14.default.dirname(real);
|
|
51506
51543
|
}
|
|
51507
|
-
function
|
|
51508
|
-
|
|
51509
|
-
|
|
51510
|
-
|
|
51511
|
-
|
|
51512
|
-
|
|
51513
|
-
|
|
51514
|
-
|
|
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);
|
|
51515
51570
|
}
|
|
51516
|
-
|
|
51517
|
-
|
|
51518
|
-
|
|
51519
|
-
|
|
51520
|
-
|
|
51521
|
-
|
|
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") {
|
|
51522
51583
|
try {
|
|
51523
|
-
|
|
51524
|
-
logger?.info("share-ui.assets-root.resolved", { root: shareUiDir });
|
|
51525
|
-
return createShareUiAssetLoader({ indexHtmlPath, assetsDir });
|
|
51526
|
-
}
|
|
51584
|
+
args = JSON.parse(rawArgs);
|
|
51527
51585
|
} catch {
|
|
51586
|
+
args = { arguments: rawArgs };
|
|
51528
51587
|
}
|
|
51529
51588
|
}
|
|
51530
|
-
|
|
51531
|
-
|
|
51532
|
-
|
|
51533
|
-
|
|
51534
|
-
|
|
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" };
|
|
51535
51719
|
}
|
|
51536
51720
|
|
|
51537
|
-
// src/
|
|
51538
|
-
var
|
|
51539
|
-
|
|
51540
|
-
// ../protocol/src/index.ts
|
|
51541
|
-
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);
|
|
51542
51724
|
|
|
51543
|
-
// src/
|
|
51544
|
-
var
|
|
51545
|
-
|
|
51546
|
-
|
|
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;
|
|
51547
51733
|
}
|
|
51548
|
-
function
|
|
51549
|
-
|
|
51550
|
-
|
|
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;
|
|
51551
51744
|
}
|
|
51552
|
-
function
|
|
51553
|
-
const
|
|
51554
|
-
if (
|
|
51555
|
-
|
|
51556
|
-
|
|
51557
|
-
|
|
51558
|
-
|
|
51559
|
-
|
|
51560
|
-
|
|
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;
|
|
51561
51759
|
}
|
|
51562
|
-
|
|
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;
|
|
51563
51781
|
try {
|
|
51564
|
-
|
|
51565
|
-
JSON.parse(Buffer.from(body, "base64url").toString("utf8"))
|
|
51566
|
-
);
|
|
51567
|
-
if (!parsed.success) return { ok: false, reason: "BAD_FORMAT" };
|
|
51568
|
-
payload = parsed.data;
|
|
51782
|
+
buf = import_node_fs14.default.readFileSync(file);
|
|
51569
51783
|
} catch {
|
|
51570
|
-
return
|
|
51784
|
+
return null;
|
|
51571
51785
|
}
|
|
51572
|
-
|
|
51573
|
-
|
|
51574
|
-
|
|
51575
|
-
|
|
51576
|
-
|
|
51577
|
-
|
|
51578
|
-
|
|
51579
|
-
|
|
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;
|
|
51580
51796
|
try {
|
|
51581
|
-
|
|
51797
|
+
parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
|
|
51582
51798
|
} catch {
|
|
51583
|
-
|
|
51799
|
+
continue;
|
|
51584
51800
|
}
|
|
51585
|
-
|
|
51586
|
-
|
|
51587
|
-
|
|
51588
|
-
|
|
51589
|
-
|
|
51590
|
-
|
|
51591
|
-
|
|
51592
|
-
const iat = Math.floor(deps.now() / 1e3);
|
|
51593
|
-
const visitorToken = signVisitorToken(deps.getSecret(), {
|
|
51594
|
-
v: 1,
|
|
51595
|
-
visitorId: id.ownerId,
|
|
51596
|
-
displayName: id.displayName,
|
|
51597
|
-
provider: id.provider,
|
|
51598
|
-
iat,
|
|
51599
|
-
exp: iat + deps.ttlSeconds
|
|
51600
|
-
});
|
|
51601
|
-
return { ok: true, visitorToken };
|
|
51602
|
-
};
|
|
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;
|
|
51603
51808
|
}
|
|
51604
|
-
|
|
51605
|
-
|
|
51606
|
-
|
|
51607
|
-
|
|
51608
|
-
|
|
51609
|
-
const file = import_node_path15.default.join(opts.dir, "visitors.json");
|
|
51610
|
-
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;
|
|
51611
51814
|
try {
|
|
51612
|
-
|
|
51815
|
+
entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
|
|
51613
51816
|
} catch {
|
|
51614
|
-
return
|
|
51817
|
+
return;
|
|
51615
51818
|
}
|
|
51616
|
-
|
|
51617
|
-
|
|
51618
|
-
|
|
51619
|
-
|
|
51620
|
-
|
|
51621
|
-
return {
|
|
51622
|
-
upsert(r2) {
|
|
51623
|
-
const rows = read();
|
|
51624
|
-
const ex = rows.find((x2) => x2.visitorId === r2.visitorId);
|
|
51625
|
-
if (ex) {
|
|
51626
|
-
ex.displayName = r2.displayName;
|
|
51627
|
-
ex.provider = r2.provider;
|
|
51628
|
-
ex.lastSeen = r2.now;
|
|
51629
|
-
if (r2.avatarUrl) ex.avatarUrl = r2.avatarUrl;
|
|
51630
|
-
} else {
|
|
51631
|
-
rows.push({
|
|
51632
|
-
visitorId: r2.visitorId,
|
|
51633
|
-
displayName: r2.displayName,
|
|
51634
|
-
provider: r2.provider,
|
|
51635
|
-
...r2.avatarUrl ? { avatarUrl: r2.avatarUrl } : {},
|
|
51636
|
-
firstSeen: r2.now,
|
|
51637
|
-
lastSeen: r2.now
|
|
51638
|
-
});
|
|
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;
|
|
51639
51824
|
}
|
|
51640
|
-
|
|
51641
|
-
|
|
51642
|
-
list() {
|
|
51643
|
-
return read();
|
|
51825
|
+
const matched = ROLLOUT_NAME_RE.exec(entry.name);
|
|
51826
|
+
if (matched) into.set(matched[1].toLowerCase(), full);
|
|
51644
51827
|
}
|
|
51645
51828
|
};
|
|
51646
|
-
|
|
51647
|
-
|
|
51648
|
-
|
|
51649
|
-
|
|
51650
|
-
|
|
51651
|
-
// src/tools/codex.ts
|
|
51652
|
-
var import_node_child_process7 = require("child_process");
|
|
51653
|
-
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
51654
|
-
|
|
51655
|
-
// src/tools/codex-session.ts
|
|
51656
|
-
var import_node_child_process4 = require("child_process");
|
|
51657
|
-
var import_node_path19 = __toESM(require("path"), 1);
|
|
51658
|
-
|
|
51659
|
-
// src/tools/codex-app-server-client.ts
|
|
51660
|
-
var import_node_readline = require("readline");
|
|
51661
|
-
function stdioLineStream(proc) {
|
|
51662
|
-
const rl = (0, import_node_readline.createInterface)({ input: proc.stdout, crlfDelay: Infinity });
|
|
51663
|
-
const lineCbs = [];
|
|
51664
|
-
const closeCbs = [];
|
|
51665
|
-
rl.on("line", (l2) => {
|
|
51666
|
-
for (const cb of lineCbs) cb(l2);
|
|
51667
|
-
});
|
|
51668
|
-
rl.on("close", () => {
|
|
51669
|
-
for (const cb of closeCbs) cb();
|
|
51670
|
-
});
|
|
51671
|
-
return {
|
|
51672
|
-
write: (line) => {
|
|
51673
|
-
proc.stdin.write(line + "\n");
|
|
51674
|
-
},
|
|
51675
|
-
onLine: (cb) => {
|
|
51676
|
-
lineCbs.push(cb);
|
|
51677
|
-
},
|
|
51678
|
-
onClose: (cb) => {
|
|
51679
|
-
closeCbs.push(cb);
|
|
51680
|
-
},
|
|
51681
|
-
close: () => {
|
|
51682
|
-
proc.stdin?.end();
|
|
51829
|
+
return (toolSessionId) => {
|
|
51830
|
+
if (index === null) {
|
|
51831
|
+
index = /* @__PURE__ */ new Map();
|
|
51832
|
+
for (const root of roots) walk2(root, index);
|
|
51683
51833
|
}
|
|
51834
|
+
return index.get(toolSessionId.toLowerCase()) ?? null;
|
|
51684
51835
|
};
|
|
51685
51836
|
}
|
|
51686
|
-
|
|
51687
|
-
|
|
51688
|
-
|
|
51689
|
-
|
|
51690
|
-
|
|
51691
|
-
|
|
51692
|
-
|
|
51693
|
-
|
|
51694
|
-
|
|
51695
|
-
|
|
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;
|
|
51696
51849
|
}
|
|
51697
|
-
|
|
51698
|
-
|
|
51699
|
-
|
|
51700
|
-
|
|
51701
|
-
|
|
51702
|
-
|
|
51703
|
-
|
|
51704
|
-
|
|
51705
|
-
this.write({ jsonrpc: "2.0", method, id, params });
|
|
51706
|
-
});
|
|
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);
|
|
51707
51858
|
}
|
|
51708
|
-
|
|
51709
|
-
|
|
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;
|
|
51710
51868
|
}
|
|
51711
|
-
|
|
51712
|
-
|
|
51713
|
-
|
|
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;
|
|
51714
51876
|
}
|
|
51715
|
-
|
|
51716
|
-
const
|
|
51717
|
-
|
|
51718
|
-
let
|
|
51719
|
-
|
|
51720
|
-
|
|
51721
|
-
|
|
51722
|
-
|
|
51723
|
-
|
|
51724
|
-
|
|
51725
|
-
|
|
51726
|
-
|
|
51727
|
-
|
|
51728
|
-
|
|
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;
|
|
51729
52994
|
this.pending.delete(m3.id);
|
|
51730
52995
|
if (m3.error) {
|
|
51731
52996
|
const e = isRecord(m3.error) ? m3.error : {};
|
|
@@ -51749,49 +53014,49 @@ function isRecord(v3) {
|
|
|
51749
53014
|
|
|
51750
53015
|
// src/tools/codex-app-server-events.ts
|
|
51751
53016
|
function translateNotification(method, params) {
|
|
51752
|
-
const p3 =
|
|
53017
|
+
const p3 = rec3(params);
|
|
51753
53018
|
switch (method) {
|
|
51754
53019
|
case "thread/started": {
|
|
51755
|
-
const id =
|
|
53020
|
+
const id = str3(rec3(p3?.thread)?.id);
|
|
51756
53021
|
return id ? [{ kind: "session_init", toolSessionId: id }] : [];
|
|
51757
53022
|
}
|
|
51758
53023
|
case "item/agentMessage/delta": {
|
|
51759
|
-
const text =
|
|
51760
|
-
const id =
|
|
53024
|
+
const text = str3(p3?.delta);
|
|
53025
|
+
const id = str3(p3?.itemId);
|
|
51761
53026
|
return text ? [{ kind: "text", text, ...id ? { partialId: id } : {} }] : [];
|
|
51762
53027
|
}
|
|
51763
53028
|
case "item/reasoning/textDelta":
|
|
51764
53029
|
case "item/reasoning/summaryTextDelta": {
|
|
51765
|
-
const text =
|
|
51766
|
-
const id =
|
|
53030
|
+
const text = str3(p3?.delta);
|
|
53031
|
+
const id = str3(p3?.itemId);
|
|
51767
53032
|
return text ? [{ kind: "thinking", text, ...id ? { partialId: id } : {} }] : [];
|
|
51768
53033
|
}
|
|
51769
53034
|
case "item/commandExecution/outputDelta":
|
|
51770
53035
|
return [];
|
|
51771
53036
|
// 命令输出增量 v1 不逐字(完成时整段给)
|
|
51772
53037
|
case "item/started":
|
|
51773
|
-
return itemStarted(
|
|
53038
|
+
return itemStarted(rec3(p3?.item));
|
|
51774
53039
|
case "item/completed":
|
|
51775
|
-
return itemCompleted(
|
|
53040
|
+
return itemCompleted(rec3(p3?.item));
|
|
51776
53041
|
case "thread/tokenUsage/updated":
|
|
51777
|
-
return tokenUsage(
|
|
53042
|
+
return tokenUsage(rec3(rec3(p3?.tokenUsage)));
|
|
51778
53043
|
// codex 自动生成的 thread 标题(实测 0.145:payload = { threadId, threadName })
|
|
51779
53044
|
case "thread/name/updated": {
|
|
51780
|
-
const name =
|
|
53045
|
+
const name = str3(p3?.threadName);
|
|
51781
53046
|
return name ? [{ kind: "meta_update", patch: { aiLabel: name } }] : [];
|
|
51782
53047
|
}
|
|
51783
53048
|
case "turn/completed":
|
|
51784
|
-
return turnCompleted(
|
|
53049
|
+
return turnCompleted(rec3(p3?.turn));
|
|
51785
53050
|
default:
|
|
51786
53051
|
return [];
|
|
51787
53052
|
}
|
|
51788
53053
|
}
|
|
51789
53054
|
function itemStarted(item) {
|
|
51790
53055
|
if (!item) return [];
|
|
51791
|
-
const id =
|
|
53056
|
+
const id = str3(item.id);
|
|
51792
53057
|
if (!id) return [];
|
|
51793
53058
|
if (item.type === "commandExecution") {
|
|
51794
|
-
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) ?? "" } }];
|
|
51795
53060
|
}
|
|
51796
53061
|
if (item.type === "fileChange") {
|
|
51797
53062
|
return [{ kind: "tool_call", toolUseId: id, tool: "fileChange", toolKind: "codex", input: { changes: Array.isArray(item.changes) ? item.changes : [] } }];
|
|
@@ -51805,28 +53070,28 @@ function itemCompleted(item) {
|
|
|
51805
53070
|
return [];
|
|
51806
53071
|
// 用户输入回显, clawd 自己 synthesize user_text
|
|
51807
53072
|
case "agentMessage": {
|
|
51808
|
-
const text =
|
|
53073
|
+
const text = str3(item.text);
|
|
51809
53074
|
return text ? [{ kind: "text", text }] : [];
|
|
51810
53075
|
}
|
|
51811
53076
|
case "reasoning": {
|
|
51812
|
-
const text =
|
|
53077
|
+
const text = str3(item.text);
|
|
51813
53078
|
return text ? [{ kind: "thinking", text }] : [];
|
|
51814
53079
|
}
|
|
51815
53080
|
case "commandExecution": {
|
|
51816
|
-
const id =
|
|
53081
|
+
const id = str3(item.id);
|
|
51817
53082
|
if (!id) return [];
|
|
51818
|
-
const exit =
|
|
51819
|
-
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) ?? "" };
|
|
51820
53085
|
if (exit !== void 0 && exit !== 0) ev.error = `exit ${exit}`;
|
|
51821
53086
|
return [ev];
|
|
51822
53087
|
}
|
|
51823
53088
|
case "fileChange": {
|
|
51824
|
-
const id =
|
|
53089
|
+
const id = str3(item.id);
|
|
51825
53090
|
if (!id) return [];
|
|
51826
53091
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
51827
|
-
const output = changes.map((c2) =>
|
|
53092
|
+
const output = changes.map((c2) => str3(c2.diff) ?? "").filter(Boolean).join("\n");
|
|
51828
53093
|
const ev = { kind: "tool_result", toolUseId: id, output };
|
|
51829
|
-
const status =
|
|
53094
|
+
const status = str3(item.status);
|
|
51830
53095
|
if (status === "failed" || status === "declined") ev.error = `patch ${status}`;
|
|
51831
53096
|
return [ev];
|
|
51832
53097
|
}
|
|
@@ -51836,26 +53101,26 @@ function itemCompleted(item) {
|
|
|
51836
53101
|
}
|
|
51837
53102
|
function tokenUsage(usage) {
|
|
51838
53103
|
if (!usage) return [];
|
|
51839
|
-
const last =
|
|
53104
|
+
const last = rec3(usage.last);
|
|
51840
53105
|
if (!last) return [];
|
|
51841
|
-
const i2 =
|
|
53106
|
+
const i2 = num3(last.inputTokens), o2 = num3(last.outputTokens);
|
|
51842
53107
|
const patch = {};
|
|
51843
53108
|
if (i2 !== void 0 && o2 !== void 0) patch.contextUsage = { inputTokens: i2, outputTokens: o2 };
|
|
51844
|
-
const w3 =
|
|
53109
|
+
const w3 = num3(usage.modelContextWindow);
|
|
51845
53110
|
if (w3 !== void 0) patch.contextWindowSize = w3;
|
|
51846
53111
|
return Object.keys(patch).length ? [{ kind: "meta_update", patch }] : [];
|
|
51847
53112
|
}
|
|
51848
53113
|
function turnCompleted(turn) {
|
|
51849
|
-
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" }];
|
|
51850
53115
|
return [{ kind: "turn_end" }];
|
|
51851
53116
|
}
|
|
51852
|
-
function
|
|
53117
|
+
function rec3(v3) {
|
|
51853
53118
|
return typeof v3 === "object" && v3 !== null ? v3 : void 0;
|
|
51854
53119
|
}
|
|
51855
|
-
function
|
|
53120
|
+
function str3(v3) {
|
|
51856
53121
|
return typeof v3 === "string" ? v3 : void 0;
|
|
51857
53122
|
}
|
|
51858
|
-
function
|
|
53123
|
+
function num3(v3) {
|
|
51859
53124
|
return typeof v3 === "number" ? v3 : void 0;
|
|
51860
53125
|
}
|
|
51861
53126
|
|
|
@@ -52319,17 +53584,17 @@ function stringValue2(value) {
|
|
|
52319
53584
|
}
|
|
52320
53585
|
|
|
52321
53586
|
// src/tools/mcp-server-defs.ts
|
|
52322
|
-
var
|
|
52323
|
-
var
|
|
53587
|
+
var import_node_fs21 = __toESM(require("fs"), 1);
|
|
53588
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
52324
53589
|
var HTTP_TYPES = /* @__PURE__ */ new Set(["http", "sse", "streamable_http", "streamable-http"]);
|
|
52325
53590
|
function loadMcpServerDefs(args) {
|
|
52326
53591
|
const out = [];
|
|
52327
53592
|
const seen = /* @__PURE__ */ new Set();
|
|
52328
|
-
const sources = [...args.mcpConfigPaths,
|
|
53593
|
+
const sources = [...args.mcpConfigPaths, import_node_path19.default.join(args.cwd, ".mcp.json")];
|
|
52329
53594
|
for (const file of sources) {
|
|
52330
53595
|
let parsed;
|
|
52331
53596
|
try {
|
|
52332
|
-
parsed = JSON.parse(
|
|
53597
|
+
parsed = JSON.parse(import_node_fs21.default.readFileSync(file, "utf8"));
|
|
52333
53598
|
} catch (e) {
|
|
52334
53599
|
const code = e.code;
|
|
52335
53600
|
if (code !== "ENOENT") {
|
|
@@ -52393,18 +53658,18 @@ function loadMcpServerDefs(args) {
|
|
|
52393
53658
|
}
|
|
52394
53659
|
|
|
52395
53660
|
// src/tools/codex-agents-config.ts
|
|
52396
|
-
var
|
|
52397
|
-
var
|
|
53661
|
+
var import_node_fs22 = __toESM(require("fs"), 1);
|
|
53662
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
52398
53663
|
var AGENTS_SECTION = /^\[agents\][^\n]*\n([\s\S]*?)(?=^\[|$(?![\s\S]))/m;
|
|
52399
53664
|
function readKey(section, key) {
|
|
52400
53665
|
const m3 = new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*(["'])(.*?)\\1`, "m").exec(section);
|
|
52401
53666
|
return m3?.[2].trim() || void 0;
|
|
52402
53667
|
}
|
|
52403
53668
|
function loadCodexAgentsConfig(args) {
|
|
52404
|
-
const file =
|
|
53669
|
+
const file = import_node_path20.default.join(args.cwd, ".codex", "config.toml");
|
|
52405
53670
|
let text;
|
|
52406
53671
|
try {
|
|
52407
|
-
text =
|
|
53672
|
+
text = import_node_fs22.default.readFileSync(file, "utf8");
|
|
52408
53673
|
} catch (e) {
|
|
52409
53674
|
const code = e.code;
|
|
52410
53675
|
if (code !== "ENOENT") {
|
|
@@ -52460,7 +53725,7 @@ function warmCodexFeatureCache() {
|
|
|
52460
53725
|
}
|
|
52461
53726
|
|
|
52462
53727
|
// src/attachment/mime.ts
|
|
52463
|
-
var
|
|
53728
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
52464
53729
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
52465
53730
|
var EXT_TO_NATIVE_MIME = {
|
|
52466
53731
|
// 图片
|
|
@@ -52567,7 +53832,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
52567
53832
|
".mk"
|
|
52568
53833
|
]);
|
|
52569
53834
|
function lookupMime(filePathOrName) {
|
|
52570
|
-
const ext =
|
|
53835
|
+
const ext = import_node_path21.default.extname(filePathOrName).toLowerCase();
|
|
52571
53836
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
52572
53837
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
52573
53838
|
return "application/octet-stream";
|
|
@@ -52616,7 +53881,7 @@ function createCodexSession(ctx, sink, deps = {}) {
|
|
|
52616
53881
|
threadParams: threadStartParams(ctx),
|
|
52617
53882
|
// persona 惯例装在 <cwd>/.claude/skills/,经 extraRoots 注入 discovery
|
|
52618
53883
|
// (skills:list RPC 侧 codex-skills.ts 同款注入,两处保持一致)。
|
|
52619
|
-
skillsExtraRoots: [
|
|
53884
|
+
skillsExtraRoots: [import_node_path22.default.join(ctx.cwd, ".claude", "skills")],
|
|
52620
53885
|
// 附件图片:验签认出本机 absPath → localImage,codex 自己读文件(TUI 拖图 / SDK local_image 同一条路)。
|
|
52621
53886
|
// 0.145 起 http(s) URL 在 turn/start 入口被拒,不能再按 url 传(codex-app-server-params.ts 头注释)。
|
|
52622
53887
|
// 与云上 cloudImageInput 同一道 mime 门:非图片(签过的 .md 之类)留文本,别让 codex 读到一半才炸。
|
|
@@ -52770,18 +54035,18 @@ function mergeProviderModels(native, provider) {
|
|
|
52770
54035
|
|
|
52771
54036
|
// src/tools/codex-provider-models.ts
|
|
52772
54037
|
var import_node_child_process6 = require("child_process");
|
|
52773
|
-
var
|
|
52774
|
-
var
|
|
52775
|
-
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);
|
|
52776
54041
|
var import_node_util = require("util");
|
|
52777
54042
|
var PROVIDER_FETCH_TIMEOUT_MS = 3e3;
|
|
52778
|
-
function
|
|
54043
|
+
function asRecord2(v3) {
|
|
52779
54044
|
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : null;
|
|
52780
54045
|
}
|
|
52781
54046
|
function readAuthKey(codexHome) {
|
|
52782
54047
|
try {
|
|
52783
|
-
const raw = JSON.parse(
|
|
52784
|
-
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;
|
|
52785
54050
|
return typeof key === "string" && key ? key : void 0;
|
|
52786
54051
|
} catch {
|
|
52787
54052
|
return void 0;
|
|
@@ -52808,19 +54073,19 @@ async function runAuthCommand(name, auth, deps) {
|
|
|
52808
54073
|
return void 0;
|
|
52809
54074
|
}
|
|
52810
54075
|
async function resolveProviderEndpoint(rawConfig, deps = {}) {
|
|
52811
|
-
const config =
|
|
54076
|
+
const config = asRecord2(asRecord2(rawConfig)?.config);
|
|
52812
54077
|
if (!config) return null;
|
|
52813
54078
|
const name = config.model_provider;
|
|
52814
54079
|
if (typeof name !== "string" || !name) return null;
|
|
52815
|
-
const entry =
|
|
54080
|
+
const entry = asRecord2(asRecord2(config.model_providers)?.[name]);
|
|
52816
54081
|
if (!entry) return null;
|
|
52817
54082
|
const baseUrl = entry.base_url;
|
|
52818
54083
|
if (typeof baseUrl !== "string" || !baseUrl) return null;
|
|
52819
54084
|
const env = deps.env ?? process.env;
|
|
52820
|
-
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");
|
|
52821
54086
|
const envKey = typeof entry.env_key === "string" && entry.env_key ? entry.env_key : null;
|
|
52822
54087
|
const inlineToken = typeof entry.experimental_bearer_token === "string" && entry.experimental_bearer_token ? entry.experimental_bearer_token : void 0;
|
|
52823
|
-
const auth =
|
|
54088
|
+
const auth = asRecord2(entry.auth);
|
|
52824
54089
|
let token;
|
|
52825
54090
|
if (envKey) token = env[envKey];
|
|
52826
54091
|
else if (inlineToken) token = inlineToken;
|
|
@@ -52848,11 +54113,11 @@ async function fetchProviderModels(endpoint, deps = {}) {
|
|
|
52848
54113
|
try {
|
|
52849
54114
|
const res = await fetchImpl(url, { headers: endpoint.headers, signal: controller.signal });
|
|
52850
54115
|
if (!res.ok) throw new Error(`provider model list failed: HTTP ${res.status} from ${url}`);
|
|
52851
|
-
const data =
|
|
54116
|
+
const data = asRecord2(await res.json())?.data;
|
|
52852
54117
|
if (!Array.isArray(data)) throw new Error(`provider model list: \`data\` is not an array (${url})`);
|
|
52853
54118
|
const out = [];
|
|
52854
54119
|
for (const item of data) {
|
|
52855
|
-
const e =
|
|
54120
|
+
const e = asRecord2(item);
|
|
52856
54121
|
if (!e || typeof e.id !== "string" || !e.id) continue;
|
|
52857
54122
|
out.push({ id: e.id, ...typeof e.owned_by === "string" ? { ownedBy: e.owned_by } : {} });
|
|
52858
54123
|
}
|
|
@@ -52932,10 +54197,10 @@ function sanitizeCodexModel(model) {
|
|
|
52932
54197
|
return model;
|
|
52933
54198
|
}
|
|
52934
54199
|
async function probeCodex(env = process.env) {
|
|
52935
|
-
if (env.CODEX_BIN &&
|
|
54200
|
+
if (env.CODEX_BIN && import_node_fs24.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
|
|
52936
54201
|
try {
|
|
52937
54202
|
const out = (0, import_node_child_process7.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
|
|
52938
|
-
if (out &&
|
|
54203
|
+
if (out && import_node_fs24.default.existsSync(out)) return { available: true, path: out };
|
|
52939
54204
|
} catch {
|
|
52940
54205
|
}
|
|
52941
54206
|
return { available: false };
|
|
@@ -53055,9 +54320,9 @@ var CodexAdapter = class {
|
|
|
53055
54320
|
|
|
53056
54321
|
// src/tools/claude-tui.ts
|
|
53057
54322
|
var import_node_crypto7 = require("crypto");
|
|
53058
|
-
var
|
|
53059
|
-
var
|
|
53060
|
-
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);
|
|
53061
54326
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
53062
54327
|
|
|
53063
54328
|
// ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
|
|
@@ -54282,10 +55547,10 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
|
|
|
54282
55547
|
}
|
|
54283
55548
|
function jsonlExistsForCtx(ctx) {
|
|
54284
55549
|
if (!ctx.toolSessionId) return false;
|
|
54285
|
-
const home =
|
|
54286
|
-
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`);
|
|
54287
55552
|
try {
|
|
54288
|
-
return
|
|
55553
|
+
return import_node_fs25.default.statSync(file).isFile();
|
|
54289
55554
|
} catch {
|
|
54290
55555
|
return false;
|
|
54291
55556
|
}
|
|
@@ -54370,7 +55635,7 @@ var PersonaDispatchManager = class {
|
|
|
54370
55635
|
|
|
54371
55636
|
// src/dispatch/store.ts
|
|
54372
55637
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
54373
|
-
var
|
|
55638
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
54374
55639
|
|
|
54375
55640
|
// src/shift/constants.ts
|
|
54376
55641
|
var MAX_RUNS_PER_SHIFT = 30;
|
|
@@ -54408,7 +55673,7 @@ function createDispatchStore(deps) {
|
|
|
54408
55673
|
flushTimer = null;
|
|
54409
55674
|
}
|
|
54410
55675
|
const content = { version: 1, records };
|
|
54411
|
-
await import_promises.default.mkdir(
|
|
55676
|
+
await import_promises.default.mkdir(import_node_path25.default.dirname(deps.filePath), { recursive: true });
|
|
54412
55677
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54413
55678
|
await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54414
55679
|
await import_promises.default.rename(tmp, deps.filePath);
|
|
@@ -54477,7 +55742,7 @@ function createDispatchStore(deps) {
|
|
|
54477
55742
|
|
|
54478
55743
|
// src/channel/json-store.ts
|
|
54479
55744
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
54480
|
-
var
|
|
55745
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
54481
55746
|
function createJsonStore(deps) {
|
|
54482
55747
|
let items = [];
|
|
54483
55748
|
let flushTimer = null;
|
|
@@ -54488,7 +55753,7 @@ function createJsonStore(deps) {
|
|
|
54488
55753
|
flushTimer = null;
|
|
54489
55754
|
}
|
|
54490
55755
|
const content = { version: version2, [deps.key]: items };
|
|
54491
|
-
await import_promises2.default.mkdir(
|
|
55756
|
+
await import_promises2.default.mkdir(import_node_path26.default.dirname(deps.filePath), { recursive: true });
|
|
54492
55757
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
54493
55758
|
await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
54494
55759
|
await import_promises2.default.rename(tmp, deps.filePath);
|
|
@@ -54780,10 +56045,10 @@ function createTopicStore(deps) {
|
|
|
54780
56045
|
}
|
|
54781
56046
|
|
|
54782
56047
|
// src/dispatch/mcp-config.ts
|
|
54783
|
-
var
|
|
54784
|
-
var
|
|
56048
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
56049
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
54785
56050
|
function dispatchMcpConfigPath(dataDir) {
|
|
54786
|
-
return
|
|
56051
|
+
return import_node_path27.default.join(dataDir, "dispatch.mcp.json");
|
|
54787
56052
|
}
|
|
54788
56053
|
function writeDispatchMcpConfig(args) {
|
|
54789
56054
|
const cfgPath = dispatchMcpConfigPath(args.dataDir);
|
|
@@ -54802,16 +56067,16 @@ function writeDispatchMcpConfig(args) {
|
|
|
54802
56067
|
}
|
|
54803
56068
|
}
|
|
54804
56069
|
};
|
|
54805
|
-
|
|
54806
|
-
|
|
56070
|
+
import_node_fs26.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56071
|
+
import_node_fs26.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54807
56072
|
return cfgPath;
|
|
54808
56073
|
}
|
|
54809
56074
|
|
|
54810
56075
|
// src/ticket/mcp-config.ts
|
|
54811
|
-
var
|
|
54812
|
-
var
|
|
56076
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
56077
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
54813
56078
|
function ticketMcpConfigPath(dataDir) {
|
|
54814
|
-
return
|
|
56079
|
+
return import_node_path28.default.join(dataDir, "ticket.mcp.json");
|
|
54815
56080
|
}
|
|
54816
56081
|
function writeTicketMcpConfig(args) {
|
|
54817
56082
|
const cfgPath = ticketMcpConfigPath(args.dataDir);
|
|
@@ -54833,16 +56098,16 @@ function writeTicketMcpConfig(args) {
|
|
|
54833
56098
|
}
|
|
54834
56099
|
}
|
|
54835
56100
|
};
|
|
54836
|
-
|
|
54837
|
-
|
|
56101
|
+
import_node_fs27.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56102
|
+
import_node_fs27.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54838
56103
|
return cfgPath;
|
|
54839
56104
|
}
|
|
54840
56105
|
|
|
54841
56106
|
// src/shift/mcp-config.ts
|
|
54842
|
-
var
|
|
54843
|
-
var
|
|
56107
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
56108
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
54844
56109
|
function shiftMcpConfigPath(dataDir) {
|
|
54845
|
-
return
|
|
56110
|
+
return import_node_path29.default.join(dataDir, "shift.mcp.json");
|
|
54846
56111
|
}
|
|
54847
56112
|
async function writeShiftMcpConfig(args) {
|
|
54848
56113
|
const cfgPath = shiftMcpConfigPath(args.dataDir);
|
|
@@ -54861,16 +56126,16 @@ async function writeShiftMcpConfig(args) {
|
|
|
54861
56126
|
}
|
|
54862
56127
|
}
|
|
54863
56128
|
};
|
|
54864
|
-
await
|
|
54865
|
-
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");
|
|
54866
56131
|
return cfgPath;
|
|
54867
56132
|
}
|
|
54868
56133
|
|
|
54869
56134
|
// src/inbox/mcp-config.ts
|
|
54870
|
-
var
|
|
54871
|
-
var
|
|
56135
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
56136
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
54872
56137
|
function inboxMcpConfigPath(dataDir) {
|
|
54873
|
-
return
|
|
56138
|
+
return import_node_path30.default.join(dataDir, "inbox.mcp.json");
|
|
54874
56139
|
}
|
|
54875
56140
|
async function writeInboxMcpConfig(args) {
|
|
54876
56141
|
const cfgPath = inboxMcpConfigPath(args.dataDir);
|
|
@@ -54889,16 +56154,16 @@ async function writeInboxMcpConfig(args) {
|
|
|
54889
56154
|
}
|
|
54890
56155
|
}
|
|
54891
56156
|
};
|
|
54892
|
-
await
|
|
54893
|
-
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");
|
|
54894
56159
|
return cfgPath;
|
|
54895
56160
|
}
|
|
54896
56161
|
|
|
54897
56162
|
// src/peer-ops/mcp-config.ts
|
|
54898
|
-
var
|
|
54899
|
-
var
|
|
56163
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
56164
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
54900
56165
|
function peerOpsMcpConfigPath(dataDir) {
|
|
54901
|
-
return
|
|
56166
|
+
return import_node_path31.default.join(dataDir, "peer-ops.mcp.json");
|
|
54902
56167
|
}
|
|
54903
56168
|
function writePeerOpsMcpConfig(args) {
|
|
54904
56169
|
const cfgPath = peerOpsMcpConfigPath(args.dataDir);
|
|
@@ -54915,16 +56180,16 @@ function writePeerOpsMcpConfig(args) {
|
|
|
54915
56180
|
}
|
|
54916
56181
|
}
|
|
54917
56182
|
};
|
|
54918
|
-
|
|
54919
|
-
|
|
56183
|
+
import_node_fs30.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56184
|
+
import_node_fs30.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
54920
56185
|
return cfgPath;
|
|
54921
56186
|
}
|
|
54922
56187
|
|
|
54923
56188
|
// src/rpc-tool/mcp-config.ts
|
|
54924
|
-
var
|
|
54925
|
-
var
|
|
56189
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
56190
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
54926
56191
|
function rpcToolMcpConfigPath(dataDir) {
|
|
54927
|
-
return
|
|
56192
|
+
return import_node_path32.default.join(dataDir, "rpc-tool.mcp.json");
|
|
54928
56193
|
}
|
|
54929
56194
|
function writeRpcToolMcpConfig(args) {
|
|
54930
56195
|
const cfgPath = rpcToolMcpConfigPath(args.dataDir);
|
|
@@ -54943,29 +56208,29 @@ function writeRpcToolMcpConfig(args) {
|
|
|
54943
56208
|
}
|
|
54944
56209
|
}
|
|
54945
56210
|
};
|
|
54946
|
-
|
|
54947
|
-
|
|
56211
|
+
import_node_fs31.default.mkdirSync(args.dataDir, { recursive: true });
|
|
56212
|
+
import_node_fs31.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
54948
56213
|
return cfgPath;
|
|
54949
56214
|
}
|
|
54950
56215
|
|
|
54951
56216
|
// src/deploy/secret-table.ts
|
|
54952
|
-
var
|
|
54953
|
-
var
|
|
56217
|
+
var import_node_fs32 = __toESM(require("fs"), 1);
|
|
56218
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
54954
56219
|
var import_node_url5 = require("url");
|
|
54955
56220
|
var import_meta5 = {};
|
|
54956
56221
|
var LOCAL_SECRETS_DIRNAME = "secrets";
|
|
54957
56222
|
function findSecretsBundleRoot(logger) {
|
|
54958
56223
|
const candidates = [];
|
|
54959
56224
|
try {
|
|
54960
|
-
const here =
|
|
54961
|
-
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"));
|
|
54962
56227
|
} catch {
|
|
54963
56228
|
}
|
|
54964
56229
|
const fromArgv = bundleSiblingFromArgv(process.argv[1], "secrets");
|
|
54965
56230
|
if (fromArgv) candidates.push(fromArgv);
|
|
54966
56231
|
for (const c2 of candidates) {
|
|
54967
56232
|
try {
|
|
54968
|
-
if (
|
|
56233
|
+
if (import_node_fs32.default.statSync(c2).isDirectory()) return c2;
|
|
54969
56234
|
} catch {
|
|
54970
56235
|
}
|
|
54971
56236
|
}
|
|
@@ -54991,7 +56256,7 @@ function readDir(dir, logger) {
|
|
|
54991
56256
|
const out = {};
|
|
54992
56257
|
let files;
|
|
54993
56258
|
try {
|
|
54994
|
-
files =
|
|
56259
|
+
files = import_node_fs32.default.readdirSync(dir).filter((f2) => f2.endsWith(".env")).sort();
|
|
54995
56260
|
} catch (e) {
|
|
54996
56261
|
const code = e.code;
|
|
54997
56262
|
if (code !== "ENOENT") logger?.warn("secrets.dir-unreadable", { dir, code: code ?? "unknown" });
|
|
@@ -54999,19 +56264,19 @@ function readDir(dir, logger) {
|
|
|
54999
56264
|
}
|
|
55000
56265
|
for (const f2 of files) {
|
|
55001
56266
|
try {
|
|
55002
|
-
Object.assign(out, parseEnvFile(
|
|
56267
|
+
Object.assign(out, parseEnvFile(import_node_fs32.default.readFileSync(import_node_path33.default.join(dir, f2), "utf8")));
|
|
55003
56268
|
} catch (e) {
|
|
55004
|
-
logger?.warn("secrets.file-unreadable", { file:
|
|
56269
|
+
logger?.warn("secrets.file-unreadable", { file: import_node_path33.default.join(dir, f2), error: e.message });
|
|
55005
56270
|
}
|
|
55006
56271
|
}
|
|
55007
56272
|
return out;
|
|
55008
56273
|
}
|
|
55009
56274
|
function upsertEnvFileVar(file, key, value) {
|
|
55010
56275
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55011
|
-
|
|
56276
|
+
import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(file), { recursive: true, mode: 448 });
|
|
55012
56277
|
let lines = [];
|
|
55013
56278
|
try {
|
|
55014
|
-
lines =
|
|
56279
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55015
56280
|
if (lines[lines.length - 1] === "") lines.pop();
|
|
55016
56281
|
} catch {
|
|
55017
56282
|
}
|
|
@@ -55019,12 +56284,12 @@ function upsertEnvFileVar(file, key, value) {
|
|
|
55019
56284
|
let replaced = false;
|
|
55020
56285
|
const out = lines.map((l2) => re2.test(l2) ? (replaced = true, `${key}=${value}`) : l2);
|
|
55021
56286
|
if (!replaced) out.push(`${key}=${value}`);
|
|
55022
|
-
|
|
56287
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55023
56288
|
`, { encoding: "utf8", mode: 384 });
|
|
55024
56289
|
}
|
|
55025
56290
|
function readEnvFileVar(file, key) {
|
|
55026
56291
|
try {
|
|
55027
|
-
return parseEnvFile(
|
|
56292
|
+
return parseEnvFile(import_node_fs32.default.readFileSync(file, "utf8"))[key];
|
|
55028
56293
|
} catch {
|
|
55029
56294
|
return void 0;
|
|
55030
56295
|
}
|
|
@@ -55033,7 +56298,7 @@ function removeEnvFileVar(file, key) {
|
|
|
55033
56298
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid env name: ${key}`);
|
|
55034
56299
|
let lines;
|
|
55035
56300
|
try {
|
|
55036
|
-
lines =
|
|
56301
|
+
lines = import_node_fs32.default.readFileSync(file, "utf8").split("\n");
|
|
55037
56302
|
} catch {
|
|
55038
56303
|
return;
|
|
55039
56304
|
}
|
|
@@ -55042,10 +56307,10 @@ function removeEnvFileVar(file, key) {
|
|
|
55042
56307
|
const out = lines.filter((l2) => !re2.test(l2));
|
|
55043
56308
|
if (out.length === lines.length) return;
|
|
55044
56309
|
if (out.every((l2) => l2.trim() === "" || l2.trim().startsWith("#"))) {
|
|
55045
|
-
|
|
56310
|
+
import_node_fs32.default.rmSync(file, { force: true });
|
|
55046
56311
|
return;
|
|
55047
56312
|
}
|
|
55048
|
-
|
|
56313
|
+
import_node_fs32.default.writeFileSync(file, `${out.join("\n")}
|
|
55049
56314
|
`, { encoding: "utf8", mode: 384 });
|
|
55050
56315
|
}
|
|
55051
56316
|
function readSecretTable(args) {
|
|
@@ -55056,12 +56321,12 @@ function readSecretTable(args) {
|
|
|
55056
56321
|
}
|
|
55057
56322
|
|
|
55058
56323
|
// src/dispatch/source-transcript.ts
|
|
55059
|
-
var
|
|
56324
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
55060
56325
|
init_claude_history();
|
|
55061
56326
|
var NO_SOURCE_TRANSCRIPT = "(no transcript yet \u2014 operate from the task description alone)";
|
|
55062
56327
|
function resolveSourceJsonlPath(sourceFile, home) {
|
|
55063
56328
|
if (!sourceFile?.toolSessionId) return NO_SOURCE_TRANSCRIPT;
|
|
55064
|
-
return
|
|
56329
|
+
return import_node_path34.default.join(
|
|
55065
56330
|
home,
|
|
55066
56331
|
".claude",
|
|
55067
56332
|
"projects",
|
|
@@ -55072,7 +56337,7 @@ function resolveSourceJsonlPath(sourceFile, home) {
|
|
|
55072
56337
|
|
|
55073
56338
|
// src/shift/store.ts
|
|
55074
56339
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
55075
|
-
var
|
|
56340
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
55076
56341
|
var import_node_crypto8 = require("crypto");
|
|
55077
56342
|
|
|
55078
56343
|
// src/shift/schedule.ts
|
|
@@ -55147,7 +56412,7 @@ function createShiftStore(deps) {
|
|
|
55147
56412
|
flushTimer = null;
|
|
55148
56413
|
}
|
|
55149
56414
|
const content = { version: 1, shifts };
|
|
55150
|
-
await import_promises3.default.mkdir(
|
|
56415
|
+
await import_promises3.default.mkdir(import_node_path35.default.dirname(deps.filePath), { recursive: true });
|
|
55151
56416
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
55152
56417
|
await import_promises3.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
55153
56418
|
await import_promises3.default.rename(tmp, deps.filePath);
|
|
@@ -55601,14 +56866,14 @@ function redactEvidence(evidence) {
|
|
|
55601
56866
|
|
|
55602
56867
|
// src/deploy/deploy-file.ts
|
|
55603
56868
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
55604
|
-
var
|
|
56869
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
55605
56870
|
var import_node_crypto9 = require("crypto");
|
|
55606
56871
|
init_zod();
|
|
55607
56872
|
init_protocol();
|
|
55608
56873
|
|
|
55609
56874
|
// src/deploy/connectors.ts
|
|
55610
|
-
var
|
|
55611
|
-
var
|
|
56875
|
+
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
56876
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
55612
56877
|
init_zod();
|
|
55613
56878
|
init_protocol();
|
|
55614
56879
|
var PLACEHOLDER2 = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -55658,11 +56923,11 @@ var PACKAGE_RUNNERS = /* @__PURE__ */ new Set(["npx", "uvx", "pnpx", "bunx"]);
|
|
|
55658
56923
|
var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["node", "bun", "deno", "python", "python3"]);
|
|
55659
56924
|
var HOST_ARG_PLACEHOLDER = "CLAWD_PERSONA_DIR";
|
|
55660
56925
|
function scanMcpConnectors(personaDir) {
|
|
55661
|
-
const file =
|
|
56926
|
+
const file = import_node_path36.default.join(personaDir, ".mcp.json");
|
|
55662
56927
|
const out = { readable: true, connectors: [], evidence: [], warnings: [] };
|
|
55663
56928
|
let parsed;
|
|
55664
56929
|
try {
|
|
55665
|
-
parsed = JSON.parse(
|
|
56930
|
+
parsed = JSON.parse(import_node_fs33.default.readFileSync(file, "utf8"));
|
|
55666
56931
|
} catch (e) {
|
|
55667
56932
|
if (e.code === "ENOENT") return out;
|
|
55668
56933
|
out.readable = false;
|
|
@@ -55757,8 +57022,8 @@ function mergeScannedConnectors(existing, scanned) {
|
|
|
55757
57022
|
}
|
|
55758
57023
|
|
|
55759
57024
|
// src/deploy/deploy-file.ts
|
|
55760
|
-
var DEPLOY_FILE =
|
|
55761
|
-
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");
|
|
55762
57027
|
async function readJson(file) {
|
|
55763
57028
|
let raw;
|
|
55764
57029
|
try {
|
|
@@ -55774,14 +57039,14 @@ async function readJson(file) {
|
|
|
55774
57039
|
}
|
|
55775
57040
|
}
|
|
55776
57041
|
async function writeJson(file, value) {
|
|
55777
|
-
await import_promises4.default.mkdir(
|
|
57042
|
+
await import_promises4.default.mkdir(import_node_path37.default.dirname(file), { recursive: true });
|
|
55778
57043
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
55779
57044
|
await import_promises4.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
|
|
55780
57045
|
`);
|
|
55781
57046
|
await import_promises4.default.rename(tmp, file);
|
|
55782
57047
|
}
|
|
55783
57048
|
async function readDeployFile(dir) {
|
|
55784
|
-
const raw = await readJson(
|
|
57049
|
+
const raw = await readJson(import_node_path37.default.join(dir, DEPLOY_FILE));
|
|
55785
57050
|
if (raw === void 0) return void 0;
|
|
55786
57051
|
const parsed = DeployFileSchema.safeParse(raw);
|
|
55787
57052
|
if (!parsed.success) {
|
|
@@ -55791,18 +57056,18 @@ async function readDeployFile(dir) {
|
|
|
55791
57056
|
return parsed.data;
|
|
55792
57057
|
}
|
|
55793
57058
|
async function writeDeployFile(dir, file) {
|
|
55794
|
-
await writeJson(
|
|
57059
|
+
await writeJson(import_node_path37.default.join(dir, DEPLOY_FILE), DeployFileSchema.parse(file));
|
|
55795
57060
|
}
|
|
55796
57061
|
async function readDeployFileRaw(dir) {
|
|
55797
57062
|
try {
|
|
55798
|
-
return await import_promises4.default.readFile(
|
|
57063
|
+
return await import_promises4.default.readFile(import_node_path37.default.join(dir, DEPLOY_FILE), "utf8");
|
|
55799
57064
|
} catch (err) {
|
|
55800
57065
|
if (err.code === "ENOENT") return void 0;
|
|
55801
57066
|
throw err;
|
|
55802
57067
|
}
|
|
55803
57068
|
}
|
|
55804
57069
|
async function restoreDeployFileRaw(dir, raw) {
|
|
55805
|
-
const file =
|
|
57070
|
+
const file = import_node_path37.default.join(dir, DEPLOY_FILE);
|
|
55806
57071
|
if (raw === void 0) await import_promises4.default.rm(file, { force: true });
|
|
55807
57072
|
else await import_promises4.default.writeFile(file, raw);
|
|
55808
57073
|
}
|
|
@@ -55843,7 +57108,7 @@ function normalizeLegacyScan(raw) {
|
|
|
55843
57108
|
return fix(raw);
|
|
55844
57109
|
}
|
|
55845
57110
|
async function readScanFile(dir) {
|
|
55846
|
-
const raw = normalizeLegacyScan(await readJson(
|
|
57111
|
+
const raw = normalizeLegacyScan(await readJson(import_node_path37.default.join(dir, SCAN_FILE)));
|
|
55847
57112
|
if (raw === void 0) return void 0;
|
|
55848
57113
|
const badShape = (issues) => {
|
|
55849
57114
|
const first = issues[0];
|
|
@@ -55864,7 +57129,7 @@ async function readScanFile(dir) {
|
|
|
55864
57129
|
return parsed.data;
|
|
55865
57130
|
}
|
|
55866
57131
|
async function writeScanFile(dir, file) {
|
|
55867
|
-
await writeJson(
|
|
57132
|
+
await writeJson(import_node_path37.default.join(dir, SCAN_FILE), DeployScanFileSchema.parse(file));
|
|
55868
57133
|
}
|
|
55869
57134
|
function replaceScan(file, entry) {
|
|
55870
57135
|
const idx = file.scans.findIndex((s2) => s2.id === entry.id);
|
|
@@ -55947,9 +57212,9 @@ function requiredEnvNames(manifest) {
|
|
|
55947
57212
|
|
|
55948
57213
|
// src/deploy/artifact.ts
|
|
55949
57214
|
var import_node_crypto10 = require("crypto");
|
|
55950
|
-
var
|
|
55951
|
-
var
|
|
55952
|
-
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);
|
|
55953
57218
|
var import_node_stream3 = require("stream");
|
|
55954
57219
|
var import_promises5 = require("stream/promises");
|
|
55955
57220
|
var ARTIFACT_MAX_BYTES = 200 * 1024 * 1024;
|
|
@@ -55983,8 +57248,8 @@ function downloadArtifact(exec) {
|
|
|
55983
57248
|
if (!res.ok || !res.body) throw new Error(`\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}\uFF1A${url}`);
|
|
55984
57249
|
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
55985
57250
|
if (declared > ARTIFACT_MAX_BYTES) throw new Error(`\u8D44\u4EA7 ${declared} \u5B57\u8282\uFF0C\u8D85\u8FC7\u4E0A\u9650 ${ARTIFACT_MAX_BYTES}\uFF1A${url}`);
|
|
55986
|
-
const dir = await
|
|
55987
|
-
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");
|
|
55988
57253
|
const hash = (0, import_node_crypto10.createHash)("sha256");
|
|
55989
57254
|
let size = 0;
|
|
55990
57255
|
const meter = new import_node_stream3.Transform({
|
|
@@ -55996,7 +57261,7 @@ function downloadArtifact(exec) {
|
|
|
55996
57261
|
}
|
|
55997
57262
|
});
|
|
55998
57263
|
try {
|
|
55999
|
-
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));
|
|
56000
57265
|
let entries;
|
|
56001
57266
|
if (listEntries) {
|
|
56002
57267
|
const r2 = await exec("tar", ["-tzf", file], { timeoutMs: 3e4 });
|
|
@@ -56005,7 +57270,7 @@ function downloadArtifact(exec) {
|
|
|
56005
57270
|
}
|
|
56006
57271
|
return { sha256: hash.digest("hex"), size, ...entries ? { entries } : {} };
|
|
56007
57272
|
} finally {
|
|
56008
|
-
await
|
|
57273
|
+
await import_node_fs34.default.promises.rm(dir, { recursive: true, force: true });
|
|
56009
57274
|
}
|
|
56010
57275
|
};
|
|
56011
57276
|
}
|
|
@@ -56056,7 +57321,7 @@ init_protocol();
|
|
|
56056
57321
|
|
|
56057
57322
|
// src/deploy/repo.ts
|
|
56058
57323
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56059
|
-
var
|
|
57324
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
56060
57325
|
init_protocol();
|
|
56061
57326
|
|
|
56062
57327
|
// ../node_modules/.pnpm/libsodium@0.8.4/node_modules/libsodium/dist/modules-esm/libsodium.mjs
|
|
@@ -56076,13 +57341,13 @@ async function A2(A4 = {}) {
|
|
|
56076
57341
|
(function() {
|
|
56077
57342
|
}).bind();
|
|
56078
57343
|
var E3, i2, D3, F3, o2, w3, h2, S3, y3, k3, c2, M3 = function() {
|
|
56079
|
-
}.bind(),
|
|
57344
|
+
}.bind(), U3 = false, G2 = false;
|
|
56080
57345
|
function J2() {
|
|
56081
57346
|
var A5 = d2.buffer;
|
|
56082
57347
|
h2 = new Int8Array(A5), o2 = new Int16Array(A5), g3.HEAPU8 = c2 = new Uint8Array(A5), new Uint16Array(A5), w3 = new Int32Array(A5), k3 = new Uint32Array(A5), S3 = new Float32Array(A5), y3 = new Float64Array(A5);
|
|
56083
57348
|
}
|
|
56084
57349
|
function Y2(A5) {
|
|
56085
|
-
g3.onAbort?.(A5), M3(A5 = `Aborted(${A5})`),
|
|
57350
|
+
g3.onAbort?.(A5), M3(A5 = `Aborted(${A5})`), U3 = true, A5 += ". Build with -sASSERTIONS for more info.";
|
|
56086
57351
|
var I4 = new WebAssembly.RuntimeError(A5);
|
|
56087
57352
|
throw D3?.(I4), I4;
|
|
56088
57353
|
}
|
|
@@ -56240,7 +57505,7 @@ async function A2(A4 = {}) {
|
|
|
56240
57505
|
})(0, F3, C3)).instance));
|
|
56241
57506
|
})(), (function() {
|
|
56242
57507
|
function A5() {
|
|
56243
|
-
g3.calledRun = true,
|
|
57508
|
+
g3.calledRun = true, U3 || (G2 = true, i2?.(g3), g3.onRuntimeInitialized?.(), (function() {
|
|
56244
57509
|
if (g3.postRun) for ("function" == typeof g3.postRun && (g3.postRun = [g3.postRun]); g3.postRun.length; ) H2(g3.postRun.shift());
|
|
56245
57510
|
N2(K2);
|
|
56246
57511
|
})());
|
|
@@ -59596,6 +60861,9 @@ function createGithubApi(args) {
|
|
|
59596
60861
|
},
|
|
59597
60862
|
async rerunJob(slug, jobId) {
|
|
59598
60863
|
await must("POST", `/repos/${slug}/actions/jobs/${jobId}/rerun`, "\u91CD\u8DD1\u8FD9\u4E00\u6BB5");
|
|
60864
|
+
},
|
|
60865
|
+
async dispatchWorkflow(slug, file, ref) {
|
|
60866
|
+
await must("POST", `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/dispatches`, "\u89E6\u53D1\u6D41\u6C34\u7EBF", { ref });
|
|
59599
60867
|
}
|
|
59600
60868
|
};
|
|
59601
60869
|
}
|
|
@@ -59663,7 +60931,10 @@ async function storeGithubAuth(args) {
|
|
|
59663
60931
|
init_protocol();
|
|
59664
60932
|
var CLAWOS_REPO = "ottin4ttc/clawos";
|
|
59665
60933
|
var CLAWOS_REF = "release";
|
|
59666
|
-
var
|
|
60934
|
+
var DEPLOY_WORKFLOW_FILE = "clawd-deploy.yml";
|
|
60935
|
+
var WORKFLOW_PATH = `.github/workflows/${DEPLOY_WORKFLOW_FILE}`;
|
|
60936
|
+
var UNPUBLISH_WORKFLOW_FILE = "clawd-unpublish.yml";
|
|
60937
|
+
var UNPUBLISH_WORKFLOW_PATH = `.github/workflows/${UNPUBLISH_WORKFLOW_FILE}`;
|
|
59667
60938
|
var DOCKERFILE_PATH = "Dockerfile";
|
|
59668
60939
|
var DOCKERIGNORE_PATH = ".dockerignore";
|
|
59669
60940
|
var ACR_REGISTRY = "registry.cn-hangzhou.aliyuncs.com";
|
|
@@ -59757,6 +61028,7 @@ var ACR_LOGIN_STEPS = [
|
|
|
59757
61028
|
];
|
|
59758
61029
|
var IMAGE_LATEST = '"$REGISTRY/$ACR_NAMESPACE/${{ steps.plan.outputs.persona_id }}:latest"';
|
|
59759
61030
|
var RESOLVE_LINE = ` IMAGE_REF=$(node clawos/cloud-host/pipeline/acr.mjs resolve --image ${IMAGE_LATEST} --vpc-registry "$VPC_REGISTRY")`;
|
|
61031
|
+
var CONCURRENCY_GROUP = " group: persona-cloud-${{ github.repository }}";
|
|
59760
61032
|
var L2 = [
|
|
59761
61033
|
"name: clawd deploy",
|
|
59762
61034
|
"",
|
|
@@ -59777,7 +61049,7 @@ var L2 = [
|
|
|
59777
61049
|
"",
|
|
59778
61050
|
"concurrency:",
|
|
59779
61051
|
" # \u540C\u4E00\u4E2A\u4ED3\u5E93\u7684\u53D1\u5E03\u6392\u961F\u8DD1\uFF0C\u4E0D\u53D6\u6D88\uFF1A\u4E0A\u4E00\u6B21\u53EF\u80FD\u5DF2\u7ECF\u5728\u767B\u8BB0\u6B63\u5F0F\u4E86",
|
|
59780
|
-
|
|
61052
|
+
CONCURRENCY_GROUP,
|
|
59781
61053
|
"",
|
|
59782
61054
|
"env:",
|
|
59783
61055
|
` REGISTRY: ${ACR_REGISTRY}`,
|
|
@@ -59913,6 +61185,44 @@ var L2 = [
|
|
|
59913
61185
|
""
|
|
59914
61186
|
];
|
|
59915
61187
|
var WORKFLOW_TEMPLATE = L2.join("\n");
|
|
61188
|
+
var U2 = [
|
|
61189
|
+
"name: clawd unpublish",
|
|
61190
|
+
"",
|
|
61191
|
+
"# \u7531 clawd \u751F\u6210\u3001\u7531 clawd \u7EF4\u62A4\uFF1A\u6BCF\u6B21\u300C\u53D1\u5E03\u300D\u90FD\u4F1A\u7528\u5F53\u524D\u6A21\u677F\u91CD\u5199\u8FD9\u4EFD\u6587\u4EF6\u518D\u63D0\u4EA4\uFF0C\u624B\u6539\u4F1A\u88AB\u76D6\u6389\u3002",
|
|
61192
|
+
"# \u4E0B\u7EBF\uFF1A\u5728 gateway \u64A4\u6389\u8FD9\u4E2A persona \u7684\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93\u548C\u955C\u50CF\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002",
|
|
61193
|
+
"# \u53EA\u7531 clawd \u4E0A\u4E91\u9875\u7684\u300C\u4E0B\u7EBF\u300D\u89E6\u53D1\uFF08workflow_dispatch\uFF09\uFF0C\u63A8\u4EE3\u7801\u4E0D\u8DD1\u3002\u6B63\u5728\u8DD1\u7684\u5BF9\u8BDD\u4E0D\u6253\u65AD\uFF0C\u8DD1\u5230\u7A7A\u95F2\u81EA\u5DF1\u9000\u3002",
|
|
61194
|
+
"#",
|
|
61195
|
+
"# \u8981\u7684\u7EC4\u7EC7\u7EA7 Secrets\uFF1ACLAWOS_READ_TOKEN\uFF08\u8BFB clawos \u4ED3\u5E93\uFF09\u3001CLAWD_GATEWAY_ADMIN_KEY\uFF1B\u7EC4\u7EC7\u7EA7 Variable\uFF1ACLAWD_GATEWAY_URL\u3002",
|
|
61196
|
+
"",
|
|
61197
|
+
"on:",
|
|
61198
|
+
" workflow_dispatch:",
|
|
61199
|
+
"",
|
|
61200
|
+
"concurrency:",
|
|
61201
|
+
" # \u4E0E\u53D1\u5E03\u540C\u4E00\u7EC4\uFF1A\u53D1\u5E03\u5728\u8DD1\u65F6\u70B9\u4E0B\u7EBF\uFF0C\u6392\u5728\u5B83\u540E\u9762\uFF1B\u540E\u70B9\u7684\u9876\u6389\u5148\u70B9\u7684",
|
|
61202
|
+
CONCURRENCY_GROUP,
|
|
61203
|
+
"",
|
|
61204
|
+
"env:",
|
|
61205
|
+
` CLAWOS_REPO: ${CLAWOS_REPO}`,
|
|
61206
|
+
` CLAWOS_REF: ${CLAWOS_REF}`,
|
|
61207
|
+
"",
|
|
61208
|
+
"permissions:",
|
|
61209
|
+
" contents: read",
|
|
61210
|
+
"",
|
|
61211
|
+
"jobs:",
|
|
61212
|
+
" unpublish:",
|
|
61213
|
+
" name: unpublish",
|
|
61214
|
+
" runs-on: ubuntu-latest",
|
|
61215
|
+
" timeout-minutes: 10",
|
|
61216
|
+
" steps:",
|
|
61217
|
+
...CONTRACT_STEPS,
|
|
61218
|
+
" - name: \u64A4\u767B\u8BB0",
|
|
61219
|
+
" env:",
|
|
61220
|
+
" CLAWD_GATEWAY_URL: ${{ vars.CLAWD_GATEWAY_URL }}",
|
|
61221
|
+
" CLAWD_GATEWAY_ADMIN_KEY: ${{ secrets.CLAWD_GATEWAY_ADMIN_KEY }}",
|
|
61222
|
+
" run: node clawos/cloud-host/pipeline/unregister.mjs --persona-id '${{ steps.plan.outputs.persona_id }}'",
|
|
61223
|
+
""
|
|
61224
|
+
];
|
|
61225
|
+
var UNPUBLISH_WORKFLOW_TEMPLATE = U2.join("\n");
|
|
59916
61226
|
|
|
59917
61227
|
// src/deploy/exec.ts
|
|
59918
61228
|
var import_node_child_process9 = require("child_process");
|
|
@@ -59953,7 +61263,7 @@ var runCommand = (cmd, args, opts = {}) => new Promise((resolve9) => {
|
|
|
59953
61263
|
});
|
|
59954
61264
|
|
|
59955
61265
|
// src/deploy/repo.ts
|
|
59956
|
-
var DEPLOY_FILE_REL = DEPLOY_FILE.split(
|
|
61266
|
+
var DEPLOY_FILE_REL = DEPLOY_FILE.split(import_node_path39.default.sep).join("/");
|
|
59957
61267
|
var PERSONA_FILE_REL = ".clawd/persona.json";
|
|
59958
61268
|
var DIRTY_IGNORED = [DEPLOY_FILE_REL, PERSONA_FILE_REL];
|
|
59959
61269
|
var RepoError = class extends Error {
|
|
@@ -59968,6 +61278,7 @@ async function mustGit(exec, dir, args, what, opts) {
|
|
|
59968
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)}`);
|
|
59969
61279
|
return r2.stdout.trim();
|
|
59970
61280
|
}
|
|
61281
|
+
var cloudPersonaIdOf = (originUrl) => personaIdOf(repoSlug(originUrl).split("/").pop() ?? "");
|
|
59971
61282
|
var INLINE_HELPER = '!f() { echo username=x-access-token; echo "password=$CLAWD_GITHUB_TOKEN"; }; f';
|
|
59972
61283
|
async function gitPush(exec, dir, auth) {
|
|
59973
61284
|
if (auth.source === "file") {
|
|
@@ -59992,7 +61303,7 @@ async function samePath(a2, b3) {
|
|
|
59992
61303
|
try {
|
|
59993
61304
|
return await import_promises6.default.realpath(x2);
|
|
59994
61305
|
} catch {
|
|
59995
|
-
return
|
|
61306
|
+
return import_node_path39.default.resolve(x2);
|
|
59996
61307
|
}
|
|
59997
61308
|
};
|
|
59998
61309
|
return await real(a2) === await real(b3);
|
|
@@ -60018,7 +61329,7 @@ async function identityArgs(exec, dir) {
|
|
|
60018
61329
|
return ["-c", "user.name=clawd", "-c", "user.email=clawd@clawos.local"];
|
|
60019
61330
|
}
|
|
60020
61331
|
async function writeGitignore(dir) {
|
|
60021
|
-
const gi =
|
|
61332
|
+
const gi = import_node_path39.default.join(dir, ".gitignore");
|
|
60022
61333
|
let existing;
|
|
60023
61334
|
try {
|
|
60024
61335
|
existing = await import_promises6.default.readFile(gi, "utf8");
|
|
@@ -60028,13 +61339,15 @@ async function writeGitignore(dir) {
|
|
|
60028
61339
|
await import_promises6.default.writeFile(gi, mergeGitignore(existing));
|
|
60029
61340
|
}
|
|
60030
61341
|
async function writeWorkflow(dir) {
|
|
60031
|
-
const
|
|
60032
|
-
|
|
60033
|
-
|
|
61342
|
+
for (const [rel, body] of [[WORKFLOW_PATH, WORKFLOW_TEMPLATE], [UNPUBLISH_WORKFLOW_PATH, UNPUBLISH_WORKFLOW_TEMPLATE]]) {
|
|
61343
|
+
const wf = import_node_path39.default.join(dir, rel);
|
|
61344
|
+
await import_promises6.default.mkdir(import_node_path39.default.dirname(wf), { recursive: true });
|
|
61345
|
+
await import_promises6.default.writeFile(wf, body);
|
|
61346
|
+
}
|
|
60034
61347
|
}
|
|
60035
61348
|
async function writeDockerfile(dir, personaId2) {
|
|
60036
|
-
await import_promises6.default.writeFile(
|
|
60037
|
-
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);
|
|
60038
61351
|
}
|
|
60039
61352
|
var RepoAlreadyExistsError = class extends RepoError {
|
|
60040
61353
|
constructor(slug) {
|
|
@@ -60071,7 +61384,7 @@ async function ensureRepo(exec, api, args) {
|
|
|
60071
61384
|
const existing = await repoInfo(exec, dir);
|
|
60072
61385
|
if (existing) return { created: false, url: existing.url };
|
|
60073
61386
|
await writeGitignore(dir);
|
|
60074
|
-
await import_promises6.default.rm(
|
|
61387
|
+
await import_promises6.default.rm(import_node_path39.default.join(dir, WORKFLOW_PATH), { force: true });
|
|
60075
61388
|
let created;
|
|
60076
61389
|
try {
|
|
60077
61390
|
created = await createOrgRepo(exec, api, { dir, org, repoName: personaId2, message: "clawd: \u4E0A\u4E91", auth: args.auth });
|
|
@@ -60109,6 +61422,10 @@ async function hasCommit(exec, dir, sha) {
|
|
|
60109
61422
|
const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}^{commit}`]);
|
|
60110
61423
|
return !r2.spawnError && r2.code === 0;
|
|
60111
61424
|
}
|
|
61425
|
+
async function fileInCommit(exec, dir, sha, rel) {
|
|
61426
|
+
const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}:${rel}`]);
|
|
61427
|
+
return !r2.spawnError && r2.code === 0;
|
|
61428
|
+
}
|
|
60112
61429
|
async function commitAll(exec, args) {
|
|
60113
61430
|
await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
|
|
60114
61431
|
const staged = await git(exec, args.dir, ["diff", "--cached", "--quiet"]);
|
|
@@ -60124,7 +61441,7 @@ async function release(exec, api, args) {
|
|
|
60124
61441
|
await api.enableActions(repoSlug(info.url));
|
|
60125
61442
|
await writeGitignore(args.dir);
|
|
60126
61443
|
await writeWorkflow(args.dir);
|
|
60127
|
-
await writeDockerfile(args.dir,
|
|
61444
|
+
await writeDockerfile(args.dir, cloudPersonaIdOf(info.url));
|
|
60128
61445
|
const identity = await identityArgs(exec, args.dir);
|
|
60129
61446
|
await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
|
|
60130
61447
|
const stamp = new Date(args.now()).toISOString();
|
|
@@ -60147,6 +61464,7 @@ var SCAN_BASE_LABEL = {
|
|
|
60147
61464
|
|
|
60148
61465
|
// src/deploy/run-status.ts
|
|
60149
61466
|
init_protocol();
|
|
61467
|
+
var runsOf = (slug, file, query) => `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/runs?${query}`;
|
|
60150
61468
|
var pipelineJobKey = (name) => DEPLOY_STAGE_KEYS.find((k3) => name === k3 || name.startsWith(`${k3} `));
|
|
60151
61469
|
function stageStatusOf(status, conclusion) {
|
|
60152
61470
|
if (status === "queued" || status === "requested" || status === "pending") return "pending";
|
|
@@ -60188,7 +61506,7 @@ async function runByHead(api, slug, head) {
|
|
|
60188
61506
|
return fetchRun(api, slug, `head_sha=${encodeURIComponent(head)}&per_page=1`);
|
|
60189
61507
|
}
|
|
60190
61508
|
async function fetchRun(api, slug, query) {
|
|
60191
|
-
const listed = await api.getJson(
|
|
61509
|
+
const listed = await api.getJson(runsOf(slug, DEPLOY_WORKFLOW_FILE, query));
|
|
60192
61510
|
const raw = listed?.workflow_runs?.[0];
|
|
60193
61511
|
if (!raw || typeof raw.id !== "number" || typeof raw.head_sha !== "string" || typeof raw.html_url !== "string") {
|
|
60194
61512
|
return void 0;
|
|
@@ -60210,6 +61528,21 @@ async function fetchRun(api, slug, query) {
|
|
|
60210
61528
|
jobs: DEPLOY_STAGE_KEYS.map((k3) => byKey.get(k3)).filter((j2) => j2 !== void 0)
|
|
60211
61529
|
};
|
|
60212
61530
|
}
|
|
61531
|
+
async function latestUnpublish(api, slug) {
|
|
61532
|
+
const listed = await api.getJson(runsOf(slug, UNPUBLISH_WORKFLOW_FILE, "per_page=1"));
|
|
61533
|
+
const runs = listed?.workflow_runs;
|
|
61534
|
+
if (!Array.isArray(runs)) return void 0;
|
|
61535
|
+
const raw = runs[0];
|
|
61536
|
+
if (!raw) return null;
|
|
61537
|
+
if (typeof raw.id !== "number" || typeof raw.html_url !== "string") return void 0;
|
|
61538
|
+
const status = runStatusOf(String(raw.status ?? ""));
|
|
61539
|
+
return {
|
|
61540
|
+
runId: raw.id,
|
|
61541
|
+
status,
|
|
61542
|
+
htmlUrl: raw.html_url,
|
|
61543
|
+
...status === "completed" ? { conclusion: raw.conclusion === "success" ? "success" : "failure" } : {}
|
|
61544
|
+
};
|
|
61545
|
+
}
|
|
60213
61546
|
async function rerunJob(api, slug, jobId) {
|
|
60214
61547
|
await api.rerunJob(slug, jobId);
|
|
60215
61548
|
}
|
|
@@ -60241,6 +61574,8 @@ function reconcileInterruptedScans(deps) {
|
|
|
60241
61574
|
// src/handlers/deploy.ts
|
|
60242
61575
|
var SECRETS_CACHE_MS = 2e4;
|
|
60243
61576
|
var RUN_CACHE_MS = 4e3;
|
|
61577
|
+
var ONLINE_CACHE_MS = 2e4;
|
|
61578
|
+
var UNPUBLISH_NEEDS_REPUBLISH = "\u8FD9\u4E2A persona \u7684\u6D41\u6C34\u7EBF\u662F\u65E7\u7248\u7684\uFF0C\u5148\u91CD\u65B0\u626B\u63CF\u3001\u53D1\u5E03\u4E00\u6B21\u518D\u4E0B\u7EBF";
|
|
60244
61579
|
function ensureOwner(ctx) {
|
|
60245
61580
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
60246
61581
|
throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: deploy:* requires owner ctx");
|
|
@@ -60301,6 +61636,28 @@ function buildDeployHandlers(deps) {
|
|
|
60301
61636
|
runCache.set(key, { at: deps.now(), run });
|
|
60302
61637
|
return run;
|
|
60303
61638
|
};
|
|
61639
|
+
const unpublishCache = /* @__PURE__ */ new Map();
|
|
61640
|
+
const cachedUnpublish = async (personaId2, api, slug) => {
|
|
61641
|
+
const hit = unpublishCache.get(personaId2);
|
|
61642
|
+
if (hit && deps.now() - hit.at < RUN_CACHE_MS) return hit.run;
|
|
61643
|
+
const run = await latestUnpublish(api, slug) ?? void 0;
|
|
61644
|
+
unpublishCache.set(personaId2, { at: deps.now(), run });
|
|
61645
|
+
return run;
|
|
61646
|
+
};
|
|
61647
|
+
const onlineCache = /* @__PURE__ */ new Map();
|
|
61648
|
+
const cachedOnline = async (personaId2, run, unpublish) => {
|
|
61649
|
+
const stamp = `${run?.runId ?? "-"}:${run?.status ?? "-"}|${unpublish?.runId ?? "-"}:${unpublish?.status ?? "-"}`;
|
|
61650
|
+
const hit = onlineCache.get(personaId2);
|
|
61651
|
+
if (hit && hit.stamp === stamp && deps.now() - hit.at < ONLINE_CACHE_MS) return hit.online;
|
|
61652
|
+
let online;
|
|
61653
|
+
try {
|
|
61654
|
+
online = await deps.cloudOnline(personaId2);
|
|
61655
|
+
} catch {
|
|
61656
|
+
online = void 0;
|
|
61657
|
+
}
|
|
61658
|
+
onlineCache.set(personaId2, { stamp, at: deps.now(), online });
|
|
61659
|
+
return online;
|
|
61660
|
+
};
|
|
60304
61661
|
deps.githubIdentity.onChange(() => secretsCache.clear());
|
|
60305
61662
|
async function requireGithub() {
|
|
60306
61663
|
const auth = await deps.githubIdentity.refresh();
|
|
@@ -60337,6 +61694,7 @@ function buildDeployHandlers(deps) {
|
|
|
60337
61694
|
const { scans } = file;
|
|
60338
61695
|
let secrets;
|
|
60339
61696
|
let run;
|
|
61697
|
+
let unpublish;
|
|
60340
61698
|
if (repo2 && api && github.status === "authorized") {
|
|
60341
61699
|
try {
|
|
60342
61700
|
secrets = await cachedSecrets(personaId2, dir, api);
|
|
@@ -60344,7 +61702,9 @@ function buildDeployHandlers(deps) {
|
|
|
60344
61702
|
secrets = void 0;
|
|
60345
61703
|
}
|
|
60346
61704
|
run = await cachedRun(personaId2, api, repoSlug(repo2.url), releaseHead);
|
|
61705
|
+
if (releaseHead === void 0) unpublish = await cachedUnpublish(personaId2, api, repoSlug(repo2.url));
|
|
60347
61706
|
}
|
|
61707
|
+
const online = releaseHead === void 0 && file.releases.length > 0 ? await cachedOnline(personaId2, run, unpublish) : void 0;
|
|
60348
61708
|
return {
|
|
60349
61709
|
personaId: personaId2,
|
|
60350
61710
|
github,
|
|
@@ -60353,6 +61713,8 @@ function buildDeployHandlers(deps) {
|
|
|
60353
61713
|
releases: file.releases,
|
|
60354
61714
|
...secrets ? { secrets } : {},
|
|
60355
61715
|
...run ? { run } : {},
|
|
61716
|
+
...online !== void 0 ? { online } : {},
|
|
61717
|
+
...unpublish ? { unpublish } : {},
|
|
60356
61718
|
...historyError ? { historyError } : {}
|
|
60357
61719
|
};
|
|
60358
61720
|
}
|
|
@@ -60562,6 +61924,14 @@ function buildDeployHandlers(deps) {
|
|
|
60562
61924
|
repo2 = await repoInfo(deps.exec, dir);
|
|
60563
61925
|
if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
|
|
60564
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`);
|
|
60565
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`);
|
|
60566
61936
|
const live = liveScans.get(personaId2);
|
|
60567
61937
|
if (live && live.sessionId === void 0) return view(personaId2);
|
|
@@ -60657,6 +62027,27 @@ function buildDeployHandlers(deps) {
|
|
|
60657
62027
|
}
|
|
60658
62028
|
return view(personaId2);
|
|
60659
62029
|
};
|
|
62030
|
+
const unpublishHandler = async (frame, _client, ctx) => {
|
|
62031
|
+
ensureOwner(ctx);
|
|
62032
|
+
const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:unpublish");
|
|
62033
|
+
const dir = mustPersona(personaId2);
|
|
62034
|
+
const repo2 = await repoInfo(deps.exec, dir);
|
|
62035
|
+
if (!repo2) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u4E0A\u8FC7\u4E91");
|
|
62036
|
+
const { api } = await requireGithub();
|
|
62037
|
+
const last = (await readHistory(dir)).releases.at(-1);
|
|
62038
|
+
if (!last || !await fileInCommit(deps.exec, dir, last.head, UNPUBLISH_WORKFLOW_PATH)) throw invalid(UNPUBLISH_NEEDS_REPUBLISH);
|
|
62039
|
+
const slug = repoSlug(repo2.url);
|
|
62040
|
+
const before = await latestUnpublish(api, slug);
|
|
62041
|
+
if (before === void 0) throw invalid("\u67E5\u4E0D\u5230\u8FD9\u4E2A\u4ED3\u5E93\u4E0A\u7684\u4E0B\u7EBF\u6D41\u6C34\u7EBF\uFF08GitHub \u6CA1\u54CD\u5E94\uFF09\uFF0C\u7A0D\u540E\u518D\u70B9\u4E00\u6B21");
|
|
62042
|
+
try {
|
|
62043
|
+
await api.dispatchWorkflow(slug, UNPUBLISH_WORKFLOW_FILE, "main");
|
|
62044
|
+
} catch (err) {
|
|
62045
|
+
throw new ClawdError(ERROR_CODES.INTERNAL, err instanceof Error ? err.message : String(err));
|
|
62046
|
+
}
|
|
62047
|
+
unpublishCache.delete(personaId2);
|
|
62048
|
+
onlineCache.delete(personaId2);
|
|
62049
|
+
return { response: { type: "deploy:unpublish", before: before?.runId ?? null } };
|
|
62050
|
+
};
|
|
60660
62051
|
return {
|
|
60661
62052
|
"deploy:get": get,
|
|
60662
62053
|
"deploy:scan": scanHandler,
|
|
@@ -60665,7 +62056,8 @@ function buildDeployHandlers(deps) {
|
|
|
60665
62056
|
"deploy:reportFinding": reportFinding,
|
|
60666
62057
|
"deploy:finishScan": finishScan,
|
|
60667
62058
|
"deploy:release": releaseHandler,
|
|
60668
|
-
"deploy:rerunJob": rerunJobHandler
|
|
62059
|
+
"deploy:rerunJob": rerunJobHandler,
|
|
62060
|
+
"deploy:unpublish": unpublishHandler
|
|
60669
62061
|
};
|
|
60670
62062
|
}
|
|
60671
62063
|
|
|
@@ -60717,10 +62109,10 @@ async function probeLocalVersion(exec, pkg) {
|
|
|
60717
62109
|
|
|
60718
62110
|
// src/tools/shell-path.ts
|
|
60719
62111
|
var import_node_child_process10 = require("child_process");
|
|
60720
|
-
var
|
|
62112
|
+
var import_node_os12 = __toESM(require("os"), 1);
|
|
60721
62113
|
function refreshPathFromLoginShell() {
|
|
60722
62114
|
if (process.platform === "win32") return;
|
|
60723
|
-
const shell = process.env.SHELL ??
|
|
62115
|
+
const shell = process.env.SHELL ?? import_node_os12.default.userInfo().shell ?? "/bin/zsh";
|
|
60724
62116
|
try {
|
|
60725
62117
|
const out = (0, import_node_child_process10.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
|
|
60726
62118
|
encoding: "utf8",
|
|
@@ -61266,15 +62658,15 @@ async function refreshTunnelUrlOnStartup(deps) {
|
|
|
61266
62658
|
}
|
|
61267
62659
|
|
|
61268
62660
|
// src/lark-bot-connector/binding-store.ts
|
|
61269
|
-
var
|
|
61270
|
-
var
|
|
62661
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
62662
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
61271
62663
|
function createLarkBotBindingStore(personaRoot) {
|
|
61272
|
-
const filePath = (personaId2) =>
|
|
62664
|
+
const filePath = (personaId2) => import_node_path40.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
|
|
61273
62665
|
const read = (personaId2) => {
|
|
61274
62666
|
const file = filePath(personaId2);
|
|
61275
|
-
if (!
|
|
62667
|
+
if (!import_node_fs35.default.existsSync(file)) return null;
|
|
61276
62668
|
try {
|
|
61277
|
-
const raw = JSON.parse(
|
|
62669
|
+
const raw = JSON.parse(import_node_fs35.default.readFileSync(file, "utf8"));
|
|
61278
62670
|
if (typeof raw?.appId !== "string" || raw.appId.length === 0) return null;
|
|
61279
62671
|
return raw;
|
|
61280
62672
|
} catch {
|
|
@@ -61283,16 +62675,16 @@ function createLarkBotBindingStore(personaRoot) {
|
|
|
61283
62675
|
};
|
|
61284
62676
|
const write = (personaId2, meta) => {
|
|
61285
62677
|
const file = filePath(personaId2);
|
|
61286
|
-
|
|
62678
|
+
import_node_fs35.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true });
|
|
61287
62679
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
61288
|
-
|
|
61289
|
-
|
|
62680
|
+
import_node_fs35.default.writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
62681
|
+
import_node_fs35.default.renameSync(tmp, file);
|
|
61290
62682
|
};
|
|
61291
62683
|
return {
|
|
61292
62684
|
read,
|
|
61293
62685
|
write,
|
|
61294
62686
|
remove(personaId2) {
|
|
61295
|
-
|
|
62687
|
+
import_node_fs35.default.rmSync(filePath(personaId2), { force: true });
|
|
61296
62688
|
},
|
|
61297
62689
|
addRemovedChat(personaId2, chatId) {
|
|
61298
62690
|
const meta = read(personaId2);
|
|
@@ -61444,8 +62836,8 @@ function createLarkBotCloudClient(opts) {
|
|
|
61444
62836
|
}
|
|
61445
62837
|
|
|
61446
62838
|
// src/lark-bot-connector/chat-router.ts
|
|
61447
|
-
var
|
|
61448
|
-
var
|
|
62839
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
62840
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
61449
62841
|
init_protocol();
|
|
61450
62842
|
var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
|
|
61451
62843
|
var LOCAL_TURN_RETRIES = 2;
|
|
@@ -61523,9 +62915,9 @@ function createLarkChatRouter(deps) {
|
|
|
61523
62915
|
continue;
|
|
61524
62916
|
}
|
|
61525
62917
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61526
|
-
|
|
61527
|
-
const p3 =
|
|
61528
|
-
|
|
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);
|
|
61529
62921
|
lines.push(`[\u56FE\u7247: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61530
62922
|
} catch (err) {
|
|
61531
62923
|
deps.logger?.warn(`larkBot image fetch failed ${key}: ${err.message}`);
|
|
@@ -61546,9 +62938,9 @@ function createLarkChatRouter(deps) {
|
|
|
61546
62938
|
continue;
|
|
61547
62939
|
}
|
|
61548
62940
|
const dir = deps.mediaRootFor(env.chatId);
|
|
61549
|
-
|
|
61550
|
-
const p3 =
|
|
61551
|
-
|
|
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);
|
|
61552
62944
|
lines.push(`[\u6587\u4EF6: ${p3}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
61553
62945
|
} catch (err) {
|
|
61554
62946
|
deps.logger?.warn(`larkBot file fetch failed ${f2.key}: ${err.message}`);
|
|
@@ -62198,952 +63590,182 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
62198
63590
|
"personaDispatch:get": get
|
|
62199
63591
|
};
|
|
62200
63592
|
}
|
|
62201
|
-
|
|
62202
|
-
// src/dispatch/peer-forward.ts
|
|
62203
|
-
init_protocol();
|
|
62204
|
-
function wsUrlToHttp(url) {
|
|
62205
|
-
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
62206
|
-
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
62207
|
-
return url;
|
|
62208
|
-
}
|
|
62209
|
-
var DISPATCH_POLL_INTERVAL_MS = 5e3;
|
|
62210
|
-
var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
|
|
62211
|
-
var DISPATCH_POLL_TOTAL_MS = 864e5;
|
|
62212
|
-
var defaultSleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
62213
|
-
async function forwardDispatchToPeer(args) {
|
|
62214
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62215
|
-
const sleep2 = args.sleepImpl ?? defaultSleep;
|
|
62216
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62217
|
-
const url = `${base}/rpc/personaDispatch:run`;
|
|
62218
|
-
let res;
|
|
62219
|
-
try {
|
|
62220
|
-
res = await f2(url, {
|
|
62221
|
-
method: "POST",
|
|
62222
|
-
headers: {
|
|
62223
|
-
"content-type": "application/json",
|
|
62224
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62225
|
-
},
|
|
62226
|
-
// 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
|
|
62227
|
-
body: JSON.stringify({
|
|
62228
|
-
targetPersona: args.targetPersona,
|
|
62229
|
-
prompt: args.prompt,
|
|
62230
|
-
// T-20: 条件展开防 undefined 污染 JSON。远端 daemon handler 会再校验一次 model 合法性。
|
|
62231
|
-
...args.model ? { model: args.model } : {}
|
|
62232
|
-
})
|
|
62233
|
-
});
|
|
62234
|
-
} catch (err) {
|
|
62235
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
62236
|
-
return { kind: "failure", reason: `forward to peer failed: ${msg}` };
|
|
62237
|
-
}
|
|
62238
|
-
let json;
|
|
62239
|
-
try {
|
|
62240
|
-
json = await res.json();
|
|
62241
|
-
} catch {
|
|
62242
|
-
return {
|
|
62243
|
-
kind: "failure",
|
|
62244
|
-
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
62245
|
-
};
|
|
62246
|
-
}
|
|
62247
|
-
if (json.ok === false) {
|
|
62248
|
-
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
62249
|
-
}
|
|
62250
|
-
if ("outcome" in json.result) return json.result.outcome;
|
|
62251
|
-
const dispatchId = json.result.dispatchId;
|
|
62252
|
-
let elapsed = 0;
|
|
62253
|
-
let unreachableFor = 0;
|
|
62254
|
-
while (elapsed < DISPATCH_POLL_TOTAL_MS) {
|
|
62255
|
-
await sleep2(DISPATCH_POLL_INTERVAL_MS);
|
|
62256
|
-
elapsed += DISPATCH_POLL_INTERVAL_MS;
|
|
62257
|
-
const markUnreachable = () => {
|
|
62258
|
-
unreachableFor += DISPATCH_POLL_INTERVAL_MS;
|
|
62259
|
-
return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
|
|
62260
|
-
kind: "failure",
|
|
62261
|
-
reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
|
|
62262
|
-
} : null;
|
|
62263
|
-
};
|
|
62264
|
-
let getRes;
|
|
62265
|
-
try {
|
|
62266
|
-
getRes = await f2(`${base}/rpc/personaDispatch:get`, {
|
|
62267
|
-
method: "POST",
|
|
62268
|
-
headers: {
|
|
62269
|
-
"content-type": "application/json",
|
|
62270
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62271
|
-
},
|
|
62272
|
-
body: JSON.stringify({ dispatchId })
|
|
62273
|
-
});
|
|
62274
|
-
} catch {
|
|
62275
|
-
const dead = markUnreachable();
|
|
62276
|
-
if (dead) return dead;
|
|
62277
|
-
continue;
|
|
62278
|
-
}
|
|
62279
|
-
let getJson;
|
|
62280
|
-
try {
|
|
62281
|
-
getJson = await getRes.json();
|
|
62282
|
-
} catch {
|
|
62283
|
-
const dead = markUnreachable();
|
|
62284
|
-
if (dead) return dead;
|
|
62285
|
-
continue;
|
|
62286
|
-
}
|
|
62287
|
-
if (getJson.ok === false) {
|
|
62288
|
-
if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
|
|
62289
|
-
return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
|
|
62290
|
-
}
|
|
62291
|
-
const dead = markUnreachable();
|
|
62292
|
-
if (dead) return dead;
|
|
62293
|
-
continue;
|
|
62294
|
-
}
|
|
62295
|
-
unreachableFor = 0;
|
|
62296
|
-
const { status, outcome } = getJson.result;
|
|
62297
|
-
if (status === "completed" || status === "failed") {
|
|
62298
|
-
return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
|
|
62299
|
-
}
|
|
62300
|
-
}
|
|
62301
|
-
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
62302
|
-
}
|
|
62303
|
-
async function forwardInboxPostToPeer(args) {
|
|
62304
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62305
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62306
|
-
const url = `${base}/rpc/inbox:postMessage`;
|
|
62307
|
-
let res;
|
|
62308
|
-
try {
|
|
62309
|
-
res = await f2(url, {
|
|
62310
|
-
method: "POST",
|
|
62311
|
-
headers: {
|
|
62312
|
-
"content-type": "application/json",
|
|
62313
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62314
|
-
},
|
|
62315
|
-
body: JSON.stringify({
|
|
62316
|
-
id: args.id,
|
|
62317
|
-
text: args.text,
|
|
62318
|
-
createdAt: args.createdAt,
|
|
62319
|
-
...args.origin ? { origin: args.origin } : {}
|
|
62320
|
-
})
|
|
62321
|
-
});
|
|
62322
|
-
} catch (err) {
|
|
62323
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
62324
|
-
}
|
|
62325
|
-
let json = null;
|
|
62326
|
-
try {
|
|
62327
|
-
json = await res.json();
|
|
62328
|
-
} catch {
|
|
62329
|
-
return { ok: false, error: `peer non-JSON (HTTP ${res.status})` };
|
|
62330
|
-
}
|
|
62331
|
-
if (res.status < 200 || res.status >= 300 || json?.ok === false) {
|
|
62332
|
-
return { ok: false, error: json?.error ?? `HTTP ${res.status}` };
|
|
62333
|
-
}
|
|
62334
|
-
return { ok: true };
|
|
62335
|
-
}
|
|
62336
|
-
async function forwardPeerExecToPeer(args) {
|
|
62337
|
-
const f2 = args.fetchImpl ?? fetch;
|
|
62338
|
-
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
62339
|
-
const url = `${base}/rpc/peerExec:run`;
|
|
62340
|
-
const tunnelFail = (msg) => ({
|
|
62341
|
-
type: "peerExec:run:ok",
|
|
62342
|
-
stdout: "",
|
|
62343
|
-
stderr: msg,
|
|
62344
|
-
exitCode: null,
|
|
62345
|
-
timedOut: false,
|
|
62346
|
-
stdoutTruncated: false,
|
|
62347
|
-
stderrTruncated: false
|
|
62348
|
-
});
|
|
62349
|
-
let res;
|
|
62350
|
-
try {
|
|
62351
|
-
res = await f2(url, {
|
|
62352
|
-
method: "POST",
|
|
62353
|
-
headers: {
|
|
62354
|
-
"content-type": "application/json",
|
|
62355
|
-
authorization: `Bearer ${args.contact.connectToken}`
|
|
62356
|
-
},
|
|
62357
|
-
body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
|
|
62358
|
-
});
|
|
62359
|
-
} catch (err) {
|
|
62360
|
-
return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
62361
|
-
}
|
|
62362
|
-
let json;
|
|
62363
|
-
try {
|
|
62364
|
-
json = await res.json();
|
|
62365
|
-
} catch {
|
|
62366
|
-
return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
|
|
62367
|
-
}
|
|
62368
|
-
if (json.ok === false) {
|
|
62369
|
-
throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
|
|
62370
|
-
}
|
|
62371
|
-
return json.result;
|
|
62372
|
-
}
|
|
62373
|
-
|
|
62374
|
-
// src/tools/codex-history.ts
|
|
62375
|
-
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
62376
|
-
var import_node_path40 = __toESM(require("path"), 1);
|
|
62377
|
-
|
|
62378
|
-
// src/tools/transcript-search.ts
|
|
62379
|
-
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
62380
|
-
var import_node_path39 = __toESM(require("path"), 1);
|
|
62381
|
-
var SNIPPET_PAD = 60;
|
|
62382
|
-
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/;
|
|
62383
|
-
var CODEX_WRAPPER_RE = /<(related-dir|environment_context|user_instructions)>[\s\S]*?<\/\1>/g;
|
|
62384
|
-
function asRecord2(v3) {
|
|
62385
|
-
return typeof v3 === "object" && v3 !== null ? v3 : null;
|
|
62386
|
-
}
|
|
62387
|
-
function textOfBlocks(content, types) {
|
|
62388
|
-
if (!Array.isArray(content)) return null;
|
|
62389
|
-
const parts = [];
|
|
62390
|
-
for (const block of content) {
|
|
62391
|
-
const b3 = asRecord2(block);
|
|
62392
|
-
if (!b3 || typeof b3.type !== "string" || !types.includes(b3.type)) continue;
|
|
62393
|
-
const text = b3.text;
|
|
62394
|
-
if (typeof text === "string" && text !== "") parts.push(text);
|
|
62395
|
-
}
|
|
62396
|
-
return parts.length > 0 ? parts.join("\n") : null;
|
|
62397
|
-
}
|
|
62398
|
-
function lineToSearchableText(raw) {
|
|
62399
|
-
const line = asRecord2(raw);
|
|
62400
|
-
if (!line) return null;
|
|
62401
|
-
if (line.type === "response_item") {
|
|
62402
|
-
const payload = asRecord2(line.payload);
|
|
62403
|
-
if (!payload || payload.type !== "message") return null;
|
|
62404
|
-
const role = payload.role;
|
|
62405
|
-
if (role !== "user" && role !== "assistant") return null;
|
|
62406
|
-
const text2 = textOfBlocks(payload.content, ["input_text", "output_text"]);
|
|
62407
|
-
if (text2 === null) return null;
|
|
62408
|
-
if (role !== "user") return text2;
|
|
62409
|
-
const stripped = text2.replace(CODEX_WRAPPER_RE, "").trim();
|
|
62410
|
-
if (stripped === "") return null;
|
|
62411
|
-
return INJECTED_ENVELOPE_RE.test(stripped) ? null : stripped;
|
|
62412
|
-
}
|
|
62413
|
-
if (line.type !== "user" && line.type !== "assistant") return null;
|
|
62414
|
-
const message = asRecord2(line.message);
|
|
62415
|
-
if (!message) return null;
|
|
62416
|
-
const content = message.content;
|
|
62417
|
-
const text = typeof content === "string" && content !== "" ? content : textOfBlocks(content, ["text"]);
|
|
62418
|
-
if (text === null) return null;
|
|
62419
|
-
if (line.type === "user" && (line.isMeta === true || line.isSynthetic === true)) return null;
|
|
62420
|
-
if (line.type === "user" && INJECTED_ENVELOPE_RE.test(text.trimStart())) return null;
|
|
62421
|
-
return text;
|
|
62422
|
-
}
|
|
62423
|
-
function escapeRegExp(s2) {
|
|
62424
|
-
return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
62425
|
-
}
|
|
62426
|
-
function makeSnippet(text, at3, needleLength) {
|
|
62427
|
-
const from = Math.max(0, at3 - SNIPPET_PAD);
|
|
62428
|
-
const to = Math.min(text.length, at3 + needleLength + SNIPPET_PAD);
|
|
62429
|
-
const body = text.slice(from, to).replace(/\s+/g, " ").trim();
|
|
62430
|
-
return `${from > 0 ? "\u2026 " : ""}${body}${to < text.length ? " \u2026" : ""}`;
|
|
62431
|
-
}
|
|
62432
|
-
function searchTranscriptFile(file, query) {
|
|
62433
|
-
let buf;
|
|
62434
|
-
try {
|
|
62435
|
-
buf = import_node_fs34.default.readFileSync(file);
|
|
62436
|
-
} catch {
|
|
62437
|
-
return null;
|
|
62438
|
-
}
|
|
62439
|
-
const haystack = buf.toString("latin1");
|
|
62440
|
-
const needle = Buffer.from(query, "utf8").toString("latin1");
|
|
62441
|
-
const re2 = new RegExp(escapeRegExp(needle), "gi");
|
|
62442
|
-
let m3;
|
|
62443
|
-
while ((m3 = re2.exec(haystack)) !== null) {
|
|
62444
|
-
const lineStart = haystack.lastIndexOf("\n", m3.index) + 1;
|
|
62445
|
-
let lineEnd = haystack.indexOf("\n", m3.index);
|
|
62446
|
-
if (lineEnd < 0) lineEnd = haystack.length;
|
|
62447
|
-
re2.lastIndex = lineEnd + 1;
|
|
62448
|
-
let parsed;
|
|
62449
|
-
try {
|
|
62450
|
-
parsed = JSON.parse(Buffer.from(haystack.slice(lineStart, lineEnd), "latin1").toString("utf8"));
|
|
62451
|
-
} catch {
|
|
62452
|
-
continue;
|
|
62453
|
-
}
|
|
62454
|
-
const text = lineToSearchableText(parsed);
|
|
62455
|
-
if (text === null) continue;
|
|
62456
|
-
const at3 = text.toLowerCase().indexOf(query.toLowerCase());
|
|
62457
|
-
if (at3 < 0) continue;
|
|
62458
|
-
return makeSnippet(text, at3, query.length);
|
|
62459
|
-
}
|
|
62460
|
-
return null;
|
|
62461
|
-
}
|
|
62462
|
-
var ROLLOUT_NAME_RE = /-([0-9a-fA-F-]{36})\.jsonl$/;
|
|
62463
|
-
function createCodexRolloutLocator(roots) {
|
|
62464
|
-
let index = null;
|
|
62465
|
-
const walk2 = (dir, into) => {
|
|
62466
|
-
let entries;
|
|
62467
|
-
try {
|
|
62468
|
-
entries = import_node_fs34.default.readdirSync(dir, { withFileTypes: true });
|
|
62469
|
-
} catch {
|
|
62470
|
-
return;
|
|
62471
|
-
}
|
|
62472
|
-
for (const entry of entries) {
|
|
62473
|
-
const full = import_node_path39.default.join(dir, entry.name);
|
|
62474
|
-
if (entry.isDirectory()) {
|
|
62475
|
-
walk2(full, into);
|
|
62476
|
-
continue;
|
|
62477
|
-
}
|
|
62478
|
-
const matched = ROLLOUT_NAME_RE.exec(entry.name);
|
|
62479
|
-
if (matched) into.set(matched[1].toLowerCase(), full);
|
|
62480
|
-
}
|
|
62481
|
-
};
|
|
62482
|
-
return (toolSessionId) => {
|
|
62483
|
-
if (index === null) {
|
|
62484
|
-
index = /* @__PURE__ */ new Map();
|
|
62485
|
-
for (const root of roots) walk2(root, index);
|
|
62486
|
-
}
|
|
62487
|
-
return index.get(toolSessionId.toLowerCase()) ?? null;
|
|
62488
|
-
};
|
|
62489
|
-
}
|
|
62490
|
-
|
|
62491
|
-
// src/tools/codex-rollout.ts
|
|
62492
|
-
function rec2(v3) {
|
|
62493
|
-
return v3 && typeof v3 === "object" && !Array.isArray(v3) ? v3 : void 0;
|
|
62494
|
-
}
|
|
62495
|
-
function str2(v3) {
|
|
62496
|
-
return typeof v3 === "string" ? v3 : void 0;
|
|
62497
|
-
}
|
|
62498
|
-
function num2(v3) {
|
|
62499
|
-
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
62500
|
-
}
|
|
62501
|
-
function parseLine(line) {
|
|
62502
|
-
const t2 = line.trim();
|
|
62503
|
-
if (!t2) return void 0;
|
|
62504
|
-
try {
|
|
62505
|
-
return rec2(JSON.parse(t2));
|
|
62506
|
-
} catch {
|
|
62507
|
-
return void 0;
|
|
62508
|
-
}
|
|
62509
|
-
}
|
|
62510
|
-
function metaFrom(obj) {
|
|
62511
|
-
if (obj.type !== "session_meta") return null;
|
|
62512
|
-
const p3 = rec2(obj.payload);
|
|
62513
|
-
const threadId = str2(p3?.id) ?? str2(p3?.session_id);
|
|
62514
|
-
if (!p3 || !threadId) return null;
|
|
62515
|
-
const meta = { threadId };
|
|
62516
|
-
const cwd = str2(p3.cwd);
|
|
62517
|
-
if (cwd) meta.cwd = cwd;
|
|
62518
|
-
const createdAt = str2(p3.timestamp) ?? str2(obj.timestamp);
|
|
62519
|
-
if (createdAt) meta.createdAt = createdAt;
|
|
62520
|
-
const parent = str2(p3.parent_thread_id);
|
|
62521
|
-
if (parent) meta.parentThreadId = parent;
|
|
62522
|
-
const cli = str2(p3.cli_version);
|
|
62523
|
-
if (cli) meta.cliVersion = cli;
|
|
62524
|
-
return meta;
|
|
62525
|
-
}
|
|
62526
|
-
function parseCodexRolloutMeta(firstLine) {
|
|
62527
|
-
const obj = parseLine(firstLine);
|
|
62528
|
-
return obj ? metaFrom(obj) : null;
|
|
62529
|
-
}
|
|
62530
|
-
function firstUserMessageText(text) {
|
|
62531
|
-
for (const line of text.split("\n")) {
|
|
62532
|
-
const obj = parseLine(line);
|
|
62533
|
-
if (!obj || obj.type !== "event_msg") continue;
|
|
62534
|
-
const p3 = rec2(obj.payload);
|
|
62535
|
-
if (p3?.type === "user_message") return str2(p3.message);
|
|
62536
|
-
}
|
|
62537
|
-
return void 0;
|
|
62538
|
-
}
|
|
62539
|
-
function outputText(output) {
|
|
62540
|
-
if (typeof output === "string") return output;
|
|
62541
|
-
if (Array.isArray(output)) {
|
|
62542
|
-
return output.map((part) => typeof part === "string" ? part : str2(rec2(part)?.text) ?? "").join("");
|
|
62543
|
-
}
|
|
62544
|
-
return "";
|
|
62545
|
-
}
|
|
62546
|
-
function functionCallInput(name, rawArgs) {
|
|
62547
|
-
let args = rawArgs;
|
|
62548
|
-
if (typeof rawArgs === "string") {
|
|
62549
|
-
try {
|
|
62550
|
-
args = JSON.parse(rawArgs);
|
|
62551
|
-
} catch {
|
|
62552
|
-
args = { arguments: rawArgs };
|
|
62553
|
-
}
|
|
62554
|
-
}
|
|
62555
|
-
const a2 = rec2(args) ?? {};
|
|
62556
|
-
if (name === "exec_command" || name === "shell" || name === "container.exec") {
|
|
62557
|
-
const cmd = a2.cmd ?? a2.command;
|
|
62558
|
-
const command = Array.isArray(cmd) ? cmd.map(String).join(" ") : str2(cmd) ?? "";
|
|
62559
|
-
return { tool: "commandExecution", input: { command } };
|
|
62560
|
-
}
|
|
62561
|
-
return { tool: name, input: args };
|
|
62562
|
-
}
|
|
62563
|
-
function parseCodexRollout(text) {
|
|
62564
|
-
const out = { meta: null, messages: [], subagents: [], turns: [] };
|
|
62565
|
-
const collabCallIds = /* @__PURE__ */ new Set();
|
|
62566
|
-
for (const line of text.split("\n")) {
|
|
62567
|
-
const obj = parseLine(line);
|
|
62568
|
-
if (!obj) continue;
|
|
62569
|
-
const ts = str2(obj.timestamp);
|
|
62570
|
-
const p3 = rec2(obj.payload);
|
|
62571
|
-
if (!p3) continue;
|
|
62572
|
-
if (obj.type === "session_meta") {
|
|
62573
|
-
if (!out.meta) out.meta = metaFrom(obj);
|
|
62574
|
-
continue;
|
|
62575
|
-
}
|
|
62576
|
-
if (obj.type === "event_msg") {
|
|
62577
|
-
switch (p3.type) {
|
|
62578
|
-
case "user_message": {
|
|
62579
|
-
const t2 = str2(p3.message);
|
|
62580
|
-
if (t2) out.messages.push({ kind: "user-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
62581
|
-
break;
|
|
62582
|
-
}
|
|
62583
|
-
case "agent_message": {
|
|
62584
|
-
const t2 = str2(p3.message);
|
|
62585
|
-
if (t2) out.messages.push({ kind: "assistant-text", text: t2, raw: obj, ...ts ? { ts } : {} });
|
|
62586
|
-
break;
|
|
62587
|
-
}
|
|
62588
|
-
case "sub_agent_activity": {
|
|
62589
|
-
if (p3.kind !== "started") break;
|
|
62590
|
-
const toolUseId = str2(p3.event_id);
|
|
62591
|
-
const agentId = str2(p3.agent_thread_id);
|
|
62592
|
-
const agentPath = str2(p3.agent_path);
|
|
62593
|
-
if (!toolUseId || !agentId || !agentPath) break;
|
|
62594
|
-
const segments = agentPath.split("/").filter(Boolean);
|
|
62595
|
-
const description = segments[segments.length - 1] ?? agentId;
|
|
62596
|
-
out.subagents.push({ toolUseId, agentId, agentPath, description });
|
|
62597
|
-
out.messages.push({
|
|
62598
|
-
kind: "tool_call",
|
|
62599
|
-
tool: "Agent",
|
|
62600
|
-
toolUseId,
|
|
62601
|
-
input: { agentId, description, agentPath },
|
|
62602
|
-
raw: obj,
|
|
62603
|
-
...ts ? { ts } : {}
|
|
62604
|
-
});
|
|
62605
|
-
break;
|
|
62606
|
-
}
|
|
62607
|
-
case "task_complete":
|
|
62608
|
-
case "turn_aborted": {
|
|
62609
|
-
const durationMs = num2(p3.duration_ms);
|
|
62610
|
-
const turnId = str2(p3.turn_id);
|
|
62611
|
-
const reason = str2(p3.reason);
|
|
62612
|
-
out.turns.push({
|
|
62613
|
-
status: p3.type === "task_complete" ? "completed" : "interrupted",
|
|
62614
|
-
...turnId ? { turnId } : {},
|
|
62615
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
62616
|
-
...reason ? { reason } : {}
|
|
62617
|
-
});
|
|
62618
|
-
out.messages.push({
|
|
62619
|
-
kind: "turn-end",
|
|
62620
|
-
raw: obj,
|
|
62621
|
-
...ts ? { ts } : {},
|
|
62622
|
-
...durationMs !== void 0 ? { durationMs } : {}
|
|
62623
|
-
});
|
|
62624
|
-
break;
|
|
62625
|
-
}
|
|
62626
|
-
default:
|
|
62627
|
-
break;
|
|
62628
|
-
}
|
|
62629
|
-
continue;
|
|
62630
|
-
}
|
|
62631
|
-
if (obj.type === "response_item") {
|
|
62632
|
-
switch (p3.type) {
|
|
62633
|
-
case "function_call": {
|
|
62634
|
-
const callId = str2(p3.call_id);
|
|
62635
|
-
const name = str2(p3.name);
|
|
62636
|
-
if (!callId || !name) break;
|
|
62637
|
-
if (p3.namespace === "collaboration") {
|
|
62638
|
-
collabCallIds.add(callId);
|
|
62639
|
-
break;
|
|
62640
|
-
}
|
|
62641
|
-
const { tool, input } = functionCallInput(name, p3.arguments);
|
|
62642
|
-
out.messages.push({ kind: "tool_call", tool, toolUseId: callId, input, raw: obj, ...ts ? { ts } : {} });
|
|
62643
|
-
break;
|
|
62644
|
-
}
|
|
62645
|
-
case "custom_tool_call": {
|
|
62646
|
-
const callId = str2(p3.call_id);
|
|
62647
|
-
const name = str2(p3.name);
|
|
62648
|
-
if (!callId || !name) break;
|
|
62649
|
-
out.messages.push({
|
|
62650
|
-
kind: "tool_call",
|
|
62651
|
-
tool: name,
|
|
62652
|
-
toolUseId: callId,
|
|
62653
|
-
input: { input: str2(p3.input) ?? "" },
|
|
62654
|
-
raw: obj,
|
|
62655
|
-
...ts ? { ts } : {}
|
|
62656
|
-
});
|
|
62657
|
-
break;
|
|
62658
|
-
}
|
|
62659
|
-
case "function_call_output":
|
|
62660
|
-
case "custom_tool_call_output": {
|
|
62661
|
-
const callId = str2(p3.call_id);
|
|
62662
|
-
if (!callId || collabCallIds.has(callId)) break;
|
|
62663
|
-
out.messages.push({
|
|
62664
|
-
kind: "tool_result",
|
|
62665
|
-
toolUseId: callId,
|
|
62666
|
-
output: outputText(p3.output),
|
|
62667
|
-
raw: obj,
|
|
62668
|
-
...ts ? { ts } : {}
|
|
62669
|
-
});
|
|
62670
|
-
break;
|
|
62671
|
-
}
|
|
62672
|
-
default:
|
|
62673
|
-
break;
|
|
62674
|
-
}
|
|
62675
|
-
continue;
|
|
62676
|
-
}
|
|
62677
|
-
}
|
|
62678
|
-
return out;
|
|
62679
|
-
}
|
|
62680
|
-
function subagentTerminalResult(started, child) {
|
|
62681
|
-
const first = child.turns[0];
|
|
62682
|
-
if (!first) return void 0;
|
|
62683
|
-
if (first.status === "completed") return { kind: "tool_result", toolUseId: started.toolUseId };
|
|
62684
|
-
return { kind: "tool_result", toolUseId: started.toolUseId, error: first.reason ?? "Codex subagent interrupted" };
|
|
62685
|
-
}
|
|
62686
|
-
|
|
62687
|
-
// src/tools/codex-history.ts
|
|
62688
|
-
var CODEX_LIST_SESSIONS_LIMIT = 100;
|
|
62689
|
-
var CODEX_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
62690
|
-
var PEEK_BYTES = 8 * 1024;
|
|
62691
|
-
var TITLE_BYTES = 256 * 1024;
|
|
62692
|
-
function readHead(file, bytes) {
|
|
62693
|
-
let fd;
|
|
62694
|
-
try {
|
|
62695
|
-
fd = import_node_fs35.default.openSync(file, "r");
|
|
62696
|
-
} catch {
|
|
62697
|
-
return null;
|
|
62698
|
-
}
|
|
62699
|
-
try {
|
|
62700
|
-
const buf = Buffer.alloc(bytes);
|
|
62701
|
-
const n2 = import_node_fs35.default.readSync(fd, buf, 0, bytes, 0);
|
|
62702
|
-
return buf.subarray(0, n2).toString("utf8");
|
|
62703
|
-
} catch {
|
|
62704
|
-
return null;
|
|
62705
|
-
} finally {
|
|
62706
|
-
import_node_fs35.default.closeSync(fd);
|
|
62707
|
-
}
|
|
62708
|
-
}
|
|
62709
|
-
function peekSessionCwd(head) {
|
|
62710
|
-
if (!head.includes('"session_meta"')) return null;
|
|
62711
|
-
const m3 = /"cwd":"((?:[^"\\]|\\.)*)"/.exec(head);
|
|
62712
|
-
if (!m3) return null;
|
|
62713
|
-
try {
|
|
62714
|
-
return JSON.parse(`"${m3[1]}"`);
|
|
62715
|
-
} catch {
|
|
62716
|
-
return null;
|
|
62717
|
-
}
|
|
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;
|
|
62718
63600
|
}
|
|
62719
|
-
|
|
62720
|
-
|
|
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;
|
|
62721
63611
|
try {
|
|
62722
|
-
|
|
62723
|
-
|
|
62724
|
-
|
|
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}` };
|
|
62725
63629
|
}
|
|
63630
|
+
let json;
|
|
62726
63631
|
try {
|
|
62727
|
-
|
|
62728
|
-
const parts = [];
|
|
62729
|
-
let total = 0;
|
|
62730
|
-
while (total < max) {
|
|
62731
|
-
const n2 = import_node_fs35.default.readSync(fd, chunk, 0, chunk.length, total);
|
|
62732
|
-
if (n2 <= 0) break;
|
|
62733
|
-
const nl = chunk.subarray(0, n2).indexOf(10);
|
|
62734
|
-
if (nl >= 0) {
|
|
62735
|
-
parts.push(Buffer.from(chunk.subarray(0, nl)));
|
|
62736
|
-
return Buffer.concat(parts).toString("utf8");
|
|
62737
|
-
}
|
|
62738
|
-
parts.push(Buffer.from(chunk.subarray(0, n2)));
|
|
62739
|
-
total += n2;
|
|
62740
|
-
}
|
|
62741
|
-
return total < max && parts.length > 0 ? Buffer.concat(parts).toString("utf8") : null;
|
|
63632
|
+
json = await res.json();
|
|
62742
63633
|
} catch {
|
|
62743
|
-
return
|
|
62744
|
-
|
|
62745
|
-
|
|
63634
|
+
return {
|
|
63635
|
+
kind: "failure",
|
|
63636
|
+
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
63637
|
+
};
|
|
62746
63638
|
}
|
|
62747
|
-
|
|
62748
|
-
|
|
62749
|
-
const names = /* @__PURE__ */ new Map();
|
|
62750
|
-
if (!sharedHome) return names;
|
|
62751
|
-
let raw;
|
|
62752
|
-
try {
|
|
62753
|
-
raw = import_node_fs35.default.readFileSync(import_node_path40.default.join(sharedHome, "session_index.jsonl"), "utf8");
|
|
62754
|
-
} catch {
|
|
62755
|
-
return names;
|
|
63639
|
+
if (json.ok === false) {
|
|
63640
|
+
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
62756
63641
|
}
|
|
62757
|
-
|
|
62758
|
-
|
|
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;
|
|
62759
63657
|
try {
|
|
62760
|
-
|
|
62761
|
-
|
|
62762
|
-
|
|
62763
|
-
|
|
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
|
+
});
|
|
62764
63666
|
} catch {
|
|
63667
|
+
const dead = markUnreachable();
|
|
63668
|
+
if (dead) return dead;
|
|
63669
|
+
continue;
|
|
62765
63670
|
}
|
|
62766
|
-
|
|
62767
|
-
return names;
|
|
62768
|
-
}
|
|
62769
|
-
function walkRollouts(dir, visit) {
|
|
62770
|
-
let entries;
|
|
62771
|
-
try {
|
|
62772
|
-
entries = import_node_fs35.default.readdirSync(dir, { withFileTypes: true });
|
|
62773
|
-
} catch {
|
|
62774
|
-
return true;
|
|
62775
|
-
}
|
|
62776
|
-
entries.sort((a2, b3) => a2.name < b3.name ? 1 : a2.name > b3.name ? -1 : 0);
|
|
62777
|
-
for (const entry of entries) {
|
|
62778
|
-
const full = import_node_path40.default.join(dir, entry.name);
|
|
62779
|
-
if (entry.isDirectory()) {
|
|
62780
|
-
if (!walkRollouts(full, visit)) return false;
|
|
62781
|
-
} else if (ROLLOUT_NAME_RE.test(entry.name)) {
|
|
62782
|
-
if (visit(full) === false) return false;
|
|
62783
|
-
}
|
|
62784
|
-
}
|
|
62785
|
-
return true;
|
|
62786
|
-
}
|
|
62787
|
-
async function withSubagentResults(parent, parentThreadId, loadChild, logger) {
|
|
62788
|
-
const messages = [];
|
|
62789
|
-
const started = new Map(parent.subagents.map((s2) => [s2.toolUseId, s2]));
|
|
62790
|
-
for (const m3 of parent.messages) {
|
|
62791
|
-
messages.push(m3);
|
|
62792
|
-
const sub = m3.kind === "tool_call" && m3.toolUseId ? started.get(m3.toolUseId) : void 0;
|
|
62793
|
-
if (!sub) continue;
|
|
62794
|
-
let text;
|
|
63671
|
+
let getJson;
|
|
62795
63672
|
try {
|
|
62796
|
-
|
|
62797
|
-
} catch
|
|
62798
|
-
|
|
63673
|
+
getJson = await getRes.json();
|
|
63674
|
+
} catch {
|
|
63675
|
+
const dead = markUnreachable();
|
|
63676
|
+
if (dead) return dead;
|
|
62799
63677
|
continue;
|
|
62800
63678
|
}
|
|
62801
|
-
if (
|
|
62802
|
-
|
|
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;
|
|
62803
63685
|
continue;
|
|
62804
63686
|
}
|
|
62805
|
-
|
|
62806
|
-
|
|
62807
|
-
|
|
62808
|
-
|
|
62809
|
-
childThreadId: sub.agentId,
|
|
62810
|
-
actualParent: child.meta?.parentThreadId ?? null
|
|
62811
|
-
});
|
|
62812
|
-
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" };
|
|
62813
63691
|
}
|
|
62814
|
-
const result = subagentTerminalResult(sub, child);
|
|
62815
|
-
if (result) messages.push(result);
|
|
62816
|
-
}
|
|
62817
|
-
return messages;
|
|
62818
|
-
}
|
|
62819
|
-
function pageMessages(messages, args) {
|
|
62820
|
-
const total = messages.length;
|
|
62821
|
-
const limit = args.limit ?? total;
|
|
62822
|
-
const offset = typeof args.offset === "number" ? Math.min(Math.max(0, args.offset), total) : Math.max(0, total - limit);
|
|
62823
|
-
return { messages: messages.slice(offset, offset + limit), total, offset };
|
|
62824
|
-
}
|
|
62825
|
-
function subagentSummaries(parent) {
|
|
62826
|
-
const seen = /* @__PURE__ */ new Set();
|
|
62827
|
-
const out = [];
|
|
62828
|
-
for (const s2 of parent.subagents) {
|
|
62829
|
-
if (seen.has(s2.agentId)) continue;
|
|
62830
|
-
seen.add(s2.agentId);
|
|
62831
|
-
out.push({ subagentId: s2.agentId, summary: s2.description, messageCount: 0 });
|
|
62832
63692
|
}
|
|
62833
|
-
return
|
|
63693
|
+
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
62834
63694
|
}
|
|
62835
|
-
|
|
62836
|
-
|
|
62837
|
-
|
|
62838
|
-
|
|
62839
|
-
}
|
|
62840
|
-
// 仍返回 []:projects/list 非 session-scoped、不知 tool,跨-tool 项目浏览器合并排后续。
|
|
62841
|
-
// codex 旧会话仍能经 clawd 活跃列表(session:list, tool-agnostic) + read 打开。
|
|
62842
|
-
async listProjects() {
|
|
62843
|
-
return [];
|
|
62844
|
-
}
|
|
62845
|
-
async listSessions(args) {
|
|
62846
|
-
const want = import_node_path40.default.resolve(args.projectPath);
|
|
62847
|
-
const names = readThreadNames(this.deps.sharedHome);
|
|
62848
|
-
const found = [];
|
|
62849
|
-
let scanned = 0;
|
|
62850
|
-
for (const root of this.deps.roots) {
|
|
62851
|
-
const goOn = walkRollouts(root, (file) => {
|
|
62852
|
-
if (++scanned > CODEX_LIST_SESSIONS_SCAN_CAP) return false;
|
|
62853
|
-
const head = readHead(file, PEEK_BYTES);
|
|
62854
|
-
const cwd = head === null ? null : peekSessionCwd(head);
|
|
62855
|
-
if (!cwd || import_node_path40.default.resolve(cwd) !== want) return;
|
|
62856
|
-
const first = readFirstLine(file);
|
|
62857
|
-
const meta = first === null ? null : parseCodexRolloutMeta(first);
|
|
62858
|
-
if (!meta) return;
|
|
62859
|
-
let mtimeMs;
|
|
62860
|
-
try {
|
|
62861
|
-
mtimeMs = import_node_fs35.default.statSync(file).mtimeMs;
|
|
62862
|
-
} catch {
|
|
62863
|
-
return;
|
|
62864
|
-
}
|
|
62865
|
-
found.push({ file, threadId: meta.threadId, mtimeMs });
|
|
62866
|
-
});
|
|
62867
|
-
if (!goOn) break;
|
|
62868
|
-
}
|
|
62869
|
-
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
62870
|
-
return found.slice(0, CODEX_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
62871
|
-
let summary = names.get(f2.threadId.toLowerCase());
|
|
62872
|
-
if (!summary) {
|
|
62873
|
-
const head = readHead(f2.file, TITLE_BYTES);
|
|
62874
|
-
summary = head === null ? void 0 : firstUserMessageText(head);
|
|
62875
|
-
}
|
|
62876
|
-
return {
|
|
62877
|
-
toolSessionId: f2.threadId,
|
|
62878
|
-
summary: summary || "(\u7A7A\u4F1A\u8BDD)",
|
|
62879
|
-
messageCount: 0,
|
|
62880
|
-
// 与此前 thread/list 一致:列表不数条数
|
|
62881
|
-
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
62882
|
-
};
|
|
62883
|
-
});
|
|
62884
|
-
}
|
|
62885
|
-
async read(args) {
|
|
62886
|
-
const locate = createCodexRolloutLocator(this.deps.roots);
|
|
62887
|
-
const parent = this.parseThread(locate, args.toolSessionId);
|
|
62888
|
-
const messages = await withSubagentResults(
|
|
62889
|
-
parent,
|
|
62890
|
-
args.toolSessionId,
|
|
62891
|
-
async (agentId) => {
|
|
62892
|
-
const file = locate(agentId);
|
|
62893
|
-
return file ? import_node_fs35.default.readFileSync(file, "utf8") : null;
|
|
62894
|
-
},
|
|
62895
|
-
this.deps.logger
|
|
62896
|
-
);
|
|
62897
|
-
return pageMessages(messages, args);
|
|
62898
|
-
}
|
|
62899
|
-
async listSubagents(args) {
|
|
62900
|
-
return subagentSummaries(this.parseThread(createCodexRolloutLocator(this.deps.roots), args.toolSessionId));
|
|
62901
|
-
}
|
|
62902
|
-
async readSubagent(args) {
|
|
62903
|
-
const child = this.parseThread(createCodexRolloutLocator(this.deps.roots), args.subagentId);
|
|
62904
|
-
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
62905
|
-
throw new Error(
|
|
62906
|
-
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
62907
|
-
);
|
|
62908
|
-
}
|
|
62909
|
-
return { messages: child.messages };
|
|
62910
|
-
}
|
|
62911
|
-
computeRewindDiff() {
|
|
62912
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
62913
|
-
}
|
|
62914
|
-
listRewindableUserMessageIds() {
|
|
62915
|
-
return [];
|
|
62916
|
-
}
|
|
62917
|
-
parseThread(locate, threadId) {
|
|
62918
|
-
const file = locate(threadId);
|
|
62919
|
-
if (!file) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
62920
|
-
return parseCodexRollout(import_node_fs35.default.readFileSync(file, "utf8"));
|
|
62921
|
-
}
|
|
62922
|
-
};
|
|
62923
|
-
|
|
62924
|
-
// src/cloud/gateway-client.ts
|
|
62925
|
-
init_protocol();
|
|
62926
|
-
init_protocol();
|
|
62927
|
-
|
|
62928
|
-
// src/feishu-auth/central-client.ts
|
|
62929
|
-
var CentralClientError = class extends Error {
|
|
62930
|
-
constructor(code, message, cause) {
|
|
62931
|
-
super(message);
|
|
62932
|
-
this.code = code;
|
|
62933
|
-
this.cause = cause;
|
|
62934
|
-
this.name = "CentralClientError";
|
|
62935
|
-
}
|
|
62936
|
-
code;
|
|
62937
|
-
cause;
|
|
62938
|
-
};
|
|
62939
|
-
async function centralRequest(opts, path102, init) {
|
|
62940
|
-
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
62941
|
-
const url = `${opts.api.replace(/\/+$/, "")}${path102}`;
|
|
62942
|
-
const ctrl = new AbortController();
|
|
62943
|
-
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`;
|
|
62944
63699
|
let res;
|
|
62945
63700
|
try {
|
|
62946
63701
|
res = await f2(url, {
|
|
62947
|
-
method:
|
|
63702
|
+
method: "POST",
|
|
62948
63703
|
headers: {
|
|
62949
63704
|
"content-type": "application/json",
|
|
62950
|
-
|
|
63705
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
62951
63706
|
},
|
|
62952
|
-
|
|
62953
|
-
|
|
63707
|
+
body: JSON.stringify({
|
|
63708
|
+
id: args.id,
|
|
63709
|
+
text: args.text,
|
|
63710
|
+
createdAt: args.createdAt,
|
|
63711
|
+
...args.origin ? { origin: args.origin } : {}
|
|
63712
|
+
})
|
|
62954
63713
|
});
|
|
62955
63714
|
} catch (err) {
|
|
62956
|
-
|
|
62957
|
-
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
62958
|
-
}
|
|
62959
|
-
clearTimeout(timer);
|
|
62960
|
-
if (res.status === 401) {
|
|
62961
|
-
throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
62962
|
-
}
|
|
62963
|
-
if (!res.ok) {
|
|
62964
|
-
let detail = "";
|
|
62965
|
-
try {
|
|
62966
|
-
const body = await res.json();
|
|
62967
|
-
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
62968
|
-
} catch {
|
|
62969
|
-
}
|
|
62970
|
-
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`);
|
|
63715
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
62971
63716
|
}
|
|
63717
|
+
let json = null;
|
|
62972
63718
|
try {
|
|
62973
|
-
|
|
62974
|
-
} catch
|
|
62975
|
-
|
|
62976
|
-
}
|
|
62977
|
-
}
|
|
62978
|
-
async function centralExchange(opts) {
|
|
62979
|
-
const body = await centralRequest(opts, "/api/clawd-identity/exchange", {
|
|
62980
|
-
method: "POST",
|
|
62981
|
-
bearer: opts.loginPass,
|
|
62982
|
-
body: { deviceId: opts.deviceId }
|
|
62983
|
-
});
|
|
62984
|
-
if (typeof body.token !== "string" || body.token.length === 0 || typeof body.ownerId !== "string" || typeof body.provider !== "string" || typeof body.displayName !== "string") {
|
|
62985
|
-
throw new CentralClientError("BAD_RESPONSE", "exchange: missing token/ownerId/provider/displayName");
|
|
62986
|
-
}
|
|
62987
|
-
const deviceUrl = typeof body.deviceUrl === "string" && body.deviceUrl.length > 0 ? body.deviceUrl : null;
|
|
62988
|
-
return { token: body.token, ownerId: body.ownerId, provider: body.provider, displayName: body.displayName, deviceUrl };
|
|
62989
|
-
}
|
|
62990
|
-
async function centralGetPublicKey(opts) {
|
|
62991
|
-
const body = await centralRequest(opts, "/api/clawd-identity/public-key", {
|
|
62992
|
-
method: "GET"
|
|
62993
|
-
});
|
|
62994
|
-
if (typeof body.publicKeyPem !== "string" || !body.publicKeyPem.includes("PUBLIC KEY")) {
|
|
62995
|
-
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})` };
|
|
62996
63722
|
}
|
|
62997
|
-
|
|
62998
|
-
}
|
|
62999
|
-
async function centralUpsertDevice(opts) {
|
|
63000
|
-
const body = await centralRequest(opts, "/api/clawd-identity/devices/self", {
|
|
63001
|
-
method: "PUT",
|
|
63002
|
-
bearer: opts.loginPass,
|
|
63003
|
-
body: {
|
|
63004
|
-
...opts.url ? { url: opts.url } : {},
|
|
63005
|
-
...opts.name ? { name: opts.name } : {}
|
|
63006
|
-
}
|
|
63007
|
-
});
|
|
63008
|
-
if (body.ok !== true) {
|
|
63009
|
-
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}` };
|
|
63010
63725
|
}
|
|
63011
63726
|
return { ok: true };
|
|
63012
63727
|
}
|
|
63013
|
-
async function
|
|
63014
|
-
const
|
|
63015
|
-
const
|
|
63016
|
-
|
|
63017
|
-
|
|
63018
|
-
|
|
63019
|
-
|
|
63020
|
-
|
|
63021
|
-
|
|
63022
|
-
|
|
63023
|
-
|
|
63024
|
-
|
|
63025
|
-
method: "GET",
|
|
63026
|
-
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
|
|
63027
63740
|
});
|
|
63028
|
-
if (body.apiKey !== null && typeof body.apiKey !== "string") {
|
|
63029
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: apiKey must be string or null");
|
|
63030
|
-
}
|
|
63031
|
-
return {
|
|
63032
|
-
apiKey: body.apiKey,
|
|
63033
|
-
createdAt: typeof body.createdAt === "string" ? body.createdAt : null,
|
|
63034
|
-
// 中心没配就是 null —— 不当成错误,key 本身照样有效
|
|
63035
|
-
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
63036
|
-
};
|
|
63037
|
-
}
|
|
63038
|
-
async function centralGetThread(opts) {
|
|
63039
|
-
const f2 = opts.fetchImpl ?? globalThis.fetch;
|
|
63040
|
-
const url = `${opts.api.replace(/\/+$/, "")}/api/persona-cloud/threads/${encodeURIComponent(opts.threadId)}`;
|
|
63041
|
-
const ctrl = new AbortController();
|
|
63042
|
-
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
63043
63741
|
let res;
|
|
63044
63742
|
try {
|
|
63045
|
-
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
|
+
});
|
|
63046
63751
|
} catch (err) {
|
|
63047
|
-
|
|
63048
|
-
throw new CentralClientError("NETWORK", `central server request failed: ${err.message}`, err);
|
|
63049
|
-
}
|
|
63050
|
-
clearTimeout(timer);
|
|
63051
|
-
if (res.status === 404) return null;
|
|
63052
|
-
if (res.status === 401) throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
|
|
63053
|
-
if (!res.ok) {
|
|
63054
|
-
let detail = "";
|
|
63055
|
-
try {
|
|
63056
|
-
const body = await res.json();
|
|
63057
|
-
if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
|
|
63058
|
-
} catch {
|
|
63059
|
-
}
|
|
63060
|
-
throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`);
|
|
63061
|
-
}
|
|
63062
|
-
return await res.text();
|
|
63063
|
-
}
|
|
63064
|
-
async function centralIssueApiKey(opts) {
|
|
63065
|
-
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
63066
|
-
method: "POST",
|
|
63067
|
-
bearer: opts.loginPass
|
|
63068
|
-
});
|
|
63069
|
-
if (typeof body.apiKey !== "string" || body.apiKey.length === 0) {
|
|
63070
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: missing apiKey");
|
|
63071
|
-
}
|
|
63072
|
-
if (typeof body.createdAt !== "string" || body.createdAt.length === 0) {
|
|
63073
|
-
throw new CentralClientError("BAD_RESPONSE", "api-key: missing createdAt");
|
|
63074
|
-
}
|
|
63075
|
-
return {
|
|
63076
|
-
apiKey: body.apiKey,
|
|
63077
|
-
createdAt: body.createdAt,
|
|
63078
|
-
gatewayUrl: typeof body.gatewayUrl === "string" ? body.gatewayUrl : null
|
|
63079
|
-
};
|
|
63080
|
-
}
|
|
63081
|
-
async function centralRevokeApiKey(opts) {
|
|
63082
|
-
const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
|
|
63083
|
-
method: "DELETE",
|
|
63084
|
-
bearer: opts.loginPass
|
|
63085
|
-
});
|
|
63086
|
-
if (typeof body.revoked !== "boolean") {
|
|
63087
|
-
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)}`);
|
|
63088
63753
|
}
|
|
63089
|
-
|
|
63090
|
-
|
|
63091
|
-
|
|
63092
|
-
|
|
63093
|
-
|
|
63094
|
-
bearer: opts.loginPass
|
|
63095
|
-
});
|
|
63096
|
-
if (!Array.isArray(body.devices)) {
|
|
63097
|
-
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})`);
|
|
63098
63759
|
}
|
|
63099
|
-
|
|
63100
|
-
|
|
63101
|
-
const d2 = raw;
|
|
63102
|
-
if (typeof d2.deviceId !== "string" || typeof d2.name !== "string" || typeof d2.url !== "string") {
|
|
63103
|
-
throw new CentralClientError("BAD_RESPONSE", "devices: malformed device entry");
|
|
63104
|
-
}
|
|
63105
|
-
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);
|
|
63106
63762
|
}
|
|
63107
|
-
return
|
|
63108
|
-
}
|
|
63109
|
-
async function centralConnectorsMine(opts) {
|
|
63110
|
-
const body = await centralRequest(opts, "/api/persona-cloud/connectors/mine", {
|
|
63111
|
-
method: "POST",
|
|
63112
|
-
bearer: opts.loginPass,
|
|
63113
|
-
body: { personaId: opts.personaId, connectors: opts.connectors }
|
|
63114
|
-
});
|
|
63115
|
-
if (!Array.isArray(body.connectors)) throw new CentralClientError("BAD_RESPONSE", "connectors/mine: missing connectors array");
|
|
63116
|
-
return { connectors: body.connectors };
|
|
63117
|
-
}
|
|
63118
|
-
async function centralConnectorFill(opts) {
|
|
63119
|
-
const body = await centralRequest(opts, "/api/persona-cloud/connectors/fields", {
|
|
63120
|
-
method: "PUT",
|
|
63121
|
-
bearer: opts.loginPass,
|
|
63122
|
-
body: { connector: opts.connector, values: opts.values }
|
|
63123
|
-
});
|
|
63124
|
-
if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
|
|
63125
|
-
}
|
|
63126
|
-
async function centralGithubAuthOauthStart(opts) {
|
|
63127
|
-
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
|
|
63128
|
-
method: "POST",
|
|
63129
|
-
bearer: opts.loginPass,
|
|
63130
|
-
body: { ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
|
|
63131
|
-
});
|
|
63132
|
-
if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
|
|
63133
|
-
return { authUrl: body.authUrl };
|
|
63134
|
-
}
|
|
63135
|
-
async function centralGithubAuthToken(opts) {
|
|
63136
|
-
const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
|
|
63137
|
-
method: "POST",
|
|
63138
|
-
bearer: opts.loginPass,
|
|
63139
|
-
body: {}
|
|
63140
|
-
});
|
|
63141
|
-
if (body.token === null || body.token === void 0) return void 0;
|
|
63142
|
-
if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
|
|
63143
|
-
return { token: body.token, login: typeof body.login === "string" ? body.login : "" };
|
|
63763
|
+
return json.result;
|
|
63144
63764
|
}
|
|
63145
63765
|
|
|
63146
63766
|
// src/cloud/gateway-client.ts
|
|
63767
|
+
init_protocol();
|
|
63768
|
+
init_protocol();
|
|
63147
63769
|
var CLOUD_WORKSPACE_SLOT = "{{WORKSPACE}}";
|
|
63148
63770
|
var CLOUD_TURN_HEADER = "x-clawd-turn";
|
|
63149
63771
|
var CLOUD_TURN_HEADER_START = "start";
|
|
@@ -63154,7 +63776,7 @@ var GATEWAY_ROUTES = {
|
|
|
63154
63776
|
sessionMirror: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}/mirror`,
|
|
63155
63777
|
// 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
|
|
63156
63778
|
volume: () => "/me/volume",
|
|
63157
|
-
// 云 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)
|
|
63158
63780
|
metrics: (personaId2) => `/metrics/${encodeURIComponent(personaId2)}`
|
|
63159
63781
|
};
|
|
63160
63782
|
var METRICS_TIMEOUT_MS = 3e4;
|
|
@@ -63205,13 +63827,17 @@ var CloudGatewayClient = class {
|
|
|
63205
63827
|
if (!apiKey) throw new CloudApiKeyRequiredError();
|
|
63206
63828
|
return { apiKey, gatewayUrl };
|
|
63207
63829
|
}
|
|
63208
|
-
|
|
63830
|
+
/**
|
|
63831
|
+
* `healthzTimeoutMs`:只给 /healthz 那一趟定上限,**问到地址之后才开始计**(上云页的线上状态用;design 2026-09-24-deploy-unpublish)。
|
|
63832
|
+
* 中心那一跳(locate)有它自己的上限,不归这里管。不给 = 同以前,不设
|
|
63833
|
+
*/
|
|
63834
|
+
async listPersonas(opts = {}) {
|
|
63209
63835
|
const { gatewayUrl } = await this.locate();
|
|
63210
|
-
return this.fetchDeployments(gatewayUrl);
|
|
63836
|
+
return this.fetchDeployments(gatewayUrl, opts.healthzTimeoutMs ? AbortSignal.timeout(opts.healthzTimeoutMs) : void 0);
|
|
63211
63837
|
}
|
|
63212
63838
|
/** /healthz 的登记表 → official 行。gatewayUrl 由调用方定:listPersonas 从中心拿,personaConnectors 用 persona 文件里的 */
|
|
63213
|
-
async fetchDeployments(gatewayUrl) {
|
|
63214
|
-
const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz());
|
|
63839
|
+
async fetchDeployments(gatewayUrl, signal) {
|
|
63840
|
+
const res = await this.fetchImpl(gatewayUrl + GATEWAY_ROUTES.healthz(), ...signal ? [{ signal }] : []);
|
|
63215
63841
|
if (!res.ok) throw new Error(`gateway /healthz \u56DE ${res.status}`);
|
|
63216
63842
|
const body = await res.json();
|
|
63217
63843
|
const rows = Array.isArray(body.deployments) ? body.deployments : [];
|
|
@@ -63510,68 +64136,7 @@ function openHttpLineStream(opts) {
|
|
|
63510
64136
|
init_protocol();
|
|
63511
64137
|
|
|
63512
64138
|
// src/tools/codex-cloud-session.ts
|
|
63513
|
-
var
|
|
63514
|
-
|
|
63515
|
-
// src/tools/codex-cloud-history.ts
|
|
63516
|
-
function personaIdFromCloudCwd(cwd) {
|
|
63517
|
-
const m3 = /\/p\/([^/?#]+)(?:[/?#]|$)/.exec(cwd);
|
|
63518
|
-
if (!m3) return void 0;
|
|
63519
|
-
try {
|
|
63520
|
-
return decodeURIComponent(m3[1]);
|
|
63521
|
-
} catch {
|
|
63522
|
-
return void 0;
|
|
63523
|
-
}
|
|
63524
|
-
}
|
|
63525
|
-
var CloudCodexHistoryReader = class {
|
|
63526
|
-
constructor(client, logger) {
|
|
63527
|
-
this.client = client;
|
|
63528
|
-
this.logger = logger;
|
|
63529
|
-
}
|
|
63530
|
-
client;
|
|
63531
|
-
logger;
|
|
63532
|
-
async listProjects() {
|
|
63533
|
-
return [];
|
|
63534
|
-
}
|
|
63535
|
-
/** 云 persona 没有「关联已有 thread」的动线:会话列表就是本机的会话记录 */
|
|
63536
|
-
async listSessions() {
|
|
63537
|
-
return [];
|
|
63538
|
-
}
|
|
63539
|
-
async read(args) {
|
|
63540
|
-
const parent = await this.fetchThread(args.toolSessionId);
|
|
63541
|
-
const messages = await withSubagentResults(
|
|
63542
|
-
parent,
|
|
63543
|
-
args.toolSessionId,
|
|
63544
|
-
(agentId) => this.client.fetchRollout(agentId),
|
|
63545
|
-
this.logger
|
|
63546
|
-
);
|
|
63547
|
-
return pageMessages(messages, args);
|
|
63548
|
-
}
|
|
63549
|
-
async listSubagents(args) {
|
|
63550
|
-
return subagentSummaries(await this.fetchThread(args.toolSessionId));
|
|
63551
|
-
}
|
|
63552
|
-
async readSubagent(args) {
|
|
63553
|
-
const child = await this.fetchThread(args.subagentId);
|
|
63554
|
-
if (child.meta?.threadId !== args.subagentId || child.meta.parentThreadId !== args.toolSessionId) {
|
|
63555
|
-
throw new Error(
|
|
63556
|
-
`Codex subagent thread ${args.subagentId} does not belong to parent thread ${args.toolSessionId}`
|
|
63557
|
-
);
|
|
63558
|
-
}
|
|
63559
|
-
return { messages: child.messages };
|
|
63560
|
-
}
|
|
63561
|
-
computeRewindDiff() {
|
|
63562
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
63563
|
-
}
|
|
63564
|
-
listRewindableUserMessageIds() {
|
|
63565
|
-
return [];
|
|
63566
|
-
}
|
|
63567
|
-
async fetchThread(threadId) {
|
|
63568
|
-
const text = await this.client.fetchRollout(threadId);
|
|
63569
|
-
if (text === null) throw new Error(`Codex rollout not found for thread ${threadId}`);
|
|
63570
|
-
return parseCodexRollout(text);
|
|
63571
|
-
}
|
|
63572
|
-
};
|
|
63573
|
-
|
|
63574
|
-
// src/tools/codex-cloud-session.ts
|
|
64139
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
63575
64140
|
function cloudThreadParams(ctx, paths) {
|
|
63576
64141
|
const { model: _model, ...base } = threadStartParams(ctx);
|
|
63577
64142
|
const developerInstructions = [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
|
|
@@ -63628,7 +64193,7 @@ function cloudImageInput(deps) {
|
|
|
63628
64193
|
const mime = lookupMime(p3);
|
|
63629
64194
|
if (!mime.startsWith("image/")) return null;
|
|
63630
64195
|
try {
|
|
63631
|
-
const bytes = (deps.readFile ??
|
|
64196
|
+
const bytes = (deps.readFile ?? import_node_fs37.default.readFileSync)(p3);
|
|
63632
64197
|
return { type: "image", url: `data:${mime};base64,${bytes.toString("base64")}` };
|
|
63633
64198
|
} catch (e) {
|
|
63634
64199
|
deps.logger?.warn("cloud image attachment unreadable", { path: p3, message: e.message });
|
|
@@ -63861,37 +64426,11 @@ var PiRpcClient = class {
|
|
|
63861
64426
|
// src/tools/pi-session.ts
|
|
63862
64427
|
var import_node_child_process11 = require("child_process");
|
|
63863
64428
|
var import_node_crypto12 = require("crypto");
|
|
63864
|
-
var
|
|
63865
|
-
var
|
|
64429
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64430
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
63866
64431
|
|
|
63867
64432
|
// src/tools/pi-events.ts
|
|
63868
64433
|
init_protocol();
|
|
63869
|
-
|
|
63870
|
-
// src/tools/pi-json.ts
|
|
63871
|
-
function rec3(v3) {
|
|
63872
|
-
return typeof v3 === "object" && v3 !== null && !Array.isArray(v3) ? v3 : void 0;
|
|
63873
|
-
}
|
|
63874
|
-
function str3(v3) {
|
|
63875
|
-
return typeof v3 === "string" ? v3 : void 0;
|
|
63876
|
-
}
|
|
63877
|
-
function num3(v3) {
|
|
63878
|
-
return typeof v3 === "number" && Number.isFinite(v3) ? v3 : void 0;
|
|
63879
|
-
}
|
|
63880
|
-
function arr(v3) {
|
|
63881
|
-
return Array.isArray(v3) ? v3 : [];
|
|
63882
|
-
}
|
|
63883
|
-
function contentText(v3, imagePlaceholder = "") {
|
|
63884
|
-
if (typeof v3 === "string") return v3;
|
|
63885
|
-
return arr(v3).map((b3) => {
|
|
63886
|
-
const r2 = rec3(b3);
|
|
63887
|
-
if (!r2) return "";
|
|
63888
|
-
if (r2.type === "text") return str3(r2.text) ?? "";
|
|
63889
|
-
if (r2.type === "image") return imagePlaceholder;
|
|
63890
|
-
return "";
|
|
63891
|
-
}).filter(Boolean).join("\n");
|
|
63892
|
-
}
|
|
63893
|
-
|
|
63894
|
-
// src/tools/pi-events.ts
|
|
63895
64434
|
function createPiTranslatorState() {
|
|
63896
64435
|
return { assistantSeq: 0, aborted: false };
|
|
63897
64436
|
}
|
|
@@ -63902,23 +64441,23 @@ function translatePiEvent(ev, st2) {
|
|
|
63902
64441
|
return [];
|
|
63903
64442
|
}
|
|
63904
64443
|
case "message_start": {
|
|
63905
|
-
if (
|
|
64444
|
+
if (rec2(ev.message)?.role === "assistant") st2.assistantSeq += 1;
|
|
63906
64445
|
return [];
|
|
63907
64446
|
}
|
|
63908
64447
|
case "message_update":
|
|
63909
|
-
return messageUpdate(
|
|
64448
|
+
return messageUpdate(rec2(ev.assistantMessageEvent), st2);
|
|
63910
64449
|
case "message_end":
|
|
63911
|
-
return messageEnd(
|
|
64450
|
+
return messageEnd(rec2(ev.message), st2);
|
|
63912
64451
|
case "tool_execution_start": {
|
|
63913
|
-
const toolUseId =
|
|
63914
|
-
const tool =
|
|
64452
|
+
const toolUseId = str2(ev.toolCallId);
|
|
64453
|
+
const tool = str2(ev.toolName);
|
|
63915
64454
|
if (!toolUseId || !tool) return [];
|
|
63916
64455
|
return [{ kind: "tool_call", toolUseId, tool, toolKind: PI_TOOL_ID, input: ev.args ?? {} }];
|
|
63917
64456
|
}
|
|
63918
64457
|
case "tool_execution_end": {
|
|
63919
|
-
const toolUseId =
|
|
64458
|
+
const toolUseId = str2(ev.toolCallId);
|
|
63920
64459
|
if (!toolUseId) return [];
|
|
63921
|
-
const text = contentText(
|
|
64460
|
+
const text = contentText(rec2(ev.result)?.content);
|
|
63922
64461
|
const out = { kind: "tool_result", toolUseId, output: text };
|
|
63923
64462
|
if (ev.isError === true) out.error = text || "tool failed";
|
|
63924
64463
|
return [out];
|
|
@@ -63934,9 +64473,9 @@ function translatePiEvent(ev, st2) {
|
|
|
63934
64473
|
}
|
|
63935
64474
|
function messageUpdate(e, st2) {
|
|
63936
64475
|
if (!e) return [];
|
|
63937
|
-
const delta =
|
|
64476
|
+
const delta = str2(e.delta);
|
|
63938
64477
|
if (!delta) return [];
|
|
63939
|
-
const partialId = `${st2.assistantSeq}-${
|
|
64478
|
+
const partialId = `${st2.assistantSeq}-${num2(e.contentIndex) ?? 0}`;
|
|
63940
64479
|
if (e.type === "text_delta") return [{ kind: "text", text: delta, partialId }];
|
|
63941
64480
|
if (e.type === "thinking_delta") return [{ kind: "thinking", text: delta, partialId }];
|
|
63942
64481
|
return [];
|
|
@@ -63945,32 +64484,32 @@ function messageEnd(m3, st2) {
|
|
|
63945
64484
|
if (!m3 || m3.role !== "assistant") return [];
|
|
63946
64485
|
const out = [];
|
|
63947
64486
|
for (const b3 of arr(m3.content)) {
|
|
63948
|
-
const block =
|
|
64487
|
+
const block = rec2(b3);
|
|
63949
64488
|
if (!block) continue;
|
|
63950
64489
|
if (block.type === "thinking") {
|
|
63951
|
-
const t2 =
|
|
64490
|
+
const t2 = str2(block.thinking);
|
|
63952
64491
|
if (t2) out.push({ kind: "thinking", text: t2 });
|
|
63953
64492
|
} else if (block.type === "text") {
|
|
63954
|
-
const t2 =
|
|
64493
|
+
const t2 = str2(block.text);
|
|
63955
64494
|
if (t2) out.push({ kind: "text", text: t2 });
|
|
63956
64495
|
}
|
|
63957
64496
|
}
|
|
63958
64497
|
const patch = {};
|
|
63959
|
-
const model =
|
|
63960
|
-
const provider =
|
|
64498
|
+
const model = str2(m3.model);
|
|
64499
|
+
const provider = str2(m3.provider);
|
|
63961
64500
|
if (model) patch.resolvedModel = provider ? `${provider}/${model}` : model;
|
|
63962
|
-
const u2 =
|
|
63963
|
-
if (u2 && (
|
|
64501
|
+
const u2 = rec2(m3.usage);
|
|
64502
|
+
if (u2 && (num2(u2.totalTokens) ?? 0) > 0) {
|
|
63964
64503
|
patch.contextUsage = {
|
|
63965
|
-
inputTokens:
|
|
63966
|
-
outputTokens:
|
|
63967
|
-
...
|
|
63968
|
-
...
|
|
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) } : {}
|
|
63969
64508
|
};
|
|
63970
64509
|
}
|
|
63971
64510
|
if (patch.resolvedModel || patch.contextUsage) out.push({ kind: "meta_update", patch });
|
|
63972
|
-
const stop =
|
|
63973
|
-
const err =
|
|
64511
|
+
const stop = str2(m3.stopReason);
|
|
64512
|
+
const err = str2(m3.errorMessage);
|
|
63974
64513
|
if (stop === "aborted") {
|
|
63975
64514
|
st2.aborted = true;
|
|
63976
64515
|
} else if (stop === "error" || err) {
|
|
@@ -64038,8 +64577,8 @@ function drivePiSession(opts) {
|
|
|
64038
64577
|
opts.onTurnCompleted?.("failure");
|
|
64039
64578
|
};
|
|
64040
64579
|
const handleUiRequest = (ev) => {
|
|
64041
|
-
const method =
|
|
64042
|
-
const id =
|
|
64580
|
+
const method = str2(ev.method);
|
|
64581
|
+
const id = str2(ev.id);
|
|
64043
64582
|
logger?.warn("pi extension_ui_request unexpected in clawd session", { method });
|
|
64044
64583
|
if (id && (method === "select" || method === "confirm" || method === "input" || method === "editor")) {
|
|
64045
64584
|
client.send({ type: "extension_ui_response", id, cancelled: true });
|
|
@@ -64054,7 +64593,7 @@ function drivePiSession(opts) {
|
|
|
64054
64593
|
}
|
|
64055
64594
|
if (ev.type === "agent_settled") busy = false;
|
|
64056
64595
|
if (ev.type === "response") {
|
|
64057
|
-
logger?.warn("pi response without pending id", { command:
|
|
64596
|
+
logger?.warn("pi response without pending id", { command: str2(ev.command), error: str2(ev.error) });
|
|
64058
64597
|
return;
|
|
64059
64598
|
}
|
|
64060
64599
|
if (ev.type === "extension_ui_request") {
|
|
@@ -64076,15 +64615,15 @@ function drivePiSession(opts) {
|
|
|
64076
64615
|
});
|
|
64077
64616
|
const ready = client.request({ type: "get_state" }).then((r2) => {
|
|
64078
64617
|
if (!r2.success) throw new Error(r2.error ?? "get_state failed");
|
|
64079
|
-
const data =
|
|
64080
|
-
const sid =
|
|
64618
|
+
const data = rec2(r2.data);
|
|
64619
|
+
const sid = str2(data?.sessionId) ?? sessionId;
|
|
64081
64620
|
if (sid !== sessionId) logger?.error("pi sessionId mismatch", { expected: sessionId, actual: sid });
|
|
64082
|
-
const model =
|
|
64621
|
+
const model = rec2(data?.model);
|
|
64083
64622
|
const patch = {};
|
|
64084
|
-
const mid =
|
|
64085
|
-
const prov =
|
|
64623
|
+
const mid = str2(model?.id);
|
|
64624
|
+
const prov = str2(model?.provider);
|
|
64086
64625
|
if (mid && mid !== "unknown") patch.resolvedModel = prov ? `${prov}/${mid}` : mid;
|
|
64087
|
-
const cw =
|
|
64626
|
+
const cw = num2(model?.contextWindow);
|
|
64088
64627
|
if (cw && cw > 0) patch.contextWindowSize = cw;
|
|
64089
64628
|
const events = [{ kind: "session_init", toolSessionId: sid }];
|
|
64090
64629
|
if (patch.resolvedModel || patch.contextWindowSize) events.push({ kind: "meta_update", patch });
|
|
@@ -64155,8 +64694,8 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
|
|
|
64155
64694
|
function createPiSession(ctx, sink, deps = {}) {
|
|
64156
64695
|
const cmd = process.env.PI_BIN ?? "pi";
|
|
64157
64696
|
const sessionId = ctx.toolSessionId ?? (0, import_node_crypto12.randomUUID)();
|
|
64158
|
-
const skillsDir =
|
|
64159
|
-
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));
|
|
64160
64699
|
const args = buildPiArgs(ctx, {
|
|
64161
64700
|
sessionId,
|
|
64162
64701
|
skillsDir: exists(skillsDir) ? skillsDir : void 0,
|
|
@@ -64199,201 +64738,6 @@ function createPiSession(ctx, sink, deps = {}) {
|
|
|
64199
64738
|
return driver.session;
|
|
64200
64739
|
}
|
|
64201
64740
|
|
|
64202
|
-
// src/tools/pi-history.ts
|
|
64203
|
-
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
64204
|
-
var import_node_os12 = __toESM(require("os"), 1);
|
|
64205
|
-
var import_node_path42 = __toESM(require("path"), 1);
|
|
64206
|
-
var PI_LIST_SESSIONS_LIMIT = 100;
|
|
64207
|
-
var PI_LIST_SESSIONS_SCAN_CAP = 4e3;
|
|
64208
|
-
function piAgentDir(env = process.env) {
|
|
64209
|
-
return env.PI_CODING_AGENT_DIR || import_node_path42.default.join(import_node_os12.default.homedir(), ".pi", "agent");
|
|
64210
|
-
}
|
|
64211
|
-
function piSessionsRoot(env = process.env) {
|
|
64212
|
-
return import_node_path42.default.join(piAgentDir(env), "sessions");
|
|
64213
|
-
}
|
|
64214
|
-
function encodePiCwd(cwd) {
|
|
64215
|
-
return `--${cwd.replace(/^[\\/]+/, "").replace(/[\\/:]/g, "-")}--`;
|
|
64216
|
-
}
|
|
64217
|
-
function piProjectSessionDir(sessionsRoot, cwd) {
|
|
64218
|
-
return import_node_path42.default.join(sessionsRoot, encodePiCwd(cwd));
|
|
64219
|
-
}
|
|
64220
|
-
function findInDir(dir, toolSessionId) {
|
|
64221
|
-
let names;
|
|
64222
|
-
try {
|
|
64223
|
-
names = import_node_fs38.default.readdirSync(dir);
|
|
64224
|
-
} catch {
|
|
64225
|
-
return null;
|
|
64226
|
-
}
|
|
64227
|
-
const suffix = `_${toolSessionId}.jsonl`;
|
|
64228
|
-
const hit = names.filter((n2) => n2.endsWith(suffix)).sort().at(-1);
|
|
64229
|
-
return hit ? import_node_path42.default.join(dir, hit) : null;
|
|
64230
|
-
}
|
|
64231
|
-
function locatePiSessionFile(sessionsRoot, cwd, toolSessionId) {
|
|
64232
|
-
const direct = findInDir(piProjectSessionDir(sessionsRoot, cwd), toolSessionId);
|
|
64233
|
-
if (direct) return direct;
|
|
64234
|
-
let dirs;
|
|
64235
|
-
try {
|
|
64236
|
-
dirs = import_node_fs38.default.readdirSync(sessionsRoot, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
|
|
64237
|
-
} catch {
|
|
64238
|
-
return null;
|
|
64239
|
-
}
|
|
64240
|
-
for (const d2 of dirs) {
|
|
64241
|
-
const hit = findInDir(import_node_path42.default.join(sessionsRoot, d2), toolSessionId);
|
|
64242
|
-
if (hit) return hit;
|
|
64243
|
-
}
|
|
64244
|
-
return null;
|
|
64245
|
-
}
|
|
64246
|
-
function parseLine2(line) {
|
|
64247
|
-
const t2 = line.trim();
|
|
64248
|
-
if (!t2) return null;
|
|
64249
|
-
try {
|
|
64250
|
-
return rec3(JSON.parse(t2)) ?? null;
|
|
64251
|
-
} catch {
|
|
64252
|
-
return null;
|
|
64253
|
-
}
|
|
64254
|
-
}
|
|
64255
|
-
function parsePiSession(text) {
|
|
64256
|
-
let meta = null;
|
|
64257
|
-
const entries = /* @__PURE__ */ new Map();
|
|
64258
|
-
let last = null;
|
|
64259
|
-
for (const line of text.split("\n")) {
|
|
64260
|
-
const o2 = parseLine2(line);
|
|
64261
|
-
if (!o2) continue;
|
|
64262
|
-
if (o2.type === "session") {
|
|
64263
|
-
if (!meta) {
|
|
64264
|
-
const timestamp = str3(o2.timestamp);
|
|
64265
|
-
meta = { id: str3(o2.id) ?? "", cwd: str3(o2.cwd) ?? "", ...timestamp ? { timestamp } : {} };
|
|
64266
|
-
}
|
|
64267
|
-
continue;
|
|
64268
|
-
}
|
|
64269
|
-
const id = str3(o2.id);
|
|
64270
|
-
if (!id) continue;
|
|
64271
|
-
entries.set(id, o2);
|
|
64272
|
-
last = o2;
|
|
64273
|
-
}
|
|
64274
|
-
const chain = [];
|
|
64275
|
-
const seen = /* @__PURE__ */ new Set();
|
|
64276
|
-
let cur = last;
|
|
64277
|
-
while (cur) {
|
|
64278
|
-
const id = str3(cur.id);
|
|
64279
|
-
if (seen.has(id)) break;
|
|
64280
|
-
seen.add(id);
|
|
64281
|
-
chain.push(cur);
|
|
64282
|
-
const pid = str3(cur.parentId);
|
|
64283
|
-
cur = pid ? entries.get(pid) ?? null : null;
|
|
64284
|
-
}
|
|
64285
|
-
chain.reverse();
|
|
64286
|
-
const messages = [];
|
|
64287
|
-
for (const e of chain) {
|
|
64288
|
-
if (e.type !== "message") continue;
|
|
64289
|
-
const m3 = rec3(e.message);
|
|
64290
|
-
if (!m3) continue;
|
|
64291
|
-
const ts = str3(e.timestamp);
|
|
64292
|
-
const base = { raw: e, uuid: str3(e.id), ...ts ? { ts } : {} };
|
|
64293
|
-
switch (m3.role) {
|
|
64294
|
-
case "user": {
|
|
64295
|
-
const t2 = contentText(m3.content, "[image]");
|
|
64296
|
-
if (t2) messages.push({ kind: "user-text", text: t2, ...base });
|
|
64297
|
-
break;
|
|
64298
|
-
}
|
|
64299
|
-
case "assistant": {
|
|
64300
|
-
for (const b3 of arr(m3.content)) {
|
|
64301
|
-
const block = rec3(b3);
|
|
64302
|
-
if (!block) continue;
|
|
64303
|
-
if (block.type === "text") {
|
|
64304
|
-
const t2 = str3(block.text);
|
|
64305
|
-
if (t2) messages.push({ kind: "assistant-text", text: t2, ...base });
|
|
64306
|
-
} else if (block.type === "toolCall") {
|
|
64307
|
-
const toolUseId = str3(block.id);
|
|
64308
|
-
const tool = str3(block.name);
|
|
64309
|
-
if (toolUseId && tool) messages.push({ kind: "tool_call", tool, toolUseId, input: block.arguments ?? {}, ...base });
|
|
64310
|
-
}
|
|
64311
|
-
}
|
|
64312
|
-
if (str3(m3.stopReason) !== "toolUse") messages.push({ kind: "turn-end", ...base });
|
|
64313
|
-
break;
|
|
64314
|
-
}
|
|
64315
|
-
case "toolResult": {
|
|
64316
|
-
const toolUseId = str3(m3.toolCallId);
|
|
64317
|
-
if (!toolUseId) break;
|
|
64318
|
-
const t2 = contentText(m3.content);
|
|
64319
|
-
const msg = { kind: "tool_result", toolUseId, output: t2, ...base };
|
|
64320
|
-
if (m3.isError === true) msg.error = t2 || "tool failed";
|
|
64321
|
-
messages.push(msg);
|
|
64322
|
-
break;
|
|
64323
|
-
}
|
|
64324
|
-
default:
|
|
64325
|
-
break;
|
|
64326
|
-
}
|
|
64327
|
-
}
|
|
64328
|
-
return { meta, messages };
|
|
64329
|
-
}
|
|
64330
|
-
var PiHistoryReader = class {
|
|
64331
|
-
constructor(deps) {
|
|
64332
|
-
this.deps = deps;
|
|
64333
|
-
}
|
|
64334
|
-
deps;
|
|
64335
|
-
// projects/list 非 session-scoped、不知 tool(同 codex 的取舍)
|
|
64336
|
-
async listProjects() {
|
|
64337
|
-
return [];
|
|
64338
|
-
}
|
|
64339
|
-
async listSessions(args) {
|
|
64340
|
-
const want = import_node_path42.default.resolve(args.projectPath);
|
|
64341
|
-
const dir = piProjectSessionDir(this.deps.sessionsRoot, want);
|
|
64342
|
-
let names;
|
|
64343
|
-
try {
|
|
64344
|
-
names = import_node_fs38.default.readdirSync(dir).filter((n2) => n2.endsWith(".jsonl"));
|
|
64345
|
-
} catch {
|
|
64346
|
-
return [];
|
|
64347
|
-
}
|
|
64348
|
-
const found = [];
|
|
64349
|
-
let scanned = 0;
|
|
64350
|
-
for (const n2 of names) {
|
|
64351
|
-
if (++scanned > PI_LIST_SESSIONS_SCAN_CAP) break;
|
|
64352
|
-
const file = import_node_path42.default.join(dir, n2);
|
|
64353
|
-
const head = parseLine2(readFirstLine(file) ?? "");
|
|
64354
|
-
if (!head || head.type !== "session") continue;
|
|
64355
|
-
const cwd = str3(head.cwd);
|
|
64356
|
-
const id = str3(head.id);
|
|
64357
|
-
if (!cwd || !id || import_node_path42.default.resolve(cwd) !== want) continue;
|
|
64358
|
-
let mtimeMs;
|
|
64359
|
-
try {
|
|
64360
|
-
mtimeMs = import_node_fs38.default.statSync(file).mtimeMs;
|
|
64361
|
-
} catch {
|
|
64362
|
-
continue;
|
|
64363
|
-
}
|
|
64364
|
-
found.push({ file, id, mtimeMs });
|
|
64365
|
-
}
|
|
64366
|
-
found.sort((a2, b3) => b3.mtimeMs - a2.mtimeMs);
|
|
64367
|
-
return found.slice(0, PI_LIST_SESSIONS_LIMIT).map((f2) => {
|
|
64368
|
-
const parsed = parsePiSession(import_node_fs38.default.readFileSync(f2.file, "utf8"));
|
|
64369
|
-
const firstUser = parsed.messages.find((m3) => m3.kind === "user-text");
|
|
64370
|
-
return {
|
|
64371
|
-
toolSessionId: f2.id,
|
|
64372
|
-
summary: firstUser?.text?.slice(0, 80) || "(\u7A7A\u4F1A\u8BDD)",
|
|
64373
|
-
messageCount: parsed.messages.length,
|
|
64374
|
-
updatedAt: new Date(f2.mtimeMs).toISOString()
|
|
64375
|
-
};
|
|
64376
|
-
});
|
|
64377
|
-
}
|
|
64378
|
-
async read(args) {
|
|
64379
|
-
const file = locatePiSessionFile(this.deps.sessionsRoot, args.cwd, args.toolSessionId);
|
|
64380
|
-
if (!file) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
64381
|
-
return pageMessages(parsePiSession(import_node_fs38.default.readFileSync(file, "utf8")).messages, args);
|
|
64382
|
-
}
|
|
64383
|
-
async listSubagents(_args) {
|
|
64384
|
-
return [];
|
|
64385
|
-
}
|
|
64386
|
-
async readSubagent(args) {
|
|
64387
|
-
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
64388
|
-
}
|
|
64389
|
-
computeRewindDiff() {
|
|
64390
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
64391
|
-
}
|
|
64392
|
-
listRewindableUserMessageIds() {
|
|
64393
|
-
return [];
|
|
64394
|
-
}
|
|
64395
|
-
};
|
|
64396
|
-
|
|
64397
64741
|
// src/tools/pi.ts
|
|
64398
64742
|
var PI_DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
64399
64743
|
var PI_THINKING_EFFORTS = [
|
|
@@ -64422,20 +64766,20 @@ async function probePi(env = process.env) {
|
|
|
64422
64766
|
return { available: false };
|
|
64423
64767
|
}
|
|
64424
64768
|
function mapPiModels(raw) {
|
|
64425
|
-
const models = arr(
|
|
64426
|
-
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));
|
|
64427
64771
|
const out = [];
|
|
64428
64772
|
for (const m3 of models) {
|
|
64429
|
-
const id =
|
|
64430
|
-
const provider =
|
|
64773
|
+
const id = str2(m3.id);
|
|
64774
|
+
const provider = str2(m3.provider);
|
|
64431
64775
|
if (!id || !provider) continue;
|
|
64432
|
-
const cw =
|
|
64776
|
+
const cw = num2(m3.contextWindow);
|
|
64433
64777
|
const levels = arr(m3.thinkingLevels).filter((v3) => typeof v3 === "string");
|
|
64434
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]];
|
|
64435
64779
|
out.push({
|
|
64436
64780
|
id: `${provider}/${id}`,
|
|
64437
64781
|
// 与 --model 同一串
|
|
64438
|
-
label:
|
|
64782
|
+
label: str2(m3.name) || id,
|
|
64439
64783
|
contextWindowSize: cw && cw > 0 ? cw : PI_DEFAULT_CONTEXT_WINDOW,
|
|
64440
64784
|
efforts,
|
|
64441
64785
|
// 分组按 provider;只有一个 provider 时不分组(一个横跨全表的标题没信息量,同 codex)
|
|
@@ -64501,18 +64845,18 @@ async function queryPiModels(deps = {}) {
|
|
|
64501
64845
|
try {
|
|
64502
64846
|
const r2 = await race(client.request({ type: "get_available_models" }));
|
|
64503
64847
|
if (!r2.success) throw new Error(r2.error ?? "get_available_models failed");
|
|
64504
|
-
const models = arr(
|
|
64848
|
+
const models = arr(rec2(r2.data)?.models).map(rec2).filter((m3) => m3 !== void 0);
|
|
64505
64849
|
const enriched = [];
|
|
64506
64850
|
for (const m3 of models) {
|
|
64507
|
-
const provider =
|
|
64508
|
-
const id =
|
|
64851
|
+
const provider = str2(m3.provider);
|
|
64852
|
+
const id = str2(m3.id);
|
|
64509
64853
|
let thinkingLevels;
|
|
64510
64854
|
if (provider && id && !timedOut) {
|
|
64511
64855
|
try {
|
|
64512
64856
|
const set = await race(client.request({ type: "set_model", provider, modelId: id }));
|
|
64513
64857
|
if (set.success) {
|
|
64514
64858
|
const lv = await race(client.request({ type: "get_available_thinking_levels" }));
|
|
64515
|
-
const levels = arr(
|
|
64859
|
+
const levels = arr(rec2(lv.data)?.levels).filter((v3) => typeof v3 === "string");
|
|
64516
64860
|
if (lv.success && levels.length > 0) thinkingLevels = levels;
|
|
64517
64861
|
}
|
|
64518
64862
|
} catch {
|
|
@@ -64634,42 +64978,6 @@ function createCloudPiSession(ctx, sink, deps) {
|
|
|
64634
64978
|
return driver.session;
|
|
64635
64979
|
}
|
|
64636
64980
|
|
|
64637
|
-
// src/tools/pi-cloud-history.ts
|
|
64638
|
-
var CloudPiHistoryReader = class {
|
|
64639
|
-
constructor(client, logger) {
|
|
64640
|
-
this.client = client;
|
|
64641
|
-
this.logger = logger;
|
|
64642
|
-
}
|
|
64643
|
-
client;
|
|
64644
|
-
logger;
|
|
64645
|
-
async listProjects() {
|
|
64646
|
-
return [];
|
|
64647
|
-
}
|
|
64648
|
-
/** 云 persona 没有「关联已有会话」的动线:会话列表就是本机的会话记录 */
|
|
64649
|
-
async listSessions() {
|
|
64650
|
-
return [];
|
|
64651
|
-
}
|
|
64652
|
-
async read(args) {
|
|
64653
|
-
const text = await this.client.fetchRollout(args.toolSessionId);
|
|
64654
|
-
if (text === null) throw new Error(`pi session not found for ${args.toolSessionId}`);
|
|
64655
|
-
const parsed = parsePiSession(text);
|
|
64656
|
-
if (!parsed.meta) this.logger?.warn("cloud pi session has no header line", { toolSessionId: args.toolSessionId });
|
|
64657
|
-
return pageMessages(parsed.messages, args);
|
|
64658
|
-
}
|
|
64659
|
-
async listSubagents(_args) {
|
|
64660
|
-
return [];
|
|
64661
|
-
}
|
|
64662
|
-
async readSubagent(args) {
|
|
64663
|
-
throw new Error(`pi has no subagent threads (${args.subagentId})`);
|
|
64664
|
-
}
|
|
64665
|
-
computeRewindDiff() {
|
|
64666
|
-
return { canRewind: false, files: [], totalInsertions: 0, totalDeletions: 0 };
|
|
64667
|
-
}
|
|
64668
|
-
listRewindableUserMessageIds() {
|
|
64669
|
-
return [];
|
|
64670
|
-
}
|
|
64671
|
-
};
|
|
64672
|
-
|
|
64673
64981
|
// src/tools/pi-cloud.ts
|
|
64674
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" };
|
|
64675
64983
|
var CLOUD_PI_CAPABILITIES = {
|
|
@@ -71686,6 +71994,7 @@ var METHOD_GRANT_MAP = {
|
|
|
71686
71994
|
"deploy:finishScan": ADMIN_ANY,
|
|
71687
71995
|
"deploy:release": ADMIN_ANY,
|
|
71688
71996
|
"deploy:rerunJob": ADMIN_ANY,
|
|
71997
|
+
"deploy:unpublish": ADMIN_ANY,
|
|
71689
71998
|
// cloud:connectors:*(云 persona 卡片上的连接器;design 2026-09-11-mcp-connector):
|
|
71690
71999
|
// 拿本机登录凭证向中心代查 / 代填使用者本人的凭据,guest 一律够不着
|
|
71691
72000
|
"cloud:connectors:list": ADMIN_ANY,
|
|
@@ -71899,6 +72208,7 @@ var HUMAN_ONLY_METHODS = [
|
|
|
71899
72208
|
"deploy:release",
|
|
71900
72209
|
"deploy:putSecret",
|
|
71901
72210
|
"deploy:rerunJob",
|
|
72211
|
+
"deploy:unpublish",
|
|
71902
72212
|
"githubAuth:oauthStart",
|
|
71903
72213
|
"githubAuth:authorize"
|
|
71904
72214
|
];
|
|
@@ -71943,7 +72253,7 @@ function computeMethodAccess(args) {
|
|
|
71943
72253
|
}
|
|
71944
72254
|
|
|
71945
72255
|
// src/version.ts
|
|
71946
|
-
var version = "0.2.
|
|
72256
|
+
var version = "0.2.583".length > 0 ? "0.2.583" : "dev";
|
|
71947
72257
|
|
|
71948
72258
|
// src/cli-probe/probe.ts
|
|
71949
72259
|
var fs80 = __toESM(require("fs"), 1);
|
|
@@ -75624,8 +75934,27 @@ async function startDaemon(config) {
|
|
|
75624
75934
|
githubIdentity,
|
|
75625
75935
|
githubApi: (token) => createGithubApi({ token }),
|
|
75626
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
|
+
},
|
|
75627
75949
|
now: () => Date.now(),
|
|
75628
75950
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
75951
|
+
// 在不在线上 = gateway 正式登记表里有没有它(design 2026-09-24-deploy-unpublish §3.3):跟 persona 列表的云卡片同一个来源。
|
|
75952
|
+
// 拉不到(没登录飞书 / gateway 不通 / healthz 5s 没回)→ undefined,页面不给下线按钮也不撤「线上」标。
|
|
75953
|
+
// 失败上界:中心那一跳自带 15s,healthz 这一趟 5s——deploy:get 要保证页面打得开,gateway 接了连接不回时不能挂到 undici 默认的 300s
|
|
75954
|
+
cloudOnline: (personaId2) => cloudGateway.listPersonas({ healthzTimeoutMs: 5e3 }).then(
|
|
75955
|
+
(list) => list.some((p3) => p3.personaId === personaId2),
|
|
75956
|
+
() => void 0
|
|
75957
|
+
),
|
|
75629
75958
|
startScan: (personaId2, scanBase, scanId) => {
|
|
75630
75959
|
const baseLines = scanBase.base ? [
|
|
75631
75960
|
`\u672C\u6B21\u662F**${SCAN_BASE_LABEL[scanBase.kind]}**\uFF1A\u53EA\u770B\u8DDF\u7EBF\u4E0A\u6B63\u8DD1\u90A3\u7248\u7684\u5DEE\u522B\uFF0C`,
|
|
@@ -75757,13 +76086,19 @@ async function startDaemon(config) {
|
|
|
75757
76086
|
const viewerAssetLoader = tryLoadViewerAssets(logger);
|
|
75758
76087
|
const shareUiAssetLoader = tryLoadShareUi(logger);
|
|
75759
76088
|
const dashboardPages = loadDashboardPages(logger);
|
|
76089
|
+
const dashboardApi = () => config.clawosApi ?? DEFAULT_CLAWOS_API;
|
|
75760
76090
|
const dashboardRoutes = createDashboardRoutes({
|
|
75761
76091
|
getSecret: () => authFile.token,
|
|
75762
76092
|
loadPageHtml: (page) => dashboardPages.get(page) ?? null,
|
|
75763
|
-
|
|
75764
|
-
|
|
75765
|
-
|
|
75766
|
-
|
|
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
|
+
})
|
|
75767
76102
|
});
|
|
75768
76103
|
const httpRouter = createHttpRouter({
|
|
75769
76104
|
authResolver,
|