@clawos-dev/clawd 0.2.553 → 0.2.555
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 +84 -55
- package/dist/cli.cjs +351 -272
- package/dist/dispatch/mcp-server.cjs +76 -49
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/mcp-server.cjs +84 -55
- package/dist/persona-defaults/persona-clawd-butler/.claude/skills/scan-persona-for-deploy/SKILL.md +26 -42
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/mcp-server.cjs +84 -55
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-CqERmFni.js → guest-CgIJexKE.js} +133 -133
- package/dist/share-ui/assets/guest-CyrcJulJ.css +32 -0
- package/dist/share-ui/guest.html +2 -2
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-BHSs9s97.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -5989,13 +5989,13 @@ function sharedConnectorSecrets(m3) {
|
|
|
5989
5989
|
function requiredSecretEnvNames(m3) {
|
|
5990
5990
|
return [.../* @__PURE__ */ new Set([...m3.secrets.flatMap((s2) => s2.env), ...sharedConnectorSecrets(m3).flatMap((s2) => s2.env)])];
|
|
5991
5991
|
}
|
|
5992
|
-
var
|
|
5992
|
+
var EnvPackageSchema, EnvSecretSchema, ConnectorNameSchema, CONNECTOR_KINDS, CONNECTOR_RUNNERS, CONNECTOR_PROTOCOLS, CREDENTIAL_SOURCES, CONNECTOR_AUTHS, ConnectorFieldSchema, EnvConnectorSchema, normalizeLegacyManifest, MANIFEST_BODY, EnvManifestBodySchema, EnvironmentManifestSchema, EnvEvidenceSchema;
|
|
5993
5993
|
var init_environment_schemas = __esm({
|
|
5994
5994
|
"../protocol/src/environment-schemas.ts"() {
|
|
5995
5995
|
"use strict";
|
|
5996
5996
|
init_zod();
|
|
5997
|
-
|
|
5998
|
-
//
|
|
5997
|
+
EnvPackageSchema = external_exports.object({
|
|
5998
|
+
// 装完后在 PATH 里能直接调到的名字('aliyun' / 's' / 'pnpm' / 'python3');npm 包报 bin 名,包名进 from
|
|
5999
5999
|
name: external_exports.string().min(1),
|
|
6000
6000
|
/**
|
|
6001
6001
|
* 从哪装,约定 `<kind>:<ref>`:`npm:@serverless-devs/s` / `github:aliyun/aliyun-cli` /
|
|
@@ -6028,9 +6028,6 @@ var init_environment_schemas = __esm({
|
|
|
6028
6028
|
// supabase 是 url/anon/service-role/jwt 四个),所以是数组不是单值。
|
|
6029
6029
|
env: external_exports.array(external_exports.string().min(1)).min(1)
|
|
6030
6030
|
});
|
|
6031
|
-
EnvAssetSchema = external_exports.object({
|
|
6032
|
-
name: external_exports.string().min(1)
|
|
6033
|
-
});
|
|
6034
6031
|
ConnectorNameSchema = external_exports.string().regex(/^[a-z0-9-]+$/, "\u8FDE\u63A5\u5668\u540D\u53EA\u80FD\u662F\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u77ED\u6A2A\u7EBF");
|
|
6035
6032
|
CONNECTOR_KINDS = ["proxy", "env"];
|
|
6036
6033
|
CONNECTOR_RUNNERS = ["mcp", "cli"];
|
|
@@ -6049,10 +6046,23 @@ var init_environment_schemas = __esm({
|
|
|
6049
6046
|
fields: external_exports.array(ConnectorFieldSchema).default([]),
|
|
6050
6047
|
http: external_exports.object({ url: external_exports.string().min(1), headers: external_exports.record(external_exports.string(), external_exports.string()) }).optional()
|
|
6051
6048
|
});
|
|
6049
|
+
normalizeLegacyManifest = (v3) => {
|
|
6050
|
+
if (!v3 || typeof v3 !== "object") return v3;
|
|
6051
|
+
let o2 = v3;
|
|
6052
|
+
if ("clis" in o2 && Array.isArray(o2.clis)) {
|
|
6053
|
+
const { clis, ...rest } = o2;
|
|
6054
|
+
o2 = "packages" in rest ? rest : { ...rest, packages: clis };
|
|
6055
|
+
}
|
|
6056
|
+
if ("assets" in o2 && Array.isArray(o2.assets) && o2.assets.length === 0) {
|
|
6057
|
+
const { assets: _dropped, ...rest } = o2;
|
|
6058
|
+
o2 = rest;
|
|
6059
|
+
}
|
|
6060
|
+
return o2;
|
|
6061
|
+
};
|
|
6052
6062
|
MANIFEST_BODY = {
|
|
6053
|
-
|
|
6063
|
+
// 构镜像时要装进去的所有东西(工具 / 运行时 / 包)。底座已有的构建器会跳过;persona 目录整个进镜像,不在这里列
|
|
6064
|
+
packages: external_exports.array(EnvPackageSchema),
|
|
6054
6065
|
secrets: external_exports.array(EnvSecretSchema),
|
|
6055
|
-
assets: external_exports.array(EnvAssetSchema),
|
|
6056
6066
|
/**
|
|
6057
6067
|
* 装了哪些连接器(design 2026-09-11-mcp-connector §3.1)。一条一个对象,形状见 `EnvConnectorSchema`。
|
|
6058
6068
|
*
|
|
@@ -6064,14 +6074,14 @@ var init_environment_schemas = __esm({
|
|
|
6064
6074
|
connectors: external_exports.array(EnvConnectorSchema).default([])
|
|
6065
6075
|
};
|
|
6066
6076
|
EnvManifestBodySchema = external_exports.object(MANIFEST_BODY);
|
|
6067
|
-
EnvironmentManifestSchema = external_exports.object({
|
|
6077
|
+
EnvironmentManifestSchema = external_exports.preprocess(normalizeLegacyManifest, external_exports.object({
|
|
6068
6078
|
// 格式版本。manifest 要跨进程(daemon 扫 → 镜像构建器读)也跨时间(部署记录留档),
|
|
6069
6079
|
// 加字段兼容但改语义不兼容,留一个显式版本位。
|
|
6070
6080
|
version: external_exports.literal(1),
|
|
6071
6081
|
...MANIFEST_BODY
|
|
6072
|
-
}).strict();
|
|
6082
|
+
}).strict());
|
|
6073
6083
|
EnvEvidenceSchema = external_exports.object({
|
|
6074
|
-
// 这条证据支持的是哪一项:`${kind}:${name}`,如 '
|
|
6084
|
+
// 这条证据支持的是哪一项:`${kind}:${name}`,如 'package:aliyun' / 'secret:aliyun-ak' / 'connector:feishu'
|
|
6075
6085
|
target: external_exports.string().min(1),
|
|
6076
6086
|
// 一句人话:为什么需要它 + 从哪看出来的。单据默认显示这句。
|
|
6077
6087
|
reason: external_exports.string().min(1),
|
|
@@ -6087,10 +6097,13 @@ var init_environment_schemas = __esm({
|
|
|
6087
6097
|
});
|
|
6088
6098
|
|
|
6089
6099
|
// ../protocol/src/deploy-schemas.ts
|
|
6090
|
-
function
|
|
6100
|
+
function contentHeadOf(repo2, released) {
|
|
6101
|
+
return released && repo2.head === released.head ? released.contentHead : repo2.head;
|
|
6102
|
+
}
|
|
6103
|
+
function isScanFresh(scan, repo2, released) {
|
|
6091
6104
|
if (!scan || !repo2) return false;
|
|
6092
6105
|
if (scan.status !== "done") return false;
|
|
6093
|
-
return scan.head === repo2
|
|
6106
|
+
return scan.head === contentHeadOf(repo2, released) && !repo2.dirty;
|
|
6094
6107
|
}
|
|
6095
6108
|
function cloudEngineOk(tool) {
|
|
6096
6109
|
return (tool || "claude") === "codex";
|
|
@@ -6098,7 +6111,7 @@ function cloudEngineOk(tool) {
|
|
|
6098
6111
|
function repoSlug(url) {
|
|
6099
6112
|
return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
|
|
6100
6113
|
}
|
|
6101
|
-
var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
|
|
6114
|
+
var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
|
|
6102
6115
|
var init_deploy_schemas = __esm({
|
|
6103
6116
|
"../protocol/src/deploy-schemas.ts"() {
|
|
6104
6117
|
"use strict";
|
|
@@ -6113,33 +6126,30 @@ var init_deploy_schemas = __esm({
|
|
|
6113
6126
|
ScanBaseKindSchema = external_exports.enum(SCAN_BASE_KINDS);
|
|
6114
6127
|
DeployScanStatusSchema = external_exports.enum(["scanning", "done", "failed"]);
|
|
6115
6128
|
DeployScanSchema = external_exports.object({
|
|
6129
|
+
/** 历史里指名用(发布 / patch / putSecret 都按它找)。daemon 生成;存量单条记录迁过来的叫 `legacy` */
|
|
6130
|
+
id: external_exports.string().min(1),
|
|
6131
|
+
/**
|
|
6132
|
+
* 这次扫描的清单——原来在 `deploy.json` 里的那份,现在每次扫描各存一份。
|
|
6133
|
+
* 扫描期由 `deploy:reportFinding` 逐条长出来,扫完人在页面上改(`deploy:patch`)也改这份;
|
|
6134
|
+
* 发布时选中的那条被写成 `deploy.json` 进仓库。
|
|
6135
|
+
*/
|
|
6136
|
+
manifest: EnvironmentManifestSchema,
|
|
6116
6137
|
/** 干这次扫描的 butler 会话,「看它怎么干的」入口 */
|
|
6117
6138
|
scanSessionId: external_exports.string().optional(),
|
|
6118
6139
|
status: DeployScanStatusSchema,
|
|
6119
6140
|
evidence: external_exports.array(EnvEvidenceSchema).default([]),
|
|
6120
6141
|
notes: external_exports.array(external_exports.string()).default([]),
|
|
6121
|
-
/** 扫描时被告知的共享资产名单(判「扫描器后来长了新本事」用) */
|
|
6122
|
-
scannedAssets: external_exports.array(external_exports.string()).default([]),
|
|
6123
6142
|
/**
|
|
6124
|
-
*
|
|
6125
|
-
*
|
|
6143
|
+
* 这次扫描是**对哪个内容版本的断言**:起扫描前先把 persona 目录提交到本地(只提交不推,推是发布的事),
|
|
6144
|
+
* 落存那一下的 HEAD 就是它——发布 commit 不算内容版本,见 `contentHeadOf`。所以这个值一定是仓库里真实存在的一版,
|
|
6145
|
+
* 同一内容上扫几次 `head` 都相同。**从此不动**:发布不再把它挪成发布 commit(线上是哪版记在 `DeployReleased` 里)。
|
|
6126
6146
|
*
|
|
6127
|
-
* 有了它,「清单过没过时」才是个可判定的等式(`head === repo
|
|
6147
|
+
* 有了它,「清单过没过时」才是个可判定的等式(`head === contentHeadOf(repo, released)`),
|
|
6128
6148
|
* 而不是从 `dirty` / `ahead` 里猜——那两个字段判不出「清单是好几版之前扫的」。
|
|
6129
6149
|
*
|
|
6130
6150
|
* 缺席 = 旧版 clawd 扫的记录。**一律当过时**:没有第三种情况,重扫一次就有了。
|
|
6131
6151
|
*/
|
|
6132
6152
|
head: external_exports.string().min(1).optional(),
|
|
6133
|
-
/**
|
|
6134
|
-
* 上次**发布**推的那个 commit ——「线上正跑的是哪一版」在本机唯一的答案,
|
|
6135
|
-
* 也就是软扫的基准。只有 `deploy:release` 写它,扫描不动。
|
|
6136
|
-
*
|
|
6137
|
-
* 为什么不能用 `head` 顶替:`head` 被扫描和发布轮流覆盖(扫描前落存一版、发布后又挪一版),
|
|
6138
|
-
* 它回答的是「清单绑在哪个 commit 上」,回答不了「上次发布是哪一版」。
|
|
6139
|
-
*
|
|
6140
|
-
* 缺席 = 这个 persona 还从没发布过,没有线上版本可比,软扫退硬扫。
|
|
6141
|
-
*/
|
|
6142
|
-
releasedHead: external_exports.string().min(1).optional(),
|
|
6143
6153
|
/**
|
|
6144
6154
|
* 这次**实际**怎么扫的。档位由 daemon 定(有线上版本可比 + 有跑完的清单 → 软扫,否则硬扫),
|
|
6145
6155
|
* 页面要显示的是真正发生的那个。
|
|
@@ -6148,12 +6158,23 @@ var init_deploy_schemas = __esm({
|
|
|
6148
6158
|
* 定成必填会让 `readScan` 当场抛、上云页面直接打不开。而老记录确实都是从零扫的。
|
|
6149
6159
|
*/
|
|
6150
6160
|
baseKind: ScanBaseKindSchema.default("hard"),
|
|
6151
|
-
/** 软扫比的那个 commit(= `
|
|
6161
|
+
/** 软扫比的那个 commit(= 当时 `DeployReleased.head`)。硬扫时缺席 */
|
|
6152
6162
|
base: external_exports.string().min(1).optional(),
|
|
6153
6163
|
startedAt: external_exports.number(),
|
|
6154
6164
|
finishedAt: external_exports.number().optional(),
|
|
6155
6165
|
error: external_exports.string().optional()
|
|
6156
6166
|
}).strict();
|
|
6167
|
+
DeployReleasedSchema = external_exports.object({
|
|
6168
|
+
head: external_exports.string().min(1),
|
|
6169
|
+
contentHead: external_exports.string().min(1),
|
|
6170
|
+
scanId: external_exports.string().min(1),
|
|
6171
|
+
at: external_exports.number()
|
|
6172
|
+
}).strict();
|
|
6173
|
+
DeployScanFileSchema = external_exports.object({
|
|
6174
|
+
version: external_exports.literal(2),
|
|
6175
|
+
scans: external_exports.array(DeployScanSchema),
|
|
6176
|
+
released: DeployReleasedSchema.optional()
|
|
6177
|
+
}).strict();
|
|
6157
6178
|
DeployRepoSchema = external_exports.object({
|
|
6158
6179
|
url: external_exports.string().min(1),
|
|
6159
6180
|
branch: external_exports.string().min(1),
|
|
@@ -6202,8 +6223,10 @@ var init_deploy_schemas = __esm({
|
|
|
6202
6223
|
/** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
|
|
6203
6224
|
github: GithubAuthStatusSchema.optional(),
|
|
6204
6225
|
repo: DeployRepoSchema.optional(),
|
|
6205
|
-
|
|
6206
|
-
|
|
6226
|
+
/** 扫描历史,升序,最后一条最新。没扫过就是空数组 */
|
|
6227
|
+
scans: external_exports.array(DeployScanSchema).default([]),
|
|
6228
|
+
/** 线上正跑的是哪一版;从没发布过就缺席 */
|
|
6229
|
+
released: DeployReleasedSchema.optional(),
|
|
6207
6230
|
/** 没仓库时缺席(查不了) */
|
|
6208
6231
|
secrets: external_exports.array(DeploySecretPresenceSchema).optional(),
|
|
6209
6232
|
/**
|
|
@@ -6212,27 +6235,28 @@ var init_deploy_schemas = __esm({
|
|
|
6212
6235
|
*/
|
|
6213
6236
|
run: DeployRunSchema.optional(),
|
|
6214
6237
|
/**
|
|
6215
|
-
* `.clawd/deploy.json`
|
|
6216
|
-
*
|
|
6238
|
+
* `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
|
|
6239
|
+
* 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
|
|
6217
6240
|
*/
|
|
6218
|
-
|
|
6241
|
+
historyError: external_exports.string().optional()
|
|
6242
|
+
}).strict();
|
|
6243
|
+
DeployReleaseResultSchema = external_exports.object({
|
|
6244
|
+
sha: external_exports.string().min(1),
|
|
6245
|
+
actionsUrl: external_exports.string().min(1)
|
|
6219
6246
|
}).strict();
|
|
6220
|
-
DeployReleaseResultSchema = external_exports.discriminatedUnion("outcome", [
|
|
6221
|
-
external_exports.object({
|
|
6222
|
-
outcome: external_exports.literal("pushed"),
|
|
6223
|
-
sha: external_exports.string().min(1),
|
|
6224
|
-
actionsUrl: external_exports.string().min(1)
|
|
6225
|
-
}).strict(),
|
|
6226
|
-
external_exports.object({ outcome: external_exports.literal("scanning") }).strict()
|
|
6227
|
-
]);
|
|
6228
6247
|
DeployPersonaArgsSchema = external_exports.object({
|
|
6229
6248
|
personaId: external_exports.string().min(1)
|
|
6230
6249
|
}).strict();
|
|
6231
|
-
|
|
6250
|
+
DeployScanArgsSchema = DeployPersonaArgsSchema.extend({
|
|
6232
6251
|
scanBase: ScanBaseKindSchema.optional()
|
|
6233
6252
|
}).strict();
|
|
6253
|
+
DeployReleaseArgsSchema = DeployPersonaArgsSchema.extend({
|
|
6254
|
+
scanId: external_exports.string().min(1)
|
|
6255
|
+
}).strict();
|
|
6234
6256
|
DeployPatchArgsSchema = external_exports.object({
|
|
6235
6257
|
personaId: external_exports.string().min(1),
|
|
6258
|
+
/** 改的是历史里哪次扫描的清单。扫描期 butler 删条目也走这条(scanId 在它的任务提示里) */
|
|
6259
|
+
scanId: external_exports.string().min(1),
|
|
6236
6260
|
patch: external_exports.object({
|
|
6237
6261
|
// 人工补依赖;整份替换 manifest
|
|
6238
6262
|
manifest: EnvironmentManifestSchema.optional()
|
|
@@ -6240,6 +6264,8 @@ var init_deploy_schemas = __esm({
|
|
|
6240
6264
|
}).strict();
|
|
6241
6265
|
DeployPutSecretArgsSchema = external_exports.object({
|
|
6242
6266
|
personaId: external_exports.string().min(1),
|
|
6267
|
+
/** 按哪次扫描的清单校验凭据名 / env 名。值本身是仓库级的(GitHub Secrets),跟扫描无关 */
|
|
6268
|
+
scanId: external_exports.string().min(1),
|
|
6243
6269
|
// 对应 manifest.secrets[].name
|
|
6244
6270
|
name: external_exports.string().min(1),
|
|
6245
6271
|
// 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
|
|
@@ -6252,7 +6278,7 @@ var init_deploy_schemas = __esm({
|
|
|
6252
6278
|
}).strict();
|
|
6253
6279
|
DeployReportFindingArgsSchema = external_exports.object({
|
|
6254
6280
|
personaId: external_exports.string().min(1),
|
|
6255
|
-
kind: external_exports.enum(["
|
|
6281
|
+
kind: external_exports.enum(["package", "secret", "connector"]),
|
|
6256
6282
|
// 该条目在 manifest 对应数组里的内容(形状随 kind 变,handler 按 kind 收窄校验)
|
|
6257
6283
|
// connector 的 item 是 butler 报的 CLI 连接器({ name, fields? }),daemon 补齐 kind / runner / id 后落进清单;
|
|
6258
6284
|
// MCP 连接器不经这里——由 daemon 从 .mcp.json 机械列出(design 2026-09-11-mcp-connector §3.2)
|
|
@@ -7578,7 +7604,7 @@ var init_persona_schemas = __esm({
|
|
|
7578
7604
|
"../protocol/src/persona-schemas.ts"() {
|
|
7579
7605
|
"use strict";
|
|
7580
7606
|
init_zod();
|
|
7581
|
-
PERSONA_SIZES = ["small", "medium", "large"];
|
|
7607
|
+
PERSONA_SIZES = ["tiny", "small", "medium", "large"];
|
|
7582
7608
|
PersonaSizeSchema = external_exports.enum(PERSONA_SIZES);
|
|
7583
7609
|
PersonaFileSchema = external_exports.object({
|
|
7584
7610
|
personaId: external_exports.string(),
|
|
@@ -7596,7 +7622,7 @@ var init_persona_schemas = __esm({
|
|
|
7596
7622
|
// 与 model 平行:persona 新建 session 的默认 tool,可平滑切换。
|
|
7597
7623
|
tool: external_exports.string().optional(),
|
|
7598
7624
|
/**
|
|
7599
|
-
* 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`small` / `medium` / `large`,没写按 medium。
|
|
7625
|
+
* 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`tiny` / `small` / `medium` / `large`,没写按 medium。
|
|
7600
7626
|
* 只有发布上云的流水线读它(cloud-host/pipeline/deploy-file.mjs 收窄值域,不认识的拒发);本机跑不看。
|
|
7601
7627
|
* 协议层同 model / effort 保持 string(文件层向前兼容);改它的入口是上云页标题行的规格下拉
|
|
7602
7628
|
* (design 2026-09-20-persona-size-ui),`persona:update` 那头按 PersonaSizeSchema 收窄。
|
|
@@ -7713,7 +7739,7 @@ var init_persona_schemas = __esm({
|
|
|
7713
7739
|
model: external_exports.string().optional(),
|
|
7714
7740
|
effort: external_exports.string().optional(),
|
|
7715
7741
|
tool: external_exports.string().optional(),
|
|
7716
|
-
// 云上任务 Pod
|
|
7742
|
+
// 云上任务 Pod 的规格档:只收四档之一(PersonaFile 层是 string,边界在这里收窄)
|
|
7717
7743
|
size: PersonaSizeSchema.optional(),
|
|
7718
7744
|
personality: external_exports.string().optional(),
|
|
7719
7745
|
// 人格补充层正文(见 PersonaInfoResponseSchema.personalityLocal)。与 personality 各写
|
|
@@ -8987,15 +9013,19 @@ var init_methods = __esm({
|
|
|
8987
9013
|
},
|
|
8988
9014
|
// ---- deploy:*(persona 上云:persona 目录 = git 仓库工作树,发布 = 推送触发仓库流水线)----
|
|
8989
9015
|
"deploy:get": {
|
|
8990
|
-
summary: "\u4E0A\u4E91\u9875\u9762\u8981\u7684\u5168\u90E8\uFF1A\u4ED3\u5E93\u7ED1\u5B9A\u72B6\u6001\uFF08git \u73B0\u7B97\uFF09\u3001\
|
|
9016
|
+
summary: "\u4E0A\u4E91\u9875\u9762\u8981\u7684\u5168\u90E8\uFF1A\u4ED3\u5E93\u7ED1\u5B9A\u72B6\u6001\uFF08git \u73B0\u7B97\uFF09\u3001\u626B\u63CF\u5386\u53F2\uFF08\u6BCF\u6761\u5E26\u6E05\u5355\u5FEB\u7167\uFF09\u3001\u7EBF\u4E0A\u6B63\u8DD1\u7684\u662F\u54EA\u4E00\u7248\u3001\u4ED3\u5E93 Secrets \u91CC\u5404\u53D8\u91CF\u540D\u586B\u8FC7\u6CA1\u6709\uFF08**\u53EA\u6709\u540D\u5B57\u548C\u65F6\u95F4\uFF0C\u6CA1\u6709\u503C**\uFF09\uFF1Bowner-only",
|
|
8991
9017
|
args: DeployPersonaArgsSchema
|
|
8992
9018
|
},
|
|
9019
|
+
"deploy:scan": {
|
|
9020
|
+
summary: "\u8D77\u4E00\u6B21\u626B\u63CF\u3002**\u6CA1\u4ED3\u5E93\u5148\u5EFA\u4ED3**\uFF08GitHub REST \u5EFA\u5728\u7EC4\u7EC7\u4E0B\u3001\u79C1\u6709\uFF0Cgit \u63A8\u9996\u4E2A\u63D0\u4EA4\uFF1Btoken \u6765\u81EA\u672C\u673A git \u7684\u8EAB\u4EFD\uFF0C\u6CA1\u6388\u6743\u5148\u62D2\uFF09\u3002\u628A persona \u76EE\u5F55\u63D0\u4EA4\u5230\u672C\u5730\uFF08\u4E0D\u63A8\uFF09\u3001\u5728\u626B\u63CF\u5386\u53F2\u91CC\u8FFD\u52A0\u4E00\u6761\u3001\u8D77 persona-butler \u7684\u626B\u63CF\u4F1A\u8BDD\uFF1B\u6B63\u5728\u626B\u7684\u8BDD\u539F\u6837\u8FD4\u56DE\u4E0D\u8D77\u7B2C\u4E8C\u4E2A\u4F1A\u8BDD\u3002\u5E26 scanBase\uFF08hard / soft\uFF09\u6309\u8BE5\u6863\u4F4D\u626B\uFF08soft \u4E0D\u6210\u7ACB\u81EA\u52A8\u9000 hard\uFF09\uFF0C\u7F3A\u5E2D daemon \u81EA\u52A8\u5B9A\uFF1Bowner-only",
|
|
9021
|
+
args: DeployScanArgsSchema
|
|
9022
|
+
},
|
|
8993
9023
|
"deploy:patch": {
|
|
8994
|
-
summary: "\u7F16\u8F91\
|
|
9024
|
+
summary: "\u7F16\u8F91\u5386\u53F2\u91CC\u67D0\u6B21\u626B\u63CF\u7684\u6E05\u5355\uFF08scanId \u6307\u540D\uFF09\uFF1A\u624B\u5DE5\u8865 / \u5220\u4F9D\u8D56\uFF0C\u626B\u63CF\u671F butler \u5220\u6761\u76EE\u4E5F\u8D70\u8FD9\u6761\u3002**\u4E0D\u80FD\u4ECE\u8FD9\u91CC\u4F20\u51ED\u636E\u7684\u503C**\uFF08\u503C\u8D70 deploy:putSecret\uFF09\uFF1Bowner-only",
|
|
8995
9025
|
args: DeployPatchArgsSchema
|
|
8996
9026
|
},
|
|
8997
9027
|
"deploy:putSecret": {
|
|
8998
|
-
summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F\
|
|
9028
|
+
summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F scanId \u90A3\u6B21\u6E05\u5355\u91CC\u8BE5\u51ED\u636E\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
|
|
8999
9029
|
args: DeployPutSecretArgsSchema
|
|
9000
9030
|
},
|
|
9001
9031
|
"deploy:reportFinding": {
|
|
@@ -9007,11 +9037,11 @@ var init_methods = __esm({
|
|
|
9007
9037
|
args: DeployFinishScanArgsSchema
|
|
9008
9038
|
},
|
|
9009
9039
|
"deploy:release": {
|
|
9010
|
-
summary: "\
|
|
9040
|
+
summary: "\u628A\u5386\u53F2\u91CC scanId \u90A3\u6B21\u626B\u63CF\u7684\u6E05\u5355\u53D1\u4E0A\u53BB\u3002\u90A3\u6B21\u5FC5\u987B\u626B\u5B8C\u3001\u4E14\u5BF9\u5E94\u5F53\u524D\u5185\u5BB9\u7248\u672C\uFF08persona \u4E4B\u540E\u6CA1\u6539\u8FC7\uFF09\uFF0C\u5426\u5219\u62D2\u2014\u2014\u5148 deploy:scan\u3002\u8FC7\u95F8\uFF08\u6BCF\u4E2A\u53D8\u91CF\u540D\u90FD\u5728 Secrets \u91CC\u3001\u8FDE\u63A5\u5668\u6765\u6E90\u70B9\u8FC7\u3001\u9009\u4E86\u6A21\u578B\uFF09\u2192 \u628A\u90A3\u4EFD\u6E05\u5355\u5199\u6210 .clawd/deploy.json \u2192 \u91CD\u5199 workflow \u6A21\u677F \u2192 git add -A \u2192 \u7A7A\u63D0\u4EA4\u4E5F\u5141\u8BB8 \u2192 push\uFF0C\u56DE commit \u4E0E Actions \u5730\u5740\uFF1B\u4ED3\u5E93\u91CC\u7684\u6D41\u6C34\u7EBF\u63A5\u7740\u6784\u955C\u50CF\u3001\u8BD5\u8DD1\u3001\u767B\u8BB0\u3002\u540C\u4E00\u7248\u53EF\u4EE5\u53CD\u590D\u53D1\uFF08\u7528\u540C\u4E00\u4EFD\u6E05\u5355\u518D\u8D70\u4E00\u904D\uFF09\uFF1Bowner-only",
|
|
9011
9041
|
args: DeployReleaseArgsSchema
|
|
9012
9042
|
},
|
|
9013
9043
|
"deploy:rerunJob": {
|
|
9014
|
-
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\
|
|
9044
|
+
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",
|
|
9015
9045
|
args: DeployRerunJobArgsSchema
|
|
9016
9046
|
},
|
|
9017
9047
|
// ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
|
|
@@ -32802,7 +32832,7 @@ var require_websocket = __commonJS({
|
|
|
32802
32832
|
var http2 = require("http");
|
|
32803
32833
|
var net2 = require("net");
|
|
32804
32834
|
var tls = require("tls");
|
|
32805
|
-
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
32835
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = require("crypto");
|
|
32806
32836
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
32807
32837
|
var { URL: URL2 } = require("url");
|
|
32808
32838
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -33332,7 +33362,7 @@ var require_websocket = __commonJS({
|
|
|
33332
33362
|
}
|
|
33333
33363
|
}
|
|
33334
33364
|
const defaultPort = isSecure ? 443 : 80;
|
|
33335
|
-
const key =
|
|
33365
|
+
const key = randomBytes2(16).toString("base64");
|
|
33336
33366
|
const request = isSecure ? https.request : http2.request;
|
|
33337
33367
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
33338
33368
|
let perMessageDeflate;
|
|
@@ -44495,7 +44525,7 @@ Env (advanced):
|
|
|
44495
44525
|
var import_node_path78 = __toESM(require("path"), 1);
|
|
44496
44526
|
var import_node_fs60 = __toESM(require("fs"), 1);
|
|
44497
44527
|
var import_node_os28 = __toESM(require("os"), 1);
|
|
44498
|
-
var
|
|
44528
|
+
var import_node_crypto23 = require("crypto");
|
|
44499
44529
|
var import_node_url5 = require("url");
|
|
44500
44530
|
|
|
44501
44531
|
// src/logger.ts
|
|
@@ -55306,6 +55336,7 @@ function redactEvidence(evidence) {
|
|
|
55306
55336
|
// src/deploy/deploy-file.ts
|
|
55307
55337
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
55308
55338
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
55339
|
+
var import_node_crypto8 = require("crypto");
|
|
55309
55340
|
init_protocol();
|
|
55310
55341
|
|
|
55311
55342
|
// src/deploy/connectors.ts
|
|
@@ -55495,26 +55526,79 @@ async function readDeployFile(dir) {
|
|
|
55495
55526
|
async function writeDeployFile(dir, file) {
|
|
55496
55527
|
await writeJson(import_node_path33.default.join(dir, DEPLOY_FILE), DeployFileSchema.parse(file));
|
|
55497
55528
|
}
|
|
55498
|
-
async function
|
|
55499
|
-
|
|
55529
|
+
async function readDeployFileRaw(dir) {
|
|
55530
|
+
try {
|
|
55531
|
+
return await import_promises4.default.readFile(import_node_path33.default.join(dir, DEPLOY_FILE), "utf8");
|
|
55532
|
+
} catch (err) {
|
|
55533
|
+
if (err.code === "ENOENT") return void 0;
|
|
55534
|
+
throw err;
|
|
55535
|
+
}
|
|
55536
|
+
}
|
|
55537
|
+
async function restoreDeployFileRaw(dir, raw) {
|
|
55538
|
+
const file = import_node_path33.default.join(dir, DEPLOY_FILE);
|
|
55539
|
+
if (raw === void 0) await import_promises4.default.rm(file, { force: true });
|
|
55540
|
+
else await import_promises4.default.writeFile(file, raw);
|
|
55541
|
+
}
|
|
55542
|
+
var emptyScanFile = () => ({ version: 2, scans: [] });
|
|
55543
|
+
var LEGACY_SCAN_ID = "legacy";
|
|
55544
|
+
var newScanId = () => (0, import_node_crypto8.randomBytes)(4).toString("hex");
|
|
55545
|
+
var z1 = DeployScanSchema.omit({ id: true, manifest: true }).extend({ releasedHead: DeployScanSchema.shape.head }).strict();
|
|
55546
|
+
function migrateV1(v1, manifest) {
|
|
55547
|
+
const { releasedHead, ...rest } = v1;
|
|
55548
|
+
const entry = { ...rest, id: LEGACY_SCAN_ID, manifest: manifest ?? emptyDeployFile().manifest };
|
|
55549
|
+
return {
|
|
55550
|
+
version: 2,
|
|
55551
|
+
scans: [entry],
|
|
55552
|
+
...releasedHead ? { released: { head: releasedHead, contentHead: releasedHead, scanId: LEGACY_SCAN_ID, at: v1.head === releasedHead ? v1.finishedAt ?? v1.startedAt : 0 } } : {}
|
|
55553
|
+
};
|
|
55554
|
+
}
|
|
55555
|
+
function normalizeLegacyScan(raw) {
|
|
55556
|
+
const fix = (o2) => {
|
|
55557
|
+
if (!o2 || typeof o2 !== "object") return o2;
|
|
55558
|
+
const { scannedAssets: _dropped, ...rest } = o2;
|
|
55559
|
+
if (Array.isArray(rest.evidence)) {
|
|
55560
|
+
rest.evidence = rest.evidence.map((e) => {
|
|
55561
|
+
if (!e || typeof e !== "object" || typeof e.target !== "string") return e;
|
|
55562
|
+
const target = e.target;
|
|
55563
|
+
return target.startsWith("cli:") ? { ...e, target: `package:${target.slice("cli:".length)}` } : e;
|
|
55564
|
+
});
|
|
55565
|
+
}
|
|
55566
|
+
return rest;
|
|
55567
|
+
};
|
|
55568
|
+
if (raw && typeof raw === "object" && Array.isArray(raw.scans)) {
|
|
55569
|
+
return { ...raw, scans: raw.scans.map(fix) };
|
|
55570
|
+
}
|
|
55571
|
+
return fix(raw);
|
|
55572
|
+
}
|
|
55573
|
+
async function readScanFile(dir) {
|
|
55574
|
+
const raw = normalizeLegacyScan(await readJson(import_node_path33.default.join(dir, SCAN_FILE)));
|
|
55500
55575
|
if (raw === void 0) return void 0;
|
|
55501
|
-
const
|
|
55502
|
-
|
|
55503
|
-
|
|
55504
|
-
|
|
55576
|
+
const badShape = (issues) => {
|
|
55577
|
+
const first = issues[0];
|
|
55578
|
+
return new Error(`${SCAN_FILE} \u4E0D\u5408\u5F62\u72B6\uFF1A${first ? `${first.path.join(".")} ${first.message}` : ""}`);
|
|
55579
|
+
};
|
|
55580
|
+
if (typeof raw === "object" && raw !== null && !("version" in raw)) {
|
|
55581
|
+
const v1 = z1.safeParse(raw);
|
|
55582
|
+
if (!v1.success) throw badShape(v1.error.issues);
|
|
55583
|
+
return migrateV1(v1.data, (await readDeployFile(dir).catch(() => void 0))?.manifest);
|
|
55505
55584
|
}
|
|
55585
|
+
const parsed = DeployScanFileSchema.safeParse(raw);
|
|
55586
|
+
if (!parsed.success) throw badShape(parsed.error.issues);
|
|
55506
55587
|
return parsed.data;
|
|
55507
55588
|
}
|
|
55508
|
-
async function
|
|
55509
|
-
await writeJson(import_node_path33.default.join(dir, SCAN_FILE),
|
|
55589
|
+
async function writeScanFile(dir, file) {
|
|
55590
|
+
await writeJson(import_node_path33.default.join(dir, SCAN_FILE), DeployScanFileSchema.parse(file));
|
|
55591
|
+
}
|
|
55592
|
+
function replaceScan(file, entry) {
|
|
55593
|
+
const idx = file.scans.findIndex((s2) => s2.id === entry.id);
|
|
55594
|
+
return { ...file, scans: idx === -1 ? [...file.scans, entry] : file.scans.map((s2, i2) => i2 === idx ? entry : s2) };
|
|
55510
55595
|
}
|
|
55511
55596
|
function emptyDeployFile() {
|
|
55512
|
-
return { version: 1, manifest: { version: 1,
|
|
55597
|
+
return { version: 1, manifest: { version: 1, packages: [], secrets: [], connectors: [] } };
|
|
55513
55598
|
}
|
|
55514
55599
|
var FINDING_SCHEMAS = {
|
|
55515
|
-
|
|
55600
|
+
package: EnvPackageSchema,
|
|
55516
55601
|
secret: EnvSecretSchema,
|
|
55517
|
-
asset: EnvAssetSchema,
|
|
55518
55602
|
// butler 报的是 CLI 连接器的「名字 + 它读的环境变量名」,kind / runner / id 由 daemon 补
|
|
55519
55603
|
//(handlers/deploy.ts 的 cliConnectorFromFinding)。MCP 连接器不经上报口,由 scanMcpConnectors 机械列出。
|
|
55520
55604
|
connector: CliConnectorFindingSchema
|
|
@@ -55527,26 +55611,26 @@ function parseFinding(kind, item) {
|
|
|
55527
55611
|
}
|
|
55528
55612
|
return parsed.data;
|
|
55529
55613
|
}
|
|
55530
|
-
function upsertFinding(
|
|
55614
|
+
function upsertFinding(manifest, kind, item) {
|
|
55531
55615
|
if (kind === "connector") {
|
|
55532
55616
|
const incoming = item;
|
|
55533
|
-
const have =
|
|
55617
|
+
const have = manifest.connectors;
|
|
55534
55618
|
const idx2 = have.findIndex((c2) => c2.name === incoming.name);
|
|
55535
|
-
if (idx2 !== -1 && have[idx2].runner === "mcp" && incoming.runner === "cli") return
|
|
55619
|
+
if (idx2 !== -1 && have[idx2].runner === "mcp" && incoming.runner === "cli") return manifest;
|
|
55536
55620
|
const merged = mergeConnector(idx2 === -1 ? void 0 : have[idx2], incoming);
|
|
55537
55621
|
const connectors = idx2 === -1 ? [...have, merged] : have.map((c2, i2) => i2 === idx2 ? merged : c2);
|
|
55538
|
-
return { ...
|
|
55622
|
+
return { ...manifest, connectors };
|
|
55539
55623
|
}
|
|
55540
|
-
const key = kind === "
|
|
55541
|
-
const list = [...
|
|
55624
|
+
const key = kind === "package" ? "packages" : "secrets";
|
|
55625
|
+
const list = [...manifest[key]];
|
|
55542
55626
|
const idx = list.findIndex((x2) => x2.name === item.name);
|
|
55543
55627
|
if (idx === -1) list.push(item);
|
|
55544
55628
|
else list[idx] = item;
|
|
55545
|
-
return { ...
|
|
55629
|
+
return { ...manifest, [key]: list };
|
|
55546
55630
|
}
|
|
55547
|
-
function requiredEnvNames(
|
|
55548
|
-
if (!
|
|
55549
|
-
return requiredSecretEnvNames(
|
|
55631
|
+
function requiredEnvNames(manifest) {
|
|
55632
|
+
if (!manifest) return [];
|
|
55633
|
+
return requiredSecretEnvNames(manifest);
|
|
55550
55634
|
}
|
|
55551
55635
|
|
|
55552
55636
|
// src/deploy/repo.ts
|
|
@@ -59313,20 +59397,20 @@ var L2 = [
|
|
|
59313
59397
|
" --persona-id '${{ steps.plan.outputs.persona_id }}' \\",
|
|
59314
59398
|
' --tag "$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
|
|
59315
59399
|
"",
|
|
59316
|
-
" - name: \u955C\u50CF\u65AD\u8A00\uFF08\u6E05\u5355\u91CC\u7684
|
|
59317
|
-
" # /opt/versions.sh \u6BCF\u884C\u90FD\u4EE5 `|| echo` \u6536\u5C3E\u3001\u6C38\u8FDC exit 0\uFF0C\u53EA\u80FD\u9A8C\u8F93\u51FA\uFF1A\u6BB5\u6570\u8981\u7B49\u4E8E\u6E05\u5355\
|
|
59318
|
-
" #
|
|
59400
|
+
" - name: \u955C\u50CF\u65AD\u8A00\uFF08\u6E05\u5355\u91CC\u8981\u88C5\u7684\u90FD\u5728\u3001bundle \u91CC persona \u5728\uFF09",
|
|
59401
|
+
" # /opt/versions.sh \u6BCF\u884C\u90FD\u4EE5 `|| echo` \u6536\u5C3E\u3001\u6C38\u8FDC exit 0\uFF0C\u53EA\u80FD\u9A8C\u8F93\u51FA\uFF1A\u6BB5\u6570\u8981\u7B49\u4E8E\u6E05\u5355 packages \u7684",
|
|
59402
|
+
" # \u6761\u6570\uFF0C\u6BCF\u6BB5\u4E0B\u9762\u8981\u6709\u7248\u672C\u53F7\u3002\u955C\u50CF ENTRYPOINT \u662F sh -c\uFF0C\u547D\u4EE4\u8981\u4F5C\u4E3A\u4E00\u4E2A\u5B57\u7B26\u4E32\u4F20\u3002",
|
|
59319
59403
|
" run: |",
|
|
59320
59404
|
' IMG="$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
|
|
59321
59405
|
` OUT=$(docker run --rm "$IMG" '/opt/versions.sh')`,
|
|
59322
59406
|
' echo "$OUT"',
|
|
59323
|
-
` WANT=$(node -e 'const m=JSON.parse(require("fs").readFileSync("persona/.clawd/deploy.json","utf8"));console.log((
|
|
59407
|
+
` WANT=$(node -e 'const m=JSON.parse(require("fs").readFileSync("persona/.clawd/deploy.json","utf8"));const b=m.manifest??m;console.log((b.packages??b.clis??[]).length)')`,
|
|
59324
59408
|
` echo "$OUT" | awk -v want="$WANT" '`,
|
|
59325
59409
|
' /^--- / { name=$2; if ((getline v) <= 0) v=""; n++',
|
|
59326
59410
|
' if (v !~ /[0-9]+\\.[0-9]+/) { printf " \u2717 %s \u6CA1\u62A5\u51FA\u7248\u672C\u53F7\uFF1A%s\\n", name, v > "/dev/stderr"; bad++ } }',
|
|
59327
|
-
' END { if (n != want) { printf " \u2717 \u955C\u50CF\u91CC\u53EA\u6709 %d \
|
|
59411
|
+
' END { if (n != want) { printf " \u2717 \u955C\u50CF\u91CC\u53EA\u6709 %d \u6BB5\u7248\u672C\u8F93\u51FA\uFF0C\u6E05\u5355\u8981\u88C5 %d \u4E2A\\n", n, want > "/dev/stderr"; bad++ }',
|
|
59328
59412
|
" exit bad ? 1 : 0 }'",
|
|
59329
|
-
' echo " \u2713 $WANT \u4E2A
|
|
59413
|
+
' echo " \u2713 \u6E05\u5355\u91CC\u8981\u88C5\u7684 $WANT \u4E2A\u90FD\u5728\u4E14\u62A5\u5F97\u51FA\u7248\u672C"',
|
|
59330
59414
|
` docker run --rm "$IMG" 'test -d /clawd/personas/\${{ steps.plan.outputs.persona_id }}' \\`,
|
|
59331
59415
|
' || { echo " \u2717 /clawd/personas/${{ steps.plan.outputs.persona_id }} \u4E0D\u5728" >&2; exit 1; }',
|
|
59332
59416
|
' echo " \u2713 persona \u76EE\u5F55\u5728"',
|
|
@@ -59600,7 +59684,7 @@ async function release(exec, api, args) {
|
|
|
59600
59684
|
await mustGit(exec, args.dir, [...identity, "commit", "--allow-empty", "-m", `clawd: \u53D1\u5E03 ${stamp}`], "\u63D0\u4EA4");
|
|
59601
59685
|
await gitPush(exec, args.dir, args.auth);
|
|
59602
59686
|
const sha = await mustGit(exec, args.dir, ["rev-parse", "HEAD"], "\u8BFB\u63D0\u4EA4");
|
|
59603
|
-
return {
|
|
59687
|
+
return { sha, actionsUrl: `https://github.com/${repoSlug(info.url)}/actions` };
|
|
59604
59688
|
}
|
|
59605
59689
|
|
|
59606
59690
|
// src/deploy/scan-base.ts
|
|
@@ -59679,17 +59763,6 @@ async function rerunJob(api, slug, jobId) {
|
|
|
59679
59763
|
|
|
59680
59764
|
// src/deploy/scan-task.ts
|
|
59681
59765
|
var BUTLER_PERSONA_ID = "persona-clawd-butler";
|
|
59682
|
-
var SCANNABLE_ASSETS = [];
|
|
59683
|
-
function staleScannerAssets(scan, known = SCANNABLE_ASSETS) {
|
|
59684
|
-
const knew = new Set(scan.scannedAssets ?? []);
|
|
59685
|
-
return known.filter((a2) => !knew.has(a2.name));
|
|
59686
|
-
}
|
|
59687
|
-
var STALE_ASSETS_NOTE_PREFIX = "\u3010\u4E0A\u4E91\u626B\u63CF\u3011";
|
|
59688
|
-
function describeStaleAssets(stale) {
|
|
59689
|
-
if (stale.length === 0) return void 0;
|
|
59690
|
-
const plain = (s2) => s2.replace(/\*\*/g, "").replace(/\s+/g, " ").trim();
|
|
59691
|
-
return `${STALE_ASSETS_NOTE_PREFIX}\u8FD9\u6B21\u626B\u63CF\u7684\u65F6\u5019\uFF0C\u626B\u63CF\u5668\u8FD8\u4E0D\u77E5\u9053\u6709${stale.map((a2) => `\u300C${a2.name}\u300D`).join("\u3001")}\u8FD9${stale.length > 1 ? "\u51E0" : ""}\u9879\u5171\u4EAB\u8D44\u4EA7\uFF0C\u6240\u4EE5\u6E05\u5355\u91CC\u53EF\u80FD\u6F0F\u4E86\u58F0\u660E\u3002${stale.map((a2) => ` ${a2.name} \u662F\uFF1A${plain(a2.what)}`).join("")} \u7528\u5F97\u4E0A\u7684\u8BDD\uFF0C\u5728\u6E05\u5355\u91CC\u8865\u4E00\u6761 asset\u3002`;
|
|
59692
|
-
}
|
|
59693
59766
|
function settleEndedScan(outcome, current, now) {
|
|
59694
59767
|
if (current?.status !== "scanning") return void 0;
|
|
59695
59768
|
return {
|
|
@@ -59701,13 +59774,11 @@ function settleEndedScan(outcome, current, now) {
|
|
|
59701
59774
|
}
|
|
59702
59775
|
function reconcileInterruptedScans(deps) {
|
|
59703
59776
|
const flipped = [];
|
|
59704
|
-
for (const { personaId: personaId2,
|
|
59705
|
-
if (
|
|
59777
|
+
for (const { personaId: personaId2, file } of deps.files) {
|
|
59778
|
+
if (!file || !file.scans.some((s2) => s2.status === "scanning")) continue;
|
|
59706
59779
|
deps.write(personaId2, {
|
|
59707
|
-
...
|
|
59708
|
-
status: "failed",
|
|
59709
|
-
finishedAt: deps.now,
|
|
59710
|
-
error: "daemon \u91CD\u542F\uFF0C\u626B\u63CF\u4F1A\u8BDD\u5DF2\u4E2D\u65AD\u2014\u2014\u518D\u70B9\u4E00\u6B21\u300C\u53D1\u5E03\u300D\u4F1A\u91CD\u65B0\u626B\u63CF"
|
|
59780
|
+
...file,
|
|
59781
|
+
scans: file.scans.map((s2) => s2.status === "scanning" ? { ...s2, status: "failed", finishedAt: deps.now, error: "daemon \u91CD\u542F\uFF0C\u626B\u63CF\u4F1A\u8BDD\u5DF2\u4E2D\u65AD\u2014\u2014\u518D\u70B9\u4E00\u6B21\u300C\u91CD\u65B0\u626B\u63CF\u300D" } : s2)
|
|
59711
59782
|
});
|
|
59712
59783
|
flipped.push(personaId2);
|
|
59713
59784
|
}
|
|
@@ -59737,21 +59808,33 @@ function isLiveScan(live, scan) {
|
|
|
59737
59808
|
return live.sessionId === void 0 || live.sessionId === scan.scanSessionId;
|
|
59738
59809
|
}
|
|
59739
59810
|
function buildDeployHandlers(deps) {
|
|
59740
|
-
const scannableAssets = deps.scannableAssets ?? SCANNABLE_ASSETS;
|
|
59741
59811
|
const liveScans = /* @__PURE__ */ new Map();
|
|
59742
|
-
const
|
|
59743
|
-
|
|
59744
|
-
|
|
59812
|
+
const readHistory = async (dir) => await readScanFile(dir) ?? emptyScanFile();
|
|
59813
|
+
const settleStale = async (personaId2, dir, file) => {
|
|
59814
|
+
const live = liveScans.get(personaId2);
|
|
59815
|
+
const stale = file.scans.filter((s2) => s2.status === "scanning" && !isLiveScan(live, s2));
|
|
59816
|
+
if (stale.length === 0) return file;
|
|
59817
|
+
const settled = {
|
|
59818
|
+
...file,
|
|
59819
|
+
scans: file.scans.map((s2) => stale.includes(s2) ? { ...s2, status: "failed", finishedAt: deps.now(), error: "\u626B\u63CF\u8BB0\u5F55\u505C\u5728 scanning\uFF0C\u4F46\u6CA1\u6709\u6B63\u5728\u8DD1\u7684\u626B\u63CF\u4F1A\u8BDD" } : s2)
|
|
59820
|
+
};
|
|
59821
|
+
await writeScanFile(dir, settled);
|
|
59745
59822
|
return settled;
|
|
59746
59823
|
};
|
|
59747
|
-
const mustBeLiveScan = (personaId2,
|
|
59748
|
-
|
|
59749
|
-
|
|
59750
|
-
|
|
59751
|
-
|
|
59752
|
-
|
|
59753
|
-
|
|
59754
|
-
|
|
59824
|
+
const mustBeLiveScan = (personaId2, file, method) => {
|
|
59825
|
+
const scanning = file.scans.filter((s2) => s2.status === "scanning");
|
|
59826
|
+
if (scanning.length === 0) {
|
|
59827
|
+
const last = file.scans[file.scans.length - 1];
|
|
59828
|
+
throw invalid(`${method} \u53EA\u5728\u626B\u63CF\u671F\u53EF\u7528\uFF08\u5F53\u524D ${last?.status ?? "\u6CA1\u6709\u626B\u63CF\u8BB0\u5F55"}\uFF09`);
|
|
59829
|
+
}
|
|
59830
|
+
const live = scanning.find((s2) => isLiveScan(liveScans.get(personaId2), s2));
|
|
59831
|
+
if (!live) throw invalid(`${method} \u53EA\u5728\u626B\u63CF\u671F\u53EF\u7528\uFF08\u8BB0\u5F55\u505C\u5728 scanning\uFF0C\u4F46\u6CA1\u6709\u6B63\u5728\u8DD1\u7684\u626B\u63CF\u4F1A\u8BDD\uFF09`);
|
|
59832
|
+
return live;
|
|
59833
|
+
};
|
|
59834
|
+
const mustScan = (file, scanId) => {
|
|
59835
|
+
const entry = file.scans.find((s2) => s2.id === scanId);
|
|
59836
|
+
if (!entry) throw invalid(`\u626B\u63CF\u5386\u53F2\u91CC\u6CA1\u6709 ${scanId} \u8FD9\u4E00\u6761\uFF0C\u5237\u65B0\u770B\u770B`);
|
|
59837
|
+
return entry;
|
|
59755
59838
|
};
|
|
59756
59839
|
const cacheMs = deps.secretsCacheMs ?? SECRETS_CACHE_MS;
|
|
59757
59840
|
const secretsCache = /* @__PURE__ */ new Map();
|
|
@@ -59781,20 +59864,15 @@ function buildDeployHandlers(deps) {
|
|
|
59781
59864
|
const github = await deps.githubIdentity.status();
|
|
59782
59865
|
const auth = deps.githubIdentity.cached();
|
|
59783
59866
|
const api = auth ? deps.githubApi(auth.token) : void 0;
|
|
59784
|
-
let file;
|
|
59785
|
-
let
|
|
59867
|
+
let file = emptyScanFile();
|
|
59868
|
+
let historyError;
|
|
59786
59869
|
try {
|
|
59787
|
-
file = await
|
|
59870
|
+
file = await readHistory(dir);
|
|
59788
59871
|
} catch (err) {
|
|
59789
|
-
|
|
59790
|
-
}
|
|
59791
|
-
let scan = await readScan(dir);
|
|
59792
|
-
if (scan?.status === "scanning" && !isLiveScan(liveScans.get(personaId2), scan)) scan = await settleStale(dir, scan);
|
|
59793
|
-
if (scan && scan.status === "done") {
|
|
59794
|
-
const stale = describeStaleAssets(staleScannerAssets(scan, scannableAssets));
|
|
59795
|
-
const kept = scan.notes.filter((n2) => !n2.startsWith(STALE_ASSETS_NOTE_PREFIX));
|
|
59796
|
-
scan = { ...scan, notes: stale ? [...kept, stale] : kept };
|
|
59872
|
+
historyError = err instanceof Error ? err.message : String(err);
|
|
59797
59873
|
}
|
|
59874
|
+
file = await settleStale(personaId2, dir, file);
|
|
59875
|
+
const { scans } = file;
|
|
59798
59876
|
let secrets;
|
|
59799
59877
|
let run;
|
|
59800
59878
|
if (repo2 && api && github.status === "authorized") {
|
|
@@ -59815,72 +59893,56 @@ function buildDeployHandlers(deps) {
|
|
|
59815
59893
|
personaId: personaId2,
|
|
59816
59894
|
github,
|
|
59817
59895
|
...repo2 ? { repo: repo2 } : {},
|
|
59818
|
-
|
|
59819
|
-
...
|
|
59896
|
+
scans,
|
|
59897
|
+
...file.released ? { released: file.released } : {},
|
|
59820
59898
|
...secrets ? { secrets } : {},
|
|
59821
59899
|
...run ? { run } : {},
|
|
59822
|
-
...
|
|
59900
|
+
...historyError ? { historyError } : {}
|
|
59823
59901
|
};
|
|
59824
59902
|
}
|
|
59825
59903
|
const view = async (personaId2) => ({
|
|
59826
59904
|
response: { type: "deploy:state", ...await stateOf(personaId2) }
|
|
59827
59905
|
});
|
|
59828
|
-
async function beginScan(personaId2, dir,
|
|
59906
|
+
async function beginScan(personaId2, dir, file, contentHead, want) {
|
|
59829
59907
|
liveScans.set(personaId2, {});
|
|
59830
|
-
const
|
|
59908
|
+
const releasedEntry = file.released ? file.scans.find((s2) => s2.id === file.released.scanId) : void 0;
|
|
59831
59909
|
const scanBase = pickScanBase({
|
|
59832
|
-
//
|
|
59833
|
-
|
|
59834
|
-
|
|
59835
|
-
releasedHead: await hasCommit(deps.exec, dir, current?.releasedHead) ? current?.releasedHead : void 0,
|
|
59910
|
+
// **有记录 ≠ 清单能用**:被发那条要是没跑完(OTA 重启 / butler 崩,见 scan-task.ts 的收尾判定)清单是半截的。
|
|
59911
|
+
hasUsableManifest: releasedEntry?.status === "done",
|
|
59912
|
+
releasedHead: await hasCommit(deps.exec, dir, file.released?.head) ? file.released?.head : void 0,
|
|
59836
59913
|
...want ? { want } : {}
|
|
59837
59914
|
});
|
|
59838
|
-
const
|
|
59839
|
-
const
|
|
59840
|
-
evidence: scanBase.kind === "hard" ? [] : current?.evidence ?? [],
|
|
59841
|
-
// notes 反过来不沿用:它是「这次扫描要老板处理的事」,沿用会把上次的事项一直复读
|
|
59842
|
-
notes: [],
|
|
59843
|
-
scannedAssets: [],
|
|
59844
|
-
startedAt,
|
|
59845
|
-
head,
|
|
59846
|
-
// 发布记的那个 commit 跟扫描无关,原样带过去,别让重扫把软扫的基准弄丢。
|
|
59847
|
-
//
|
|
59848
|
-
// **哪怕上面 hasCommit 刚判定它已经不在仓库里,也照样留着**:探测失败也包括 git 没跑起来
|
|
59849
|
-
// 这种一过性情况,据此把它抹掉就等于「一次 git 抽风 → 永久失去软扫,直到下次发布」,
|
|
59850
|
-
// 比留一个死 sha 糟得多。留着的代价只是 UI 上「从零重扫」多显示一阵子(那期间两个按钮
|
|
59851
|
-
// 都退硬扫、行为一致不出错),下次发布就自愈。
|
|
59852
|
-
...current?.releasedHead ? { releasedHead: current.releasedHead } : {},
|
|
59853
|
-
baseKind: scanBase.kind,
|
|
59854
|
-
...scanBase.base ? { base: scanBase.base } : {}
|
|
59855
|
-
};
|
|
59856
|
-
await writeScan(dir, { status: "scanning", ...base });
|
|
59857
|
-
if (scanBase.kind === "hard") await writeDeployFile(dir, emptyDeployFile());
|
|
59915
|
+
const lastDone = [...file.scans].reverse().find((s2) => s2.status === "done");
|
|
59916
|
+
const seed = scanBase.kind === "soft" && releasedEntry ? releasedEntry.manifest : { ...emptyDeployFile().manifest, connectors: (lastDone?.manifest.connectors ?? []).filter((c2) => c2.runner === "mcp") };
|
|
59858
59917
|
const mcp = scanMcpConnectors(dir);
|
|
59859
|
-
const current0 = await readDeployFile(dir) ?? emptyDeployFile();
|
|
59860
|
-
const carried = scanBase.kind === "hard" ? (prevManifest?.manifest.connectors ?? []).filter((c2) => c2.runner === "mcp") : current0.manifest.connectors;
|
|
59861
|
-
await writeDeployFile(dir, {
|
|
59862
|
-
...current0,
|
|
59863
|
-
// 文件读不出来(坏 JSON)时保持原样:当成「零个 MCP」会把清单里的连接器清光,而发布闸对零连接器是放行的
|
|
59864
|
-
manifest: { ...current0.manifest, connectors: mcp.readable ? mergeScannedConnectors(carried, mcp.connectors) : carried }
|
|
59865
|
-
});
|
|
59866
59918
|
const mcpTargets = new Set(mcp.evidence.map((e) => e.target));
|
|
59867
|
-
const
|
|
59868
|
-
|
|
59869
|
-
|
|
59870
|
-
|
|
59919
|
+
const carriedEvidence = scanBase.kind === "soft" && releasedEntry ? releasedEntry.evidence : [];
|
|
59920
|
+
const entry = {
|
|
59921
|
+
id: newScanId(),
|
|
59922
|
+
status: "scanning",
|
|
59923
|
+
manifest: { ...seed, connectors: mcp.readable ? mergeScannedConnectors(seed.connectors, mcp.connectors) : seed.connectors },
|
|
59924
|
+
evidence: [...carriedEvidence.filter((e) => !mcpTargets.has(e.target)), ...mcp.evidence.map(redactEvidence)],
|
|
59925
|
+
// notes 不沿用:它是「这次扫描要老板处理的事」,沿用会把上次的事项一直复读
|
|
59926
|
+
notes: mcp.warnings.map((w3) => `${MCP_SCAN_NOTE_PREFIX}${w3}`),
|
|
59927
|
+
startedAt: deps.now(),
|
|
59928
|
+
head: contentHead,
|
|
59929
|
+
baseKind: scanBase.kind,
|
|
59930
|
+
...scanBase.base ? { base: scanBase.base } : {}
|
|
59871
59931
|
};
|
|
59872
|
-
|
|
59932
|
+
await writeScanFile(dir, replaceScan(file, entry));
|
|
59933
|
+
const started = deps.startScan(personaId2, scanBase, entry.id);
|
|
59873
59934
|
if ("sessionId" in started) {
|
|
59874
59935
|
liveScans.set(personaId2, { sessionId: started.sessionId });
|
|
59875
|
-
await
|
|
59936
|
+
await writeScanFile(dir, replaceScan(await readHistory(dir), { ...entry, scanSessionId: started.sessionId }));
|
|
59876
59937
|
const mine = () => liveScans.get(personaId2)?.sessionId === started.sessionId;
|
|
59877
59938
|
void started.ended.then(async (outcome) => {
|
|
59878
59939
|
if (mine()) liveScans.delete(personaId2);
|
|
59879
|
-
const
|
|
59880
|
-
|
|
59881
|
-
|
|
59940
|
+
const current = await readHistory(dir).catch(() => void 0);
|
|
59941
|
+
const own = current?.scans.find((s2) => s2.id === entry.id);
|
|
59942
|
+
if (!current || !own || liveScans.has(personaId2) || own.scanSessionId !== started.sessionId) return;
|
|
59943
|
+
const settled = settleEndedScan(outcome, own, deps.now());
|
|
59882
59944
|
if (!settled) return;
|
|
59883
|
-
await
|
|
59945
|
+
await writeScanFile(dir, replaceScan(current, settled));
|
|
59884
59946
|
deps.warn?.("deploy scan ended without finishScan", { personaId: personaId2, why: settled.error });
|
|
59885
59947
|
}).catch((err) => {
|
|
59886
59948
|
if (mine()) liveScans.delete(personaId2);
|
|
@@ -59888,12 +59950,12 @@ function buildDeployHandlers(deps) {
|
|
|
59888
59950
|
});
|
|
59889
59951
|
} else {
|
|
59890
59952
|
liveScans.delete(personaId2);
|
|
59891
|
-
await
|
|
59953
|
+
await writeScanFile(dir, replaceScan(await readHistory(dir), {
|
|
59954
|
+
...entry,
|
|
59892
59955
|
status: "failed",
|
|
59893
|
-
...withMcp,
|
|
59894
59956
|
finishedAt: deps.now(),
|
|
59895
59957
|
error: `\u8D77\u626B\u63CF\u4F1A\u8BDD\u5931\u8D25\uFF1A${started.error}`
|
|
59896
|
-
});
|
|
59958
|
+
}));
|
|
59897
59959
|
}
|
|
59898
59960
|
}
|
|
59899
59961
|
const get = async (frame, _client, ctx) => {
|
|
@@ -59906,20 +59968,21 @@ function buildDeployHandlers(deps) {
|
|
|
59906
59968
|
ensureOwner(ctx);
|
|
59907
59969
|
const args = parseArgs2(DeployPatchArgsSchema, frame, "deploy:patch");
|
|
59908
59970
|
const dir = mustPersona(args.personaId);
|
|
59909
|
-
const file = await
|
|
59910
|
-
const
|
|
59911
|
-
|
|
59912
|
-
|
|
59913
|
-
|
|
59914
|
-
|
|
59971
|
+
const file = await readHistory(dir);
|
|
59972
|
+
const entry = mustScan(file, args.scanId);
|
|
59973
|
+
if (entry.status === "failed") throw invalid("\u8FD9\u6B21\u626B\u63CF\u6CA1\u8DD1\u5B8C\uFF0C\u6E05\u5355\u4E0D\u5B8C\u6574\uFF0C\u6539\u5B83\u6CA1\u610F\u4E49\u2014\u2014\u91CD\u65B0\u626B\u63CF");
|
|
59974
|
+
if (entry.status === "scanning" && mustBeLiveScan(args.personaId, file, "deploy:patch").id !== entry.id) {
|
|
59975
|
+
throw invalid("deploy:patch \u6307\u540D\u7684\u4E0D\u662F\u6B63\u5728\u8DD1\u7684\u90A3\u6B21\u626B\u63CF");
|
|
59976
|
+
}
|
|
59977
|
+
if (args.patch.manifest) await writeScanFile(dir, replaceScan(file, { ...entry, manifest: args.patch.manifest }));
|
|
59915
59978
|
return view(args.personaId);
|
|
59916
59979
|
};
|
|
59917
59980
|
const putSecret = async (frame, _client, ctx) => {
|
|
59918
59981
|
ensureOwner(ctx);
|
|
59919
59982
|
const args = parseArgs2(DeployPutSecretArgsSchema, frame, "deploy:putSecret");
|
|
59920
59983
|
const dir = mustPersona(args.personaId);
|
|
59921
|
-
const
|
|
59922
|
-
const declared =
|
|
59984
|
+
const { manifest } = mustScan(await readHistory(dir), args.scanId);
|
|
59985
|
+
const declared = [...manifest.secrets, ...sharedConnectorSecrets(manifest)].find((s2) => s2.name === args.name);
|
|
59923
59986
|
if (!declared) throw invalid(`\u6E05\u5355\u91CC\u6CA1\u6709\u53EB\u300C${args.name}\u300D\u7684\u51ED\u636E`);
|
|
59924
59987
|
const allowed = new Set(declared.env);
|
|
59925
59988
|
for (const key of Object.keys(args.values)) {
|
|
@@ -59940,7 +60003,8 @@ function buildDeployHandlers(deps) {
|
|
|
59940
60003
|
ensureOwner(ctx);
|
|
59941
60004
|
const args = parseArgs2(DeployReportFindingArgsSchema, frame, "deploy:reportFinding");
|
|
59942
60005
|
const dir = mustPersona(args.personaId);
|
|
59943
|
-
const
|
|
60006
|
+
const file = await readHistory(dir);
|
|
60007
|
+
const scan = mustBeLiveScan(args.personaId, file, "deploy:reportFinding");
|
|
59944
60008
|
let item;
|
|
59945
60009
|
try {
|
|
59946
60010
|
item = parseFinding(args.kind, args.item);
|
|
@@ -59948,37 +60012,34 @@ function buildDeployHandlers(deps) {
|
|
|
59948
60012
|
throw invalid(err instanceof Error ? err.message : String(err));
|
|
59949
60013
|
}
|
|
59950
60014
|
if (args.kind === "connector") item = cliConnectorFromFinding(item);
|
|
59951
|
-
const file = await readDeployFile(dir) ?? emptyDeployFile();
|
|
59952
|
-
await writeDeployFile(dir, upsertFinding(file, args.kind, item));
|
|
59953
60015
|
const target = `${args.kind}:${item.name}`;
|
|
59954
|
-
await
|
|
60016
|
+
await writeScanFile(dir, replaceScan(file, {
|
|
59955
60017
|
...scan,
|
|
60018
|
+
manifest: upsertFinding(scan.manifest, args.kind, item),
|
|
59956
60019
|
evidence: [...scan.evidence.filter((e) => e.target !== target), redactEvidence({ target, ...args.evidence })]
|
|
59957
|
-
});
|
|
60020
|
+
}));
|
|
59958
60021
|
return view(args.personaId);
|
|
59959
60022
|
};
|
|
59960
60023
|
const finishScan = async (frame, _client, ctx) => {
|
|
59961
60024
|
ensureOwner(ctx);
|
|
59962
60025
|
const args = parseArgs2(DeployFinishScanArgsSchema, frame, "deploy:finishScan");
|
|
59963
60026
|
const dir = mustPersona(args.personaId);
|
|
59964
|
-
const
|
|
59965
|
-
|
|
60027
|
+
const file = await readHistory(dir);
|
|
60028
|
+
const scan = mustBeLiveScan(args.personaId, file, "deploy:finishScan");
|
|
60029
|
+
await writeScanFile(dir, replaceScan(file, {
|
|
59966
60030
|
...scan,
|
|
59967
60031
|
status: "done",
|
|
59968
60032
|
finishedAt: deps.now(),
|
|
59969
60033
|
// 存疑项也是 agent 的自由文本,跟证据同一道涂抹。daemon 自己扫 .mcp.json 时发现的问题
|
|
59970
60034
|
//(带 MCP_SCAN_NOTE_PREFIX)留着——butler 不知道那些,它的那份不该把它们冲掉
|
|
59971
60035
|
notes: [...scan.notes.filter((n2) => n2.startsWith(MCP_SCAN_NOTE_PREFIX)), ...args.notes.map(redactSecretValues)],
|
|
59972
|
-
// 记下这次扫描**被告知有哪些共享资产**。以后资产表长出新条目时,只有扫的时候还不知道
|
|
59973
|
-
// 它的那些记录会被提示一次(见 staleScannerAssets)
|
|
59974
|
-
scannedAssets: scannableAssets.map((a2) => a2.name),
|
|
59975
60036
|
error: void 0
|
|
59976
|
-
});
|
|
60037
|
+
}));
|
|
59977
60038
|
return view(args.personaId);
|
|
59978
60039
|
};
|
|
59979
|
-
const
|
|
60040
|
+
const scanHandler = async (frame, _client, ctx) => {
|
|
59980
60041
|
ensureOwner(ctx);
|
|
59981
|
-
const { personaId: personaId2, scanBase } = parseArgs2(
|
|
60042
|
+
const { personaId: personaId2, scanBase } = parseArgs2(DeployScanArgsSchema, frame, "deploy:scan");
|
|
59982
60043
|
const dir = mustPersona(personaId2);
|
|
59983
60044
|
const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
|
|
59984
60045
|
if (wrong) throw invalid(wrong);
|
|
@@ -59994,33 +60055,43 @@ function buildDeployHandlers(deps) {
|
|
|
59994
60055
|
repo2 = await repoInfo(deps.exec, dir);
|
|
59995
60056
|
if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
|
|
59996
60057
|
}
|
|
60058
|
+
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`);
|
|
60059
|
+
const live = liveScans.get(personaId2);
|
|
60060
|
+
if (live && live.sessionId === void 0) return view(personaId2);
|
|
60061
|
+
let file = await readHistory(dir).catch(() => emptyScanFile());
|
|
60062
|
+
if (file.scans.some((s2) => s2.status === "scanning" && isLiveScan(live, s2))) return view(personaId2);
|
|
60063
|
+
file = await settleStale(personaId2, dir, file);
|
|
60064
|
+
let head;
|
|
60065
|
+
try {
|
|
60066
|
+
head = await commitAll(deps.exec, { dir, message: `clawd: \u626B\u63CF\u524D\u843D\u5B58 ${new Date(deps.now()).toISOString()}` });
|
|
60067
|
+
} catch (err) {
|
|
60068
|
+
throw toClawdError(err);
|
|
60069
|
+
}
|
|
60070
|
+
runCache.delete(personaId2);
|
|
60071
|
+
try {
|
|
60072
|
+
await beginScan(personaId2, dir, file, contentHeadOf({ head }, file.released), scanBase);
|
|
60073
|
+
} catch (err) {
|
|
60074
|
+
liveScans.delete(personaId2);
|
|
60075
|
+
throw err;
|
|
60076
|
+
}
|
|
60077
|
+
return view(personaId2);
|
|
60078
|
+
};
|
|
60079
|
+
const releaseHandler = async (frame, _client, ctx) => {
|
|
60080
|
+
ensureOwner(ctx);
|
|
60081
|
+
const { personaId: personaId2, scanId } = parseArgs2(DeployReleaseArgsSchema, frame, "deploy:release");
|
|
60082
|
+
const dir = mustPersona(personaId2);
|
|
60083
|
+
const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
|
|
60084
|
+
if (wrong) throw invalid(wrong);
|
|
60085
|
+
const { auth, api } = await requireGithub();
|
|
60086
|
+
const repo2 = await repoInfo(deps.exec, dir);
|
|
60087
|
+
if (!repo2) throw invalid("\u8FD8\u6CA1\u6709\u4ED3\u5E93\u2014\u2014\u5148\u70B9\u300C\u5F00\u59CB\u626B\u63CF\u300D\uFF08\u4F1A\u5EFA\u4ED3\uFF09");
|
|
59997
60088
|
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\u53D1\u5E03`);
|
|
59998
|
-
const
|
|
59999
|
-
|
|
60000
|
-
|
|
60001
|
-
if (
|
|
60002
|
-
|
|
60003
|
-
current = await settleStale(dir, current);
|
|
60004
|
-
}
|
|
60005
|
-
const fileUsable = await readDeployFile(dir).then((f2) => f2 !== void 0).catch(() => false);
|
|
60006
|
-
if (scanBase !== void 0 || !fileUsable || !isScanFresh(current, repo2)) {
|
|
60007
|
-
let head;
|
|
60008
|
-
try {
|
|
60009
|
-
head = await commitAll(deps.exec, { dir, message: `clawd: \u626B\u63CF\u524D\u843D\u5B58 ${new Date(deps.now()).toISOString()}` });
|
|
60010
|
-
} catch (err) {
|
|
60011
|
-
throw toClawdError(err);
|
|
60012
|
-
}
|
|
60013
|
-
runCache.delete(personaId2);
|
|
60014
|
-
try {
|
|
60015
|
-
await beginScan(personaId2, dir, current, head, scanBase);
|
|
60016
|
-
} catch (err) {
|
|
60017
|
-
liveScans.delete(personaId2);
|
|
60018
|
-
throw err;
|
|
60019
|
-
}
|
|
60020
|
-
return { response: { type: "deploy:release", outcome: "scanning" } };
|
|
60089
|
+
const file = await readHistory(dir);
|
|
60090
|
+
const entry = mustScan(file, scanId);
|
|
60091
|
+
if (entry.status !== "done") throw invalid(entry.status === "scanning" ? "\u8FD9\u6B21\u626B\u63CF\u8FD8\u5728\u8DD1\uFF0C\u7B49\u5B83\u626B\u5B8C" : "\u8FD9\u6B21\u626B\u63CF\u6CA1\u8DD1\u5B8C\uFF0C\u6E05\u5355\u4E0D\u5B8C\u6574\u2014\u2014\u91CD\u65B0\u626B\u63CF");
|
|
60092
|
+
if (!isScanFresh(entry, repo2, file.released)) {
|
|
60093
|
+
throw invalid(repo2.dirty ? "\u6709\u672A\u63D0\u4EA4\u7684\u6539\u52A8\uFF0C\u6E05\u5355\u662F\u6539\u52A8\u524D\u626B\u7684\u2014\u2014\u91CD\u65B0\u626B\u63CF" : "\u8FD9\u6B21\u626B\u63CF\u5BF9\u5E94\u7684\u4E0D\u662F\u5F53\u524D\u8FD9\u4E00\u7248 persona\u2014\u2014\u91CD\u65B0\u626B\u63CF");
|
|
60021
60094
|
}
|
|
60022
|
-
const file = await readDeployFile(dir);
|
|
60023
|
-
if (!file) throw invalid("\u8FD8\u6CA1\u626B\u63CF\u51FA\u6E05\u5355");
|
|
60024
60095
|
const blockers = [];
|
|
60025
60096
|
let present;
|
|
60026
60097
|
try {
|
|
@@ -60028,18 +60099,28 @@ function buildDeployHandlers(deps) {
|
|
|
60028
60099
|
} catch (err) {
|
|
60029
60100
|
throw toClawdError(err);
|
|
60030
60101
|
}
|
|
60031
|
-
const missing = requiredEnvNames(
|
|
60102
|
+
const missing = requiredEnvNames(entry.manifest).filter((e) => !present.has(e));
|
|
60032
60103
|
if (missing.length > 0) blockers.push(`${missing.join(" / ")} \u8FD8\u6CA1\u586B`);
|
|
60033
|
-
for (const c2 of
|
|
60104
|
+
for (const c2 of entry.manifest.connectors) blockers.push(...connectorBlockers(c2));
|
|
60034
60105
|
if (!deps.personaEngine(personaId2)?.model) {
|
|
60035
60106
|
blockers.push("\u8FD8\u6CA1\u9009\u6A21\u578B\uFF08\u5207\u5F15\u64CE\u65F6\u4F1A\u6E05\u7A7A\uFF09\u2014\u2014\u53BB persona \u8BBE\u7F6E\u91CC\u9009\u4E00\u4E2A");
|
|
60036
60107
|
}
|
|
60037
60108
|
if (blockers.length > 0) throw invalid(`\u8FD8\u5DEE\uFF1A${blockers.join("\uFF1B")}`);
|
|
60109
|
+
const before = await readDeployFileRaw(dir);
|
|
60038
60110
|
try {
|
|
60039
|
-
|
|
60111
|
+
await writeDeployFile(dir, { version: 1, manifest: entry.manifest });
|
|
60112
|
+
let result;
|
|
60113
|
+
try {
|
|
60114
|
+
result = await release(deps.exec, api, { dir, now: deps.now, auth });
|
|
60115
|
+
} catch (err) {
|
|
60116
|
+
if ((await repoInfo(deps.exec, dir))?.head === repo2.head) await restoreDeployFileRaw(dir, before);
|
|
60117
|
+
throw err;
|
|
60118
|
+
}
|
|
60040
60119
|
runCache.delete(personaId2);
|
|
60041
|
-
|
|
60042
|
-
|
|
60120
|
+
await writeScanFile(dir, {
|
|
60121
|
+
...await readHistory(dir),
|
|
60122
|
+
released: { head: result.sha, contentHead: entry.head, scanId: entry.id, at: deps.now() }
|
|
60123
|
+
});
|
|
60043
60124
|
return { response: { type: "deploy:release", ...result } };
|
|
60044
60125
|
} catch (err) {
|
|
60045
60126
|
throw toClawdError(err);
|
|
@@ -60050,7 +60131,7 @@ function buildDeployHandlers(deps) {
|
|
|
60050
60131
|
const { personaId: personaId2, jobId } = parseArgs2(DeployRerunJobArgsSchema, frame, "deploy:rerunJob");
|
|
60051
60132
|
const dir = mustPersona(personaId2);
|
|
60052
60133
|
const repo2 = await repoInfo(deps.exec, dir);
|
|
60053
|
-
if (!repo2) throw invalid("\u8FD8\u6CA1\u6709\u4ED3\u5E93\
|
|
60134
|
+
if (!repo2) throw invalid("\u8FD8\u6CA1\u6709\u4ED3\u5E93\u2014\u2014\u5148\u70B9\u300C\u5F00\u59CB\u626B\u63CF\u300D\uFF08\u4F1A\u5EFA\u4ED3\uFF09");
|
|
60054
60135
|
const slug = repoSlug(repo2.url);
|
|
60055
60136
|
const { api } = await requireGithub();
|
|
60056
60137
|
const run = await latestRun(api, slug);
|
|
@@ -60068,6 +60149,7 @@ function buildDeployHandlers(deps) {
|
|
|
60068
60149
|
};
|
|
60069
60150
|
return {
|
|
60070
60151
|
"deploy:get": get,
|
|
60152
|
+
"deploy:scan": scanHandler,
|
|
60071
60153
|
"deploy:patch": patch,
|
|
60072
60154
|
"deploy:putSecret": putSecret,
|
|
60073
60155
|
"deploy:reportFinding": reportFinding,
|
|
@@ -61106,7 +61188,7 @@ ${formatLarkSpeakerReminder(speaker)}`
|
|
|
61106
61188
|
}
|
|
61107
61189
|
|
|
61108
61190
|
// src/lark-bot-connector/envelope.ts
|
|
61109
|
-
var
|
|
61191
|
+
var import_node_crypto9 = require("crypto");
|
|
61110
61192
|
var LARK_ENVELOPE_PREFIX = "clawdlbe1";
|
|
61111
61193
|
function verifyLarkEventEnvelope(args) {
|
|
61112
61194
|
const { token, publicKeyPem } = args;
|
|
@@ -61125,7 +61207,7 @@ function verifyLarkEventEnvelope(args) {
|
|
|
61125
61207
|
const data = Buffer.from(`${prefix}.${payloadB64}`, "utf8");
|
|
61126
61208
|
let sigOk = false;
|
|
61127
61209
|
try {
|
|
61128
|
-
sigOk = (0,
|
|
61210
|
+
sigOk = (0, import_node_crypto9.verify)(null, data, publicKeyPem, Buffer.from(sigB64, "base64url"));
|
|
61129
61211
|
} catch {
|
|
61130
61212
|
sigOk = false;
|
|
61131
61213
|
}
|
|
@@ -63136,7 +63218,7 @@ var PiRpcClient = class {
|
|
|
63136
63218
|
|
|
63137
63219
|
// src/tools/pi-session.ts
|
|
63138
63220
|
var import_node_child_process10 = require("child_process");
|
|
63139
|
-
var
|
|
63221
|
+
var import_node_crypto10 = require("crypto");
|
|
63140
63222
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
63141
63223
|
var import_node_path39 = __toESM(require("path"), 1);
|
|
63142
63224
|
|
|
@@ -63314,7 +63396,7 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
|
|
|
63314
63396
|
}
|
|
63315
63397
|
function createPiSession(ctx, sink, deps = {}) {
|
|
63316
63398
|
const cmd = process.env.PI_BIN ?? "pi";
|
|
63317
|
-
const sessionId = ctx.toolSessionId ?? (0,
|
|
63399
|
+
const sessionId = ctx.toolSessionId ?? (0, import_node_crypto10.randomUUID)();
|
|
63318
63400
|
const skillsDir = import_node_path39.default.join(ctx.cwd, ".claude", "skills");
|
|
63319
63401
|
const exists = deps.skillsDirExists ?? ((d2) => import_node_fs35.default.existsSync(d2));
|
|
63320
63402
|
const args = buildPiArgs(ctx, {
|
|
@@ -64761,7 +64843,7 @@ var wrapper_default = import_websocket.default;
|
|
|
64761
64843
|
|
|
64762
64844
|
// src/transport/local-ws-server.ts
|
|
64763
64845
|
var import_node_http = __toESM(require("http"), 1);
|
|
64764
|
-
var
|
|
64846
|
+
var import_node_crypto11 = require("crypto");
|
|
64765
64847
|
var LocalWsServer = class {
|
|
64766
64848
|
constructor(opts) {
|
|
64767
64849
|
this.opts = opts;
|
|
@@ -64978,7 +65060,7 @@ var LocalWsServer = class {
|
|
|
64978
65060
|
}
|
|
64979
65061
|
}
|
|
64980
65062
|
onConnection(socket, remoteAddress) {
|
|
64981
|
-
const id = (0,
|
|
65063
|
+
const id = (0, import_node_crypto11.randomUUID)();
|
|
64982
65064
|
const subscribedSessions = /* @__PURE__ */ new Map();
|
|
64983
65065
|
const handle = {
|
|
64984
65066
|
id,
|
|
@@ -65943,7 +66025,7 @@ var import_node_path49 = __toESM(require("path"), 1);
|
|
|
65943
66025
|
// src/attachment/upload.ts
|
|
65944
66026
|
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
65945
66027
|
var import_node_path47 = __toESM(require("path"), 1);
|
|
65946
|
-
var
|
|
66028
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
65947
66029
|
var import_promises7 = require("stream/promises");
|
|
65948
66030
|
var UploadError = class extends Error {
|
|
65949
66031
|
constructor(code, message) {
|
|
@@ -65967,11 +66049,11 @@ async function writeUploadedAttachment(args) {
|
|
|
65967
66049
|
} catch (err) {
|
|
65968
66050
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
65969
66051
|
}
|
|
65970
|
-
const hasher =
|
|
66052
|
+
const hasher = import_node_crypto12.default.createHash("sha256");
|
|
65971
66053
|
let actualSize = 0;
|
|
65972
66054
|
const tmpPath = import_node_path47.default.join(
|
|
65973
66055
|
attachmentsRoot,
|
|
65974
|
-
`.upload-${process.pid}-${Date.now()}-${
|
|
66056
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto12.default.randomBytes(4).toString("hex")}`
|
|
65975
66057
|
);
|
|
65976
66058
|
try {
|
|
65977
66059
|
await (0, import_promises7.pipeline)(
|
|
@@ -66815,7 +66897,7 @@ function runAttachmentGc(args) {
|
|
|
66815
66897
|
// src/attachment/group.ts
|
|
66816
66898
|
var import_node_fs46 = __toESM(require("fs"), 1);
|
|
66817
66899
|
var import_node_path51 = __toESM(require("path"), 1);
|
|
66818
|
-
var
|
|
66900
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
66819
66901
|
init_protocol();
|
|
66820
66902
|
var GroupFileStore = class {
|
|
66821
66903
|
dataDir;
|
|
@@ -66904,7 +66986,7 @@ var GroupFileStore = class {
|
|
|
66904
66986
|
entries[idx] = next;
|
|
66905
66987
|
} else {
|
|
66906
66988
|
next = {
|
|
66907
|
-
id: `gf-${
|
|
66989
|
+
id: `gf-${import_node_crypto13.default.randomBytes(6).toString("base64url")}`,
|
|
66908
66990
|
relPath: input.relPath,
|
|
66909
66991
|
from: input.from,
|
|
66910
66992
|
label: input.label,
|
|
@@ -67050,7 +67132,7 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
|
|
|
67050
67132
|
// src/tunnel/tunnel-manager.ts
|
|
67051
67133
|
var import_node_fs51 = __toESM(require("fs"), 1);
|
|
67052
67134
|
var import_node_path56 = __toESM(require("path"), 1);
|
|
67053
|
-
var
|
|
67135
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
67054
67136
|
var import_node_child_process15 = require("child_process");
|
|
67055
67137
|
|
|
67056
67138
|
// src/tunnel/tunnel-store.ts
|
|
@@ -67549,7 +67631,7 @@ var TunnelManager = class {
|
|
|
67549
67631
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
67550
67632
|
});
|
|
67551
67633
|
const tomlPath = import_node_path56.default.join(this.deps.dataDir, "frpc.toml");
|
|
67552
|
-
const proxyName = `clawd-${t2.subdomain}-${localPort}-${
|
|
67634
|
+
const proxyName = `clawd-${t2.subdomain}-${localPort}-${import_node_crypto14.default.randomBytes(3).toString("hex")}`;
|
|
67553
67635
|
const toml = buildFrpcToml({
|
|
67554
67636
|
serverAddr: t2.frpsHost,
|
|
67555
67637
|
serverPort: t2.frpsPort,
|
|
@@ -67650,7 +67732,7 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
67650
67732
|
// src/tunnel/device-key.ts
|
|
67651
67733
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
67652
67734
|
var import_node_path57 = __toESM(require("path"), 1);
|
|
67653
|
-
var
|
|
67735
|
+
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
67654
67736
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
67655
67737
|
function deriveStableDeviceKey(opts = {}) {
|
|
67656
67738
|
const hostname = opts.hostname ?? import_node_os18.default.hostname();
|
|
@@ -67660,13 +67742,13 @@ function deriveStableDeviceKey(opts = {}) {
|
|
|
67660
67742
|
const normalizedDataDir = opts.dataDir ? import_node_path57.default.resolve(opts.dataDir) : null;
|
|
67661
67743
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
67662
67744
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
67663
|
-
return
|
|
67745
|
+
return import_node_crypto15.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
67664
67746
|
}
|
|
67665
67747
|
|
|
67666
67748
|
// src/auth-store.ts
|
|
67667
67749
|
var import_node_fs52 = __toESM(require("fs"), 1);
|
|
67668
67750
|
var import_node_path58 = __toESM(require("path"), 1);
|
|
67669
|
-
var
|
|
67751
|
+
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
67670
67752
|
var AUTH_FILE_NAME = "auth.json";
|
|
67671
67753
|
function authFilePath(dataDir) {
|
|
67672
67754
|
return import_node_path58.default.join(dataDir, AUTH_FILE_NAME);
|
|
@@ -67703,10 +67785,10 @@ function loadOrCreateTunnelDeviceKey(opts) {
|
|
|
67703
67785
|
return tunnelDeviceKey;
|
|
67704
67786
|
}
|
|
67705
67787
|
function defaultGenerateToken() {
|
|
67706
|
-
return
|
|
67788
|
+
return import_node_crypto16.default.randomBytes(32).toString("base64url");
|
|
67707
67789
|
}
|
|
67708
67790
|
function defaultGenerateOwnerPrincipalId() {
|
|
67709
|
-
return `owner-${
|
|
67791
|
+
return `owner-${import_node_crypto16.default.randomUUID()}`;
|
|
67710
67792
|
}
|
|
67711
67793
|
function readAuthFile(file) {
|
|
67712
67794
|
try {
|
|
@@ -67838,7 +67920,7 @@ var OwnerIdentityStore = class {
|
|
|
67838
67920
|
};
|
|
67839
67921
|
|
|
67840
67922
|
// src/feishu-auth/login-flow.ts
|
|
67841
|
-
var
|
|
67923
|
+
var import_node_crypto17 = __toESM(require("crypto"), 1);
|
|
67842
67924
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
67843
67925
|
var LoginFlow = class {
|
|
67844
67926
|
constructor(deps) {
|
|
@@ -67847,7 +67929,7 @@ var LoginFlow = class {
|
|
|
67847
67929
|
deps;
|
|
67848
67930
|
pendingStates = /* @__PURE__ */ new Map();
|
|
67849
67931
|
start() {
|
|
67850
|
-
const state =
|
|
67932
|
+
const state = import_node_crypto17.default.randomBytes(16).toString("base64url");
|
|
67851
67933
|
const now = (this.deps.now ?? Date.now)();
|
|
67852
67934
|
this.pendingStates.set(state, now);
|
|
67853
67935
|
this.gcExpired(now);
|
|
@@ -68378,7 +68460,7 @@ init_protocol();
|
|
|
68378
68460
|
var import_node_fs55 = __toESM(require("fs"), 1);
|
|
68379
68461
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
68380
68462
|
var import_node_path61 = __toESM(require("path"), 1);
|
|
68381
|
-
var
|
|
68463
|
+
var import_node_crypto18 = require("crypto");
|
|
68382
68464
|
init_claude_history();
|
|
68383
68465
|
function readJsonlEntries(file) {
|
|
68384
68466
|
const raw = import_node_fs55.default.readFileSync(file, "utf8");
|
|
@@ -68415,7 +68497,7 @@ function forkSession(input) {
|
|
|
68415
68497
|
throw new Error(`fork: messageUuid not found in transcript: ${input.messageUuid}`);
|
|
68416
68498
|
}
|
|
68417
68499
|
const sliced = mainEntries.slice(0, targetIdx + 1);
|
|
68418
|
-
const forkedToolSessionId = (0,
|
|
68500
|
+
const forkedToolSessionId = (0, import_node_crypto18.randomUUID)();
|
|
68419
68501
|
let parentUuid = null;
|
|
68420
68502
|
const forkedLines = [];
|
|
68421
68503
|
for (const entry of sliced) {
|
|
@@ -70741,6 +70823,7 @@ var METHOD_GRANT_MAP = {
|
|
|
70741
70823
|
// deploy:*(persona 上云):全部 owner-only。里面有凭据录入口、建仓和推送,
|
|
70742
70824
|
// guest 不该看见更不该动。
|
|
70743
70825
|
"deploy:get": ADMIN_ANY,
|
|
70826
|
+
"deploy:scan": ADMIN_ANY,
|
|
70744
70827
|
"deploy:patch": ADMIN_ANY,
|
|
70745
70828
|
"deploy:putSecret": ADMIN_ANY,
|
|
70746
70829
|
"deploy:reportFinding": ADMIN_ANY,
|
|
@@ -70954,6 +71037,7 @@ var METHOD_GRANT_MAP = {
|
|
|
70954
71037
|
"device:connections": ADMIN_ANY
|
|
70955
71038
|
};
|
|
70956
71039
|
var HUMAN_ONLY_METHODS = [
|
|
71040
|
+
"deploy:scan",
|
|
70957
71041
|
"deploy:release",
|
|
70958
71042
|
"deploy:putSecret",
|
|
70959
71043
|
"deploy:rerunJob",
|
|
@@ -71001,7 +71085,7 @@ function computeMethodAccess(args) {
|
|
|
71001
71085
|
}
|
|
71002
71086
|
|
|
71003
71087
|
// src/version.ts
|
|
71004
|
-
var version = "0.2.
|
|
71088
|
+
var version = "0.2.555".length > 0 ? "0.2.555" : "dev";
|
|
71005
71089
|
|
|
71006
71090
|
// src/cli-probe/probe.ts
|
|
71007
71091
|
var fs78 = __toESM(require("fs"), 1);
|
|
@@ -71398,7 +71482,7 @@ init_protocol();
|
|
|
71398
71482
|
// src/extension/bundle-zip.ts
|
|
71399
71483
|
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
71400
71484
|
var import_node_path67 = __toESM(require("path"), 1);
|
|
71401
|
-
var
|
|
71485
|
+
var import_node_crypto19 = __toESM(require("crypto"), 1);
|
|
71402
71486
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
71403
71487
|
async function bundleExtensionDir(dir) {
|
|
71404
71488
|
const entries = await listFilesSorted(dir);
|
|
@@ -71413,7 +71497,7 @@ async function bundleExtensionDir(dir) {
|
|
|
71413
71497
|
compression: "DEFLATE",
|
|
71414
71498
|
compressionOptions: { level: 6 }
|
|
71415
71499
|
});
|
|
71416
|
-
const sha256 =
|
|
71500
|
+
const sha256 = import_node_crypto19.default.createHash("sha256").update(buffer).digest("hex");
|
|
71417
71501
|
return { buffer, sha256 };
|
|
71418
71502
|
}
|
|
71419
71503
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -71481,7 +71565,7 @@ function computePublishCheck(args) {
|
|
|
71481
71565
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
71482
71566
|
var import_node_path69 = __toESM(require("path"), 1);
|
|
71483
71567
|
var import_node_os25 = __toESM(require("os"), 1);
|
|
71484
|
-
var
|
|
71568
|
+
var import_node_crypto20 = __toESM(require("crypto"), 1);
|
|
71485
71569
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
71486
71570
|
|
|
71487
71571
|
// src/extension/paths.ts
|
|
@@ -71510,7 +71594,7 @@ var InstallError = class extends Error {
|
|
|
71510
71594
|
};
|
|
71511
71595
|
async function installFromChannel(args, deps) {
|
|
71512
71596
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
71513
|
-
const computed =
|
|
71597
|
+
const computed = import_node_crypto20.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
71514
71598
|
if (computed !== snapshotHash) {
|
|
71515
71599
|
throw new InstallError(
|
|
71516
71600
|
"HASH_MISMATCH",
|
|
@@ -71602,7 +71686,7 @@ async function installFromChannel(args, deps) {
|
|
|
71602
71686
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
71603
71687
|
var import_node_path70 = __toESM(require("path"), 1);
|
|
71604
71688
|
var import_node_os26 = __toESM(require("os"), 1);
|
|
71605
|
-
var
|
|
71689
|
+
var import_node_crypto21 = __toESM(require("crypto"), 1);
|
|
71606
71690
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
71607
71691
|
var UpdateError = class extends Error {
|
|
71608
71692
|
constructor(code, message) {
|
|
@@ -71640,7 +71724,7 @@ async function updateFromChannel(args, deps) {
|
|
|
71640
71724
|
if (e instanceof UpdateError) throw e;
|
|
71641
71725
|
throw e;
|
|
71642
71726
|
}
|
|
71643
|
-
const computed =
|
|
71727
|
+
const computed = import_node_crypto21.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
71644
71728
|
if (computed !== snapshotHash) {
|
|
71645
71729
|
throw new UpdateError(
|
|
71646
71730
|
"HASH_MISMATCH",
|
|
@@ -72769,7 +72853,7 @@ async function uninstall(deps) {
|
|
|
72769
72853
|
}
|
|
72770
72854
|
|
|
72771
72855
|
// src/handlers/index.ts
|
|
72772
|
-
var
|
|
72856
|
+
var import_node_crypto22 = require("crypto");
|
|
72773
72857
|
|
|
72774
72858
|
// src/handlers/peer-exec.ts
|
|
72775
72859
|
init_protocol();
|
|
@@ -72924,7 +73008,7 @@ function buildMethodHandlers(deps) {
|
|
|
72924
73008
|
const c2 = deps.contactStore.get(deviceId);
|
|
72925
73009
|
return c2 ? { deviceId: c2.deviceId, remoteUrl: c2.remoteUrl, connectToken: c2.connectToken } : null;
|
|
72926
73010
|
},
|
|
72927
|
-
genId: () => (0,
|
|
73011
|
+
genId: () => (0, import_node_crypto22.randomUUID)(),
|
|
72928
73012
|
now: () => Date.now(),
|
|
72929
73013
|
forwardInboxPostToPeer,
|
|
72930
73014
|
logger: deps.logger
|
|
@@ -73653,7 +73737,7 @@ async function startDaemon(config) {
|
|
|
73653
73737
|
const channelStore = createChannelStore({
|
|
73654
73738
|
filePath: import_node_path78.default.join(config.dataDir, "channels.json"),
|
|
73655
73739
|
now: () => Date.now(),
|
|
73656
|
-
genId: () => `ch-${(0,
|
|
73740
|
+
genId: () => `ch-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
|
|
73657
73741
|
onFlushError: (err) => logger.error("channel store flush failed", {
|
|
73658
73742
|
reason: err instanceof Error ? err.message : String(err)
|
|
73659
73743
|
})
|
|
@@ -73662,7 +73746,7 @@ async function startDaemon(config) {
|
|
|
73662
73746
|
const topicStore = createTopicStore({
|
|
73663
73747
|
filePath: import_node_path78.default.join(config.dataDir, "topics.json"),
|
|
73664
73748
|
now: () => Date.now(),
|
|
73665
|
-
genId: () => `tp-${(0,
|
|
73749
|
+
genId: () => `tp-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
|
|
73666
73750
|
onFlushError: (err) => logger.error("topic store flush failed", {
|
|
73667
73751
|
reason: err instanceof Error ? err.message : String(err)
|
|
73668
73752
|
})
|
|
@@ -73670,7 +73754,7 @@ async function startDaemon(config) {
|
|
|
73670
73754
|
await topicStore.load();
|
|
73671
73755
|
let deliverPendingRef;
|
|
73672
73756
|
const personaDispatchManager = new PersonaDispatchManager({
|
|
73673
|
-
genId: () => (0,
|
|
73757
|
+
genId: () => (0, import_node_crypto23.randomUUID)(),
|
|
73674
73758
|
store: dispatchStore,
|
|
73675
73759
|
now: () => Date.now(),
|
|
73676
73760
|
onCompleted: (sid) => deliverPendingRef?.(sid),
|
|
@@ -74635,15 +74719,15 @@ async function startDaemon(config) {
|
|
|
74635
74719
|
});
|
|
74636
74720
|
Object.assign(handlers, shiftHandlers);
|
|
74637
74721
|
{
|
|
74638
|
-
const
|
|
74722
|
+
const files = await Promise.all(personaStore.list().map(async (personaId2) => ({
|
|
74639
74723
|
personaId: personaId2,
|
|
74640
|
-
|
|
74724
|
+
file: await readScanFile(personaStore.personaDirPath(personaId2)).catch(() => void 0)
|
|
74641
74725
|
})));
|
|
74642
74726
|
const interrupted = reconcileInterruptedScans({
|
|
74643
|
-
|
|
74727
|
+
files,
|
|
74644
74728
|
now: Date.now(),
|
|
74645
|
-
write: (personaId2,
|
|
74646
|
-
void
|
|
74729
|
+
write: (personaId2, file) => {
|
|
74730
|
+
void writeScanFile(personaStore.personaDirPath(personaId2), file).catch((err) => {
|
|
74647
74731
|
logger.warn("deploy scan reconcile write failed", { personaId: personaId2, error: String(err) });
|
|
74648
74732
|
});
|
|
74649
74733
|
}
|
|
@@ -74664,10 +74748,7 @@ async function startDaemon(config) {
|
|
|
74664
74748
|
org: PERSONA_REPO_ORG,
|
|
74665
74749
|
now: () => Date.now(),
|
|
74666
74750
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
74667
|
-
startScan: (personaId2, scanBase) => {
|
|
74668
|
-
const assetLines = SCANNABLE_ASSETS.map(
|
|
74669
|
-
(a2) => ` - ${a2.name}${a2.path ? `\uFF08${a2.path(config.dataDir)}\uFF09` : ""}\uFF1A${a2.what}`
|
|
74670
|
-
).join("\n");
|
|
74751
|
+
startScan: (personaId2, scanBase, scanId) => {
|
|
74671
74752
|
const baseLines = scanBase.base ? [
|
|
74672
74753
|
`\u672C\u6B21\u662F**${SCAN_BASE_LABEL[scanBase.kind]}**\uFF1A\u53EA\u770B\u8DDF\u7EBF\u4E0A\u6B63\u8DD1\u90A3\u7248\u7684\u5DEE\u522B\uFF0C`,
|
|
74673
74754
|
`\u90A3\u4E00\u7248\u662F commit ${scanBase.base}\u3002`,
|
|
@@ -74677,14 +74758,12 @@ async function startDaemon(config) {
|
|
|
74677
74758
|
`\u672C\u6B21\u662F**${SCAN_BASE_LABEL[scanBase.kind]}**\uFF1A\u4ECE\u96F6\u628A persona \u6574\u4E2A\u8BFB\u4E00\u904D\uFF0C\u6E05\u5355\u662F\u7A7A\u7684\u3002`
|
|
74678
74759
|
];
|
|
74679
74760
|
const prompt = [
|
|
74680
|
-
`\u626B\u63CF persona\u300C${personaId2}\u300D\u7684\u90E8\u7F72\u73AF\u5883\uFF0CpersonaId=${personaId2}\u3002`,
|
|
74761
|
+
`\u626B\u63CF persona\u300C${personaId2}\u300D\u7684\u90E8\u7F72\u73AF\u5883\uFF0CpersonaId=${personaId2}\uFF0CscanId=${scanId}\u3002`,
|
|
74681
74762
|
"",
|
|
74682
74763
|
`persona \u76EE\u5F55\uFF1A${personaStore.personaDirPath(personaId2)}`,
|
|
74764
|
+
`\u8FD9\u6B21\u626B\u63CF\u5728\u5386\u53F2\u91CC\u7684 id \u662F ${scanId}\uFF08\u6536\u5C3E\u524D\u8981\u5220\u6E05\u5355\u6761\u76EE\u65F6 deploy:patch \u7528\u5B83\u6307\u540D\uFF09\u3002`,
|
|
74683
74765
|
...baseLines,
|
|
74684
74766
|
"",
|
|
74685
|
-
"\u53EF\u4EE5\u58F0\u660E\u7684 persona \u76EE\u5F55\u4E4B\u5916\u7684\u5171\u4EAB\u8D44\u4EA7\uFF08\u7528\u5230\u54EA\u6761\u5C31\u62A5\u54EA\u6761\uFF09\uFF1A",
|
|
74686
|
-
assetLines || " \uFF08\u65E0\uFF09",
|
|
74687
|
-
"",
|
|
74688
74767
|
"\u6309 scan-persona-for-deploy skill \u5E72\u6D3B\uFF1A\u8FB9\u67E5\u8FB9\u7528 deploy:reportFinding \u4E0A\u62A5\uFF0C",
|
|
74689
74768
|
"\u5168\u90E8\u67E5\u5B8C\u8C03 deploy:finishScan \u6536\u5C3E\u3002"
|
|
74690
74769
|
].join("\n");
|