@clawos-dev/clawd 0.2.524 → 0.2.526

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
@@ -6450,7 +6450,7 @@ var init_feishu_auth = __esm({
6450
6450
  // mirror peer sessions(远端会话镜像)
6451
6451
  "peerSession:upsert",
6452
6452
  "peerSession:remove",
6453
- // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的 ttcJwt)
6453
+ // 设备目录 + 连接(决策 #15:daemon 代理中心 server,需要 owner 的登录票)
6454
6454
  "device:list",
6455
6455
  "device:connect",
6456
6456
  // iOS TestFlight 意向登记(需要 owner unionId 上报中心 server)
@@ -6520,13 +6520,7 @@ var init_feishu_auth = __esm({
6520
6520
  * 设备属性,与登录态无关、登录登出不变。UI"我的设备标识"用它拼
6521
6521
  * clawos://device/<deviceId>(替代旧的 unionId——不再向外暴露人的身份标识)。
6522
6522
  */
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()
6523
+ deviceId: external_exports.string().min(1)
6530
6524
  });
6531
6525
  AuthLogoutResponseSchema = external_exports.object({
6532
6526
  type: external_exports.literal("auth:logout:ok")
@@ -9309,7 +9303,7 @@ var init_methods = __esm({
9309
9303
  args: NO_ARGS
9310
9304
  },
9311
9305
  "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",
9306
+ 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
9307
  args: NO_ARGS
9314
9308
  },
9315
9309
  "auth:logout": {
@@ -60156,8 +60150,7 @@ function buildLarkBotHandlers(deps) {
60156
60150
  const { personaId: personaId2 } = parseArgs2(LarkBotProvisionArgs, frame, "larkBot:provision");
60157
60151
  const tunnelUrl = deps.getTunnelUrl();
60158
60152
  if (!tunnelUrl) throw new Error("larkBot provision requires tunnel to be up");
60159
- const ownerId = deps.getOwnerId();
60160
- if (!ownerId) throw new Error("larkBot provision requires feishu login (owner identity)");
60153
+ if (!deps.getOwnerId()) throw new Error("larkBot provision requires feishu login (owner identity)");
60161
60154
  if (deps.store.read(personaId2)) throw new Error(`larkBot already bound: ${personaId2}`);
60162
60155
  if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
60163
60156
  const botName = deps.getPersonaName(personaId2) ?? personaId2;
@@ -60166,12 +60159,7 @@ function buildLarkBotHandlers(deps) {
60166
60159
  let qrUrl;
60167
60160
  try {
60168
60161
  ;
60169
- ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({
60170
- personaId: personaId2,
60171
- botName,
60172
- tunnelUrl,
60173
- ownerId
60174
- }));
60162
+ ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({ personaId: personaId2, botName, tunnelUrl }));
60175
60163
  } catch (err) {
60176
60164
  inFlight.delete(personaId2);
60177
60165
  throw err;
@@ -60340,11 +60328,10 @@ function buildLarkBotHandlers(deps) {
60340
60328
  if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
60341
60329
  const tunnelUrl = deps.getTunnelUrl();
60342
60330
  if (!tunnelUrl) throw new Error("larkBot bind requires tunnel to be up");
60343
- const ownerId = deps.getOwnerId();
60344
- if (!ownerId) throw new Error("larkBot bind requires feishu login (owner identity)");
60331
+ if (!deps.getOwnerId()) throw new Error("larkBot bind requires feishu login (owner identity)");
60345
60332
  const name = botName ?? deps.getPersonaName(personaId2) ?? personaId2;
60346
60333
  upgradeInFlight.delete(personaId2);
60347
- await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName: name, tunnelUrl, ownerId });
60334
+ await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName: name, tunnelUrl });
60348
60335
  deps.store.write(personaId2, { appId, botName: name, boundAt: now() });
60349
60336
  emit(personaId2, { state: "bound", appId, botName: name, groups: groupsOf(personaId2) });
60350
60337
  return { response: { type: "larkBot:bindManual:ok" } };
@@ -60510,21 +60497,22 @@ function createLarkBotCloudClient(opts) {
60510
60497
  const f2 = opts.fetchImpl ?? globalThis.fetch;
60511
60498
  if (!f2) throw new Error("fetch is not available (Node >= 18 required)");
60512
60499
  const base = opts.api.replace(/\/+$/, "");
60500
+ function authHeaders() {
60501
+ const pass = opts.getLoginPass();
60502
+ if (!pass) throw new LarkBotCloudError("lark-bot cloud: no login pass (login / restart to obtain one)", 401);
60503
+ return { authorization: `Bearer ${pass}` };
60504
+ }
60513
60505
  async function request(method, pathname, body) {
60506
+ const headers = authHeaders();
60507
+ const url = `${base}${pathname}`;
60514
60508
  const ac = new AbortController();
60515
60509
  const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60516
60510
  let res;
60517
60511
  try {
60518
- res = await f2(`${base}${pathname}`, {
60512
+ res = await f2(url, {
60519
60513
  method,
60520
60514
  signal: ac.signal,
60521
- headers: {
60522
- authorization: `Bearer ${opts.apiKey}`,
60523
- "x-did": encodeURIComponent(opts.deviceId),
60524
- "x-name": encodeURIComponent(opts.displayName),
60525
- "x-scene": "daemon",
60526
- "content-type": "application/json"
60527
- },
60515
+ headers: { ...headers, "content-type": "application/json" },
60528
60516
  body: body ? JSON.stringify(body) : void 0
60529
60517
  });
60530
60518
  } catch (err) {
@@ -60606,8 +60594,6 @@ function createLarkBotCloudClient(opts) {
60606
60594
  });
60607
60595
  },
60608
60596
  async fetchResource(appId, messageId, fileKey, maxBytes, type) {
60609
- const ac = new AbortController();
60610
- const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60611
60597
  const qs = new URLSearchParams({
60612
60598
  appId,
60613
60599
  messageId,
@@ -60615,18 +60601,13 @@ function createLarkBotCloudClient(opts) {
60615
60601
  ...maxBytes ? { maxBytes: String(maxBytes) } : {},
60616
60602
  ...type ? { type } : {}
60617
60603
  }).toString();
60604
+ const headers = authHeaders();
60605
+ const url = `${base}/api/lark-bot/resource?${qs}`;
60606
+ const ac = new AbortController();
60607
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60618
60608
  let res;
60619
60609
  try {
60620
- res = await f2(`${base}/api/lark-bot/resource?${qs}`, {
60621
- method: "GET",
60622
- signal: ac.signal,
60623
- headers: {
60624
- authorization: `Bearer ${opts.apiKey}`,
60625
- "x-did": encodeURIComponent(opts.deviceId),
60626
- "x-name": encodeURIComponent(opts.displayName),
60627
- "x-scene": "daemon"
60628
- }
60629
- });
60610
+ res = await f2(url, { method: "GET", signal: ac.signal, headers });
60630
60611
  } catch (err) {
60631
60612
  throw new LarkBotCloudError(`lark-bot cloud resource fetch failed: ${err.message}`);
60632
60613
  } finally {
@@ -62157,7 +62138,7 @@ async function centralRequest(opts, path101, init) {
62157
62138
  }
62158
62139
  clearTimeout(timer);
62159
62140
  if (res.status === 401) {
62160
- throw new CentralClientError("UNAUTHORIZED", "TTC JWT invalid or expired");
62141
+ throw new CentralClientError("UNAUTHORIZED", "login pass rejected by center");
62161
62142
  }
62162
62143
  if (!res.ok) {
62163
62144
  let detail = "";
@@ -62177,12 +62158,8 @@ async function centralRequest(opts, path101, init) {
62177
62158
  async function centralExchange(opts) {
62178
62159
  const body = await centralRequest(opts, "/api/clawd-identity/exchange", {
62179
62160
  method: "POST",
62180
- body: {
62181
- jwt: opts.jwt,
62182
- provider: opts.provider,
62183
- deviceId: opts.deviceId,
62184
- selfDeviceId: opts.selfDeviceId
62185
- }
62161
+ bearer: opts.loginPass,
62162
+ body: { deviceId: opts.deviceId }
62186
62163
  });
62187
62164
  if (typeof body.token !== "string" || body.token.length === 0 || typeof body.ownerId !== "string" || typeof body.provider !== "string" || typeof body.displayName !== "string") {
62188
62165
  throw new CentralClientError("BAD_RESPONSE", "exchange: missing token/ownerId/provider/displayName");
@@ -62202,10 +62179,8 @@ async function centralGetPublicKey(opts) {
62202
62179
  async function centralUpsertDevice(opts) {
62203
62180
  const body = await centralRequest(opts, "/api/clawd-identity/devices/self", {
62204
62181
  method: "PUT",
62205
- bearer: opts.ttcJwt,
62182
+ bearer: opts.loginPass,
62206
62183
  body: {
62207
- deviceId: opts.deviceId,
62208
- provider: opts.provider,
62209
62184
  ...opts.url ? { url: opts.url } : {},
62210
62185
  ...opts.name ? { name: opts.name } : {}
62211
62186
  }
@@ -62226,11 +62201,10 @@ async function centralPrincipalStatus(opts) {
62226
62201
  return { active: body.active };
62227
62202
  }
62228
62203
  async function centralGetApiKey(opts) {
62229
- const body = await centralRequest(
62230
- opts,
62231
- `/api/persona-cloud/api-key?provider=${encodeURIComponent(opts.provider)}`,
62232
- { method: "GET", bearer: opts.ttcJwt }
62233
- );
62204
+ const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
62205
+ method: "GET",
62206
+ bearer: opts.loginPass
62207
+ });
62234
62208
  if (body.apiKey !== null && typeof body.apiKey !== "string") {
62235
62209
  throw new CentralClientError("BAD_RESPONSE", "api-key: apiKey must be string or null");
62236
62210
  }
@@ -62244,8 +62218,7 @@ async function centralGetApiKey(opts) {
62244
62218
  async function centralIssueApiKey(opts) {
62245
62219
  const body = await centralRequest(opts, "/api/persona-cloud/api-key", {
62246
62220
  method: "POST",
62247
- bearer: opts.ttcJwt,
62248
- body: { provider: opts.provider }
62221
+ bearer: opts.loginPass
62249
62222
  });
62250
62223
  if (typeof body.apiKey !== "string" || body.apiKey.length === 0) {
62251
62224
  throw new CentralClientError("BAD_RESPONSE", "api-key: missing apiKey");
@@ -62260,11 +62233,10 @@ async function centralIssueApiKey(opts) {
62260
62233
  };
62261
62234
  }
62262
62235
  async function centralListDevices(opts) {
62263
- const body = await centralRequest(
62264
- opts,
62265
- `/api/clawd-identity/devices?provider=${encodeURIComponent(opts.provider)}`,
62266
- { method: "GET", bearer: opts.ttcJwt }
62267
- );
62236
+ const body = await centralRequest(opts, "/api/clawd-identity/devices", {
62237
+ method: "GET",
62238
+ bearer: opts.loginPass
62239
+ });
62268
62240
  if (!Array.isArray(body.devices)) {
62269
62241
  throw new CentralClientError("BAD_RESPONSE", "devices: missing devices array");
62270
62242
  }
@@ -62281,8 +62253,8 @@ async function centralListDevices(opts) {
62281
62253
  async function centralConnectorsMine(opts) {
62282
62254
  const body = await centralRequest(opts, "/api/persona-cloud/connectors/mine", {
62283
62255
  method: "POST",
62284
- bearer: opts.ttcJwt,
62285
- body: { provider: opts.provider, personaId: opts.personaId, connectors: opts.connectors }
62256
+ bearer: opts.loginPass,
62257
+ body: { personaId: opts.personaId, connectors: opts.connectors }
62286
62258
  });
62287
62259
  if (!Array.isArray(body.connectors)) throw new CentralClientError("BAD_RESPONSE", "connectors/mine: missing connectors array");
62288
62260
  return { connectors: body.connectors };
@@ -62290,16 +62262,16 @@ async function centralConnectorsMine(opts) {
62290
62262
  async function centralConnectorFill(opts) {
62291
62263
  const body = await centralRequest(opts, "/api/persona-cloud/connectors/fields", {
62292
62264
  method: "PUT",
62293
- bearer: opts.ttcJwt,
62294
- body: { provider: opts.provider, connector: opts.connector, values: opts.values }
62265
+ bearer: opts.loginPass,
62266
+ body: { connector: opts.connector, values: opts.values }
62295
62267
  });
62296
62268
  if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
62297
62269
  }
62298
62270
  async function centralGithubAuthOauthStart(opts) {
62299
62271
  const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
62300
62272
  method: "POST",
62301
- bearer: opts.ttcJwt,
62302
- body: { provider: opts.provider, ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
62273
+ bearer: opts.loginPass,
62274
+ body: { ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
62303
62275
  });
62304
62276
  if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
62305
62277
  return { authUrl: body.authUrl };
@@ -62307,8 +62279,8 @@ async function centralGithubAuthOauthStart(opts) {
62307
62279
  async function centralGithubAuthToken(opts) {
62308
62280
  const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
62309
62281
  method: "POST",
62310
- bearer: opts.ttcJwt,
62311
- body: { provider: opts.provider }
62282
+ bearer: opts.loginPass,
62283
+ body: {}
62312
62284
  });
62313
62285
  if (body.token === null || body.token === void 0) return void 0;
62314
62286
  if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
@@ -62347,10 +62319,10 @@ var CloudGatewayClient = class {
62347
62319
  deps;
62348
62320
  fetchImpl;
62349
62321
  async credentials() {
62350
- const rec5 = this.deps.readIdentity();
62351
- if (!rec5) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7528\u4E0D\u4E86");
62322
+ const loginPass = this.deps.getLoginPass();
62323
+ if (!loginPass) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7528\u4E0D\u4E86");
62352
62324
  const get = this.deps.getApiKey ?? ((args) => centralGetApiKey({ api: this.deps.centralApi ?? "", fetchImpl: this.fetchImpl, ...args }));
62353
- const { apiKey, gatewayUrl } = await get({ ttcJwt: rec5.ttcToken, provider: rec5.provider });
62325
+ const { apiKey, gatewayUrl } = await get({ loginPass });
62354
62326
  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");
62355
62327
  if (!gatewayUrl) throw new Error("\u4E2D\u5FC3\u6CA1\u914D\u4E91 gateway \u5730\u5740\uFF08gatewayUrl \u4E3A\u7A7A\uFF09");
62356
62328
  return { apiKey, gatewayUrl: trimSlash(gatewayUrl) };
@@ -65906,8 +65878,7 @@ async function importZip(buf, root) {
65906
65878
  function parseLoginCallbackQuery(url) {
65907
65879
  return {
65908
65880
  token: url.searchParams.get("token") ?? "",
65909
- state: url.searchParams.get("state") ?? "",
65910
- expiresAt: url.searchParams.get("expires_at")
65881
+ state: url.searchParams.get("state") ?? ""
65911
65882
  };
65912
65883
  }
65913
65884
  function escapeHtml(s2) {
@@ -67568,7 +67539,7 @@ var OwnerIdentityStore = class {
67568
67539
  return null;
67569
67540
  }
67570
67541
  const r2 = parsed;
67571
- 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) {
67542
+ 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) {
67572
67543
  return null;
67573
67544
  }
67574
67545
  return {
@@ -67579,8 +67550,7 @@ var OwnerIdentityStore = class {
67579
67550
  ...typeof r2.identity.avatarUrl === "string" ? { avatarUrl: r2.identity.avatarUrl } : {},
67580
67551
  ...typeof r2.identity.unionId === "string" ? { unionId: r2.identity.unionId } : {}
67581
67552
  },
67582
- ttcToken: r2.ttcToken,
67583
- ttcTokenExpiresAt: typeof r2.ttcTokenExpiresAt === "number" ? r2.ttcTokenExpiresAt : null,
67553
+ loginPass: r2.loginPass,
67584
67554
  loginAt: typeof r2.loginAt === "string" ? r2.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
67585
67555
  };
67586
67556
  }
@@ -67644,23 +67614,23 @@ var LoginFlow = class {
67644
67614
  if (!identity.unionId) {
67645
67615
  return { ok: false, reason: "union_id missing from TTC user_info" };
67646
67616
  }
67617
+ let loginPass;
67647
67618
  try {
67648
- await this.deps.upsertBinding({
67619
+ ;
67620
+ ({ loginPass } = await this.deps.upsertBinding({
67649
67621
  deviceId: this.deps.getDeviceId(),
67650
67622
  ttcToken: params.token,
67651
67623
  identity
67652
- });
67624
+ }));
67653
67625
  } catch (err) {
67654
67626
  return {
67655
67627
  ok: false,
67656
67628
  reason: `device_binding upsert failed: ${err.message}`
67657
67629
  };
67658
67630
  }
67659
- const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
67660
67631
  this.deps.store.write({
67661
67632
  identity,
67662
- ttcToken: params.token,
67663
- ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
67633
+ loginPass,
67664
67634
  loginAt: new Date(now).toISOString()
67665
67635
  });
67666
67636
  return { ok: true, identity };
@@ -67766,6 +67736,15 @@ async function upsertDeviceBinding(opts) {
67766
67736
  }
67767
67737
  throw new DeviceBindingClientError("API_ERROR", `HTTP ${res.status}: ${detail}`);
67768
67738
  }
67739
+ let body = {};
67740
+ try {
67741
+ body = await res.json();
67742
+ } catch {
67743
+ }
67744
+ if (typeof body.loginPass !== "string" || body.loginPass.length === 0) {
67745
+ throw new DeviceBindingClientError("BAD_RESPONSE", "center did not issue a login pass (api too old?)");
67746
+ }
67747
+ return { loginPass: body.loginPass };
67769
67748
  }
67770
67749
 
67771
67750
  // src/contact/org-mesh.ts
@@ -67789,12 +67768,7 @@ async function fetchOrgPeers(opts) {
67789
67768
  res = await doFetch(`${opts.apiUrl}/api/device-bindings/org-peers`, {
67790
67769
  method: "GET",
67791
67770
  signal: ac.signal,
67792
- headers: {
67793
- authorization: `Bearer ${opts.apiKey}`,
67794
- "x-did": encodeURIComponent(opts.deviceId),
67795
- "x-name": encodeURIComponent(opts.displayName),
67796
- "x-scene": "daemon"
67797
- }
67771
+ headers: { authorization: `Bearer ${opts.loginPass}` }
67798
67772
  });
67799
67773
  } catch (err) {
67800
67774
  throw new OrgMeshClientError("NETWORK", `network error: ${err.message}`, err);
@@ -67877,7 +67851,7 @@ function rethrowLoginExpired(e) {
67877
67851
  if (e instanceof CentralClientError && e.code === "UNAUTHORIZED") {
67878
67852
  throw new ClawdError(
67879
67853
  ERROR_CODES.VALIDATION_ERROR,
67880
- `${LOGIN_EXPIRED_ERROR_MARKER}: \u767B\u5F55\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55`
67854
+ `${LOGIN_EXPIRED_ERROR_MARKER}: \u767B\u5F55\u7968\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55`
67881
67855
  );
67882
67856
  }
67883
67857
  throw e;
@@ -69979,9 +69953,7 @@ function buildFeishuAuthHandlers(deps) {
69979
69953
  type: "auth:getIdentity:ok",
69980
69954
  identity: record4 ? record4.identity : null,
69981
69955
  // 决策 #15:设备属性,与登录态无关——UI"分享我的 Hub 标识"用
69982
- deviceId: deps.deviceId,
69983
- // 过期时间戳(仅时间戳,token 本体永不出 ws)——UI 派生 过期/临期 提示
69984
- ttcTokenExpiresAt: record4 ? record4.ttcTokenExpiresAt : null
69956
+ deviceId: deps.deviceId
69985
69957
  }
69986
69958
  };
69987
69959
  };
@@ -70001,7 +69973,7 @@ function buildFeishuAuthHandlers(deps) {
70001
69973
  "\u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u62FF\u4E0D\u5230\u4E91 persona \u7684\u8C03\u7528 key"
70002
69974
  );
70003
69975
  }
70004
- return { ttcJwt: record4.ttcToken, provider: record4.identity.provider };
69976
+ return { loginPass: record4.loginPass };
70005
69977
  }
70006
69978
  const apiKeyGet = async () => {
70007
69979
  const { apiKey, createdAt, gatewayUrl } = await deps.apiKeyClient.get(requireLogin());
@@ -70034,9 +70006,9 @@ function ensureOwner6(ctx) {
70034
70006
  var invalid2 = (msg) => new ClawdError(ERROR_CODES.VALIDATION_ERROR, `VALIDATION_ERROR: ${msg}`);
70035
70007
  function buildCloudConnectorsHandlers(deps) {
70036
70008
  function requireLogin() {
70037
- const rec5 = deps.readIdentity();
70038
- 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");
70039
- return rec5;
70009
+ const loginPass = deps.getLoginPass();
70010
+ 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");
70011
+ return { loginPass };
70040
70012
  }
70041
70013
  async function personalConnectors(personaId2) {
70042
70014
  const persona = deps.getPersona(personaId2);
@@ -70322,7 +70294,7 @@ var METHOD_GRANT_MAP = {
70322
70294
  "auth:apiKey:get": ADMIN_ANY,
70323
70295
  "auth:apiKey:regenerate": ADMIN_ANY,
70324
70296
  // ---- device:* 设备目录 + 连接(决策 #15,owner-only) ----
70325
- // 设备目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
70297
+ // 设备目录 / 连接都是 owner 本机操作(用本机登录票找中心 server),guest 不可调
70326
70298
  "device:list": ADMIN_ANY,
70327
70299
  "device:connect": ADMIN_ANY,
70328
70300
  "device:connections": ADMIN_ANY
@@ -70376,7 +70348,7 @@ function computeMethodAccess(args) {
70376
70348
  }
70377
70349
 
70378
70350
  // src/version.ts
70379
- var version = "0.2.524".length > 0 ? "0.2.524" : "dev";
70351
+ var version = "0.2.526".length > 0 ? "0.2.526" : "dev";
70380
70352
 
70381
70353
  // src/cli-probe/probe.ts
70382
70354
  var fs77 = __toESM(require("fs"), 1);
@@ -72458,14 +72430,7 @@ async function submitIosInterest(opts) {
72458
72430
  res = await doFetch(`${opts.apiUrl}/api/ios-interest`, {
72459
72431
  method: "POST",
72460
72432
  signal: ac.signal,
72461
- headers: {
72462
- authorization: `Bearer ${opts.apiKey}`,
72463
- "x-did": encodeURIComponent(opts.deviceId),
72464
- "x-name": encodeURIComponent(opts.displayName),
72465
- "x-scene": "daemon",
72466
- "content-type": "application/json"
72467
- },
72468
- body: JSON.stringify({ unionId: opts.unionId, displayName: opts.displayName })
72433
+ headers: { authorization: `Bearer ${opts.loginPass}` }
72469
72434
  });
72470
72435
  } catch (err) {
72471
72436
  clearTimeout(timer);
@@ -72500,16 +72465,13 @@ function buildOnboardingHandlers(deps) {
72500
72465
  };
72501
72466
  const iosInterest = async (frame) => {
72502
72467
  parseArgs2(OnboardingIosInterestArgs, frame, "onboarding:iosInterest args");
72503
- const identity = deps.getOwnerIdentity();
72504
- if (!identity) {
72505
- throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "onboarding:iosInterest \u9700\u8981\u98DE\u4E66\u767B\u5F55");
72468
+ const loginPass = deps.getLoginPass();
72469
+ if (!loginPass) {
72470
+ throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "onboarding:iosInterest \u9700\u8981\u98DE\u4E66\u767B\u5F55\uFF08\u5DF2\u767B\u5F55\u7684\u8BDD\u91CD\u542F\u4E00\u6B21 clawd \u9886\u767B\u5F55\u7968\uFF09");
72506
72471
  }
72507
72472
  await submitIosInterest({
72508
72473
  apiUrl: deps.apiUrl,
72509
- apiKey: deps.apiKey,
72510
- deviceId: deps.deviceId,
72511
- unionId: identity.unionId,
72512
- displayName: identity.displayName,
72474
+ loginPass,
72513
72475
  ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
72514
72476
  });
72515
72477
  return { response: { registered: true } };
@@ -73047,8 +73009,8 @@ async function startDaemon(config) {
73047
73009
  });
73048
73010
  },
73049
73011
  // 验票第二段(spec 2026-08-28):本地验签只证明"票是中心签的",这一步问中心
73050
- // "持票人还在职吗"。token 不再过期,撤销全靠它。端点无鉴权——不带本机 ttcJwt,
73051
- // 否则飞书过期又会牵连互联。
73012
+ // "持票人还在职吗"。token 不再过期,撤销全靠它。端点无鉴权——不带本机登录票,
73013
+ // 本机没登录也能验别人的票。
73052
73014
  checkPrincipalActive: async (provider, ownerId2) => {
73053
73015
  const r2 = await centralPrincipalStatus({
73054
73016
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
@@ -73198,7 +73160,7 @@ async function startDaemon(config) {
73198
73160
  const r2 = ownerIdentityStore.read();
73199
73161
  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");
73200
73162
  try {
73201
- return await centralGithubAuthOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider, ...returnTo ? { returnTo } : {} });
73163
+ return await centralGithubAuthOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass: r2.loginPass, ...returnTo ? { returnTo } : {} });
73202
73164
  } catch (err) {
73203
73165
  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)}`);
73204
73166
  }
@@ -73206,7 +73168,7 @@ async function startDaemon(config) {
73206
73168
  token: async () => {
73207
73169
  const r2 = ownerIdentityStore.read();
73208
73170
  if (!r2) return void 0;
73209
- return centralGithubAuthToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider });
73171
+ return centralGithubAuthToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass: r2.loginPass });
73210
73172
  }
73211
73173
  },
73212
73174
  now: () => Date.now(),
@@ -73608,10 +73570,7 @@ async function startDaemon(config) {
73608
73570
  screenIdleProbeLogger
73609
73571
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
73610
73572
  const cloudGateway = new CloudGatewayClient({
73611
- readIdentity: () => {
73612
- const r2 = ownerIdentityStore.read();
73613
- return r2 ? { ttcToken: r2.ttcToken, provider: r2.identity.provider } : null;
73614
- },
73573
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
73615
73574
  centralApi: config.clawosApi ?? DEFAULT_CLAWOS_API,
73616
73575
  logger
73617
73576
  });
@@ -73718,9 +73677,7 @@ async function startDaemon(config) {
73718
73677
  try {
73719
73678
  await centralUpsertDevice({
73720
73679
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73721
- ttcJwt: record4.ttcToken,
73722
- provider: record4.identity.provider,
73723
- deviceId: authFile.deviceId,
73680
+ loginPass: record4.loginPass,
73724
73681
  url: currentTunnelUrl ?? void 0,
73725
73682
  name: ownerDisplayName
73726
73683
  });
@@ -73737,15 +73694,14 @@ async function startDaemon(config) {
73737
73694
  if (!feishuActive || orgMeshInFlight) return;
73738
73695
  const record4 = ownerIdentityStore.read();
73739
73696
  if (!record4) return;
73697
+ const loginPass = record4.loginPass;
73740
73698
  orgMeshInFlight = true;
73741
73699
  try {
73742
73700
  await syncOrgMesh({
73743
73701
  store: contactStore,
73744
73702
  fetchPeers: () => fetchOrgPeers({
73745
73703
  apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
73746
- apiKey: resolveClawdTicketsApiKey(),
73747
- deviceId: authFile.deviceId,
73748
- displayName: record4.identity.displayName || authFile.deviceId
73704
+ loginPass
73749
73705
  }),
73750
73706
  connectToDevice: createConnectToDevice(feishuDeviceDeps),
73751
73707
  log: (msg, meta) => logger.info(msg, meta),
@@ -73785,10 +73741,8 @@ async function startDaemon(config) {
73785
73741
  }
73786
73742
  return centralExchange({
73787
73743
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73788
- jwt: record4.ttcToken,
73789
- provider: record4.identity.provider,
73790
- deviceId,
73791
- selfDeviceId: authFile.deviceId
73744
+ loginPass: record4.loginPass,
73745
+ deviceId
73792
73746
  });
73793
73747
  },
73794
73748
  listDevices: async () => {
@@ -73798,8 +73752,7 @@ async function startDaemon(config) {
73798
73752
  }
73799
73753
  return centralListDevices({
73800
73754
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73801
- ttcJwt: record4.ttcToken,
73802
- provider: record4.identity.provider
73755
+ loginPass: record4.loginPass
73803
73756
  });
73804
73757
  },
73805
73758
  connectRemote,
@@ -73816,13 +73769,13 @@ async function startDaemon(config) {
73816
73769
  fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
73817
73770
  ttcAuthBase: TTC_HOSTS.auth,
73818
73771
  getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`,
73819
- // device_bindings upsert(did ↔ 飞书 unionId 映射):
73820
- // deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;鉴权 = 刚登录拿到的 TTC JWT。
73772
+ // 登录换票(design 2026-09-15-api-auth-tiers §2):device_bindings upsert(did ↔ 飞书 unionId 映射)
73773
+ // + 领登录票。deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;鉴权 = 刚登录拿到的 TTC JWT。
73821
73774
  // 失败即登录失败(fail-hard,login-flow.spec §upsert 失败)。
73822
73775
  getDeviceId: () => authFile.deviceId,
73823
73776
  upsertBinding: async (opts) => {
73824
73777
  try {
73825
- await upsertDeviceBinding({
73778
+ const r2 = await upsertDeviceBinding({
73826
73779
  apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
73827
73780
  ttcToken: opts.ttcToken,
73828
73781
  provider: opts.identity.provider,
@@ -73832,6 +73785,7 @@ async function startDaemon(config) {
73832
73785
  deviceId: opts.deviceId,
73833
73786
  unionId: opts.identity.unionId
73834
73787
  });
73788
+ return r2;
73835
73789
  } catch (err) {
73836
73790
  logger.warn("device-binding upsert failed", {
73837
73791
  deviceId: opts.deviceId,
@@ -73971,8 +73925,8 @@ async function startDaemon(config) {
73971
73925
  // 联系人列表 store(device:connect 出站 / contact:handshake 入站落同一 store)
73972
73926
  contactStore,
73973
73927
  // contact:handshake 换票(spec 2026-07-22;2026-08-17 起硬失败,不再是回填):
73974
- // 用 owner 当前 jwt 换 aud=对方设备 的票,现读 ownerIdentityStore(热切换后用新身份)。
73975
- // 契约(ContactHandlerDeps.exchangeToken):null = 本机登录问题(未登录 / JWT 过期),
73928
+ // 用本机登录票换 aud=对方设备 的票,现读 ownerIdentityStore(热切换后用新身份)。
73929
+ // 契约(ContactHandlerDeps.exchangeToken):null = 本机登录问题(未登录),
73976
73930
  // 其他失败原样抛给 handler——两种都会让握手硬失败,只是给对端的错误文案不同。
73977
73931
  exchangeToken: async (targetDeviceId) => {
73978
73932
  const record4 = ownerIdentityStore.read();
@@ -73980,10 +73934,8 @@ async function startDaemon(config) {
73980
73934
  try {
73981
73935
  const r2 = await centralExchange({
73982
73936
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73983
- jwt: record4.ttcToken,
73984
- provider: record4.identity.provider,
73937
+ loginPass: record4.loginPass,
73985
73938
  deviceId: targetDeviceId,
73986
- selfDeviceId: authFile.deviceId,
73987
73939
  // 换票在握手关键路径上(硬失败),对端 connect-remote RPC_TIMEOUT_MS=5s:
73988
73940
  // 必须在其内失败/成功,否则对端先超时不落库、本机后换票成功落库 → 反向半连,
73989
73941
  // 破坏「两侧都不落库」不变量。4s 留 1s 余量给 ws 往返
@@ -74036,21 +73988,18 @@ async function startDaemon(config) {
74036
73988
  }
74037
73989
  },
74038
73990
  // 云 persona 卡片上的连接器区(design 2026-09-11-mcp-connector §3.8):装了什么以 gateway 登记行为真源,
74039
- // 连没连上问中心。登录凭证只在 daemon 内部传给中心,与 apiKeyClient 同一条纪律。
73991
+ // 连没连上问中心。登录票只在 daemon 内部传给中心,与 apiKeyClient 同一条纪律。
74040
73992
  cloudConnectors: {
74041
73993
  getPersona: (personaId2) => personaRegistry.get(personaId2),
74042
73994
  gatewayConnectors: (gatewayPersonaId) => cloudGateway.personaConnectors(gatewayPersonaId),
74043
- readIdentity: () => {
74044
- const r2 = ownerIdentityStore.read();
74045
- return r2 ? { ttcJwt: r2.ttcToken, provider: r2.identity.provider } : null;
74046
- },
73995
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
74047
73996
  central: {
74048
73997
  mine: (args) => centralConnectorsMine({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ...args }),
74049
73998
  fill: (args) => centralConnectorFill({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ...args })
74050
73999
  }
74051
74000
  },
74052
74001
  // 设备目录 + 连接(决策 #15:daemon 代理中心 server)。
74053
- // exchange/listDevices 包掉 owner ttcJwt(每次现读 store——热切换后立即用新身份);
74002
+ // exchange/listDevices 包掉本机登录票(每次现读 store——热切换后立即用新身份);
74054
74003
  // dispatcher 的 FEISHU_GATED_METHODS gate 已保证未登录时这些 RPC 到不了 handler,
74055
74004
  // 这里的 FEISHU_LOGIN_REQUIRED 抛错只是防御纵深。
74056
74005
  feishuDevice: feishuDeviceDeps,
@@ -74238,9 +74187,7 @@ async function startDaemon(config) {
74238
74187
  const larkBotStore = createLarkBotBindingStore(import_node_path79.default.join(config.dataDir, "personas"));
74239
74188
  const larkBotCloud = createLarkBotCloudClient({
74240
74189
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
74241
- apiKey: resolveClawdTicketsApiKey(),
74242
- deviceId: authFile.deviceId,
74243
- displayName: ownerDisplayName
74190
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null
74244
74191
  });
74245
74192
  let larkDraining = false;
74246
74193
  const larkChatRouter = createLarkChatRouter({
@@ -74280,17 +74227,8 @@ async function startDaemon(config) {
74280
74227
  buildOnboardingHandlers({
74281
74228
  dataDir: config.dataDir,
74282
74229
  // 现读(登录/登出热切换);unionId 缺失时回落 ownerId(老登录记录无 unionId)
74283
- getOwnerIdentity: () => {
74284
- const rec5 = ownerIdentityStore.read();
74285
- if (!rec5) return null;
74286
- return {
74287
- unionId: rec5.identity.unionId ?? rec5.identity.ownerId,
74288
- displayName: rec5.identity.displayName
74289
- };
74290
- },
74291
- apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
74292
- apiKey: resolveClawdTicketsApiKey(),
74293
- deviceId: authFile.deviceId
74230
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
74231
+ apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API
74294
74232
  })
74295
74233
  );
74296
74234
  const authResolver = new AuthContextResolver({
@@ -74533,21 +74471,6 @@ async function startDaemon(config) {
74533
74471
  stateMgr.write(stateSnapshot);
74534
74472
  currentTunnelUrl = r2.url;
74535
74473
  wss.broadcastAll({ type: "tunnel:ready", url: r2.url, subdomain: r2.subdomain });
74536
- void refreshTunnelUrlOnStartup(larkBotDeps);
74537
- void (async () => {
74538
- if (larkBotDeps.listBoundPersonaIds().length === 0) return;
74539
- try {
74540
- const items = await larkBotCloud.claimEvents();
74541
- routeClaimedEvents(items, {
74542
- router: larkChatRouter,
74543
- dedupe: larkEventDedupe,
74544
- nowSec: Math.floor(Date.now() / 1e3)
74545
- });
74546
- if (items.length > 0) logger.info("larkBot pending events claimed", { count: items.length });
74547
- } catch (err) {
74548
- logger.warn("larkBot pending events claim failed", { err: err.message });
74549
- }
74550
- })();
74551
74474
  const connectUrl = resolvedAuthToken ? `${r2.url}#token=${resolvedAuthToken}` : r2.url;
74552
74475
  const lines = [
74553
74476
  `Tunnel: ${r2.url}`,
@@ -74592,9 +74515,23 @@ ${bar}
74592
74515
  logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
74593
74516
  }
74594
74517
  }
74595
- void reportDevice();
74518
+ void reportDevice().then(async () => {
74519
+ void runOrgMesh();
74520
+ if (!currentTunnelUrl || larkBotDeps.listBoundPersonaIds().length === 0) return;
74521
+ void refreshTunnelUrlOnStartup(larkBotDeps);
74522
+ try {
74523
+ const items = await larkBotCloud.claimEvents();
74524
+ routeClaimedEvents(items, {
74525
+ router: larkChatRouter,
74526
+ dedupe: larkEventDedupe,
74527
+ nowSec: Math.floor(Date.now() / 1e3)
74528
+ });
74529
+ if (items.length > 0) logger.info("larkBot pending events claimed", { count: items.length });
74530
+ } catch (err) {
74531
+ logger.warn("larkBot pending events claim failed", { err: err.message });
74532
+ }
74533
+ });
74596
74534
  void fetchServerKey();
74597
- void runOrgMesh();
74598
74535
  const tickAttachmentGc = () => {
74599
74536
  try {
74600
74537
  runAttachmentGc({