@clawos-dev/clawd 0.2.525 → 0.2.527

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/cli.cjs CHANGED
@@ -6098,7 +6098,7 @@ function cloudEngineOk(tool) {
6098
6098
  function repoSlug(url) {
6099
6099
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6100
6100
  }
6101
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployStartArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6101
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6102
6102
  var init_deploy_schemas = __esm({
6103
6103
  "../protocol/src/deploy-schemas.ts"() {
6104
6104
  "use strict";
@@ -6121,8 +6121,8 @@ var init_deploy_schemas = __esm({
6121
6121
  /** 扫描时被告知的共享资产名单(判「扫描器后来长了新本事」用) */
6122
6122
  scannedAssets: external_exports.array(external_exports.string()).default([]),
6123
6123
  /**
6124
- * 这次扫描是**对哪个 commit 的断言**。`deploy:start` 在起扫描之前会先把 persona 目录
6125
- * 提交并推上去,所以这个值一定是仓库里真实存在的一版。
6124
+ * 这次扫描是**对哪个 commit 的断言**。`deploy:release` 在起扫描之前会先把 persona 目录
6125
+ * 提交(只提交不推,推是发布末尾那一下的事),所以这个值一定是仓库里真实存在的一版。
6126
6126
  *
6127
6127
  * 有了它,「清单过没过时」才是个可判定的等式(`head === repo.head`),
6128
6128
  * 而不是从 `dirty` / `ahead` 里猜——那两个字段判不出「清单是好几版之前扫的」。
@@ -6141,8 +6141,8 @@ var init_deploy_schemas = __esm({
6141
6141
  */
6142
6142
  releasedHead: external_exports.string().min(1).optional(),
6143
6143
  /**
6144
- * 这次**实际**怎么扫的。是结果不是入参——`deploy:start` 收到的 mode 可能因为没有可比的
6145
- * 基准而退成硬扫,页面要显示的是真正发生的那个。
6144
+ * 这次**实际**怎么扫的。档位由 daemon 定(有线上版本可比 + 有跑完的清单 → 软扫,否则硬扫),
6145
+ * 页面要显示的是真正发生的那个。
6146
6146
  *
6147
6147
  * `.default('hard')` 而不是必填:这个文件是 strict 解析的,存量记录没有这个字段,
6148
6148
  * 定成必填会让 `readScan` 当场抛、上云页面直接打不开。而老记录确实都是从零扫的。
@@ -6217,20 +6217,17 @@ var init_deploy_schemas = __esm({
6217
6217
  */
6218
6218
  fileError: external_exports.string().optional()
6219
6219
  }).strict();
6220
- DeployReleaseResultSchema = external_exports.object({
6221
- sha: external_exports.string().min(1),
6222
- actionsUrl: external_exports.string().min(1)
6223
- }).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
+ ]);
6224
6228
  DeployPersonaArgsSchema = external_exports.object({
6225
6229
  personaId: external_exports.string().min(1)
6226
6230
  }).strict();
6227
- DeployStartArgsSchema = DeployPersonaArgsSchema.extend({
6228
- /**
6229
- * 缺省 soft。真正用哪一档由 daemon 定(软扫比不了时会退硬扫),结果看 `DeployScan.baseKind`。
6230
- * 复用上面那个中央定义的字面量,不在这里手抄一份。
6231
- */
6232
- mode: ScanBaseKindSchema.optional()
6233
- }).strict();
6234
6231
  DeployPatchArgsSchema = external_exports.object({
6235
6232
  personaId: external_exports.string().min(1),
6236
6233
  patch: external_exports.object({
@@ -6450,7 +6447,7 @@ var init_feishu_auth = __esm({
6450
6447
  // mirror peer sessions(远端会话镜像)
6451
6448
  "peerSession:upsert",
6452
6449
  "peerSession:remove",
6453
- // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的 ttcJwt)
6450
+ // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的登录票)
6454
6451
  "device:list",
6455
6452
  "device:connect",
6456
6453
  // iOS TestFlight 意向登记(需要 owner unionId 上报中心 server)
@@ -6520,13 +6517,7 @@ var init_feishu_auth = __esm({
6520
6517
  * 设备属性,与登录态无关、登录登出不变。UI"我的设备标识"用它拼
6521
6518
  * clawos://device/<deviceId>(替代旧的 unionId——不再向外暴露人的身份标识)。
6522
6519
  */
6523
- deviceId: external_exports.string().min(1),
6524
- /**
6525
- * TTC JWT 过期时间(unix ms)。null = 登录回调未携带 expires_at;字段缺省 = 老 daemon。
6526
- * 仅时间戳——token 本体永不出 ws(spec §9 不变)。UI 据此派生 过期/临期 提示
6527
- * (见 spec 2026-08-17-ttc-jwt-expiry-ux-design §2)。
6528
- */
6529
- ttcTokenExpiresAt: external_exports.number().nullable().optional()
6520
+ deviceId: external_exports.string().min(1)
6530
6521
  });
6531
6522
  AuthLogoutResponseSchema = external_exports.object({
6532
6523
  type: external_exports.literal("auth:logout:ok")
@@ -8886,10 +8877,6 @@ var init_methods = __esm({
8886
8877
  args: WorkspaceReadArgs
8887
8878
  },
8888
8879
  // ---- deploy:*(persona 上云:persona 目录 = git 仓库工作树,发布 = 推送触发仓库流水线)----
8889
- "deploy:start": {
8890
- summary: "\u5BF9\u67D0\u4E2A persona \u8D77\u4E00\u6B21\u4E0A\u4E91\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\uFF0C\u518D\u8D77 persona-butler \u7684\u626B\u63CF\u4F1A\u8BDD\uFF0C\u7ED3\u679C\u5199\u8FDB persona \u76EE\u5F55\u7684 .clawd/deploy.json \uFF08\u73AF\u5883\u6E05\u5355\uFF0C\u8FDB\u4ED3\u5E93\uFF09\u4E0E .clawd/deploy-scan.json\uFF08\u8BC1\u636E\uFF0C\u4E0D\u8FDB\u4ED3\u5E93\uFF09\u3002\u5DF2\u7ECF\u5728\u626B\u7684\u8BDD\u539F\u6837\u8FD4\u56DE\u3001\u4E0D\u8D77\u7B2C\u4E8C\u4E2A\u626B\u63CF\u4F1A\u8BDD\uFF1Bowner-only",
8891
- args: DeployPersonaArgsSchema
8892
- },
8893
8880
  "deploy:get": {
8894
8881
  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",
8895
8882
  args: DeployPersonaArgsSchema
@@ -8911,7 +8898,7 @@ var init_methods = __esm({
8911
8898
  args: DeployFinishScanArgsSchema
8912
8899
  },
8913
8900
  "deploy:release": {
8914
- summary: "\u53D1\u5E03 = \u63D0\u4EA4\u5E76\u63A8\u9001\u5230\u4ED3\u5E93\uFF1A\u91CD\u5199 workflow \u6A21\u677F \u2192 git add -A \u2192 \u7A7A\u63D0\u4EA4\u4E5F\u5141\u8BB8 \u2192 push\u3002\u4ED3\u5E93\u91CC\u7684\u6D41\u6C34\u7EBF\u63A5\u7740\u6784\u955C\u50CF\u3001\u5728\u96C6\u7FA4\u4E0A\u771F\u8D77\u4E00\u6B21\u3001\u767B\u8BB0\u6210\u6B63\u5F0F\uFF0C\u8FDB\u5EA6\u5728 GitHub Actions \u770B\u3002\u524D\u7F6E\uFF1A\u5DF2\u7ED1\u5B9A\u4ED3\u5E93\u3001\u6709\u6E05\u5355\u3001\u6BCF\u4E2A\u53D8\u91CF\u540D\u90FD\u5728 Secrets \u91CC\uFF1Bowner-only",
8901
+ 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\u3002owner-only",
8915
8902
  args: DeployPersonaArgsSchema
8916
8903
  },
8917
8904
  "deploy:rerunJob": {
@@ -9309,7 +9296,7 @@ var init_methods = __esm({
9309
9296
  args: NO_ARGS
9310
9297
  },
9311
9298
  "auth:getIdentity": {
9312
- summary: "\u53D6\u672C\u673A owner \u7684\u98DE\u4E66\u8EAB\u4EFD\uFF08ownerId / provider / displayName / unionId\uFF09+ **\u672C\u673A deviceId**\uFF08\u8BBE\u5907\u5C5E\u6027\uFF0C\u4E0E\u767B\u5F55\u6001\u65E0\u5173\uFF1Bdevice:connect / contact:* / inbox:* \u7684\u4E3B\u952E\u90FD\u662F\u5B83\uFF09\uFF1B\u7EDD\u4E0D\u8FD4\u56DE ttcToken",
9299
+ summary: "\u53D6\u672C\u673A owner \u7684\u98DE\u4E66\u8EAB\u4EFD\uFF08ownerId / provider / displayName / unionId\uFF09+ **\u672C\u673A deviceId**\uFF08\u8BBE\u5907\u5C5E\u6027\uFF0C\u4E0E\u767B\u5F55\u6001\u65E0\u5173\uFF1Bdevice:connect / contact:* / inbox:* \u7684\u4E3B\u952E\u90FD\u662F\u5B83\uFF09\uFF1B\u7EDD\u4E0D\u8FD4\u56DE\u767B\u5F55\u7968",
9313
9300
  args: NO_ARGS
9314
9301
  },
9315
9302
  "auth:logout": {
@@ -55209,7 +55196,7 @@ function fieldsOf(name, where, placeholders, warnings) {
55209
55196
  const ok = [];
55210
55197
  for (const p3 of uniq(placeholders)) {
55211
55198
  if (ConnectorFieldSchema.safeParse(p3).success) ok.push(p3);
55212
- else warnings.push(`MCP server\u300C${name}\u300D\u7684 ${where} \u91CC\u6709\u5360\u4F4D \${${p3}}\uFF0C\u53D8\u91CF\u540D\u4E0D\u5408\u5F62\u72B6\uFF08\u53EA\u80FD\u5927\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u5B57\u6BCD\u5F00\u5934\uFF09\uFF0C\u6CA1\u7B97\u8FDB\u8981\u586B\u7684\u53D8\u91CF\uFF1B\u6539\u540D\u540E\u91CD\u65B0\u626B\u63CF`);
55199
+ else warnings.push(`MCP server\u300C${name}\u300D\u7684 ${where} \u91CC\u6709\u5360\u4F4D \${${p3}}\uFF0C\u53D8\u91CF\u540D\u4E0D\u5408\u5F62\u72B6\uFF08\u53EA\u80FD\u5927\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\uFF0C\u5B57\u6BCD\u5F00\u5934\uFF09\uFF0C\u6CA1\u7B97\u8FDB\u8981\u586B\u7684\u53D8\u91CF\uFF1B\u6539\u540D\u540E\u518D\u53D1\u5E03\u4E00\u6B21\uFF08\u4F1A\u91CD\u65B0\u626B\u63CF\uFF09`);
55213
55200
  }
55214
55201
  return ok;
55215
55202
  }
@@ -55250,7 +55237,7 @@ function scanMcpConnectors(personaDir) {
55250
55237
  for (const [name, raw] of Object.entries(servers)) {
55251
55238
  const def = raw && typeof raw === "object" ? raw : {};
55252
55239
  if (!ConnectorNameSchema.safeParse(name).success) {
55253
- out.warnings.push(`MCP server\u300C${name}\u300D\u7684\u540D\u5B57\u4E0D\u5408\u5F62\u72B6\uFF08\u53EA\u80FD\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u77ED\u6A2A\u7EBF\uFF09\uFF0C\u6CA1\u5217\u8FDB\u6E05\u5355\uFF1B\u6539\u540D\u540E\u91CD\u65B0\u626B\u63CF`);
55240
+ out.warnings.push(`MCP server\u300C${name}\u300D\u7684\u540D\u5B57\u4E0D\u5408\u5F62\u72B6\uFF08\u53EA\u80FD\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u77ED\u6A2A\u7EBF\uFF09\uFF0C\u6CA1\u5217\u8FDB\u6E05\u5355\uFF1B\u6539\u540D\u540E\u518D\u53D1\u5E03\u4E00\u6B21\uFF08\u4F1A\u91CD\u65B0\u626B\u63CF\uFF09`);
55254
55241
  continue;
55255
55242
  }
55256
55243
  const type = typeof def.type === "string" ? def.type.toLowerCase() : typeof def.url === "string" ? "http" : "stdio";
@@ -59358,7 +59345,7 @@ async function identityArgs(exec, dir) {
59358
59345
  if (has(email) && has(name)) return [];
59359
59346
  return ["-c", "user.name=clawd", "-c", "user.email=clawd@clawos.local"];
59360
59347
  }
59361
- async function writeTemplates(dir) {
59348
+ async function writeGitignore(dir) {
59362
59349
  const gi = import_node_path34.default.join(dir, ".gitignore");
59363
59350
  let existing;
59364
59351
  try {
@@ -59367,6 +59354,8 @@ async function writeTemplates(dir) {
59367
59354
  existing = void 0;
59368
59355
  }
59369
59356
  await import_promises5.default.writeFile(gi, mergeGitignore(existing));
59357
+ }
59358
+ async function writeWorkflow(dir) {
59370
59359
  const wf = import_node_path34.default.join(dir, WORKFLOW_PATH);
59371
59360
  await import_promises5.default.mkdir(import_node_path34.default.dirname(wf), { recursive: true });
59372
59361
  await import_promises5.default.writeFile(wf, WORKFLOW_TEMPLATE);
@@ -59405,7 +59394,8 @@ async function ensureRepo(exec, api, args) {
59405
59394
  const { dir, org, personaId: personaId2 } = args;
59406
59395
  const existing = await repoInfo(exec, dir);
59407
59396
  if (existing) return { created: false, url: existing.url };
59408
- await writeTemplates(dir);
59397
+ await writeGitignore(dir);
59398
+ await import_promises5.default.rm(import_node_path34.default.join(dir, WORKFLOW_PATH), { force: true });
59409
59399
  let created;
59410
59400
  try {
59411
59401
  created = await createOrgRepo(exec, api, { dir, org, repoName: personaId2, message: "clawd: \u4E0A\u4E91", auth: args.auth });
@@ -59443,33 +59433,32 @@ async function hasCommit(exec, dir, sha) {
59443
59433
  const r2 = await git(exec, dir, ["cat-file", "-e", `${sha}^{commit}`]);
59444
59434
  return !r2.spawnError && r2.code === 0;
59445
59435
  }
59446
- async function commitAndPush(exec, args) {
59436
+ async function commitAll(exec, args) {
59447
59437
  await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
59448
59438
  const staged = await git(exec, args.dir, ["diff", "--cached", "--quiet"]);
59449
59439
  if (staged.code === 1) {
59450
59440
  const identity = await identityArgs(exec, args.dir);
59451
59441
  await mustGit(exec, args.dir, [...identity, "commit", "-m", args.message], "\u63D0\u4EA4");
59452
59442
  }
59453
- await gitPush(exec, args.dir, args.auth);
59454
59443
  return mustGit(exec, args.dir, ["rev-parse", "HEAD"], "\u8BFB\u63D0\u4EA4");
59455
59444
  }
59456
59445
  async function release(exec, api, args) {
59457
59446
  const info = await repoInfo(exec, args.dir);
59458
59447
  if (!info) throw new RepoError("\u8FD8\u6CA1\u6709\u4ED3\u5E93\uFF0C\u5148\u70B9\u4E00\u6B21\u300C\u53D1\u5E03\u5230\u4E91\u4E0A\u300D\u5EFA\u4ED3");
59459
59448
  await api.enableActions(repoSlug(info.url));
59460
- await writeTemplates(args.dir);
59449
+ await writeGitignore(args.dir);
59450
+ await writeWorkflow(args.dir);
59461
59451
  const identity = await identityArgs(exec, args.dir);
59462
59452
  await mustGit(exec, args.dir, ["add", "-A"], "\u6682\u5B58\u6587\u4EF6");
59463
59453
  const stamp = new Date(args.now()).toISOString();
59464
59454
  await mustGit(exec, args.dir, [...identity, "commit", "--allow-empty", "-m", `clawd: \u53D1\u5E03 ${stamp}`], "\u63D0\u4EA4");
59465
59455
  await gitPush(exec, args.dir, args.auth);
59466
59456
  const sha = await mustGit(exec, args.dir, ["rev-parse", "HEAD"], "\u8BFB\u63D0\u4EA4");
59467
- return { sha, actionsUrl: `https://github.com/${repoSlug(info.url)}/actions` };
59457
+ return { outcome: "pushed", sha, actionsUrl: `https://github.com/${repoSlug(info.url)}/actions` };
59468
59458
  }
59469
59459
 
59470
59460
  // src/deploy/scan-base.ts
59471
59461
  function pickScanBase(args) {
59472
- if (args.mode === "hard") return { kind: "hard" };
59473
59462
  if (!args.hasUsableManifest || !args.releasedHead) return { kind: "hard" };
59474
59463
  return { kind: "soft", base: args.releasedHead };
59475
59464
  }
@@ -59563,7 +59552,7 @@ var STALE_ASSETS_NOTE_PREFIX = "\u3010\u4E0A\u4E91\u626B\u63CF\u3011";
59563
59552
  function describeStaleAssets(stale) {
59564
59553
  if (stale.length === 0) return void 0;
59565
59554
  const plain = (s2) => s2.replace(/\*\*/g, "").replace(/\s+/g, " ").trim();
59566
- 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\uFF0C\u6216\u8005\u91CD\u65B0\u626B\u63CF\u4E00\u904D\u3002`;
59555
+ 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`;
59567
59556
  }
59568
59557
  function settleEndedScan(outcome, current, now) {
59569
59558
  if (current?.status !== "scanning") return void 0;
@@ -59582,7 +59571,7 @@ function reconcileInterruptedScans(deps) {
59582
59571
  ...scan,
59583
59572
  status: "failed",
59584
59573
  finishedAt: deps.now,
59585
- error: "daemon \u91CD\u542F\uFF0C\u626B\u63CF\u4F1A\u8BDD\u5DF2\u4E2D\u65AD\u2014\u2014\u8BF7\u91CD\u65B0\u626B\u63CF"
59574
+ 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"
59586
59575
  });
59587
59576
  flipped.push(personaId2);
59588
59577
  }
@@ -59685,33 +59674,9 @@ function buildDeployHandlers(deps) {
59685
59674
  const view = async (personaId2) => ({
59686
59675
  response: { type: "deploy:state", ...await stateOf(personaId2) }
59687
59676
  });
59688
- const start = async (frame, _client, ctx) => {
59689
- ensureOwner(ctx);
59690
- const { personaId: personaId2, mode } = parseArgs2(DeployStartArgsSchema, frame, "deploy:start");
59691
- const dir = mustPersona(personaId2);
59692
- const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
59693
- if (wrong) throw invalid(wrong);
59694
- const current = await readScan(dir);
59695
- if (current?.status === "scanning") return view(personaId2);
59696
- const { auth, api } = await requireGithub();
59697
- if (!await repoInfo(deps.exec, dir)) {
59698
- try {
59699
- await ensureRepo(deps.exec, api, { dir, org: deps.org, personaId: personaId2, auth });
59700
- } catch (err) {
59701
- throw toClawdError(err);
59702
- }
59703
- secretsCache.delete(personaId2);
59704
- }
59705
- let head;
59706
- try {
59707
- head = await commitAndPush(deps.exec, { dir, message: `clawd: \u626B\u63CF\u524D\u843D\u5B58 ${new Date(deps.now()).toISOString()}`, auth });
59708
- } catch (err) {
59709
- throw toClawdError(err);
59710
- }
59711
- runCache.delete(personaId2);
59677
+ async function beginScan(personaId2, dir, current, head) {
59712
59678
  const prevManifest = await readDeployFile(dir).catch(() => void 0);
59713
59679
  const scanBase = pickScanBase({
59714
- mode,
59715
59680
  // **文件在 ≠ 清单能用**:硬扫一开头就写了一份空清单,那一轮没跑完(OTA 重启 / butler 崩,
59716
59681
  // 见 scan-task.ts 的收尾判定)的话文件在、内容是空的。所以判据连上「上一轮扫完了」。
59717
59682
  hasUsableManifest: prevManifest !== void 0 && current?.status === "done",
@@ -59762,8 +59727,7 @@ function buildDeployHandlers(deps) {
59762
59727
  error: `\u8D77\u626B\u63CF\u4F1A\u8BDD\u5931\u8D25\uFF1A${started.error}`
59763
59728
  });
59764
59729
  }
59765
- return view(personaId2);
59766
- };
59730
+ }
59767
59731
  const get = async (frame, _client, ctx) => {
59768
59732
  ensureOwner(ctx);
59769
59733
  const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:get");
@@ -59848,20 +59812,37 @@ function buildDeployHandlers(deps) {
59848
59812
  ensureOwner(ctx);
59849
59813
  const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:release");
59850
59814
  const dir = mustPersona(personaId2);
59851
- const repo2 = await repoInfo(deps.exec, dir);
59852
- if (!repo2) throw invalid("\u8FD8\u6CA1\u6709\u4ED3\u5E93\uFF0C\u5148\u70B9\u4E00\u6B21\u300C\u53D1\u5E03\u5230\u4E91\u4E0A\u300D\u5EFA\u4ED3");
59815
+ const wrong = wrongEngine(deps.personaEngine(personaId2)?.tool);
59816
+ if (wrong) throw invalid(wrong);
59817
+ const { auth, api } = await requireGithub();
59818
+ let repo2 = await repoInfo(deps.exec, dir);
59819
+ if (!repo2) {
59820
+ try {
59821
+ await ensureRepo(deps.exec, api, { dir, org: deps.org, personaId: personaId2, auth });
59822
+ } catch (err) {
59823
+ throw toClawdError(err);
59824
+ }
59825
+ secretsCache.delete(personaId2);
59826
+ repo2 = await repoInfo(deps.exec, dir);
59827
+ if (!repo2) throw new ClawdError(ERROR_CODES.INTERNAL, "\u4ED3\u5E93\u5EFA\u597D\u4E86\u4F46\u8BFB\u4E0D\u5230 origin");
59828
+ }
59853
59829
  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`);
59830
+ const current = await readScan(dir);
59831
+ if (current?.status === "scanning") return { response: { type: "deploy:release", outcome: "scanning" } };
59832
+ const fileUsable = await readDeployFile(dir).then((f2) => f2 !== void 0).catch(() => false);
59833
+ if (!fileUsable || !isScanFresh(current, repo2)) {
59834
+ let head;
59835
+ try {
59836
+ head = await commitAll(deps.exec, { dir, message: `clawd: \u626B\u63CF\u524D\u843D\u5B58 ${new Date(deps.now()).toISOString()}` });
59837
+ } catch (err) {
59838
+ throw toClawdError(err);
59839
+ }
59840
+ runCache.delete(personaId2);
59841
+ await beginScan(personaId2, dir, current, head);
59842
+ return { response: { type: "deploy:release", outcome: "scanning" } };
59843
+ }
59854
59844
  const file = await readDeployFile(dir);
59855
59845
  if (!file) throw invalid("\u8FD8\u6CA1\u626B\u63CF\u51FA\u6E05\u5355");
59856
- const scan = await readScan(dir);
59857
- if (scan?.status === "scanning") throw invalid("\u626B\u63CF\u8FD8\u6CA1\u7ED3\u675F");
59858
- if (scan?.status !== "done") throw invalid(scan?.status === "failed" ? "\u4E0A\u6B21\u626B\u63CF\u5931\u8D25\u4E86\uFF0C\u5148\u91CD\u65B0\u626B\u63CF" : "\u8FD8\u6CA1\u626B\u63CF\u8FC7\uFF0C\u5148\u626B\u63CF");
59859
- if (!isScanFresh(scan, repo2)) {
59860
- throw invalid(
59861
- repo2.dirty ? "persona \u76EE\u5F55\u6709\u6539\u52A8\u8FD8\u6CA1\u626B\u8FC7\uFF0C\u5148\u91CD\u65B0\u626B\u63CF\u4E00\u6B21\uFF08\u6E05\u5355\u8981\u8DDF\u7740\u6539\u52A8\u8D70\uFF09" : "\u6E05\u5355\u662F\u66F4\u65E9\u90A3\u4E00\u7248\u626B\u51FA\u6765\u7684\uFF0C\u5148\u91CD\u65B0\u626B\u63CF\u4E00\u6B21"
59862
- );
59863
- }
59864
- const { auth, api } = await requireGithub();
59865
59846
  const blockers = [];
59866
59847
  let present;
59867
59848
  try {
@@ -59872,11 +59853,7 @@ function buildDeployHandlers(deps) {
59872
59853
  const missing = requiredEnvNames(file).filter((e) => !present.has(e));
59873
59854
  if (missing.length > 0) blockers.push(`${missing.join(" / ")} \u8FD8\u6CA1\u586B`);
59874
59855
  for (const c2 of file.manifest.connectors) blockers.push(...connectorBlockers(c2));
59875
- const engine = deps.personaEngine(personaId2);
59876
- const wrongTool = wrongEngine(engine?.tool);
59877
- if (wrongTool) {
59878
- blockers.push(wrongTool);
59879
- } else if (!engine?.model) {
59856
+ if (!deps.personaEngine(personaId2)?.model) {
59880
59857
  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");
59881
59858
  }
59882
59859
  if (blockers.length > 0) throw invalid(`\u8FD8\u5DEE\uFF1A${blockers.join("\uFF1B")}`);
@@ -59912,7 +59889,6 @@ function buildDeployHandlers(deps) {
59912
59889
  return view(personaId2);
59913
59890
  };
59914
59891
  return {
59915
- "deploy:start": start,
59916
59892
  "deploy:get": get,
59917
59893
  "deploy:patch": patch,
59918
59894
  "deploy:putSecret": putSecret,
@@ -62144,7 +62120,7 @@ async function centralRequest(opts, path101, init) {
62144
62120
  }
62145
62121
  clearTimeout(timer);
62146
62122
  if (res.status === 401) {
62147
- throw new CentralClientError("UNAUTHORIZED", "TTC JWT invalid or expired");
62123
+ throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
62148
62124
  }
62149
62125
  if (!res.ok) {
62150
62126
  let detail = "";
@@ -62164,12 +62140,8 @@ async function centralRequest(opts, path101, init) {
62164
62140
  async function centralExchange(opts) {
62165
62141
  const body = await centralRequest(opts, "/api/clawd-identity/exchange", {
62166
62142
  method: "POST",
62167
- body: {
62168
- jwt: opts.jwt,
62169
- provider: opts.provider,
62170
- deviceId: opts.deviceId,
62171
- selfDeviceId: opts.selfDeviceId
62172
- }
62143
+ bearer: opts.loginPass,
62144
+ body: { deviceId: opts.deviceId }
62173
62145
  });
62174
62146
  if (typeof body.token !== "string" || body.token.length === 0 || typeof body.ownerId !== "string" || typeof body.provider !== "string" || typeof body.displayName !== "string") {
62175
62147
  throw new CentralClientError("BAD_RESPONSE", "exchange: missing token/ownerId/provider/displayName");
@@ -62189,10 +62161,8 @@ async function centralGetPublicKey(opts) {
62189
62161
  async function centralUpsertDevice(opts) {
62190
62162
  const body = await centralRequest(opts, "/api/clawd-identity/devices/self", {
62191
62163
  method: "PUT",
62192
- bearer: opts.ttcJwt,
62164
+ bearer: opts.loginPass,
62193
62165
  body: {
62194
- deviceId: opts.deviceId,
62195
- provider: opts.provider,
62196
62166
  ...opts.url ? { url: opts.url } : {},
62197
62167
  ...opts.name ? { name: opts.name } : {}
62198
62168
  }
@@ -62200,10 +62170,7 @@ async function centralUpsertDevice(opts) {
62200
62170
  if (body.ok !== true) {
62201
62171
  throw new CentralClientError("BAD_RESPONSE", "upsertDevice: server did not ack ok");
62202
62172
  }
62203
- return {
62204
- ok: true,
62205
- ...typeof body.loginPass === "string" && body.loginPass.length > 0 ? { loginPass: body.loginPass } : {}
62206
- };
62173
+ return { ok: true };
62207
62174
  }
62208
62175
  async function centralPrincipalStatus(opts) {
62209
62176
  const query = `provider=${encodeURIComponent(opts.provider)}&ownerId=${encodeURIComponent(opts.ownerId)}`;
@@ -62216,11 +62183,10 @@ async function centralPrincipalStatus(opts) {
62216
62183
  return { active: body.active };
62217
62184
  }
62218
62185
  async function centralGetApiKey(opts) {
62219
- const body = await centralRequest(
62220
- opts,
62221
- `/api/persona-cloud/api-key?provider=${encodeURIComponent(opts.provider)}`,
62222
- { method: "GET", bearer: opts.ttcJwt }
62223
- );
62186
+ const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
62187
+ method: "GET",
62188
+ bearer: opts.loginPass
62189
+ });
62224
62190
  if (body.apiKey !== null && typeof body.apiKey !== "string") {
62225
62191
  throw new CentralClientError("BAD_RESPONSE", "api-key: apiKey must be string or null");
62226
62192
  }
@@ -62234,8 +62200,7 @@ async function centralGetApiKey(opts) {
62234
62200
  async function centralIssueApiKey(opts) {
62235
62201
  const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
62236
62202
  method: "POST",
62237
- bearer: opts.ttcJwt,
62238
- body: { provider: opts.provider }
62203
+ bearer: opts.loginPass
62239
62204
  });
62240
62205
  if (typeof body.apiKey !== "string" || body.apiKey.length === 0) {
62241
62206
  throw new CentralClientError("BAD_RESPONSE", "api-key: missing apiKey");
@@ -62250,11 +62215,10 @@ async function centralIssueApiKey(opts) {
62250
62215
  };
62251
62216
  }
62252
62217
  async function centralListDevices(opts) {
62253
- const body = await centralRequest(
62254
- opts,
62255
- `/api/clawd-identity/devices?provider=${encodeURIComponent(opts.provider)}`,
62256
- { method: "GET", bearer: opts.ttcJwt }
62257
- );
62218
+ const body = await centralRequest(opts, "/api/clawd-identity/devices", {
62219
+ method: "GET",
62220
+ bearer: opts.loginPass
62221
+ });
62258
62222
  if (!Array.isArray(body.devices)) {
62259
62223
  throw new CentralClientError("BAD_RESPONSE", "devices: missing devices array");
62260
62224
  }
@@ -62271,8 +62235,8 @@ async function centralListDevices(opts) {
62271
62235
  async function centralConnectorsMine(opts) {
62272
62236
  const body = await centralRequest(opts, "/api/persona-cloud/connectors/mine", {
62273
62237
  method: "POST",
62274
- bearer: opts.ttcJwt,
62275
- body: { provider: opts.provider, personaId: opts.personaId, connectors: opts.connectors }
62238
+ bearer: opts.loginPass,
62239
+ body: { personaId: opts.personaId, connectors: opts.connectors }
62276
62240
  });
62277
62241
  if (!Array.isArray(body.connectors)) throw new CentralClientError("BAD_RESPONSE", "connectors/mine: missing connectors array");
62278
62242
  return { connectors: body.connectors };
@@ -62280,16 +62244,16 @@ async function centralConnectorsMine(opts) {
62280
62244
  async function centralConnectorFill(opts) {
62281
62245
  const body = await centralRequest(opts, "/api/persona-cloud/connectors/fields", {
62282
62246
  method: "PUT",
62283
- bearer: opts.ttcJwt,
62284
- body: { provider: opts.provider, connector: opts.connector, values: opts.values }
62247
+ bearer: opts.loginPass,
62248
+ body: { connector: opts.connector, values: opts.values }
62285
62249
  });
62286
62250
  if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
62287
62251
  }
62288
62252
  async function centralGithubAuthOauthStart(opts) {
62289
62253
  const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
62290
62254
  method: "POST",
62291
- bearer: opts.ttcJwt,
62292
- body: { provider: opts.provider, ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
62255
+ bearer: opts.loginPass,
62256
+ body: { ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
62293
62257
  });
62294
62258
  if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
62295
62259
  return { authUrl: body.authUrl };
@@ -62297,8 +62261,8 @@ async function centralGithubAuthOauthStart(opts) {
62297
62261
  async function centralGithubAuthToken(opts) {
62298
62262
  const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
62299
62263
  method: "POST",
62300
- bearer: opts.ttcJwt,
62301
- body: { provider: opts.provider }
62264
+ bearer: opts.loginPass,
62265
+ body: {}
62302
62266
  });
62303
62267
  if (body.token === null || body.token === void 0) return void 0;
62304
62268
  if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
@@ -62337,10 +62301,10 @@ var CloudGatewayClient = class {
62337
62301
  deps;
62338
62302
  fetchImpl;
62339
62303
  async credentials() {
62340
- const rec5 = this.deps.readIdentity();
62341
- if (!rec5) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7528\u4E0D\u4E86");
62304
+ const loginPass = this.deps.getLoginPass();
62305
+ if (!loginPass) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7528\u4E0D\u4E86");
62342
62306
  const get = this.deps.getApiKey ?? ((args) => centralGetApiKey({ api: this.deps.centralApi ?? "", fetchImpl: this.fetchImpl, ...args }));
62343
- const { apiKey, gatewayUrl } = await get({ ttcJwt: rec5.ttcToken, provider: rec5.provider });
62307
+ const { apiKey, gatewayUrl } = await get({ loginPass });
62344
62308
  if (!apiKey) throw new Error("\u8FD8\u6CA1\u751F\u6210\u4E91 persona \u7684\u8C03\u7528 key\uFF0C\u53BB\u300C\u8BBE\u7F6E \u2192 \u4E91\u4E0A persona\u300D\u751F\u6210\u4E00\u628A");
62345
62309
  if (!gatewayUrl) throw new Error("\u4E2D\u5FC3\u6CA1\u914D\u4E91 gateway \u5730\u5740\uFF08gatewayUrl \u4E3A\u7A7A\uFF09");
62346
62310
  return { apiKey, gatewayUrl: trimSlash(gatewayUrl) };
@@ -65896,8 +65860,7 @@ async function importZip(buf, root) {
65896
65860
  function parseLoginCallbackQuery(url) {
65897
65861
  return {
65898
65862
  token: url.searchParams.get("token") ?? "",
65899
- state: url.searchParams.get("state") ?? "",
65900
- expiresAt: url.searchParams.get("expires_at")
65863
+ state: url.searchParams.get("state") ?? ""
65901
65864
  };
65902
65865
  }
65903
65866
  function escapeHtml(s2) {
@@ -67558,7 +67521,7 @@ var OwnerIdentityStore = class {
67558
67521
  return null;
67559
67522
  }
67560
67523
  const r2 = parsed;
67561
- if (!r2.identity || typeof r2.identity.ownerId !== "string" || r2.identity.ownerId.length === 0 || typeof r2.identity.provider !== "string" || r2.identity.provider.length === 0 || typeof r2.identity.displayName !== "string" || r2.identity.displayName.length === 0 || typeof r2.ttcToken !== "string" || r2.ttcToken.length === 0) {
67524
+ if (!r2.identity || typeof r2.identity.ownerId !== "string" || r2.identity.ownerId.length === 0 || typeof r2.identity.provider !== "string" || r2.identity.provider.length === 0 || typeof r2.identity.displayName !== "string" || r2.identity.displayName.length === 0 || typeof r2.loginPass !== "string" || r2.loginPass.length === 0) {
67562
67525
  return null;
67563
67526
  }
67564
67527
  return {
@@ -67569,10 +67532,8 @@ var OwnerIdentityStore = class {
67569
67532
  ...typeof r2.identity.avatarUrl === "string" ? { avatarUrl: r2.identity.avatarUrl } : {},
67570
67533
  ...typeof r2.identity.unionId === "string" ? { unionId: r2.identity.unionId } : {}
67571
67534
  },
67572
- ttcToken: r2.ttcToken,
67573
- ttcTokenExpiresAt: typeof r2.ttcTokenExpiresAt === "number" ? r2.ttcTokenExpiresAt : null,
67574
- loginAt: typeof r2.loginAt === "string" ? r2.loginAt : (/* @__PURE__ */ new Date(0)).toISOString(),
67575
- ...typeof r2.loginPass === "string" && r2.loginPass.length > 0 ? { loginPass: r2.loginPass } : {}
67535
+ loginPass: r2.loginPass,
67536
+ loginAt: typeof r2.loginAt === "string" ? r2.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
67576
67537
  };
67577
67538
  }
67578
67539
  write(record4) {
@@ -67649,13 +67610,10 @@ var LoginFlow = class {
67649
67610
  reason: `device_binding upsert failed: ${err.message}`
67650
67611
  };
67651
67612
  }
67652
- const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
67653
67613
  this.deps.store.write({
67654
67614
  identity,
67655
- ttcToken: params.token,
67656
- ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
67657
- loginAt: new Date(now).toISOString(),
67658
- ...loginPass ? { loginPass } : {}
67615
+ loginPass,
67616
+ loginAt: new Date(now).toISOString()
67659
67617
  });
67660
67618
  return { ok: true, identity };
67661
67619
  }
@@ -67765,7 +67723,10 @@ async function upsertDeviceBinding(opts) {
67765
67723
  body = await res.json();
67766
67724
  } catch {
67767
67725
  }
67768
- return typeof body.loginPass === "string" && body.loginPass.length > 0 ? { loginPass: body.loginPass } : {};
67726
+ if (typeof body.loginPass !== "string" || body.loginPass.length === 0) {
67727
+ throw new DeviceBindingClientError("BAD_RESPONSE", "center did not issue a login pass (api too old?)");
67728
+ }
67729
+ return { loginPass: body.loginPass };
67769
67730
  }
67770
67731
 
67771
67732
  // src/contact/org-mesh.ts
@@ -67872,7 +67833,7 @@ function rethrowLoginExpired(e) {
67872
67833
  if (e instanceof CentralClientError && e.code === "UNAUTHORIZED") {
67873
67834
  throw new ClawdError(
67874
67835
  ERROR_CODES.VALIDATION_ERROR,
67875
- `${LOGIN_EXPIRED_ERROR_MARKER}: \u767B\u5F55\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55`
67836
+ `${LOGIN_EXPIRED_ERROR_MARKER}: \u767B\u5F55\u7968\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55`
67876
67837
  );
67877
67838
  }
67878
67839
  throw e;
@@ -69974,9 +69935,7 @@ function buildFeishuAuthHandlers(deps) {
69974
69935
  type: "auth:getIdentity:ok",
69975
69936
  identity: record4 ? record4.identity : null,
69976
69937
  // 决策 #15:设备属性,与登录态无关——UI"分享我的 Hub 标识"用
69977
- deviceId: deps.deviceId,
69978
- // 过期时间戳(仅时间戳,token 本体永不出 ws)——UI 派生 过期/临期 提示
69979
- ttcTokenExpiresAt: record4 ? record4.ttcTokenExpiresAt : null
69938
+ deviceId: deps.deviceId
69980
69939
  }
69981
69940
  };
69982
69941
  };
@@ -69996,7 +69955,7 @@ function buildFeishuAuthHandlers(deps) {
69996
69955
  "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u62FF\u4E0D\u5230\u4E91 persona \u7684\u8C03\u7528 key"
69997
69956
  );
69998
69957
  }
69999
- return { ttcJwt: record4.ttcToken, provider: record4.identity.provider };
69958
+ return { loginPass: record4.loginPass };
70000
69959
  }
70001
69960
  const apiKeyGet = async () => {
70002
69961
  const { apiKey, createdAt, gatewayUrl } = await deps.apiKeyClient.get(requireLogin());
@@ -70029,9 +69988,9 @@ function ensureOwner6(ctx) {
70029
69988
  var invalid2 = (msg) => new ClawdError(ERROR_CODES.VALIDATION_ERROR, `VALIDATION_ERROR: ${msg}`);
70030
69989
  function buildCloudConnectorsHandlers(deps) {
70031
69990
  function requireLogin() {
70032
- const rec5 = deps.readIdentity();
70033
- if (!rec5) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7684\u8FDE\u63A5\u5668\u7528\u4E0D\u4E86");
70034
- return rec5;
69991
+ const loginPass = deps.getLoginPass();
69992
+ if (!loginPass) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7684\u8FDE\u63A5\u5668\u7528\u4E0D\u4E86");
69993
+ return { loginPass };
70035
69994
  }
70036
69995
  async function personalConnectors(personaId2) {
70037
69996
  const persona = deps.getPersona(personaId2);
@@ -70115,7 +70074,6 @@ var METHOD_GRANT_MAP = {
70115
70074
  "visitor:list": ADMIN_ANY,
70116
70075
  // deploy:*(persona 上云):全部 owner-only。里面有凭据录入口、建仓和推送,
70117
70076
  // guest 不该看见更不该动。
70118
- "deploy:start": ADMIN_ANY,
70119
70077
  "deploy:get": ADMIN_ANY,
70120
70078
  "deploy:patch": ADMIN_ANY,
70121
70079
  "deploy:putSecret": ADMIN_ANY,
@@ -70317,13 +70275,12 @@ var METHOD_GRANT_MAP = {
70317
70275
  "auth:apiKey:get": ADMIN_ANY,
70318
70276
  "auth:apiKey:regenerate": ADMIN_ANY,
70319
70277
  // ---- device:* 设备目录 + 连接(决策 #15,owner-only) ----
70320
- // 设备目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
70278
+ // 设备目录 / 连接都是 owner 本机操作(用本机登录票找中心 server),guest 不可调
70321
70279
  "device:list": ADMIN_ANY,
70322
70280
  "device:connect": ADMIN_ANY,
70323
70281
  "device:connections": ADMIN_ANY
70324
70282
  };
70325
70283
  var HUMAN_ONLY_METHODS = [
70326
- "deploy:start",
70327
70284
  "deploy:release",
70328
70285
  "deploy:putSecret",
70329
70286
  "deploy:rerunJob",
@@ -70371,7 +70328,7 @@ function computeMethodAccess(args) {
70371
70328
  }
70372
70329
 
70373
70330
  // src/version.ts
70374
- var version = "0.2.525".length > 0 ? "0.2.525" : "dev";
70331
+ var version = "0.2.527".length > 0 ? "0.2.527" : "dev";
70375
70332
 
70376
70333
  // src/cli-probe/probe.ts
70377
70334
  var fs77 = __toESM(require("fs"), 1);
@@ -73032,8 +72989,8 @@ async function startDaemon(config) {
73032
72989
  });
73033
72990
  },
73034
72991
  // 验票第二段(spec 2026-08-28):本地验签只证明"票是中心签的",这一步问中心
73035
- // "持票人还在职吗"。token 不再过期,撤销全靠它。端点无鉴权——不带本机 ttcJwt,
73036
- // 否则飞书过期又会牵连互联。
72992
+ // "持票人还在职吗"。token 不再过期,撤销全靠它。端点无鉴权——不带本机登录票,
72993
+ // 本机没登录也能验别人的票。
73037
72994
  checkPrincipalActive: async (provider, ownerId2) => {
73038
72995
  const r2 = await centralPrincipalStatus({
73039
72996
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
@@ -73183,7 +73140,7 @@ async function startDaemon(config) {
73183
73140
  const r2 = ownerIdentityStore.read();
73184
73141
  if (!r2) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u5173\u8054 GitHub \u8981\u7ECF\u4E2D\u5FC3\uFF1B\u5148\u767B\u5F55\uFF0C\u6216\u5728\u5361\u7247\u4E0A\u624B\u52A8\u7C98 token");
73185
73142
  try {
73186
- return await centralGithubAuthOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider, ...returnTo ? { returnTo } : {} });
73143
+ return await centralGithubAuthOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass: r2.loginPass, ...returnTo ? { returnTo } : {} });
73187
73144
  } catch (err) {
73188
73145
  throw new ClawdError(ERROR_CODES.INTERNAL, `\u5411\u4E2D\u5FC3\u8981 GitHub \u6388\u6743\u94FE\u63A5\u6CA1\u6210\uFF1A${err instanceof Error ? err.message : String(err)}`);
73189
73146
  }
@@ -73191,7 +73148,7 @@ async function startDaemon(config) {
73191
73148
  token: async () => {
73192
73149
  const r2 = ownerIdentityStore.read();
73193
73150
  if (!r2) return void 0;
73194
- return centralGithubAuthToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider });
73151
+ return centralGithubAuthToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass: r2.loginPass });
73195
73152
  }
73196
73153
  },
73197
73154
  now: () => Date.now(),
@@ -73593,10 +73550,7 @@ async function startDaemon(config) {
73593
73550
  screenIdleProbeLogger
73594
73551
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
73595
73552
  const cloudGateway = new CloudGatewayClient({
73596
- readIdentity: () => {
73597
- const r2 = ownerIdentityStore.read();
73598
- return r2 ? { ttcToken: r2.ttcToken, provider: r2.identity.provider } : null;
73599
- },
73553
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
73600
73554
  centralApi: config.clawosApi ?? DEFAULT_CLAWOS_API,
73601
73555
  logger
73602
73556
  });
@@ -73701,24 +73655,15 @@ async function startDaemon(config) {
73701
73655
  const record4 = ownerIdentityStore.read();
73702
73656
  if (!record4) return;
73703
73657
  try {
73704
- const r2 = await centralUpsertDevice({
73658
+ await centralUpsertDevice({
73705
73659
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73706
- ttcJwt: record4.ttcToken,
73707
- provider: record4.identity.provider,
73708
- deviceId: authFile.deviceId,
73660
+ loginPass: record4.loginPass,
73709
73661
  url: currentTunnelUrl ?? void 0,
73710
73662
  name: ownerDisplayName
73711
73663
  });
73712
- if (r2.loginPass) {
73713
- const latest = ownerIdentityStore.read();
73714
- if (latest && latest.ttcToken === record4.ttcToken && latest.loginPass !== r2.loginPass) {
73715
- ownerIdentityStore.write({ ...latest, loginPass: r2.loginPass });
73716
- }
73717
- }
73718
73664
  logger.info("device registered to central server", {
73719
73665
  deviceId: authFile.deviceId,
73720
- url: currentTunnelUrl ?? "(none)",
73721
- loginPass: r2.loginPass ? "issued" : "absent"
73666
+ url: currentTunnelUrl ?? "(none)"
73722
73667
  });
73723
73668
  } catch (err) {
73724
73669
  logger.warn("device register failed (best-effort)", { err: err.message });
@@ -73728,7 +73673,7 @@ async function startDaemon(config) {
73728
73673
  const runOrgMesh = async () => {
73729
73674
  if (!feishuActive || orgMeshInFlight) return;
73730
73675
  const record4 = ownerIdentityStore.read();
73731
- if (!record4?.loginPass) return;
73676
+ if (!record4) return;
73732
73677
  const loginPass = record4.loginPass;
73733
73678
  orgMeshInFlight = true;
73734
73679
  try {
@@ -73776,10 +73721,8 @@ async function startDaemon(config) {
73776
73721
  }
73777
73722
  return centralExchange({
73778
73723
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73779
- jwt: record4.ttcToken,
73780
- provider: record4.identity.provider,
73781
- deviceId,
73782
- selfDeviceId: authFile.deviceId
73724
+ loginPass: record4.loginPass,
73725
+ deviceId
73783
73726
  });
73784
73727
  },
73785
73728
  listDevices: async () => {
@@ -73789,8 +73732,7 @@ async function startDaemon(config) {
73789
73732
  }
73790
73733
  return centralListDevices({
73791
73734
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73792
- ttcJwt: record4.ttcToken,
73793
- provider: record4.identity.provider
73735
+ loginPass: record4.loginPass
73794
73736
  });
73795
73737
  },
73796
73738
  connectRemote,
@@ -73821,8 +73763,7 @@ async function startDaemon(config) {
73821
73763
  });
73822
73764
  logger.info("device-binding upsert succeeded", {
73823
73765
  deviceId: opts.deviceId,
73824
- unionId: opts.identity.unionId,
73825
- loginPass: r2.loginPass ? "issued" : "absent"
73766
+ unionId: opts.identity.unionId
73826
73767
  });
73827
73768
  return r2;
73828
73769
  } catch (err) {
@@ -73964,8 +73905,8 @@ async function startDaemon(config) {
73964
73905
  // 联系人列表 store(device:connect 出站 / contact:handshake 入站落同一 store)
73965
73906
  contactStore,
73966
73907
  // contact:handshake 换票(spec 2026-07-22;2026-08-17 起硬失败,不再是回填):
73967
- // 用 owner 当前 jwt 换 aud=对方设备 的票,现读 ownerIdentityStore(热切换后用新身份)。
73968
- // 契约(ContactHandlerDeps.exchangeToken):null = 本机登录问题(未登录 / JWT 过期),
73908
+ // 用本机登录票换 aud=对方设备 的票,现读 ownerIdentityStore(热切换后用新身份)。
73909
+ // 契约(ContactHandlerDeps.exchangeToken):null = 本机登录问题(未登录),
73969
73910
  // 其他失败原样抛给 handler——两种都会让握手硬失败,只是给对端的错误文案不同。
73970
73911
  exchangeToken: async (targetDeviceId) => {
73971
73912
  const record4 = ownerIdentityStore.read();
@@ -73973,10 +73914,8 @@ async function startDaemon(config) {
73973
73914
  try {
73974
73915
  const r2 = await centralExchange({
73975
73916
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73976
- jwt: record4.ttcToken,
73977
- provider: record4.identity.provider,
73917
+ loginPass: record4.loginPass,
73978
73918
  deviceId: targetDeviceId,
73979
- selfDeviceId: authFile.deviceId,
73980
73919
  // 换票在握手关键路径上(硬失败),对端 connect-remote RPC_TIMEOUT_MS=5s:
73981
73920
  // 必须在其内失败/成功,否则对端先超时不落库、本机后换票成功落库 → 反向半连,
73982
73921
  // 破坏「两侧都不落库」不变量。4s 留 1s 余量给 ws 往返
@@ -74029,21 +73968,18 @@ async function startDaemon(config) {
74029
73968
  }
74030
73969
  },
74031
73970
  // 云 persona 卡片上的连接器区(design 2026-09-11-mcp-connector §3.8):装了什么以 gateway 登记行为真源,
74032
- // 连没连上问中心。登录凭证只在 daemon 内部传给中心,与 apiKeyClient 同一条纪律。
73971
+ // 连没连上问中心。登录票只在 daemon 内部传给中心,与 apiKeyClient 同一条纪律。
74033
73972
  cloudConnectors: {
74034
73973
  getPersona: (personaId2) => personaRegistry.get(personaId2),
74035
73974
  gatewayConnectors: (gatewayPersonaId) => cloudGateway.personaConnectors(gatewayPersonaId),
74036
- readIdentity: () => {
74037
- const r2 = ownerIdentityStore.read();
74038
- return r2 ? { ttcJwt: r2.ttcToken, provider: r2.identity.provider } : null;
74039
- },
73975
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
74040
73976
  central: {
74041
73977
  mine: (args) => centralConnectorsMine({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ...args }),
74042
73978
  fill: (args) => centralConnectorFill({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ...args })
74043
73979
  }
74044
73980
  },
74045
73981
  // 设备目录 + 连接(决策 #15:daemon 代理中心 server)。
74046
- // exchange/listDevices 包掉 owner ttcJwt(每次现读 store——热切换后立即用新身份);
73982
+ // exchange/listDevices 包掉本机登录票(每次现读 store——热切换后立即用新身份);
74047
73983
  // dispatcher 的 FEISHU_GATED_METHODS gate 已保证未登录时这些 RPC 到不了 handler,
74048
73984
  // 这里的 FEISHU_LOGIN_REQUIRED 抛错只是防御纵深。
74049
73985
  feishuDevice: feishuDeviceDeps,