@clawos-dev/clawd 0.2.552 → 0.2.554
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 +91 -56
- package/dist/cli.cjs +386 -281
- package/dist/dispatch/mcp-server.cjs +80 -49
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/mcp-server.cjs +91 -56
- 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 +91 -56
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-C7Ou9JHX.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)----
|
|
@@ -9718,11 +9748,12 @@ function isCodexFamily(tool) {
|
|
|
9718
9748
|
function isRpcAgentTool(tool) {
|
|
9719
9749
|
return isCodexFamily(tool) || tool === PI_TOOL_ID;
|
|
9720
9750
|
}
|
|
9721
|
-
var CODEX_CLOUD_TOOL_ID, PI_TOOL_ID;
|
|
9751
|
+
var CLOUD_TOOL_SUFFIX, CODEX_CLOUD_TOOL_ID, PI_TOOL_ID;
|
|
9722
9752
|
var init_tool_family = __esm({
|
|
9723
9753
|
"../protocol/src/tool-family.ts"() {
|
|
9724
9754
|
"use strict";
|
|
9725
|
-
|
|
9755
|
+
CLOUD_TOOL_SUFFIX = "-cloud";
|
|
9756
|
+
CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
|
|
9726
9757
|
PI_TOOL_ID = "pi";
|
|
9727
9758
|
}
|
|
9728
9759
|
});
|
|
@@ -32801,7 +32832,7 @@ var require_websocket = __commonJS({
|
|
|
32801
32832
|
var http2 = require("http");
|
|
32802
32833
|
var net2 = require("net");
|
|
32803
32834
|
var tls = require("tls");
|
|
32804
|
-
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
32835
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = require("crypto");
|
|
32805
32836
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
32806
32837
|
var { URL: URL2 } = require("url");
|
|
32807
32838
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -33331,7 +33362,7 @@ var require_websocket = __commonJS({
|
|
|
33331
33362
|
}
|
|
33332
33363
|
}
|
|
33333
33364
|
const defaultPort = isSecure ? 443 : 80;
|
|
33334
|
-
const key =
|
|
33365
|
+
const key = randomBytes2(16).toString("base64");
|
|
33335
33366
|
const request = isSecure ? https.request : http2.request;
|
|
33336
33367
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
33337
33368
|
let perMessageDeflate;
|
|
@@ -44494,7 +44525,7 @@ Env (advanced):
|
|
|
44494
44525
|
var import_node_path78 = __toESM(require("path"), 1);
|
|
44495
44526
|
var import_node_fs60 = __toESM(require("fs"), 1);
|
|
44496
44527
|
var import_node_os28 = __toESM(require("os"), 1);
|
|
44497
|
-
var
|
|
44528
|
+
var import_node_crypto23 = require("crypto");
|
|
44498
44529
|
var import_node_url5 = require("url");
|
|
44499
44530
|
|
|
44500
44531
|
// src/logger.ts
|
|
@@ -51856,6 +51887,7 @@ function driveCodexThread(opts) {
|
|
|
51856
51887
|
...translateNotification(method, params)
|
|
51857
51888
|
] : [];
|
|
51858
51889
|
if (events.length) sink.pushEvents(events);
|
|
51890
|
+
if (route === "foreground" && method === "turn/completed") opts.onTurnCompleted?.();
|
|
51859
51891
|
},
|
|
51860
51892
|
onServerRequest: (id, method, params) => {
|
|
51861
51893
|
const p3 = params ?? {};
|
|
@@ -55304,6 +55336,7 @@ function redactEvidence(evidence) {
|
|
|
55304
55336
|
// src/deploy/deploy-file.ts
|
|
55305
55337
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
55306
55338
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
55339
|
+
var import_node_crypto8 = require("crypto");
|
|
55307
55340
|
init_protocol();
|
|
55308
55341
|
|
|
55309
55342
|
// src/deploy/connectors.ts
|
|
@@ -55493,26 +55526,79 @@ async function readDeployFile(dir) {
|
|
|
55493
55526
|
async function writeDeployFile(dir, file) {
|
|
55494
55527
|
await writeJson(import_node_path33.default.join(dir, DEPLOY_FILE), DeployFileSchema.parse(file));
|
|
55495
55528
|
}
|
|
55496
|
-
async function
|
|
55497
|
-
|
|
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)));
|
|
55498
55575
|
if (raw === void 0) return void 0;
|
|
55499
|
-
const
|
|
55500
|
-
|
|
55501
|
-
|
|
55502
|
-
|
|
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);
|
|
55503
55584
|
}
|
|
55585
|
+
const parsed = DeployScanFileSchema.safeParse(raw);
|
|
55586
|
+
if (!parsed.success) throw badShape(parsed.error.issues);
|
|
55504
55587
|
return parsed.data;
|
|
55505
55588
|
}
|
|
55506
|
-
async function
|
|
55507
|
-
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) };
|
|
55508
55595
|
}
|
|
55509
55596
|
function emptyDeployFile() {
|
|
55510
|
-
return { version: 1, manifest: { version: 1,
|
|
55597
|
+
return { version: 1, manifest: { version: 1, packages: [], secrets: [], connectors: [] } };
|
|
55511
55598
|
}
|
|
55512
55599
|
var FINDING_SCHEMAS = {
|
|
55513
|
-
|
|
55600
|
+
package: EnvPackageSchema,
|
|
55514
55601
|
secret: EnvSecretSchema,
|
|
55515
|
-
asset: EnvAssetSchema,
|
|
55516
55602
|
// butler 报的是 CLI 连接器的「名字 + 它读的环境变量名」,kind / runner / id 由 daemon 补
|
|
55517
55603
|
//(handlers/deploy.ts 的 cliConnectorFromFinding)。MCP 连接器不经上报口,由 scanMcpConnectors 机械列出。
|
|
55518
55604
|
connector: CliConnectorFindingSchema
|
|
@@ -55525,26 +55611,26 @@ function parseFinding(kind, item) {
|
|
|
55525
55611
|
}
|
|
55526
55612
|
return parsed.data;
|
|
55527
55613
|
}
|
|
55528
|
-
function upsertFinding(
|
|
55614
|
+
function upsertFinding(manifest, kind, item) {
|
|
55529
55615
|
if (kind === "connector") {
|
|
55530
55616
|
const incoming = item;
|
|
55531
|
-
const have =
|
|
55617
|
+
const have = manifest.connectors;
|
|
55532
55618
|
const idx2 = have.findIndex((c2) => c2.name === incoming.name);
|
|
55533
|
-
if (idx2 !== -1 && have[idx2].runner === "mcp" && incoming.runner === "cli") return
|
|
55619
|
+
if (idx2 !== -1 && have[idx2].runner === "mcp" && incoming.runner === "cli") return manifest;
|
|
55534
55620
|
const merged = mergeConnector(idx2 === -1 ? void 0 : have[idx2], incoming);
|
|
55535
55621
|
const connectors = idx2 === -1 ? [...have, merged] : have.map((c2, i2) => i2 === idx2 ? merged : c2);
|
|
55536
|
-
return { ...
|
|
55622
|
+
return { ...manifest, connectors };
|
|
55537
55623
|
}
|
|
55538
|
-
const key = kind === "
|
|
55539
|
-
const list = [...
|
|
55624
|
+
const key = kind === "package" ? "packages" : "secrets";
|
|
55625
|
+
const list = [...manifest[key]];
|
|
55540
55626
|
const idx = list.findIndex((x2) => x2.name === item.name);
|
|
55541
55627
|
if (idx === -1) list.push(item);
|
|
55542
55628
|
else list[idx] = item;
|
|
55543
|
-
return { ...
|
|
55629
|
+
return { ...manifest, [key]: list };
|
|
55544
55630
|
}
|
|
55545
|
-
function requiredEnvNames(
|
|
55546
|
-
if (!
|
|
55547
|
-
return requiredSecretEnvNames(
|
|
55631
|
+
function requiredEnvNames(manifest) {
|
|
55632
|
+
if (!manifest) return [];
|
|
55633
|
+
return requiredSecretEnvNames(manifest);
|
|
55548
55634
|
}
|
|
55549
55635
|
|
|
55550
55636
|
// src/deploy/repo.ts
|
|
@@ -59305,26 +59391,26 @@ var L2 = [
|
|
|
59305
59391
|
" rm -rf persona/.git",
|
|
59306
59392
|
" node clawos/cloud-host/provision/build-image.mjs \\",
|
|
59307
59393
|
" --manifest persona/.clawd/deploy.json \\",
|
|
59308
|
-
' --from "$REGISTRY/$IMAGE_NAME:base" \\',
|
|
59394
|
+
' --from "$REGISTRY/$IMAGE_NAME:base-${{ steps.plan.outputs.engine }}" \\',
|
|
59309
59395
|
" --persona-dir persona \\",
|
|
59310
59396
|
// persona 在镜像里落 /clawd/personas/<id>/(与本机同构),id 是 gateway 登记的名字;同 smoke / register 用的那个输出
|
|
59311
59397
|
" --persona-id '${{ steps.plan.outputs.persona_id }}' \\",
|
|
59312
59398
|
' --tag "$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
|
|
59313
59399
|
"",
|
|
59314
|
-
" - name: \u955C\u50CF\u65AD\u8A00\uFF08\u6E05\u5355\u91CC\u7684
|
|
59315
|
-
" # /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\
|
|
59316
|
-
" #
|
|
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",
|
|
59317
59403
|
" run: |",
|
|
59318
59404
|
' IMG="$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
|
|
59319
59405
|
` OUT=$(docker run --rm "$IMG" '/opt/versions.sh')`,
|
|
59320
59406
|
' echo "$OUT"',
|
|
59321
|
-
` 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)')`,
|
|
59322
59408
|
` echo "$OUT" | awk -v want="$WANT" '`,
|
|
59323
59409
|
' /^--- / { name=$2; if ((getline v) <= 0) v=""; n++',
|
|
59324
59410
|
' if (v !~ /[0-9]+\\.[0-9]+/) { printf " \u2717 %s \u6CA1\u62A5\u51FA\u7248\u672C\u53F7\uFF1A%s\\n", name, v > "/dev/stderr"; bad++ } }',
|
|
59325
|
-
' 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++ }',
|
|
59326
59412
|
" exit bad ? 1 : 0 }'",
|
|
59327
|
-
' echo " \u2713 $WANT \u4E2A
|
|
59413
|
+
' echo " \u2713 \u6E05\u5355\u91CC\u8981\u88C5\u7684 $WANT \u4E2A\u90FD\u5728\u4E14\u62A5\u5F97\u51FA\u7248\u672C"',
|
|
59328
59414
|
` docker run --rm "$IMG" 'test -d /clawd/personas/\${{ steps.plan.outputs.persona_id }}' \\`,
|
|
59329
59415
|
' || { echo " \u2717 /clawd/personas/${{ steps.plan.outputs.persona_id }} \u4E0D\u5728" >&2; exit 1; }',
|
|
59330
59416
|
' echo " \u2713 persona \u76EE\u5F55\u5728"',
|
|
@@ -59357,6 +59443,7 @@ var L2 = [
|
|
|
59357
59443
|
" --model '${{ steps.plan.outputs.model }}' \\",
|
|
59358
59444
|
" --effort '${{ steps.plan.outputs.effort }}' \\",
|
|
59359
59445
|
" --size '${{ steps.plan.outputs.size }}' \\",
|
|
59446
|
+
" --engine '${{ steps.plan.outputs.engine }}' \\",
|
|
59360
59447
|
" --connectors '${{ steps.plan.outputs.connectors }}' \\",
|
|
59361
59448
|
" --run-number '${{ github.run_number }}'",
|
|
59362
59449
|
"",
|
|
@@ -59381,6 +59468,7 @@ var L2 = [
|
|
|
59381
59468
|
" --model '${{ steps.plan.outputs.model }}' \\",
|
|
59382
59469
|
" --effort '${{ steps.plan.outputs.effort }}' \\",
|
|
59383
59470
|
" --size '${{ steps.plan.outputs.size }}' \\",
|
|
59471
|
+
" --engine '${{ steps.plan.outputs.engine }}' \\",
|
|
59384
59472
|
" --connectors '${{ steps.plan.outputs.connectors }}'",
|
|
59385
59473
|
""
|
|
59386
59474
|
];
|
|
@@ -59596,7 +59684,7 @@ async function release(exec, api, args) {
|
|
|
59596
59684
|
await mustGit(exec, args.dir, [...identity, "commit", "--allow-empty", "-m", `clawd: \u53D1\u5E03 ${stamp}`], "\u63D0\u4EA4");
|
|
59597
59685
|
await gitPush(exec, args.dir, args.auth);
|
|
59598
59686
|
const sha = await mustGit(exec, args.dir, ["rev-parse", "HEAD"], "\u8BFB\u63D0\u4EA4");
|
|
59599
|
-
return {
|
|
59687
|
+
return { sha, actionsUrl: `https://github.com/${repoSlug(info.url)}/actions` };
|
|
59600
59688
|
}
|
|
59601
59689
|
|
|
59602
59690
|
// src/deploy/scan-base.ts
|
|
@@ -59675,17 +59763,6 @@ async function rerunJob(api, slug, jobId) {
|
|
|
59675
59763
|
|
|
59676
59764
|
// src/deploy/scan-task.ts
|
|
59677
59765
|
var BUTLER_PERSONA_ID = "persona-clawd-butler";
|
|
59678
|
-
var SCANNABLE_ASSETS = [];
|
|
59679
|
-
function staleScannerAssets(scan, known = SCANNABLE_ASSETS) {
|
|
59680
|
-
const knew = new Set(scan.scannedAssets ?? []);
|
|
59681
|
-
return known.filter((a2) => !knew.has(a2.name));
|
|
59682
|
-
}
|
|
59683
|
-
var STALE_ASSETS_NOTE_PREFIX = "\u3010\u4E0A\u4E91\u626B\u63CF\u3011";
|
|
59684
|
-
function describeStaleAssets(stale) {
|
|
59685
|
-
if (stale.length === 0) return void 0;
|
|
59686
|
-
const plain = (s2) => s2.replace(/\*\*/g, "").replace(/\s+/g, " ").trim();
|
|
59687
|
-
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`;
|
|
59688
|
-
}
|
|
59689
59766
|
function settleEndedScan(outcome, current, now) {
|
|
59690
59767
|
if (current?.status !== "scanning") return void 0;
|
|
59691
59768
|
return {
|
|
@@ -59697,13 +59774,11 @@ function settleEndedScan(outcome, current, now) {
|
|
|
59697
59774
|
}
|
|
59698
59775
|
function reconcileInterruptedScans(deps) {
|
|
59699
59776
|
const flipped = [];
|
|
59700
|
-
for (const { personaId: personaId2,
|
|
59701
|
-
if (
|
|
59777
|
+
for (const { personaId: personaId2, file } of deps.files) {
|
|
59778
|
+
if (!file || !file.scans.some((s2) => s2.status === "scanning")) continue;
|
|
59702
59779
|
deps.write(personaId2, {
|
|
59703
|
-
...
|
|
59704
|
-
status: "failed",
|
|
59705
|
-
finishedAt: deps.now,
|
|
59706
|
-
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)
|
|
59707
59782
|
});
|
|
59708
59783
|
flipped.push(personaId2);
|
|
59709
59784
|
}
|
|
@@ -59733,21 +59808,33 @@ function isLiveScan(live, scan) {
|
|
|
59733
59808
|
return live.sessionId === void 0 || live.sessionId === scan.scanSessionId;
|
|
59734
59809
|
}
|
|
59735
59810
|
function buildDeployHandlers(deps) {
|
|
59736
|
-
const scannableAssets = deps.scannableAssets ?? SCANNABLE_ASSETS;
|
|
59737
59811
|
const liveScans = /* @__PURE__ */ new Map();
|
|
59738
|
-
const
|
|
59739
|
-
|
|
59740
|
-
|
|
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);
|
|
59741
59822
|
return settled;
|
|
59742
59823
|
};
|
|
59743
|
-
const mustBeLiveScan = (personaId2,
|
|
59744
|
-
|
|
59745
|
-
|
|
59746
|
-
|
|
59747
|
-
|
|
59748
|
-
|
|
59749
|
-
|
|
59750
|
-
|
|
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;
|
|
59751
59838
|
};
|
|
59752
59839
|
const cacheMs = deps.secretsCacheMs ?? SECRETS_CACHE_MS;
|
|
59753
59840
|
const secretsCache = /* @__PURE__ */ new Map();
|
|
@@ -59777,20 +59864,15 @@ function buildDeployHandlers(deps) {
|
|
|
59777
59864
|
const github = await deps.githubIdentity.status();
|
|
59778
59865
|
const auth = deps.githubIdentity.cached();
|
|
59779
59866
|
const api = auth ? deps.githubApi(auth.token) : void 0;
|
|
59780
|
-
let file;
|
|
59781
|
-
let
|
|
59867
|
+
let file = emptyScanFile();
|
|
59868
|
+
let historyError;
|
|
59782
59869
|
try {
|
|
59783
|
-
file = await
|
|
59870
|
+
file = await readHistory(dir);
|
|
59784
59871
|
} catch (err) {
|
|
59785
|
-
|
|
59786
|
-
}
|
|
59787
|
-
let scan = await readScan(dir);
|
|
59788
|
-
if (scan?.status === "scanning" && !isLiveScan(liveScans.get(personaId2), scan)) scan = await settleStale(dir, scan);
|
|
59789
|
-
if (scan && scan.status === "done") {
|
|
59790
|
-
const stale = describeStaleAssets(staleScannerAssets(scan, scannableAssets));
|
|
59791
|
-
const kept = scan.notes.filter((n2) => !n2.startsWith(STALE_ASSETS_NOTE_PREFIX));
|
|
59792
|
-
scan = { ...scan, notes: stale ? [...kept, stale] : kept };
|
|
59872
|
+
historyError = err instanceof Error ? err.message : String(err);
|
|
59793
59873
|
}
|
|
59874
|
+
file = await settleStale(personaId2, dir, file);
|
|
59875
|
+
const { scans } = file;
|
|
59794
59876
|
let secrets;
|
|
59795
59877
|
let run;
|
|
59796
59878
|
if (repo2 && api && github.status === "authorized") {
|
|
@@ -59811,72 +59893,56 @@ function buildDeployHandlers(deps) {
|
|
|
59811
59893
|
personaId: personaId2,
|
|
59812
59894
|
github,
|
|
59813
59895
|
...repo2 ? { repo: repo2 } : {},
|
|
59814
|
-
|
|
59815
|
-
...
|
|
59896
|
+
scans,
|
|
59897
|
+
...file.released ? { released: file.released } : {},
|
|
59816
59898
|
...secrets ? { secrets } : {},
|
|
59817
59899
|
...run ? { run } : {},
|
|
59818
|
-
...
|
|
59900
|
+
...historyError ? { historyError } : {}
|
|
59819
59901
|
};
|
|
59820
59902
|
}
|
|
59821
59903
|
const view = async (personaId2) => ({
|
|
59822
59904
|
response: { type: "deploy:state", ...await stateOf(personaId2) }
|
|
59823
59905
|
});
|
|
59824
|
-
async function beginScan(personaId2, dir,
|
|
59906
|
+
async function beginScan(personaId2, dir, file, contentHead, want) {
|
|
59825
59907
|
liveScans.set(personaId2, {});
|
|
59826
|
-
const
|
|
59908
|
+
const releasedEntry = file.released ? file.scans.find((s2) => s2.id === file.released.scanId) : void 0;
|
|
59827
59909
|
const scanBase = pickScanBase({
|
|
59828
|
-
//
|
|
59829
|
-
|
|
59830
|
-
|
|
59831
|
-
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,
|
|
59832
59913
|
...want ? { want } : {}
|
|
59833
59914
|
});
|
|
59834
|
-
const
|
|
59835
|
-
const
|
|
59836
|
-
evidence: scanBase.kind === "hard" ? [] : current?.evidence ?? [],
|
|
59837
|
-
// notes 反过来不沿用:它是「这次扫描要老板处理的事」,沿用会把上次的事项一直复读
|
|
59838
|
-
notes: [],
|
|
59839
|
-
scannedAssets: [],
|
|
59840
|
-
startedAt,
|
|
59841
|
-
head,
|
|
59842
|
-
// 发布记的那个 commit 跟扫描无关,原样带过去,别让重扫把软扫的基准弄丢。
|
|
59843
|
-
//
|
|
59844
|
-
// **哪怕上面 hasCommit 刚判定它已经不在仓库里,也照样留着**:探测失败也包括 git 没跑起来
|
|
59845
|
-
// 这种一过性情况,据此把它抹掉就等于「一次 git 抽风 → 永久失去软扫,直到下次发布」,
|
|
59846
|
-
// 比留一个死 sha 糟得多。留着的代价只是 UI 上「从零重扫」多显示一阵子(那期间两个按钮
|
|
59847
|
-
// 都退硬扫、行为一致不出错),下次发布就自愈。
|
|
59848
|
-
...current?.releasedHead ? { releasedHead: current.releasedHead } : {},
|
|
59849
|
-
baseKind: scanBase.kind,
|
|
59850
|
-
...scanBase.base ? { base: scanBase.base } : {}
|
|
59851
|
-
};
|
|
59852
|
-
await writeScan(dir, { status: "scanning", ...base });
|
|
59853
|
-
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") };
|
|
59854
59917
|
const mcp = scanMcpConnectors(dir);
|
|
59855
|
-
const current0 = await readDeployFile(dir) ?? emptyDeployFile();
|
|
59856
|
-
const carried = scanBase.kind === "hard" ? (prevManifest?.manifest.connectors ?? []).filter((c2) => c2.runner === "mcp") : current0.manifest.connectors;
|
|
59857
|
-
await writeDeployFile(dir, {
|
|
59858
|
-
...current0,
|
|
59859
|
-
// 文件读不出来(坏 JSON)时保持原样:当成「零个 MCP」会把清单里的连接器清光,而发布闸对零连接器是放行的
|
|
59860
|
-
manifest: { ...current0.manifest, connectors: mcp.readable ? mergeScannedConnectors(carried, mcp.connectors) : carried }
|
|
59861
|
-
});
|
|
59862
59918
|
const mcpTargets = new Set(mcp.evidence.map((e) => e.target));
|
|
59863
|
-
const
|
|
59864
|
-
|
|
59865
|
-
|
|
59866
|
-
|
|
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 } : {}
|
|
59867
59931
|
};
|
|
59868
|
-
|
|
59932
|
+
await writeScanFile(dir, replaceScan(file, entry));
|
|
59933
|
+
const started = deps.startScan(personaId2, scanBase, entry.id);
|
|
59869
59934
|
if ("sessionId" in started) {
|
|
59870
59935
|
liveScans.set(personaId2, { sessionId: started.sessionId });
|
|
59871
|
-
await
|
|
59936
|
+
await writeScanFile(dir, replaceScan(await readHistory(dir), { ...entry, scanSessionId: started.sessionId }));
|
|
59872
59937
|
const mine = () => liveScans.get(personaId2)?.sessionId === started.sessionId;
|
|
59873
59938
|
void started.ended.then(async (outcome) => {
|
|
59874
59939
|
if (mine()) liveScans.delete(personaId2);
|
|
59875
|
-
const
|
|
59876
|
-
|
|
59877
|
-
|
|
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());
|
|
59878
59944
|
if (!settled) return;
|
|
59879
|
-
await
|
|
59945
|
+
await writeScanFile(dir, replaceScan(current, settled));
|
|
59880
59946
|
deps.warn?.("deploy scan ended without finishScan", { personaId: personaId2, why: settled.error });
|
|
59881
59947
|
}).catch((err) => {
|
|
59882
59948
|
if (mine()) liveScans.delete(personaId2);
|
|
@@ -59884,12 +59950,12 @@ function buildDeployHandlers(deps) {
|
|
|
59884
59950
|
});
|
|
59885
59951
|
} else {
|
|
59886
59952
|
liveScans.delete(personaId2);
|
|
59887
|
-
await
|
|
59953
|
+
await writeScanFile(dir, replaceScan(await readHistory(dir), {
|
|
59954
|
+
...entry,
|
|
59888
59955
|
status: "failed",
|
|
59889
|
-
...withMcp,
|
|
59890
59956
|
finishedAt: deps.now(),
|
|
59891
59957
|
error: `\u8D77\u626B\u63CF\u4F1A\u8BDD\u5931\u8D25\uFF1A${started.error}`
|
|
59892
|
-
});
|
|
59958
|
+
}));
|
|
59893
59959
|
}
|
|
59894
59960
|
}
|
|
59895
59961
|
const get = async (frame, _client, ctx) => {
|
|
@@ -59902,20 +59968,21 @@ function buildDeployHandlers(deps) {
|
|
|
59902
59968
|
ensureOwner(ctx);
|
|
59903
59969
|
const args = parseArgs2(DeployPatchArgsSchema, frame, "deploy:patch");
|
|
59904
59970
|
const dir = mustPersona(args.personaId);
|
|
59905
|
-
const file = await
|
|
59906
|
-
const
|
|
59907
|
-
|
|
59908
|
-
|
|
59909
|
-
|
|
59910
|
-
|
|
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 }));
|
|
59911
59978
|
return view(args.personaId);
|
|
59912
59979
|
};
|
|
59913
59980
|
const putSecret = async (frame, _client, ctx) => {
|
|
59914
59981
|
ensureOwner(ctx);
|
|
59915
59982
|
const args = parseArgs2(DeployPutSecretArgsSchema, frame, "deploy:putSecret");
|
|
59916
59983
|
const dir = mustPersona(args.personaId);
|
|
59917
|
-
const
|
|
59918
|
-
const declared =
|
|
59984
|
+
const { manifest } = mustScan(await readHistory(dir), args.scanId);
|
|
59985
|
+
const declared = [...manifest.secrets, ...sharedConnectorSecrets(manifest)].find((s2) => s2.name === args.name);
|
|
59919
59986
|
if (!declared) throw invalid(`\u6E05\u5355\u91CC\u6CA1\u6709\u53EB\u300C${args.name}\u300D\u7684\u51ED\u636E`);
|
|
59920
59987
|
const allowed = new Set(declared.env);
|
|
59921
59988
|
for (const key of Object.keys(args.values)) {
|
|
@@ -59936,7 +60003,8 @@ function buildDeployHandlers(deps) {
|
|
|
59936
60003
|
ensureOwner(ctx);
|
|
59937
60004
|
const args = parseArgs2(DeployReportFindingArgsSchema, frame, "deploy:reportFinding");
|
|
59938
60005
|
const dir = mustPersona(args.personaId);
|
|
59939
|
-
const
|
|
60006
|
+
const file = await readHistory(dir);
|
|
60007
|
+
const scan = mustBeLiveScan(args.personaId, file, "deploy:reportFinding");
|
|
59940
60008
|
let item;
|
|
59941
60009
|
try {
|
|
59942
60010
|
item = parseFinding(args.kind, args.item);
|
|
@@ -59944,37 +60012,34 @@ function buildDeployHandlers(deps) {
|
|
|
59944
60012
|
throw invalid(err instanceof Error ? err.message : String(err));
|
|
59945
60013
|
}
|
|
59946
60014
|
if (args.kind === "connector") item = cliConnectorFromFinding(item);
|
|
59947
|
-
const file = await readDeployFile(dir) ?? emptyDeployFile();
|
|
59948
|
-
await writeDeployFile(dir, upsertFinding(file, args.kind, item));
|
|
59949
60015
|
const target = `${args.kind}:${item.name}`;
|
|
59950
|
-
await
|
|
60016
|
+
await writeScanFile(dir, replaceScan(file, {
|
|
59951
60017
|
...scan,
|
|
60018
|
+
manifest: upsertFinding(scan.manifest, args.kind, item),
|
|
59952
60019
|
evidence: [...scan.evidence.filter((e) => e.target !== target), redactEvidence({ target, ...args.evidence })]
|
|
59953
|
-
});
|
|
60020
|
+
}));
|
|
59954
60021
|
return view(args.personaId);
|
|
59955
60022
|
};
|
|
59956
60023
|
const finishScan = async (frame, _client, ctx) => {
|
|
59957
60024
|
ensureOwner(ctx);
|
|
59958
60025
|
const args = parseArgs2(DeployFinishScanArgsSchema, frame, "deploy:finishScan");
|
|
59959
60026
|
const dir = mustPersona(args.personaId);
|
|
59960
|
-
const
|
|
59961
|
-
|
|
60027
|
+
const file = await readHistory(dir);
|
|
60028
|
+
const scan = mustBeLiveScan(args.personaId, file, "deploy:finishScan");
|
|
60029
|
+
await writeScanFile(dir, replaceScan(file, {
|
|
59962
60030
|
...scan,
|
|
59963
60031
|
status: "done",
|
|
59964
60032
|
finishedAt: deps.now(),
|
|
59965
60033
|
// 存疑项也是 agent 的自由文本,跟证据同一道涂抹。daemon 自己扫 .mcp.json 时发现的问题
|
|
59966
60034
|
//(带 MCP_SCAN_NOTE_PREFIX)留着——butler 不知道那些,它的那份不该把它们冲掉
|
|
59967
60035
|
notes: [...scan.notes.filter((n2) => n2.startsWith(MCP_SCAN_NOTE_PREFIX)), ...args.notes.map(redactSecretValues)],
|
|
59968
|
-
// 记下这次扫描**被告知有哪些共享资产**。以后资产表长出新条目时,只有扫的时候还不知道
|
|
59969
|
-
// 它的那些记录会被提示一次(见 staleScannerAssets)
|
|
59970
|
-
scannedAssets: scannableAssets.map((a2) => a2.name),
|
|
59971
60036
|
error: void 0
|
|
59972
|
-
});
|
|
60037
|
+
}));
|
|
59973
60038
|
return view(args.personaId);
|
|
59974
60039
|
};
|
|
59975
|
-
const
|
|
60040
|
+
const scanHandler = async (frame, _client, ctx) => {
|
|
59976
60041
|
ensureOwner(ctx);
|
|
59977
|
-
const { personaId: personaId2, scanBase } = parseArgs2(
|
|
60042
|
+
const { personaId: personaId2, scanBase } = parseArgs2(DeployScanArgsSchema, frame, "deploy:scan");
|
|
59978
60043
|
const dir = mustPersona(personaId2);
|
|
59979
60044
|
const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
|
|
59980
60045
|
if (wrong) throw invalid(wrong);
|
|
@@ -59990,33 +60055,43 @@ function buildDeployHandlers(deps) {
|
|
|
59990
60055
|
repo2 = await repoInfo(deps.exec, dir);
|
|
59991
60056
|
if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
|
|
59992
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");
|
|
59993
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`);
|
|
59994
|
-
const
|
|
59995
|
-
|
|
59996
|
-
|
|
59997
|
-
if (
|
|
59998
|
-
|
|
59999
|
-
current = await settleStale(dir, current);
|
|
60000
|
-
}
|
|
60001
|
-
const fileUsable = await readDeployFile(dir).then((f2) => f2 !== void 0).catch(() => false);
|
|
60002
|
-
if (scanBase !== void 0 || !fileUsable || !isScanFresh(current, repo2)) {
|
|
60003
|
-
let head;
|
|
60004
|
-
try {
|
|
60005
|
-
head = await commitAll(deps.exec, { dir, message: `clawd: \u626B\u63CF\u524D\u843D\u5B58 ${new Date(deps.now()).toISOString()}` });
|
|
60006
|
-
} catch (err) {
|
|
60007
|
-
throw toClawdError(err);
|
|
60008
|
-
}
|
|
60009
|
-
runCache.delete(personaId2);
|
|
60010
|
-
try {
|
|
60011
|
-
await beginScan(personaId2, dir, current, head, scanBase);
|
|
60012
|
-
} catch (err) {
|
|
60013
|
-
liveScans.delete(personaId2);
|
|
60014
|
-
throw err;
|
|
60015
|
-
}
|
|
60016
|
-
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");
|
|
60017
60094
|
}
|
|
60018
|
-
const file = await readDeployFile(dir);
|
|
60019
|
-
if (!file) throw invalid("\u8FD8\u6CA1\u626B\u63CF\u51FA\u6E05\u5355");
|
|
60020
60095
|
const blockers = [];
|
|
60021
60096
|
let present;
|
|
60022
60097
|
try {
|
|
@@ -60024,18 +60099,28 @@ function buildDeployHandlers(deps) {
|
|
|
60024
60099
|
} catch (err) {
|
|
60025
60100
|
throw toClawdError(err);
|
|
60026
60101
|
}
|
|
60027
|
-
const missing = requiredEnvNames(
|
|
60102
|
+
const missing = requiredEnvNames(entry.manifest).filter((e) => !present.has(e));
|
|
60028
60103
|
if (missing.length > 0) blockers.push(`${missing.join(" / ")} \u8FD8\u6CA1\u586B`);
|
|
60029
|
-
for (const c2 of
|
|
60104
|
+
for (const c2 of entry.manifest.connectors) blockers.push(...connectorBlockers(c2));
|
|
60030
60105
|
if (!deps.personaEngine(personaId2)?.model) {
|
|
60031
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");
|
|
60032
60107
|
}
|
|
60033
60108
|
if (blockers.length > 0) throw invalid(`\u8FD8\u5DEE\uFF1A${blockers.join("\uFF1B")}`);
|
|
60109
|
+
const before = await readDeployFileRaw(dir);
|
|
60034
60110
|
try {
|
|
60035
|
-
|
|
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
|
+
}
|
|
60036
60119
|
runCache.delete(personaId2);
|
|
60037
|
-
|
|
60038
|
-
|
|
60120
|
+
await writeScanFile(dir, {
|
|
60121
|
+
...await readHistory(dir),
|
|
60122
|
+
released: { head: result.sha, contentHead: entry.head, scanId: entry.id, at: deps.now() }
|
|
60123
|
+
});
|
|
60039
60124
|
return { response: { type: "deploy:release", ...result } };
|
|
60040
60125
|
} catch (err) {
|
|
60041
60126
|
throw toClawdError(err);
|
|
@@ -60046,7 +60131,7 @@ function buildDeployHandlers(deps) {
|
|
|
60046
60131
|
const { personaId: personaId2, jobId } = parseArgs2(DeployRerunJobArgsSchema, frame, "deploy:rerunJob");
|
|
60047
60132
|
const dir = mustPersona(personaId2);
|
|
60048
60133
|
const repo2 = await repoInfo(deps.exec, dir);
|
|
60049
|
-
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");
|
|
60050
60135
|
const slug = repoSlug(repo2.url);
|
|
60051
60136
|
const { api } = await requireGithub();
|
|
60052
60137
|
const run = await latestRun(api, slug);
|
|
@@ -60064,6 +60149,7 @@ function buildDeployHandlers(deps) {
|
|
|
60064
60149
|
};
|
|
60065
60150
|
return {
|
|
60066
60151
|
"deploy:get": get,
|
|
60152
|
+
"deploy:scan": scanHandler,
|
|
60067
60153
|
"deploy:patch": patch,
|
|
60068
60154
|
"deploy:putSecret": putSecret,
|
|
60069
60155
|
"deploy:reportFinding": reportFinding,
|
|
@@ -61102,7 +61188,7 @@ ${formatLarkSpeakerReminder(speaker)}`
|
|
|
61102
61188
|
}
|
|
61103
61189
|
|
|
61104
61190
|
// src/lark-bot-connector/envelope.ts
|
|
61105
|
-
var
|
|
61191
|
+
var import_node_crypto9 = require("crypto");
|
|
61106
61192
|
var LARK_ENVELOPE_PREFIX = "clawdlbe1";
|
|
61107
61193
|
function verifyLarkEventEnvelope(args) {
|
|
61108
61194
|
const { token, publicKeyPem } = args;
|
|
@@ -61121,7 +61207,7 @@ function verifyLarkEventEnvelope(args) {
|
|
|
61121
61207
|
const data = Buffer.from(`${prefix}.${payloadB64}`, "utf8");
|
|
61122
61208
|
let sigOk = false;
|
|
61123
61209
|
try {
|
|
61124
|
-
sigOk = (0,
|
|
61210
|
+
sigOk = (0, import_node_crypto9.verify)(null, data, publicKeyPem, Buffer.from(sigB64, "base64url"));
|
|
61125
61211
|
} catch {
|
|
61126
61212
|
sigOk = false;
|
|
61127
61213
|
}
|
|
@@ -62488,6 +62574,7 @@ var GATEWAY_ROUTES = {
|
|
|
62488
62574
|
healthz: () => "/healthz",
|
|
62489
62575
|
session: (personaId2) => `/p/${encodeURIComponent(personaId2)}/session`,
|
|
62490
62576
|
sessionSend: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}`,
|
|
62577
|
+
sessionMirror: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}/mirror`,
|
|
62491
62578
|
// 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
|
|
62492
62579
|
volume: () => "/me/volume"
|
|
62493
62580
|
};
|
|
@@ -62504,7 +62591,7 @@ function cloudPersonaToFile(p3) {
|
|
|
62504
62591
|
return {
|
|
62505
62592
|
personaId: p3.personaId + CLOUD_PERSONA_ID_SUFFIX,
|
|
62506
62593
|
label: p3.personaId,
|
|
62507
|
-
tool:
|
|
62594
|
+
tool: p3.engine + CLOUD_TOOL_SUFFIX,
|
|
62508
62595
|
...p3.model ? { model: p3.model } : {},
|
|
62509
62596
|
...p3.effort ? { effort: p3.effort } : {},
|
|
62510
62597
|
public: false,
|
|
@@ -62552,9 +62639,14 @@ var CloudGatewayClient = class {
|
|
|
62552
62639
|
for (const raw of rows) {
|
|
62553
62640
|
const d2 = raw;
|
|
62554
62641
|
if (d2.kind !== "official" || typeof d2.personaId !== "string") continue;
|
|
62642
|
+
if (typeof d2.engine !== "string" || !d2.engine) {
|
|
62643
|
+
this.deps.logger?.warn("cloud persona row has no engine, skipped", { personaId: d2.personaId });
|
|
62644
|
+
continue;
|
|
62645
|
+
}
|
|
62555
62646
|
out.push({
|
|
62556
62647
|
personaId: d2.personaId,
|
|
62557
62648
|
gatewayUrl,
|
|
62649
|
+
engine: d2.engine,
|
|
62558
62650
|
...typeof d2.image === "string" ? { image: d2.image } : {},
|
|
62559
62651
|
...typeof d2.model === "string" && d2.model ? { model: d2.model } : {},
|
|
62560
62652
|
...typeof d2.effort === "string" && d2.effort ? { effort: d2.effort } : {},
|
|
@@ -62577,14 +62669,17 @@ var CloudGatewayClient = class {
|
|
|
62577
62669
|
/**
|
|
62578
62670
|
* 会话通道。返回的行流在 Pod 就绪前就能 write:写入先攒着,收到 `ready` 控制行后按序 POST 出去——
|
|
62579
62671
|
* 编排那头一构造就发 initialize,不用知道 Pod 还在起。
|
|
62672
|
+
* `toolSessionId`:调用方预分配的引擎会话 id(pi 的 --session-id;codex 的 thread id 是协议内的、不传),进 open 的 body `engineSessionId`
|
|
62673
|
+
* (契约 2026-09-18 §3.2)。
|
|
62580
62674
|
*/
|
|
62581
|
-
async openSession(personaId2) {
|
|
62675
|
+
async openSession(personaId2, opts = {}) {
|
|
62582
62676
|
const { apiKey, gatewayUrl } = await this.credentials();
|
|
62583
62677
|
return openHttpLineStream({
|
|
62584
62678
|
fetchImpl: this.fetchImpl,
|
|
62585
62679
|
gatewayUrl,
|
|
62586
62680
|
apiKey,
|
|
62587
62681
|
personaId: personaId2,
|
|
62682
|
+
...opts.toolSessionId ? { engineSessionId: opts.toolSessionId } : {},
|
|
62588
62683
|
logger: this.deps.logger
|
|
62589
62684
|
});
|
|
62590
62685
|
}
|
|
@@ -62629,7 +62724,7 @@ function parseControlLine(line) {
|
|
|
62629
62724
|
}
|
|
62630
62725
|
}
|
|
62631
62726
|
function openHttpLineStream(opts) {
|
|
62632
|
-
const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, logger } = opts;
|
|
62727
|
+
const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, logger } = opts;
|
|
62633
62728
|
const headers = { authorization: `Bearer ${apiKey}`, "content-type": "application/json" };
|
|
62634
62729
|
const ac = new AbortController();
|
|
62635
62730
|
const lineCbs = [];
|
|
@@ -62686,7 +62781,7 @@ function openHttpLineStream(opts) {
|
|
|
62686
62781
|
res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.session(personaId2), {
|
|
62687
62782
|
method: "POST",
|
|
62688
62783
|
headers,
|
|
62689
|
-
body:
|
|
62784
|
+
body: JSON.stringify(engineSessionId ? { engineSessionId } : {}),
|
|
62690
62785
|
signal: ac.signal
|
|
62691
62786
|
});
|
|
62692
62787
|
} catch (e) {
|
|
@@ -62782,6 +62877,16 @@ function openHttpLineStream(opts) {
|
|
|
62782
62877
|
close: () => {
|
|
62783
62878
|
ac.abort();
|
|
62784
62879
|
finish();
|
|
62880
|
+
},
|
|
62881
|
+
mirror: async () => {
|
|
62882
|
+
if (closed || !sessionId) return;
|
|
62883
|
+
const url = gatewayUrl + GATEWAY_ROUTES.sessionMirror(personaId2, sessionId);
|
|
62884
|
+
try {
|
|
62885
|
+
const res = await fetchImpl(url, { method: "POST", headers, body: "" });
|
|
62886
|
+
if (!res.ok) logger?.warn("cloud session mirror failed", { status: res.status });
|
|
62887
|
+
} catch (e) {
|
|
62888
|
+
if (!closed) logger?.warn("cloud session mirror error", { message: e.message });
|
|
62889
|
+
}
|
|
62785
62890
|
}
|
|
62786
62891
|
};
|
|
62787
62892
|
}
|
|
@@ -62928,6 +63033,7 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
62928
63033
|
paths.catch(() => {
|
|
62929
63034
|
});
|
|
62930
63035
|
stream.onClose(() => rejectPaths(new Error("\u4E91\u4E0A\u901A\u9053\u5728 ready \u4E4B\u524D\u5173\u4E86\uFF0C\u62FF\u4E0D\u5230 Pod \u8DEF\u5F84")));
|
|
63036
|
+
let channel = null;
|
|
62931
63037
|
const driver = driveCodexThread({
|
|
62932
63038
|
ctx,
|
|
62933
63039
|
sink,
|
|
@@ -62937,7 +63043,8 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
62937
63043
|
imageInput: cloudImageInput(deps),
|
|
62938
63044
|
// skills 在 Pod 里由 persona 目录自带,本机路径对它没意义
|
|
62939
63045
|
// 「让对端走人」= 断开下行流,gateway 收到就删 Job;信号对 Pod 没有意义
|
|
62940
|
-
kill: () => stream.close()
|
|
63046
|
+
kill: () => stream.close(),
|
|
63047
|
+
onTurnCompleted: () => void channel?.mirror()
|
|
62941
63048
|
});
|
|
62942
63049
|
if (!personaId2) {
|
|
62943
63050
|
queueMicrotask(() => {
|
|
@@ -62947,7 +63054,8 @@ function createCloudCodexSession(ctx, sink, deps) {
|
|
|
62947
63054
|
return driver.session;
|
|
62948
63055
|
}
|
|
62949
63056
|
void deps.client.openSession(personaId2).then(
|
|
62950
|
-
(
|
|
63057
|
+
(opened) => {
|
|
63058
|
+
channel = opened;
|
|
62951
63059
|
channel.onControl((c2) => {
|
|
62952
63060
|
if (c2.kind === "ready") {
|
|
62953
63061
|
resolvePaths({ personaDir: c2.personaDir, workDir: c2.workDir });
|
|
@@ -63110,7 +63218,7 @@ var PiRpcClient = class {
|
|
|
63110
63218
|
|
|
63111
63219
|
// src/tools/pi-session.ts
|
|
63112
63220
|
var import_node_child_process10 = require("child_process");
|
|
63113
|
-
var
|
|
63221
|
+
var import_node_crypto10 = require("crypto");
|
|
63114
63222
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
63115
63223
|
var import_node_path39 = __toESM(require("path"), 1);
|
|
63116
63224
|
|
|
@@ -63288,7 +63396,7 @@ ${dirs.map((d2) => `- ${d2}`).join("\n")}`;
|
|
|
63288
63396
|
}
|
|
63289
63397
|
function createPiSession(ctx, sink, deps = {}) {
|
|
63290
63398
|
const cmd = process.env.PI_BIN ?? "pi";
|
|
63291
|
-
const sessionId = ctx.toolSessionId ?? (0,
|
|
63399
|
+
const sessionId = ctx.toolSessionId ?? (0, import_node_crypto10.randomUUID)();
|
|
63292
63400
|
const skillsDir = import_node_path39.default.join(ctx.cwd, ".claude", "skills");
|
|
63293
63401
|
const exists = deps.skillsDirExists ?? ((d2) => import_node_fs35.default.existsSync(d2));
|
|
63294
63402
|
const args = buildPiArgs(ctx, {
|
|
@@ -64735,7 +64843,7 @@ var wrapper_default = import_websocket.default;
|
|
|
64735
64843
|
|
|
64736
64844
|
// src/transport/local-ws-server.ts
|
|
64737
64845
|
var import_node_http = __toESM(require("http"), 1);
|
|
64738
|
-
var
|
|
64846
|
+
var import_node_crypto11 = require("crypto");
|
|
64739
64847
|
var LocalWsServer = class {
|
|
64740
64848
|
constructor(opts) {
|
|
64741
64849
|
this.opts = opts;
|
|
@@ -64952,7 +65060,7 @@ var LocalWsServer = class {
|
|
|
64952
65060
|
}
|
|
64953
65061
|
}
|
|
64954
65062
|
onConnection(socket, remoteAddress) {
|
|
64955
|
-
const id = (0,
|
|
65063
|
+
const id = (0, import_node_crypto11.randomUUID)();
|
|
64956
65064
|
const subscribedSessions = /* @__PURE__ */ new Map();
|
|
64957
65065
|
const handle = {
|
|
64958
65066
|
id,
|
|
@@ -65917,7 +66025,7 @@ var import_node_path49 = __toESM(require("path"), 1);
|
|
|
65917
66025
|
// src/attachment/upload.ts
|
|
65918
66026
|
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
65919
66027
|
var import_node_path47 = __toESM(require("path"), 1);
|
|
65920
|
-
var
|
|
66028
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
65921
66029
|
var import_promises7 = require("stream/promises");
|
|
65922
66030
|
var UploadError = class extends Error {
|
|
65923
66031
|
constructor(code, message) {
|
|
@@ -65941,11 +66049,11 @@ async function writeUploadedAttachment(args) {
|
|
|
65941
66049
|
} catch (err) {
|
|
65942
66050
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
65943
66051
|
}
|
|
65944
|
-
const hasher =
|
|
66052
|
+
const hasher = import_node_crypto12.default.createHash("sha256");
|
|
65945
66053
|
let actualSize = 0;
|
|
65946
66054
|
const tmpPath = import_node_path47.default.join(
|
|
65947
66055
|
attachmentsRoot,
|
|
65948
|
-
`.upload-${process.pid}-${Date.now()}-${
|
|
66056
|
+
`.upload-${process.pid}-${Date.now()}-${import_node_crypto12.default.randomBytes(4).toString("hex")}`
|
|
65949
66057
|
);
|
|
65950
66058
|
try {
|
|
65951
66059
|
await (0, import_promises7.pipeline)(
|
|
@@ -66789,7 +66897,7 @@ function runAttachmentGc(args) {
|
|
|
66789
66897
|
// src/attachment/group.ts
|
|
66790
66898
|
var import_node_fs46 = __toESM(require("fs"), 1);
|
|
66791
66899
|
var import_node_path51 = __toESM(require("path"), 1);
|
|
66792
|
-
var
|
|
66900
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
66793
66901
|
init_protocol();
|
|
66794
66902
|
var GroupFileStore = class {
|
|
66795
66903
|
dataDir;
|
|
@@ -66878,7 +66986,7 @@ var GroupFileStore = class {
|
|
|
66878
66986
|
entries[idx] = next;
|
|
66879
66987
|
} else {
|
|
66880
66988
|
next = {
|
|
66881
|
-
id: `gf-${
|
|
66989
|
+
id: `gf-${import_node_crypto13.default.randomBytes(6).toString("base64url")}`,
|
|
66882
66990
|
relPath: input.relPath,
|
|
66883
66991
|
from: input.from,
|
|
66884
66992
|
label: input.label,
|
|
@@ -67024,7 +67132,7 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
|
|
|
67024
67132
|
// src/tunnel/tunnel-manager.ts
|
|
67025
67133
|
var import_node_fs51 = __toESM(require("fs"), 1);
|
|
67026
67134
|
var import_node_path56 = __toESM(require("path"), 1);
|
|
67027
|
-
var
|
|
67135
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
67028
67136
|
var import_node_child_process15 = require("child_process");
|
|
67029
67137
|
|
|
67030
67138
|
// src/tunnel/tunnel-store.ts
|
|
@@ -67523,7 +67631,7 @@ var TunnelManager = class {
|
|
|
67523
67631
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
67524
67632
|
});
|
|
67525
67633
|
const tomlPath = import_node_path56.default.join(this.deps.dataDir, "frpc.toml");
|
|
67526
|
-
const proxyName = `clawd-${t2.subdomain}-${localPort}-${
|
|
67634
|
+
const proxyName = `clawd-${t2.subdomain}-${localPort}-${import_node_crypto14.default.randomBytes(3).toString("hex")}`;
|
|
67527
67635
|
const toml = buildFrpcToml({
|
|
67528
67636
|
serverAddr: t2.frpsHost,
|
|
67529
67637
|
serverPort: t2.frpsPort,
|
|
@@ -67624,7 +67732,7 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
67624
67732
|
// src/tunnel/device-key.ts
|
|
67625
67733
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
67626
67734
|
var import_node_path57 = __toESM(require("path"), 1);
|
|
67627
|
-
var
|
|
67735
|
+
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
67628
67736
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
67629
67737
|
function deriveStableDeviceKey(opts = {}) {
|
|
67630
67738
|
const hostname = opts.hostname ?? import_node_os18.default.hostname();
|
|
@@ -67634,13 +67742,13 @@ function deriveStableDeviceKey(opts = {}) {
|
|
|
67634
67742
|
const normalizedDataDir = opts.dataDir ? import_node_path57.default.resolve(opts.dataDir) : null;
|
|
67635
67743
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
67636
67744
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
67637
|
-
return
|
|
67745
|
+
return import_node_crypto15.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
67638
67746
|
}
|
|
67639
67747
|
|
|
67640
67748
|
// src/auth-store.ts
|
|
67641
67749
|
var import_node_fs52 = __toESM(require("fs"), 1);
|
|
67642
67750
|
var import_node_path58 = __toESM(require("path"), 1);
|
|
67643
|
-
var
|
|
67751
|
+
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
67644
67752
|
var AUTH_FILE_NAME = "auth.json";
|
|
67645
67753
|
function authFilePath(dataDir) {
|
|
67646
67754
|
return import_node_path58.default.join(dataDir, AUTH_FILE_NAME);
|
|
@@ -67677,10 +67785,10 @@ function loadOrCreateTunnelDeviceKey(opts) {
|
|
|
67677
67785
|
return tunnelDeviceKey;
|
|
67678
67786
|
}
|
|
67679
67787
|
function defaultGenerateToken() {
|
|
67680
|
-
return
|
|
67788
|
+
return import_node_crypto16.default.randomBytes(32).toString("base64url");
|
|
67681
67789
|
}
|
|
67682
67790
|
function defaultGenerateOwnerPrincipalId() {
|
|
67683
|
-
return `owner-${
|
|
67791
|
+
return `owner-${import_node_crypto16.default.randomUUID()}`;
|
|
67684
67792
|
}
|
|
67685
67793
|
function readAuthFile(file) {
|
|
67686
67794
|
try {
|
|
@@ -67812,7 +67920,7 @@ var OwnerIdentityStore = class {
|
|
|
67812
67920
|
};
|
|
67813
67921
|
|
|
67814
67922
|
// src/feishu-auth/login-flow.ts
|
|
67815
|
-
var
|
|
67923
|
+
var import_node_crypto17 = __toESM(require("crypto"), 1);
|
|
67816
67924
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
67817
67925
|
var LoginFlow = class {
|
|
67818
67926
|
constructor(deps) {
|
|
@@ -67821,7 +67929,7 @@ var LoginFlow = class {
|
|
|
67821
67929
|
deps;
|
|
67822
67930
|
pendingStates = /* @__PURE__ */ new Map();
|
|
67823
67931
|
start() {
|
|
67824
|
-
const state =
|
|
67932
|
+
const state = import_node_crypto17.default.randomBytes(16).toString("base64url");
|
|
67825
67933
|
const now = (this.deps.now ?? Date.now)();
|
|
67826
67934
|
this.pendingStates.set(state, now);
|
|
67827
67935
|
this.gcExpired(now);
|
|
@@ -68352,7 +68460,7 @@ init_protocol();
|
|
|
68352
68460
|
var import_node_fs55 = __toESM(require("fs"), 1);
|
|
68353
68461
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
68354
68462
|
var import_node_path61 = __toESM(require("path"), 1);
|
|
68355
|
-
var
|
|
68463
|
+
var import_node_crypto18 = require("crypto");
|
|
68356
68464
|
init_claude_history();
|
|
68357
68465
|
function readJsonlEntries(file) {
|
|
68358
68466
|
const raw = import_node_fs55.default.readFileSync(file, "utf8");
|
|
@@ -68389,7 +68497,7 @@ function forkSession(input) {
|
|
|
68389
68497
|
throw new Error(`fork: messageUuid not found in transcript: ${input.messageUuid}`);
|
|
68390
68498
|
}
|
|
68391
68499
|
const sliced = mainEntries.slice(0, targetIdx + 1);
|
|
68392
|
-
const forkedToolSessionId = (0,
|
|
68500
|
+
const forkedToolSessionId = (0, import_node_crypto18.randomUUID)();
|
|
68393
68501
|
let parentUuid = null;
|
|
68394
68502
|
const forkedLines = [];
|
|
68395
68503
|
for (const entry of sliced) {
|
|
@@ -70715,6 +70823,7 @@ var METHOD_GRANT_MAP = {
|
|
|
70715
70823
|
// deploy:*(persona 上云):全部 owner-only。里面有凭据录入口、建仓和推送,
|
|
70716
70824
|
// guest 不该看见更不该动。
|
|
70717
70825
|
"deploy:get": ADMIN_ANY,
|
|
70826
|
+
"deploy:scan": ADMIN_ANY,
|
|
70718
70827
|
"deploy:patch": ADMIN_ANY,
|
|
70719
70828
|
"deploy:putSecret": ADMIN_ANY,
|
|
70720
70829
|
"deploy:reportFinding": ADMIN_ANY,
|
|
@@ -70928,6 +71037,7 @@ var METHOD_GRANT_MAP = {
|
|
|
70928
71037
|
"device:connections": ADMIN_ANY
|
|
70929
71038
|
};
|
|
70930
71039
|
var HUMAN_ONLY_METHODS = [
|
|
71040
|
+
"deploy:scan",
|
|
70931
71041
|
"deploy:release",
|
|
70932
71042
|
"deploy:putSecret",
|
|
70933
71043
|
"deploy:rerunJob",
|
|
@@ -70975,7 +71085,7 @@ function computeMethodAccess(args) {
|
|
|
70975
71085
|
}
|
|
70976
71086
|
|
|
70977
71087
|
// src/version.ts
|
|
70978
|
-
var version = "0.2.
|
|
71088
|
+
var version = "0.2.554".length > 0 ? "0.2.554" : "dev";
|
|
70979
71089
|
|
|
70980
71090
|
// src/cli-probe/probe.ts
|
|
70981
71091
|
var fs78 = __toESM(require("fs"), 1);
|
|
@@ -71372,7 +71482,7 @@ init_protocol();
|
|
|
71372
71482
|
// src/extension/bundle-zip.ts
|
|
71373
71483
|
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
71374
71484
|
var import_node_path67 = __toESM(require("path"), 1);
|
|
71375
|
-
var
|
|
71485
|
+
var import_node_crypto19 = __toESM(require("crypto"), 1);
|
|
71376
71486
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
71377
71487
|
async function bundleExtensionDir(dir) {
|
|
71378
71488
|
const entries = await listFilesSorted(dir);
|
|
@@ -71387,7 +71497,7 @@ async function bundleExtensionDir(dir) {
|
|
|
71387
71497
|
compression: "DEFLATE",
|
|
71388
71498
|
compressionOptions: { level: 6 }
|
|
71389
71499
|
});
|
|
71390
|
-
const sha256 =
|
|
71500
|
+
const sha256 = import_node_crypto19.default.createHash("sha256").update(buffer).digest("hex");
|
|
71391
71501
|
return { buffer, sha256 };
|
|
71392
71502
|
}
|
|
71393
71503
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -71455,7 +71565,7 @@ function computePublishCheck(args) {
|
|
|
71455
71565
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
71456
71566
|
var import_node_path69 = __toESM(require("path"), 1);
|
|
71457
71567
|
var import_node_os25 = __toESM(require("os"), 1);
|
|
71458
|
-
var
|
|
71568
|
+
var import_node_crypto20 = __toESM(require("crypto"), 1);
|
|
71459
71569
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
71460
71570
|
|
|
71461
71571
|
// src/extension/paths.ts
|
|
@@ -71484,7 +71594,7 @@ var InstallError = class extends Error {
|
|
|
71484
71594
|
};
|
|
71485
71595
|
async function installFromChannel(args, deps) {
|
|
71486
71596
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
71487
|
-
const computed =
|
|
71597
|
+
const computed = import_node_crypto20.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
71488
71598
|
if (computed !== snapshotHash) {
|
|
71489
71599
|
throw new InstallError(
|
|
71490
71600
|
"HASH_MISMATCH",
|
|
@@ -71576,7 +71686,7 @@ async function installFromChannel(args, deps) {
|
|
|
71576
71686
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
71577
71687
|
var import_node_path70 = __toESM(require("path"), 1);
|
|
71578
71688
|
var import_node_os26 = __toESM(require("os"), 1);
|
|
71579
|
-
var
|
|
71689
|
+
var import_node_crypto21 = __toESM(require("crypto"), 1);
|
|
71580
71690
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
71581
71691
|
var UpdateError = class extends Error {
|
|
71582
71692
|
constructor(code, message) {
|
|
@@ -71614,7 +71724,7 @@ async function updateFromChannel(args, deps) {
|
|
|
71614
71724
|
if (e instanceof UpdateError) throw e;
|
|
71615
71725
|
throw e;
|
|
71616
71726
|
}
|
|
71617
|
-
const computed =
|
|
71727
|
+
const computed = import_node_crypto21.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
71618
71728
|
if (computed !== snapshotHash) {
|
|
71619
71729
|
throw new UpdateError(
|
|
71620
71730
|
"HASH_MISMATCH",
|
|
@@ -72743,7 +72853,7 @@ async function uninstall(deps) {
|
|
|
72743
72853
|
}
|
|
72744
72854
|
|
|
72745
72855
|
// src/handlers/index.ts
|
|
72746
|
-
var
|
|
72856
|
+
var import_node_crypto22 = require("crypto");
|
|
72747
72857
|
|
|
72748
72858
|
// src/handlers/peer-exec.ts
|
|
72749
72859
|
init_protocol();
|
|
@@ -72898,7 +73008,7 @@ function buildMethodHandlers(deps) {
|
|
|
72898
73008
|
const c2 = deps.contactStore.get(deviceId);
|
|
72899
73009
|
return c2 ? { deviceId: c2.deviceId, remoteUrl: c2.remoteUrl, connectToken: c2.connectToken } : null;
|
|
72900
73010
|
},
|
|
72901
|
-
genId: () => (0,
|
|
73011
|
+
genId: () => (0, import_node_crypto22.randomUUID)(),
|
|
72902
73012
|
now: () => Date.now(),
|
|
72903
73013
|
forwardInboxPostToPeer,
|
|
72904
73014
|
logger: deps.logger
|
|
@@ -73627,7 +73737,7 @@ async function startDaemon(config) {
|
|
|
73627
73737
|
const channelStore = createChannelStore({
|
|
73628
73738
|
filePath: import_node_path78.default.join(config.dataDir, "channels.json"),
|
|
73629
73739
|
now: () => Date.now(),
|
|
73630
|
-
genId: () => `ch-${(0,
|
|
73740
|
+
genId: () => `ch-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
|
|
73631
73741
|
onFlushError: (err) => logger.error("channel store flush failed", {
|
|
73632
73742
|
reason: err instanceof Error ? err.message : String(err)
|
|
73633
73743
|
})
|
|
@@ -73636,7 +73746,7 @@ async function startDaemon(config) {
|
|
|
73636
73746
|
const topicStore = createTopicStore({
|
|
73637
73747
|
filePath: import_node_path78.default.join(config.dataDir, "topics.json"),
|
|
73638
73748
|
now: () => Date.now(),
|
|
73639
|
-
genId: () => `tp-${(0,
|
|
73749
|
+
genId: () => `tp-${(0, import_node_crypto23.randomUUID)().slice(0, 8)}`,
|
|
73640
73750
|
onFlushError: (err) => logger.error("topic store flush failed", {
|
|
73641
73751
|
reason: err instanceof Error ? err.message : String(err)
|
|
73642
73752
|
})
|
|
@@ -73644,7 +73754,7 @@ async function startDaemon(config) {
|
|
|
73644
73754
|
await topicStore.load();
|
|
73645
73755
|
let deliverPendingRef;
|
|
73646
73756
|
const personaDispatchManager = new PersonaDispatchManager({
|
|
73647
|
-
genId: () => (0,
|
|
73757
|
+
genId: () => (0, import_node_crypto23.randomUUID)(),
|
|
73648
73758
|
store: dispatchStore,
|
|
73649
73759
|
now: () => Date.now(),
|
|
73650
73760
|
onCompleted: (sid) => deliverPendingRef?.(sid),
|
|
@@ -74609,15 +74719,15 @@ async function startDaemon(config) {
|
|
|
74609
74719
|
});
|
|
74610
74720
|
Object.assign(handlers, shiftHandlers);
|
|
74611
74721
|
{
|
|
74612
|
-
const
|
|
74722
|
+
const files = await Promise.all(personaStore.list().map(async (personaId2) => ({
|
|
74613
74723
|
personaId: personaId2,
|
|
74614
|
-
|
|
74724
|
+
file: await readScanFile(personaStore.personaDirPath(personaId2)).catch(() => void 0)
|
|
74615
74725
|
})));
|
|
74616
74726
|
const interrupted = reconcileInterruptedScans({
|
|
74617
|
-
|
|
74727
|
+
files,
|
|
74618
74728
|
now: Date.now(),
|
|
74619
|
-
write: (personaId2,
|
|
74620
|
-
void
|
|
74729
|
+
write: (personaId2, file) => {
|
|
74730
|
+
void writeScanFile(personaStore.personaDirPath(personaId2), file).catch((err) => {
|
|
74621
74731
|
logger.warn("deploy scan reconcile write failed", { personaId: personaId2, error: String(err) });
|
|
74622
74732
|
});
|
|
74623
74733
|
}
|
|
@@ -74638,10 +74748,7 @@ async function startDaemon(config) {
|
|
|
74638
74748
|
org: PERSONA_REPO_ORG,
|
|
74639
74749
|
now: () => Date.now(),
|
|
74640
74750
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
74641
|
-
startScan: (personaId2, scanBase) => {
|
|
74642
|
-
const assetLines = SCANNABLE_ASSETS.map(
|
|
74643
|
-
(a2) => ` - ${a2.name}${a2.path ? `\uFF08${a2.path(config.dataDir)}\uFF09` : ""}\uFF1A${a2.what}`
|
|
74644
|
-
).join("\n");
|
|
74751
|
+
startScan: (personaId2, scanBase, scanId) => {
|
|
74645
74752
|
const baseLines = scanBase.base ? [
|
|
74646
74753
|
`\u672C\u6B21\u662F**${SCAN_BASE_LABEL[scanBase.kind]}**\uFF1A\u53EA\u770B\u8DDF\u7EBF\u4E0A\u6B63\u8DD1\u90A3\u7248\u7684\u5DEE\u522B\uFF0C`,
|
|
74647
74754
|
`\u90A3\u4E00\u7248\u662F commit ${scanBase.base}\u3002`,
|
|
@@ -74651,14 +74758,12 @@ async function startDaemon(config) {
|
|
|
74651
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`
|
|
74652
74759
|
];
|
|
74653
74760
|
const prompt = [
|
|
74654
|
-
`\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`,
|
|
74655
74762
|
"",
|
|
74656
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`,
|
|
74657
74765
|
...baseLines,
|
|
74658
74766
|
"",
|
|
74659
|
-
"\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",
|
|
74660
|
-
assetLines || " \uFF08\u65E0\uFF09",
|
|
74661
|
-
"",
|
|
74662
74767
|
"\u6309 scan-persona-for-deploy skill \u5E72\u6D3B\uFF1A\u8FB9\u67E5\u8FB9\u7528 deploy:reportFinding \u4E0A\u62A5\uFF0C",
|
|
74663
74768
|
"\u5168\u90E8\u67E5\u5B8C\u8C03 deploy:finishScan \u6536\u5C3E\u3002"
|
|
74664
74769
|
].join("\n");
|