@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.
@@ -21287,8 +21287,8 @@ var GithubAuthOauthStartArgsSchema = external_exports.object({
21287
21287
  }).strict();
21288
21288
 
21289
21289
  // ../protocol/src/environment-schemas.ts
21290
- var EnvCliSchema = external_exports.object({
21291
- // 可执行文件名,装配后要能在 PATH 里直接调到('aliyun' / 's' / 'pnpm' / 'python3')
21290
+ var EnvPackageSchema = external_exports.object({
21291
+ // 装完后在 PATH 里能直接调到的名字('aliyun' / 's' / 'pnpm' / 'python3');npm 包报 bin 名,包名进 from
21292
21292
  name: external_exports.string().min(1),
21293
21293
  /**
21294
21294
  * 从哪装,约定 `<kind>:<ref>`:`npm:@serverless-devs/s` / `github:aliyun/aliyun-cli` /
@@ -21321,9 +21321,6 @@ var EnvSecretSchema = external_exports.object({
21321
21321
  // supabase 是 url/anon/service-role/jwt 四个),所以是数组不是单值。
21322
21322
  env: external_exports.array(external_exports.string().min(1)).min(1)
21323
21323
  });
21324
- var EnvAssetSchema = external_exports.object({
21325
- name: external_exports.string().min(1)
21326
- });
21327
21324
  var 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");
21328
21325
  var CONNECTOR_KINDS = ["proxy", "env"];
21329
21326
  var CONNECTOR_RUNNERS = ["mcp", "cli"];
@@ -21342,10 +21339,23 @@ var EnvConnectorSchema = external_exports.object({
21342
21339
  fields: external_exports.array(ConnectorFieldSchema).default([]),
21343
21340
  http: external_exports.object({ url: external_exports.string().min(1), headers: external_exports.record(external_exports.string(), external_exports.string()) }).optional()
21344
21341
  });
21342
+ var normalizeLegacyManifest = (v) => {
21343
+ if (!v || typeof v !== "object") return v;
21344
+ let o = v;
21345
+ if ("clis" in o && Array.isArray(o.clis)) {
21346
+ const { clis, ...rest } = o;
21347
+ o = "packages" in rest ? rest : { ...rest, packages: clis };
21348
+ }
21349
+ if ("assets" in o && Array.isArray(o.assets) && o.assets.length === 0) {
21350
+ const { assets: _dropped, ...rest } = o;
21351
+ o = rest;
21352
+ }
21353
+ return o;
21354
+ };
21345
21355
  var MANIFEST_BODY = {
21346
- clis: external_exports.array(EnvCliSchema),
21356
+ // 构镜像时要装进去的所有东西(工具 / 运行时 / 包)。底座已有的构建器会跳过;persona 目录整个进镜像,不在这里列
21357
+ packages: external_exports.array(EnvPackageSchema),
21347
21358
  secrets: external_exports.array(EnvSecretSchema),
21348
- assets: external_exports.array(EnvAssetSchema),
21349
21359
  /**
21350
21360
  * 装了哪些连接器(design 2026-09-11-mcp-connector §3.1)。一条一个对象,形状见 `EnvConnectorSchema`。
21351
21361
  *
@@ -21357,14 +21367,14 @@ var MANIFEST_BODY = {
21357
21367
  connectors: external_exports.array(EnvConnectorSchema).default([])
21358
21368
  };
21359
21369
  var EnvManifestBodySchema = external_exports.object(MANIFEST_BODY);
21360
- var EnvironmentManifestSchema = external_exports.object({
21370
+ var EnvironmentManifestSchema = external_exports.preprocess(normalizeLegacyManifest, external_exports.object({
21361
21371
  // 格式版本。manifest 要跨进程(daemon 扫 → 镜像构建器读)也跨时间(部署记录留档),
21362
21372
  // 加字段兼容但改语义不兼容,留一个显式版本位。
21363
21373
  version: external_exports.literal(1),
21364
21374
  ...MANIFEST_BODY
21365
- }).strict();
21375
+ }).strict());
21366
21376
  var EnvEvidenceSchema = external_exports.object({
21367
- // 这条证据支持的是哪一项:`${kind}:${name}`,如 'cli:aliyun' / 'secret:aliyun-ak'
21377
+ // 这条证据支持的是哪一项:`${kind}:${name}`,如 'package:aliyun' / 'secret:aliyun-ak' / 'connector:feishu'
21368
21378
  target: external_exports.string().min(1),
21369
21379
  // 一句人话:为什么需要它 + 从哪看出来的。单据默认显示这句。
21370
21380
  reason: external_exports.string().min(1),
@@ -21386,33 +21396,30 @@ var SCAN_BASE_KINDS = ["hard", "soft"];
21386
21396
  var ScanBaseKindSchema = external_exports.enum(SCAN_BASE_KINDS);
21387
21397
  var DeployScanStatusSchema = external_exports.enum(["scanning", "done", "failed"]);
21388
21398
  var DeployScanSchema = external_exports.object({
21399
+ /** 历史里指名用(发布 / patch / putSecret 都按它找)。daemon 生成;存量单条记录迁过来的叫 `legacy` */
21400
+ id: external_exports.string().min(1),
21401
+ /**
21402
+ * 这次扫描的清单——原来在 `deploy.json` 里的那份,现在每次扫描各存一份。
21403
+ * 扫描期由 `deploy:reportFinding` 逐条长出来,扫完人在页面上改(`deploy:patch`)也改这份;
21404
+ * 发布时选中的那条被写成 `deploy.json` 进仓库。
21405
+ */
21406
+ manifest: EnvironmentManifestSchema,
21389
21407
  /** 干这次扫描的 butler 会话,「看它怎么干的」入口 */
21390
21408
  scanSessionId: external_exports.string().optional(),
21391
21409
  status: DeployScanStatusSchema,
21392
21410
  evidence: external_exports.array(EnvEvidenceSchema).default([]),
21393
21411
  notes: external_exports.array(external_exports.string()).default([]),
21394
- /** 扫描时被告知的共享资产名单(判「扫描器后来长了新本事」用) */
21395
- scannedAssets: external_exports.array(external_exports.string()).default([]),
21396
21412
  /**
21397
- * 这次扫描是**对哪个 commit 的断言**。`deploy:release` 在起扫描之前会先把 persona 目录
21398
- * 提交(只提交不推,推是发布末尾那一下的事),所以这个值一定是仓库里真实存在的一版。
21413
+ * 这次扫描是**对哪个内容版本的断言**:起扫描前先把 persona 目录提交到本地(只提交不推,推是发布的事),
21414
+ * 落存那一下的 HEAD 就是它——发布 commit 不算内容版本,见 `contentHeadOf`。所以这个值一定是仓库里真实存在的一版,
21415
+ * 同一内容上扫几次 `head` 都相同。**从此不动**:发布不再把它挪成发布 commit(线上是哪版记在 `DeployReleased` 里)。
21399
21416
  *
21400
- * 有了它,「清单过没过时」才是个可判定的等式(`head === repo.head`),
21417
+ * 有了它,「清单过没过时」才是个可判定的等式(`head === contentHeadOf(repo, released)`),
21401
21418
  * 而不是从 `dirty` / `ahead` 里猜——那两个字段判不出「清单是好几版之前扫的」。
21402
21419
  *
21403
21420
  * 缺席 = 旧版 clawd 扫的记录。**一律当过时**:没有第三种情况,重扫一次就有了。
21404
21421
  */
21405
21422
  head: external_exports.string().min(1).optional(),
21406
- /**
21407
- * 上次**发布**推的那个 commit ——「线上正跑的是哪一版」在本机唯一的答案,
21408
- * 也就是软扫的基准。只有 `deploy:release` 写它,扫描不动。
21409
- *
21410
- * 为什么不能用 `head` 顶替:`head` 被扫描和发布轮流覆盖(扫描前落存一版、发布后又挪一版),
21411
- * 它回答的是「清单绑在哪个 commit 上」,回答不了「上次发布是哪一版」。
21412
- *
21413
- * 缺席 = 这个 persona 还从没发布过,没有线上版本可比,软扫退硬扫。
21414
- */
21415
- releasedHead: external_exports.string().min(1).optional(),
21416
21423
  /**
21417
21424
  * 这次**实际**怎么扫的。档位由 daemon 定(有线上版本可比 + 有跑完的清单 → 软扫,否则硬扫),
21418
21425
  * 页面要显示的是真正发生的那个。
@@ -21421,12 +21428,23 @@ var DeployScanSchema = external_exports.object({
21421
21428
  * 定成必填会让 `readScan` 当场抛、上云页面直接打不开。而老记录确实都是从零扫的。
21422
21429
  */
21423
21430
  baseKind: ScanBaseKindSchema.default("hard"),
21424
- /** 软扫比的那个 commit(= `releasedHead`)。硬扫时缺席 */
21431
+ /** 软扫比的那个 commit(= 当时 `DeployReleased.head`)。硬扫时缺席 */
21425
21432
  base: external_exports.string().min(1).optional(),
21426
21433
  startedAt: external_exports.number(),
21427
21434
  finishedAt: external_exports.number().optional(),
21428
21435
  error: external_exports.string().optional()
21429
21436
  }).strict();
21437
+ var DeployReleasedSchema = external_exports.object({
21438
+ head: external_exports.string().min(1),
21439
+ contentHead: external_exports.string().min(1),
21440
+ scanId: external_exports.string().min(1),
21441
+ at: external_exports.number()
21442
+ }).strict();
21443
+ var DeployScanFileSchema = external_exports.object({
21444
+ version: external_exports.literal(2),
21445
+ scans: external_exports.array(DeployScanSchema),
21446
+ released: DeployReleasedSchema.optional()
21447
+ }).strict();
21430
21448
  var DeployRepoSchema = external_exports.object({
21431
21449
  url: external_exports.string().min(1),
21432
21450
  branch: external_exports.string().min(1),
@@ -21475,8 +21493,10 @@ var DeployStateSchema = external_exports.object({
21475
21493
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
21476
21494
  github: GithubAuthStatusSchema.optional(),
21477
21495
  repo: DeployRepoSchema.optional(),
21478
- file: DeployFileSchema.optional(),
21479
- scan: DeployScanSchema.optional(),
21496
+ /** 扫描历史,升序,最后一条最新。没扫过就是空数组 */
21497
+ scans: external_exports.array(DeployScanSchema).default([]),
21498
+ /** 线上正跑的是哪一版;从没发布过就缺席 */
21499
+ released: DeployReleasedSchema.optional(),
21480
21500
  /** 没仓库时缺席(查不了) */
21481
21501
  secrets: external_exports.array(DeploySecretPresenceSchema).optional(),
21482
21502
  /**
@@ -21485,27 +21505,28 @@ var DeployStateSchema = external_exports.object({
21485
21505
  */
21486
21506
  run: DeployRunSchema.optional(),
21487
21507
  /**
21488
- * `.clawd/deploy.json` 读不出来(人手改坏了)时的人话。有它就没有 `file`。
21489
- * 页面据此给一条能走的路(重新扫描会整份重写),而不是白屏。
21508
+ * `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
21509
+ * 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
21490
21510
  */
21491
- fileError: external_exports.string().optional()
21511
+ historyError: external_exports.string().optional()
21512
+ }).strict();
21513
+ var DeployReleaseResultSchema = external_exports.object({
21514
+ sha: external_exports.string().min(1),
21515
+ actionsUrl: external_exports.string().min(1)
21492
21516
  }).strict();
21493
- var DeployReleaseResultSchema = external_exports.discriminatedUnion("outcome", [
21494
- external_exports.object({
21495
- outcome: external_exports.literal("pushed"),
21496
- sha: external_exports.string().min(1),
21497
- actionsUrl: external_exports.string().min(1)
21498
- }).strict(),
21499
- external_exports.object({ outcome: external_exports.literal("scanning") }).strict()
21500
- ]);
21501
21517
  var DeployPersonaArgsSchema = external_exports.object({
21502
21518
  personaId: external_exports.string().min(1)
21503
21519
  }).strict();
21504
- var DeployReleaseArgsSchema = DeployPersonaArgsSchema.extend({
21520
+ var DeployScanArgsSchema = DeployPersonaArgsSchema.extend({
21505
21521
  scanBase: ScanBaseKindSchema.optional()
21506
21522
  }).strict();
21523
+ var DeployReleaseArgsSchema = DeployPersonaArgsSchema.extend({
21524
+ scanId: external_exports.string().min(1)
21525
+ }).strict();
21507
21526
  var DeployPatchArgsSchema = external_exports.object({
21508
21527
  personaId: external_exports.string().min(1),
21528
+ /** 改的是历史里哪次扫描的清单。扫描期 butler 删条目也走这条(scanId 在它的任务提示里) */
21529
+ scanId: external_exports.string().min(1),
21509
21530
  patch: external_exports.object({
21510
21531
  // 人工补依赖;整份替换 manifest
21511
21532
  manifest: EnvironmentManifestSchema.optional()
@@ -21513,6 +21534,8 @@ var DeployPatchArgsSchema = external_exports.object({
21513
21534
  }).strict();
21514
21535
  var DeployPutSecretArgsSchema = external_exports.object({
21515
21536
  personaId: external_exports.string().min(1),
21537
+ /** 按哪次扫描的清单校验凭据名 / env 名。值本身是仓库级的(GitHub Secrets),跟扫描无关 */
21538
+ scanId: external_exports.string().min(1),
21516
21539
  // 对应 manifest.secrets[].name
21517
21540
  name: external_exports.string().min(1),
21518
21541
  // 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
@@ -21525,7 +21548,7 @@ var DeployRerunJobArgsSchema = external_exports.object({
21525
21548
  }).strict();
21526
21549
  var DeployReportFindingArgsSchema = external_exports.object({
21527
21550
  personaId: external_exports.string().min(1),
21528
- kind: external_exports.enum(["cli", "secret", "asset", "connector"]),
21551
+ kind: external_exports.enum(["package", "secret", "connector"]),
21529
21552
  // 该条目在 manifest 对应数组里的内容(形状随 kind 变,handler 按 kind 收窄校验)
21530
21553
  // connector 的 item 是 butler 报的 CLI 连接器({ name, fields? }),daemon 补齐 kind / runner / id 后落进清单;
21531
21554
  // MCP 连接器不经这里——由 daemon 从 .mcp.json 机械列出(design 2026-09-11-mcp-connector §3.2)
@@ -22662,7 +22685,7 @@ var EngineConnectPoolResponseSchema = external_exports.object({
22662
22685
  }).strict();
22663
22686
 
22664
22687
  // ../protocol/src/persona-schemas.ts
22665
- var PERSONA_SIZES = ["small", "medium", "large"];
22688
+ var PERSONA_SIZES = ["tiny", "small", "medium", "large"];
22666
22689
  var PersonaSizeSchema = external_exports.enum(PERSONA_SIZES);
22667
22690
  var PersonaFileSchema = external_exports.object({
22668
22691
  personaId: external_exports.string(),
@@ -22680,7 +22703,7 @@ var PersonaFileSchema = external_exports.object({
22680
22703
  // 与 model 平行:persona 新建 session 的默认 tool,可平滑切换。
22681
22704
  tool: external_exports.string().optional(),
22682
22705
  /**
22683
- * 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`small` / `medium` / `large`,没写按 medium。
22706
+ * 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`tiny` / `small` / `medium` / `large`,没写按 medium。
22684
22707
  * 只有发布上云的流水线读它(cloud-host/pipeline/deploy-file.mjs 收窄值域,不认识的拒发);本机跑不看。
22685
22708
  * 协议层同 model / effort 保持 string(文件层向前兼容);改它的入口是上云页标题行的规格下拉
22686
22709
  * (design 2026-09-20-persona-size-ui),`persona:update` 那头按 PersonaSizeSchema 收窄。
@@ -22797,7 +22820,7 @@ var PersonaUpdateArgsSchema = external_exports.object({
22797
22820
  model: external_exports.string().optional(),
22798
22821
  effort: external_exports.string().optional(),
22799
22822
  tool: external_exports.string().optional(),
22800
- // 云上任务 Pod 的规格档:只收三档之一(PersonaFile 层是 string,边界在这里收窄)
22823
+ // 云上任务 Pod 的规格档:只收四档之一(PersonaFile 层是 string,边界在这里收窄)
22801
22824
  size: PersonaSizeSchema.optional(),
22802
22825
  personality: external_exports.string().optional(),
22803
22826
  // 人格补充层正文(见 PersonaInfoResponseSchema.personalityLocal)。与 personality 各写
@@ -23975,15 +23998,19 @@ var METHOD_DOCS = {
23975
23998
  },
23976
23999
  // ---- deploy:*(persona 上云:persona 目录 = git 仓库工作树,发布 = 推送触发仓库流水线)----
23977
24000
  "deploy:get": {
23978
- summary: "\u4E0A\u4E91\u9875\u9762\u8981\u7684\u5168\u90E8\uFF1A\u4ED3\u5E93\u7ED1\u5B9A\u72B6\u6001\uFF08git \u73B0\u7B97\uFF09\u3001\u90E8\u7F72\u6587\u4EF6\u3001\u626B\u63CF\u8BB0\u5F55\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",
24001
+ 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",
23979
24002
  args: DeployPersonaArgsSchema
23980
24003
  },
24004
+ "deploy:scan": {
24005
+ 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",
24006
+ args: DeployScanArgsSchema
24007
+ },
23981
24008
  "deploy:patch": {
23982
- summary: "\u7F16\u8F91\u90E8\u7F72\u6587\u4EF6\uFF1A\u624B\u5DE5\u8865\u4F9D\u8D56\u3002**\u4E0D\u80FD\u4ECE\u8FD9\u91CC\u4F20\u51ED\u636E\u7684\u503C**\uFF08\u503C\u8D70 deploy:putSecret\uFF09\uFF1Bowner-only",
24009
+ 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",
23983
24010
  args: DeployPatchArgsSchema
23984
24011
  },
23985
24012
  "deploy:putSecret": {
23986
- 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\u8BE5\u51ED\u636E\u5728\u6E05\u5355\u91CC\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
24013
+ 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",
23987
24014
  args: DeployPutSecretArgsSchema
23988
24015
  },
23989
24016
  "deploy:reportFinding": {
@@ -23995,11 +24022,11 @@ var METHOD_DOCS = {
23995
24022
  args: DeployFinishScanArgsSchema
23996
24023
  },
23997
24024
  "deploy:release": {
23998
- summary: "\u53D1\u5E03\u2014\u2014\u4E0A\u4E91\u552F\u4E00\u7684\u52A8\u4F5C\uFF0C\u626B\u63CF\u662F\u5B83\u7684\u7B2C\u4E00\u6BB5\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\u6E05\u5355\u8FC7\u65F6\uFF08persona \u6539\u8FC7 / \u6CA1\u626B\u8FC7 / \u4E0A\u6B21\u626B\u5931\u8D25\uFF09\uFF1A\u5148\u628A persona \u76EE\u5F55\u63D0\u4EA4\u5230\u672C\u5730\uFF08\u4E0D\u63A8\uFF09\uFF0C\u8D77 persona-butler \u7684\u626B\u63CF\u4F1A\u8BDD\uFF0C\u56DE outcome=scanning\u2014\u2014\u7ED3\u679C\u5199\u8FDB .clawd/deploy.json\uFF08\u6E05\u5355\uFF0C\u8FDB\u4ED3\u5E93\uFF09\u4E0E .clawd/deploy-scan.json\uFF08\u8BC1\u636E\uFF0C\u4E0D\u8FDB\u4ED3\u5E93\uFF09\uFF0C\u6B63\u5728\u626B\u7684\u8BDD\u539F\u6837\u8FD4\u56DE\u4E0D\u8D77\u7B2C\u4E8C\u4E2A\u4F1A\u8BDD\u3002\u6E05\u5355\u65B0\u9C9C\uFF1A\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 \u91CD\u5199 workflow \u6A21\u677F \u2192 git add -A \u2192 \u7A7A\u63D0\u4EA4\u4E5F\u5141\u8BB8 \u2192 push\uFF0C\u56DE outcome=pushed \u5E26 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\u5E26 scanBase\uFF08hard / soft\uFF09= \u8FD9\u6B21\u4E00\u5B9A\u5148\u626B\u3001\u6309\u8BE5\u6863\u4F4D\u626B\uFF08soft \u4E0D\u6210\u7ACB\u81EA\u52A8\u9000 hard\uFF09\uFF0C\u6E05\u5355\u65B0\u9C9C\u4E5F\u56DE scanning\uFF1Bowner-only",
24025
+ 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",
23999
24026
  args: DeployReleaseArgsSchema
24000
24027
  },
24001
24028
  "deploy:rerunJob": {
24002
- 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\u6216\u51ED\u636E\u8981\u8D70 deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
24029
+ 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",
24003
24030
  args: DeployRerunJobArgsSchema
24004
24031
  },
24005
24032
  // ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
@@ -21473,8 +21473,8 @@ var GithubAuthOauthStartArgsSchema = external_exports.object({
21473
21473
  }).strict();
21474
21474
 
21475
21475
  // ../protocol/src/environment-schemas.ts
21476
- var EnvCliSchema = external_exports.object({
21477
- // 可执行文件名,装配后要能在 PATH 里直接调到('aliyun' / 's' / 'pnpm' / 'python3')
21476
+ var EnvPackageSchema = external_exports.object({
21477
+ // 装完后在 PATH 里能直接调到的名字('aliyun' / 's' / 'pnpm' / 'python3');npm 包报 bin 名,包名进 from
21478
21478
  name: external_exports.string().min(1),
21479
21479
  /**
21480
21480
  * 从哪装,约定 `<kind>:<ref>`:`npm:@serverless-devs/s` / `github:aliyun/aliyun-cli` /
@@ -21507,9 +21507,6 @@ var EnvSecretSchema = external_exports.object({
21507
21507
  // supabase 是 url/anon/service-role/jwt 四个),所以是数组不是单值。
21508
21508
  env: external_exports.array(external_exports.string().min(1)).min(1)
21509
21509
  });
21510
- var EnvAssetSchema = external_exports.object({
21511
- name: external_exports.string().min(1)
21512
- });
21513
21510
  var 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");
21514
21511
  var CONNECTOR_KINDS = ["proxy", "env"];
21515
21512
  var CONNECTOR_RUNNERS = ["mcp", "cli"];
@@ -21528,10 +21525,23 @@ var EnvConnectorSchema = external_exports.object({
21528
21525
  fields: external_exports.array(ConnectorFieldSchema).default([]),
21529
21526
  http: external_exports.object({ url: external_exports.string().min(1), headers: external_exports.record(external_exports.string(), external_exports.string()) }).optional()
21530
21527
  });
21528
+ var normalizeLegacyManifest = (v) => {
21529
+ if (!v || typeof v !== "object") return v;
21530
+ let o = v;
21531
+ if ("clis" in o && Array.isArray(o.clis)) {
21532
+ const { clis, ...rest } = o;
21533
+ o = "packages" in rest ? rest : { ...rest, packages: clis };
21534
+ }
21535
+ if ("assets" in o && Array.isArray(o.assets) && o.assets.length === 0) {
21536
+ const { assets: _dropped, ...rest } = o;
21537
+ o = rest;
21538
+ }
21539
+ return o;
21540
+ };
21531
21541
  var MANIFEST_BODY = {
21532
- clis: external_exports.array(EnvCliSchema),
21542
+ // 构镜像时要装进去的所有东西(工具 / 运行时 / 包)。底座已有的构建器会跳过;persona 目录整个进镜像,不在这里列
21543
+ packages: external_exports.array(EnvPackageSchema),
21533
21544
  secrets: external_exports.array(EnvSecretSchema),
21534
- assets: external_exports.array(EnvAssetSchema),
21535
21545
  /**
21536
21546
  * 装了哪些连接器(design 2026-09-11-mcp-connector §3.1)。一条一个对象,形状见 `EnvConnectorSchema`。
21537
21547
  *
@@ -21543,14 +21553,14 @@ var MANIFEST_BODY = {
21543
21553
  connectors: external_exports.array(EnvConnectorSchema).default([])
21544
21554
  };
21545
21555
  var EnvManifestBodySchema = external_exports.object(MANIFEST_BODY);
21546
- var EnvironmentManifestSchema = external_exports.object({
21556
+ var EnvironmentManifestSchema = external_exports.preprocess(normalizeLegacyManifest, external_exports.object({
21547
21557
  // 格式版本。manifest 要跨进程(daemon 扫 → 镜像构建器读)也跨时间(部署记录留档),
21548
21558
  // 加字段兼容但改语义不兼容,留一个显式版本位。
21549
21559
  version: external_exports.literal(1),
21550
21560
  ...MANIFEST_BODY
21551
- }).strict();
21561
+ }).strict());
21552
21562
  var EnvEvidenceSchema = external_exports.object({
21553
- // 这条证据支持的是哪一项:`${kind}:${name}`,如 'cli:aliyun' / 'secret:aliyun-ak'
21563
+ // 这条证据支持的是哪一项:`${kind}:${name}`,如 'package:aliyun' / 'secret:aliyun-ak' / 'connector:feishu'
21554
21564
  target: external_exports.string().min(1),
21555
21565
  // 一句人话:为什么需要它 + 从哪看出来的。单据默认显示这句。
21556
21566
  reason: external_exports.string().min(1),
@@ -21572,33 +21582,30 @@ var SCAN_BASE_KINDS = ["hard", "soft"];
21572
21582
  var ScanBaseKindSchema = external_exports.enum(SCAN_BASE_KINDS);
21573
21583
  var DeployScanStatusSchema = external_exports.enum(["scanning", "done", "failed"]);
21574
21584
  var DeployScanSchema = external_exports.object({
21585
+ /** 历史里指名用(发布 / patch / putSecret 都按它找)。daemon 生成;存量单条记录迁过来的叫 `legacy` */
21586
+ id: external_exports.string().min(1),
21587
+ /**
21588
+ * 这次扫描的清单——原来在 `deploy.json` 里的那份,现在每次扫描各存一份。
21589
+ * 扫描期由 `deploy:reportFinding` 逐条长出来,扫完人在页面上改(`deploy:patch`)也改这份;
21590
+ * 发布时选中的那条被写成 `deploy.json` 进仓库。
21591
+ */
21592
+ manifest: EnvironmentManifestSchema,
21575
21593
  /** 干这次扫描的 butler 会话,「看它怎么干的」入口 */
21576
21594
  scanSessionId: external_exports.string().optional(),
21577
21595
  status: DeployScanStatusSchema,
21578
21596
  evidence: external_exports.array(EnvEvidenceSchema).default([]),
21579
21597
  notes: external_exports.array(external_exports.string()).default([]),
21580
- /** 扫描时被告知的共享资产名单(判「扫描器后来长了新本事」用) */
21581
- scannedAssets: external_exports.array(external_exports.string()).default([]),
21582
21598
  /**
21583
- * 这次扫描是**对哪个 commit 的断言**。`deploy:release` 在起扫描之前会先把 persona 目录
21584
- * 提交(只提交不推,推是发布末尾那一下的事),所以这个值一定是仓库里真实存在的一版。
21599
+ * 这次扫描是**对哪个内容版本的断言**:起扫描前先把 persona 目录提交到本地(只提交不推,推是发布的事),
21600
+ * 落存那一下的 HEAD 就是它——发布 commit 不算内容版本,见 `contentHeadOf`。所以这个值一定是仓库里真实存在的一版,
21601
+ * 同一内容上扫几次 `head` 都相同。**从此不动**:发布不再把它挪成发布 commit(线上是哪版记在 `DeployReleased` 里)。
21585
21602
  *
21586
- * 有了它,「清单过没过时」才是个可判定的等式(`head === repo.head`),
21603
+ * 有了它,「清单过没过时」才是个可判定的等式(`head === contentHeadOf(repo, released)`),
21587
21604
  * 而不是从 `dirty` / `ahead` 里猜——那两个字段判不出「清单是好几版之前扫的」。
21588
21605
  *
21589
21606
  * 缺席 = 旧版 clawd 扫的记录。**一律当过时**:没有第三种情况,重扫一次就有了。
21590
21607
  */
21591
21608
  head: external_exports.string().min(1).optional(),
21592
- /**
21593
- * 上次**发布**推的那个 commit ——「线上正跑的是哪一版」在本机唯一的答案,
21594
- * 也就是软扫的基准。只有 `deploy:release` 写它,扫描不动。
21595
- *
21596
- * 为什么不能用 `head` 顶替:`head` 被扫描和发布轮流覆盖(扫描前落存一版、发布后又挪一版),
21597
- * 它回答的是「清单绑在哪个 commit 上」,回答不了「上次发布是哪一版」。
21598
- *
21599
- * 缺席 = 这个 persona 还从没发布过,没有线上版本可比,软扫退硬扫。
21600
- */
21601
- releasedHead: external_exports.string().min(1).optional(),
21602
21609
  /**
21603
21610
  * 这次**实际**怎么扫的。档位由 daemon 定(有线上版本可比 + 有跑完的清单 → 软扫,否则硬扫),
21604
21611
  * 页面要显示的是真正发生的那个。
@@ -21607,12 +21614,23 @@ var DeployScanSchema = external_exports.object({
21607
21614
  * 定成必填会让 `readScan` 当场抛、上云页面直接打不开。而老记录确实都是从零扫的。
21608
21615
  */
21609
21616
  baseKind: ScanBaseKindSchema.default("hard"),
21610
- /** 软扫比的那个 commit(= `releasedHead`)。硬扫时缺席 */
21617
+ /** 软扫比的那个 commit(= 当时 `DeployReleased.head`)。硬扫时缺席 */
21611
21618
  base: external_exports.string().min(1).optional(),
21612
21619
  startedAt: external_exports.number(),
21613
21620
  finishedAt: external_exports.number().optional(),
21614
21621
  error: external_exports.string().optional()
21615
21622
  }).strict();
21623
+ var DeployReleasedSchema = external_exports.object({
21624
+ head: external_exports.string().min(1),
21625
+ contentHead: external_exports.string().min(1),
21626
+ scanId: external_exports.string().min(1),
21627
+ at: external_exports.number()
21628
+ }).strict();
21629
+ var DeployScanFileSchema = external_exports.object({
21630
+ version: external_exports.literal(2),
21631
+ scans: external_exports.array(DeployScanSchema),
21632
+ released: DeployReleasedSchema.optional()
21633
+ }).strict();
21616
21634
  var DeployRepoSchema = external_exports.object({
21617
21635
  url: external_exports.string().min(1),
21618
21636
  branch: external_exports.string().min(1),
@@ -21661,8 +21679,10 @@ var DeployStateSchema = external_exports.object({
21661
21679
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
21662
21680
  github: GithubAuthStatusSchema.optional(),
21663
21681
  repo: DeployRepoSchema.optional(),
21664
- file: DeployFileSchema.optional(),
21665
- scan: DeployScanSchema.optional(),
21682
+ /** 扫描历史,升序,最后一条最新。没扫过就是空数组 */
21683
+ scans: external_exports.array(DeployScanSchema).default([]),
21684
+ /** 线上正跑的是哪一版;从没发布过就缺席 */
21685
+ released: DeployReleasedSchema.optional(),
21666
21686
  /** 没仓库时缺席(查不了) */
21667
21687
  secrets: external_exports.array(DeploySecretPresenceSchema).optional(),
21668
21688
  /**
@@ -21671,27 +21691,28 @@ var DeployStateSchema = external_exports.object({
21671
21691
  */
21672
21692
  run: DeployRunSchema.optional(),
21673
21693
  /**
21674
- * `.clawd/deploy.json` 读不出来(人手改坏了)时的人话。有它就没有 `file`。
21675
- * 页面据此给一条能走的路(重新扫描会整份重写),而不是白屏。
21694
+ * `.clawd/deploy-scan.json` 读不出来(坏 JSON / 不合形状)时的人话。有它 `scans` 为空。
21695
+ * 页面据此给一条能走的路(重新扫描会重建这个文件),而不是白屏。
21676
21696
  */
21677
- fileError: external_exports.string().optional()
21697
+ historyError: external_exports.string().optional()
21698
+ }).strict();
21699
+ var DeployReleaseResultSchema = external_exports.object({
21700
+ sha: external_exports.string().min(1),
21701
+ actionsUrl: external_exports.string().min(1)
21678
21702
  }).strict();
21679
- var DeployReleaseResultSchema = external_exports.discriminatedUnion("outcome", [
21680
- external_exports.object({
21681
- outcome: external_exports.literal("pushed"),
21682
- sha: external_exports.string().min(1),
21683
- actionsUrl: external_exports.string().min(1)
21684
- }).strict(),
21685
- external_exports.object({ outcome: external_exports.literal("scanning") }).strict()
21686
- ]);
21687
21703
  var DeployPersonaArgsSchema = external_exports.object({
21688
21704
  personaId: external_exports.string().min(1)
21689
21705
  }).strict();
21690
- var DeployReleaseArgsSchema = DeployPersonaArgsSchema.extend({
21706
+ var DeployScanArgsSchema = DeployPersonaArgsSchema.extend({
21691
21707
  scanBase: ScanBaseKindSchema.optional()
21692
21708
  }).strict();
21709
+ var DeployReleaseArgsSchema = DeployPersonaArgsSchema.extend({
21710
+ scanId: external_exports.string().min(1)
21711
+ }).strict();
21693
21712
  var DeployPatchArgsSchema = external_exports.object({
21694
21713
  personaId: external_exports.string().min(1),
21714
+ /** 改的是历史里哪次扫描的清单。扫描期 butler 删条目也走这条(scanId 在它的任务提示里) */
21715
+ scanId: external_exports.string().min(1),
21695
21716
  patch: external_exports.object({
21696
21717
  // 人工补依赖;整份替换 manifest
21697
21718
  manifest: EnvironmentManifestSchema.optional()
@@ -21699,6 +21720,8 @@ var DeployPatchArgsSchema = external_exports.object({
21699
21720
  }).strict();
21700
21721
  var DeployPutSecretArgsSchema = external_exports.object({
21701
21722
  personaId: external_exports.string().min(1),
21723
+ /** 按哪次扫描的清单校验凭据名 / env 名。值本身是仓库级的(GitHub Secrets),跟扫描无关 */
21724
+ scanId: external_exports.string().min(1),
21702
21725
  // 对应 manifest.secrets[].name
21703
21726
  name: external_exports.string().min(1),
21704
21727
  // 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
@@ -21711,7 +21734,7 @@ var DeployRerunJobArgsSchema = external_exports.object({
21711
21734
  }).strict();
21712
21735
  var DeployReportFindingArgsSchema = external_exports.object({
21713
21736
  personaId: external_exports.string().min(1),
21714
- kind: external_exports.enum(["cli", "secret", "asset", "connector"]),
21737
+ kind: external_exports.enum(["package", "secret", "connector"]),
21715
21738
  // 该条目在 manifest 对应数组里的内容(形状随 kind 变,handler 按 kind 收窄校验)
21716
21739
  // connector 的 item 是 butler 报的 CLI 连接器({ name, fields? }),daemon 补齐 kind / runner / id 后落进清单;
21717
21740
  // MCP 连接器不经这里——由 daemon 从 .mcp.json 机械列出(design 2026-09-11-mcp-connector §3.2)
@@ -22848,7 +22871,7 @@ var EngineConnectPoolResponseSchema = external_exports.object({
22848
22871
  }).strict();
22849
22872
 
22850
22873
  // ../protocol/src/persona-schemas.ts
22851
- var PERSONA_SIZES = ["small", "medium", "large"];
22874
+ var PERSONA_SIZES = ["tiny", "small", "medium", "large"];
22852
22875
  var PersonaSizeSchema = external_exports.enum(PERSONA_SIZES);
22853
22876
  var PersonaFileSchema = external_exports.object({
22854
22877
  personaId: external_exports.string(),
@@ -22866,7 +22889,7 @@ var PersonaFileSchema = external_exports.object({
22866
22889
  // 与 model 平行:persona 新建 session 的默认 tool,可平滑切换。
22867
22890
  tool: external_exports.string().optional(),
22868
22891
  /**
22869
- * 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`small` / `medium` / `large`,没写按 medium。
22892
+ * 云上任务 Pod 的规格档(design 2026-09-18-persona-size):`tiny` / `small` / `medium` / `large`,没写按 medium。
22870
22893
  * 只有发布上云的流水线读它(cloud-host/pipeline/deploy-file.mjs 收窄值域,不认识的拒发);本机跑不看。
22871
22894
  * 协议层同 model / effort 保持 string(文件层向前兼容);改它的入口是上云页标题行的规格下拉
22872
22895
  * (design 2026-09-20-persona-size-ui),`persona:update` 那头按 PersonaSizeSchema 收窄。
@@ -22983,7 +23006,7 @@ var PersonaUpdateArgsSchema = external_exports.object({
22983
23006
  model: external_exports.string().optional(),
22984
23007
  effort: external_exports.string().optional(),
22985
23008
  tool: external_exports.string().optional(),
22986
- // 云上任务 Pod 的规格档:只收三档之一(PersonaFile 层是 string,边界在这里收窄)
23009
+ // 云上任务 Pod 的规格档:只收四档之一(PersonaFile 层是 string,边界在这里收窄)
22987
23010
  size: PersonaSizeSchema.optional(),
22988
23011
  personality: external_exports.string().optional(),
22989
23012
  // 人格补充层正文(见 PersonaInfoResponseSchema.personalityLocal)。与 personality 各写
@@ -24161,15 +24184,19 @@ var METHOD_DOCS = {
24161
24184
  },
24162
24185
  // ---- deploy:*(persona 上云:persona 目录 = git 仓库工作树,发布 = 推送触发仓库流水线)----
24163
24186
  "deploy:get": {
24164
- summary: "\u4E0A\u4E91\u9875\u9762\u8981\u7684\u5168\u90E8\uFF1A\u4ED3\u5E93\u7ED1\u5B9A\u72B6\u6001\uFF08git \u73B0\u7B97\uFF09\u3001\u90E8\u7F72\u6587\u4EF6\u3001\u626B\u63CF\u8BB0\u5F55\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",
24187
+ 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",
24165
24188
  args: DeployPersonaArgsSchema
24166
24189
  },
24190
+ "deploy:scan": {
24191
+ 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",
24192
+ args: DeployScanArgsSchema
24193
+ },
24167
24194
  "deploy:patch": {
24168
- summary: "\u7F16\u8F91\u90E8\u7F72\u6587\u4EF6\uFF1A\u624B\u5DE5\u8865\u4F9D\u8D56\u3002**\u4E0D\u80FD\u4ECE\u8FD9\u91CC\u4F20\u51ED\u636E\u7684\u503C**\uFF08\u503C\u8D70 deploy:putSecret\uFF09\uFF1Bowner-only",
24195
+ 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",
24169
24196
  args: DeployPatchArgsSchema
24170
24197
  },
24171
24198
  "deploy:putSecret": {
24172
- 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\u8BE5\u51ED\u636E\u5728\u6E05\u5355\u91CC\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
24199
+ 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",
24173
24200
  args: DeployPutSecretArgsSchema
24174
24201
  },
24175
24202
  "deploy:reportFinding": {
@@ -24181,11 +24208,11 @@ var METHOD_DOCS = {
24181
24208
  args: DeployFinishScanArgsSchema
24182
24209
  },
24183
24210
  "deploy:release": {
24184
- summary: "\u53D1\u5E03\u2014\u2014\u4E0A\u4E91\u552F\u4E00\u7684\u52A8\u4F5C\uFF0C\u626B\u63CF\u662F\u5B83\u7684\u7B2C\u4E00\u6BB5\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\u6E05\u5355\u8FC7\u65F6\uFF08persona \u6539\u8FC7 / \u6CA1\u626B\u8FC7 / \u4E0A\u6B21\u626B\u5931\u8D25\uFF09\uFF1A\u5148\u628A persona \u76EE\u5F55\u63D0\u4EA4\u5230\u672C\u5730\uFF08\u4E0D\u63A8\uFF09\uFF0C\u8D77 persona-butler \u7684\u626B\u63CF\u4F1A\u8BDD\uFF0C\u56DE outcome=scanning\u2014\u2014\u7ED3\u679C\u5199\u8FDB .clawd/deploy.json\uFF08\u6E05\u5355\uFF0C\u8FDB\u4ED3\u5E93\uFF09\u4E0E .clawd/deploy-scan.json\uFF08\u8BC1\u636E\uFF0C\u4E0D\u8FDB\u4ED3\u5E93\uFF09\uFF0C\u6B63\u5728\u626B\u7684\u8BDD\u539F\u6837\u8FD4\u56DE\u4E0D\u8D77\u7B2C\u4E8C\u4E2A\u4F1A\u8BDD\u3002\u6E05\u5355\u65B0\u9C9C\uFF1A\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 \u91CD\u5199 workflow \u6A21\u677F \u2192 git add -A \u2192 \u7A7A\u63D0\u4EA4\u4E5F\u5141\u8BB8 \u2192 push\uFF0C\u56DE outcome=pushed \u5E26 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\u5E26 scanBase\uFF08hard / soft\uFF09= \u8FD9\u6B21\u4E00\u5B9A\u5148\u626B\u3001\u6309\u8BE5\u6863\u4F4D\u626B\uFF08soft \u4E0D\u6210\u7ACB\u81EA\u52A8\u9000 hard\uFF09\uFF0C\u6E05\u5355\u65B0\u9C9C\u4E5F\u56DE scanning\uFF1Bowner-only",
24211
+ 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",
24185
24212
  args: DeployReleaseArgsSchema
24186
24213
  },
24187
24214
  "deploy:rerunJob": {
24188
- 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\u6216\u51ED\u636E\u8981\u8D70 deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
24215
+ 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",
24189
24216
  args: DeployRerunJobArgsSchema
24190
24217
  },
24191
24218
  // ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
@@ -24722,6 +24749,7 @@ var CODEX_CLOUD_TOOL_ID = `codex${CLOUD_TOOL_SUFFIX}`;
24722
24749
  // src/deploy/deploy-file.ts
24723
24750
  var import_promises = __toESM(require("fs/promises"), 1);
24724
24751
  var import_node_path3 = __toESM(require("path"), 1);
24752
+ var import_node_crypto = require("crypto");
24725
24753
 
24726
24754
  // src/deploy/connectors.ts
24727
24755
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -24746,6 +24774,7 @@ var CliConnectorFindingSchema = external_exports.object({
24746
24774
  // src/deploy/deploy-file.ts
24747
24775
  var DEPLOY_FILE = import_node_path3.default.join(".clawd", "deploy.json");
24748
24776
  var SCAN_FILE = import_node_path3.default.join(".clawd", "deploy-scan.json");
24777
+ var z1 = DeployScanSchema.omit({ id: true, manifest: true }).extend({ releasedHead: DeployScanSchema.shape.head }).strict();
24749
24778
 
24750
24779
  // src/deploy/templates.ts
24751
24780
  var CLAWOS_REPO = "ottin4ttc/clawos";
@@ -24886,20 +24915,20 @@ var L = [
24886
24915
  " --persona-id '${{ steps.plan.outputs.persona_id }}' \\",
24887
24916
  ' --tag "$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
24888
24917
  "",
24889
- " - name: \u955C\u50CF\u65AD\u8A00\uFF08\u6E05\u5355\u91CC\u7684 CLI \u90FD\u5728\u3001bundle \u91CC persona \u5728\uFF09",
24890
- " # /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\u91CC\u7684",
24891
- " # CLI \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",
24918
+ " - name: \u955C\u50CF\u65AD\u8A00\uFF08\u6E05\u5355\u91CC\u8981\u88C5\u7684\u90FD\u5728\u3001bundle \u91CC persona \u5728\uFF09",
24919
+ " # /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",
24920
+ " # \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",
24892
24921
  " run: |",
24893
24922
  ' IMG="$REGISTRY/$IMAGE_NAME:${{ steps.plan.outputs.tag }}"',
24894
24923
  ` OUT=$(docker run --rm "$IMG" '/opt/versions.sh')`,
24895
24924
  ' echo "$OUT"',
24896
- ` WANT=$(node -e 'const m=JSON.parse(require("fs").readFileSync("persona/.clawd/deploy.json","utf8"));console.log(((m.manifest??m).clis??[]).length)')`,
24925
+ ` 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)')`,
24897
24926
  ` echo "$OUT" | awk -v want="$WANT" '`,
24898
24927
  ' /^--- / { name=$2; if ((getline v) <= 0) v=""; n++',
24899
24928
  ' if (v !~ /[0-9]+\\.[0-9]+/) { printf " \u2717 %s \u6CA1\u62A5\u51FA\u7248\u672C\u53F7\uFF1A%s\\n", name, v > "/dev/stderr"; bad++ } }',
24900
- ' END { if (n != want) { printf " \u2717 \u955C\u50CF\u91CC\u53EA\u6709 %d \u4E2A CLI \u6BB5\uFF0C\u6E05\u5355\u8981\u6C42 %d \u4E2A\\n", n, want > "/dev/stderr"; bad++ }',
24929
+ ' 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++ }',
24901
24930
  " exit bad ? 1 : 0 }'",
24902
- ' echo " \u2713 $WANT \u4E2A CLI \u90FD\u5728\u4E14\u62A5\u5F97\u51FA\u7248\u672C"',
24931
+ ' echo " \u2713 \u6E05\u5355\u91CC\u8981\u88C5\u7684 $WANT \u4E2A\u90FD\u5728\u4E14\u62A5\u5F97\u51FA\u7248\u672C"',
24903
24932
  ` docker run --rm "$IMG" 'test -d /clawd/personas/\${{ steps.plan.outputs.persona_id }}' \\`,
24904
24933
  ' || { echo " \u2717 /clawd/personas/${{ steps.plan.outputs.persona_id }} \u4E0D\u5728" >&2; exit 1; }',
24905
24934
  ' echo " \u2713 persona \u76EE\u5F55\u5728"',