@clawos-dev/clawd 0.2.523 → 0.2.525

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +80 -99
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -60156,8 +60156,7 @@ function buildLarkBotHandlers(deps) {
60156
60156
  const { personaId: personaId2 } = parseArgs2(LarkBotProvisionArgs, frame, "larkBot:provision");
60157
60157
  const tunnelUrl = deps.getTunnelUrl();
60158
60158
  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)");
60159
+ if (!deps.getOwnerId()) throw new Error("larkBot provision requires feishu login (owner identity)");
60161
60160
  if (deps.store.read(personaId2)) throw new Error(`larkBot already bound: ${personaId2}`);
60162
60161
  if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
60163
60162
  const botName = deps.getPersonaName(personaId2) ?? personaId2;
@@ -60166,12 +60165,7 @@ function buildLarkBotHandlers(deps) {
60166
60165
  let qrUrl;
60167
60166
  try {
60168
60167
  ;
60169
- ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({
60170
- personaId: personaId2,
60171
- botName,
60172
- tunnelUrl,
60173
- ownerId
60174
- }));
60168
+ ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({ personaId: personaId2, botName, tunnelUrl }));
60175
60169
  } catch (err) {
60176
60170
  inFlight.delete(personaId2);
60177
60171
  throw err;
@@ -60340,11 +60334,10 @@ function buildLarkBotHandlers(deps) {
60340
60334
  if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
60341
60335
  const tunnelUrl = deps.getTunnelUrl();
60342
60336
  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)");
60337
+ if (!deps.getOwnerId()) throw new Error("larkBot bind requires feishu login (owner identity)");
60345
60338
  const name = botName ?? deps.getPersonaName(personaId2) ?? personaId2;
60346
60339
  upgradeInFlight.delete(personaId2);
60347
- await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName: name, tunnelUrl, ownerId });
60340
+ await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName: name, tunnelUrl });
60348
60341
  deps.store.write(personaId2, { appId, botName: name, boundAt: now() });
60349
60342
  emit(personaId2, { state: "bound", appId, botName: name, groups: groupsOf(personaId2) });
60350
60343
  return { response: { type: "larkBot:bindManual:ok" } };
@@ -60510,21 +60503,22 @@ function createLarkBotCloudClient(opts) {
60510
60503
  const f2 = opts.fetchImpl ?? globalThis.fetch;
60511
60504
  if (!f2) throw new Error("fetch is not available (Node >= 18 required)");
60512
60505
  const base = opts.api.replace(/\/+$/, "");
60506
+ function authHeaders() {
60507
+ const pass = opts.getLoginPass();
60508
+ if (!pass) throw new LarkBotCloudError("lark-bot cloud: no login pass (login / restart to obtain one)", 401);
60509
+ return { authorization: `Bearer ${pass}` };
60510
+ }
60513
60511
  async function request(method, pathname, body) {
60512
+ const headers = authHeaders();
60513
+ const url = `${base}${pathname}`;
60514
60514
  const ac = new AbortController();
60515
60515
  const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60516
60516
  let res;
60517
60517
  try {
60518
- res = await f2(`${base}${pathname}`, {
60518
+ res = await f2(url, {
60519
60519
  method,
60520
60520
  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
- },
60521
+ headers: { ...headers, "content-type": "application/json" },
60528
60522
  body: body ? JSON.stringify(body) : void 0
60529
60523
  });
60530
60524
  } catch (err) {
@@ -60606,8 +60600,6 @@ function createLarkBotCloudClient(opts) {
60606
60600
  });
60607
60601
  },
60608
60602
  async fetchResource(appId, messageId, fileKey, maxBytes, type) {
60609
- const ac = new AbortController();
60610
- const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60611
60603
  const qs = new URLSearchParams({
60612
60604
  appId,
60613
60605
  messageId,
@@ -60615,18 +60607,13 @@ function createLarkBotCloudClient(opts) {
60615
60607
  ...maxBytes ? { maxBytes: String(maxBytes) } : {},
60616
60608
  ...type ? { type } : {}
60617
60609
  }).toString();
60610
+ const headers = authHeaders();
60611
+ const url = `${base}/api/lark-bot/resource?${qs}`;
60612
+ const ac = new AbortController();
60613
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
60618
60614
  let res;
60619
60615
  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
- });
60616
+ res = await f2(url, { method: "GET", signal: ac.signal, headers });
60630
60617
  } catch (err) {
60631
60618
  throw new LarkBotCloudError(`lark-bot cloud resource fetch failed: ${err.message}`);
60632
60619
  } finally {
@@ -62213,7 +62200,10 @@ async function centralUpsertDevice(opts) {
62213
62200
  if (body.ok !== true) {
62214
62201
  throw new CentralClientError("BAD_RESPONSE", "upsertDevice: server did not ack ok");
62215
62202
  }
62216
- return { ok: true };
62203
+ return {
62204
+ ok: true,
62205
+ ...typeof body.loginPass === "string" && body.loginPass.length > 0 ? { loginPass: body.loginPass } : {}
62206
+ };
62217
62207
  }
62218
62208
  async function centralPrincipalStatus(opts) {
62219
62209
  const query = `provider=${encodeURIComponent(opts.provider)}&ownerId=${encodeURIComponent(opts.ownerId)}`;
@@ -67581,7 +67571,8 @@ var OwnerIdentityStore = class {
67581
67571
  },
67582
67572
  ttcToken: r2.ttcToken,
67583
67573
  ttcTokenExpiresAt: typeof r2.ttcTokenExpiresAt === "number" ? r2.ttcTokenExpiresAt : null,
67584
- loginAt: typeof r2.loginAt === "string" ? r2.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
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 } : {}
67585
67576
  };
67586
67577
  }
67587
67578
  write(record4) {
@@ -67644,12 +67635,14 @@ var LoginFlow = class {
67644
67635
  if (!identity.unionId) {
67645
67636
  return { ok: false, reason: "union_id missing from TTC user_info" };
67646
67637
  }
67638
+ let loginPass;
67647
67639
  try {
67648
- await this.deps.upsertBinding({
67640
+ ;
67641
+ ({ loginPass } = await this.deps.upsertBinding({
67649
67642
  deviceId: this.deps.getDeviceId(),
67650
67643
  ttcToken: params.token,
67651
67644
  identity
67652
- });
67645
+ }));
67653
67646
  } catch (err) {
67654
67647
  return {
67655
67648
  ok: false,
@@ -67661,7 +67654,8 @@ var LoginFlow = class {
67661
67654
  identity,
67662
67655
  ttcToken: params.token,
67663
67656
  ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
67664
- loginAt: new Date(now).toISOString()
67657
+ loginAt: new Date(now).toISOString(),
67658
+ ...loginPass ? { loginPass } : {}
67665
67659
  });
67666
67660
  return { ok: true, identity };
67667
67661
  }
@@ -67766,6 +67760,12 @@ async function upsertDeviceBinding(opts) {
67766
67760
  }
67767
67761
  throw new DeviceBindingClientError("API_ERROR", `HTTP ${res.status}: ${detail}`);
67768
67762
  }
67763
+ let body = {};
67764
+ try {
67765
+ body = await res.json();
67766
+ } catch {
67767
+ }
67768
+ return typeof body.loginPass === "string" && body.loginPass.length > 0 ? { loginPass: body.loginPass } : {};
67769
67769
  }
67770
67770
 
67771
67771
  // src/contact/org-mesh.ts
@@ -67789,12 +67789,7 @@ async function fetchOrgPeers(opts) {
67789
67789
  res = await doFetch(`${opts.apiUrl}/api/device-bindings/org-peers`, {
67790
67790
  method: "GET",
67791
67791
  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
- }
67792
+ headers: { authorization: `Bearer ${opts.loginPass}` }
67798
67793
  });
67799
67794
  } catch (err) {
67800
67795
  throw new OrgMeshClientError("NETWORK", `network error: ${err.message}`, err);
@@ -70376,7 +70371,7 @@ function computeMethodAccess(args) {
70376
70371
  }
70377
70372
 
70378
70373
  // src/version.ts
70379
- var version = "0.2.523".length > 0 ? "0.2.523" : "dev";
70374
+ var version = "0.2.525".length > 0 ? "0.2.525" : "dev";
70380
70375
 
70381
70376
  // src/cli-probe/probe.ts
70382
70377
  var fs77 = __toESM(require("fs"), 1);
@@ -72458,14 +72453,7 @@ async function submitIosInterest(opts) {
72458
72453
  res = await doFetch(`${opts.apiUrl}/api/ios-interest`, {
72459
72454
  method: "POST",
72460
72455
  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 })
72456
+ headers: { authorization: `Bearer ${opts.loginPass}` }
72469
72457
  });
72470
72458
  } catch (err) {
72471
72459
  clearTimeout(timer);
@@ -72500,16 +72488,13 @@ function buildOnboardingHandlers(deps) {
72500
72488
  };
72501
72489
  const iosInterest = async (frame) => {
72502
72490
  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");
72491
+ const loginPass = deps.getLoginPass();
72492
+ if (!loginPass) {
72493
+ 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
72494
  }
72507
72495
  await submitIosInterest({
72508
72496
  apiUrl: deps.apiUrl,
72509
- apiKey: deps.apiKey,
72510
- deviceId: deps.deviceId,
72511
- unionId: identity.unionId,
72512
- displayName: identity.displayName,
72497
+ loginPass,
72513
72498
  ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
72514
72499
  });
72515
72500
  return { response: { registered: true } };
@@ -73716,7 +73701,7 @@ async function startDaemon(config) {
73716
73701
  const record4 = ownerIdentityStore.read();
73717
73702
  if (!record4) return;
73718
73703
  try {
73719
- await centralUpsertDevice({
73704
+ const r2 = await centralUpsertDevice({
73720
73705
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
73721
73706
  ttcJwt: record4.ttcToken,
73722
73707
  provider: record4.identity.provider,
@@ -73724,9 +73709,16 @@ async function startDaemon(config) {
73724
73709
  url: currentTunnelUrl ?? void 0,
73725
73710
  name: ownerDisplayName
73726
73711
  });
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
+ }
73727
73718
  logger.info("device registered to central server", {
73728
73719
  deviceId: authFile.deviceId,
73729
- url: currentTunnelUrl ?? "(none)"
73720
+ url: currentTunnelUrl ?? "(none)",
73721
+ loginPass: r2.loginPass ? "issued" : "absent"
73730
73722
  });
73731
73723
  } catch (err) {
73732
73724
  logger.warn("device register failed (best-effort)", { err: err.message });
@@ -73736,16 +73728,15 @@ async function startDaemon(config) {
73736
73728
  const runOrgMesh = async () => {
73737
73729
  if (!feishuActive || orgMeshInFlight) return;
73738
73730
  const record4 = ownerIdentityStore.read();
73739
- if (!record4) return;
73731
+ if (!record4?.loginPass) return;
73732
+ const loginPass = record4.loginPass;
73740
73733
  orgMeshInFlight = true;
73741
73734
  try {
73742
73735
  await syncOrgMesh({
73743
73736
  store: contactStore,
73744
73737
  fetchPeers: () => fetchOrgPeers({
73745
73738
  apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
73746
- apiKey: resolveClawdTicketsApiKey(),
73747
- deviceId: authFile.deviceId,
73748
- displayName: record4.identity.displayName || authFile.deviceId
73739
+ loginPass
73749
73740
  }),
73750
73741
  connectToDevice: createConnectToDevice(feishuDeviceDeps),
73751
73742
  log: (msg, meta) => logger.info(msg, meta),
@@ -73816,13 +73807,13 @@ async function startDaemon(config) {
73816
73807
  fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
73817
73808
  ttcAuthBase: TTC_HOSTS.auth,
73818
73809
  getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`,
73819
- // device_bindings upsert(did ↔ 飞书 unionId 映射):
73820
- // deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;鉴权 = 刚登录拿到的 TTC JWT。
73810
+ // 登录换票(design 2026-09-15-api-auth-tiers §2):device_bindings upsert(did ↔ 飞书 unionId 映射)
73811
+ // + 领登录票。deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;鉴权 = 刚登录拿到的 TTC JWT。
73821
73812
  // 失败即登录失败(fail-hard,login-flow.spec §upsert 失败)。
73822
73813
  getDeviceId: () => authFile.deviceId,
73823
73814
  upsertBinding: async (opts) => {
73824
73815
  try {
73825
- await upsertDeviceBinding({
73816
+ const r2 = await upsertDeviceBinding({
73826
73817
  apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
73827
73818
  ttcToken: opts.ttcToken,
73828
73819
  provider: opts.identity.provider,
@@ -73830,8 +73821,10 @@ async function startDaemon(config) {
73830
73821
  });
73831
73822
  logger.info("device-binding upsert succeeded", {
73832
73823
  deviceId: opts.deviceId,
73833
- unionId: opts.identity.unionId
73824
+ unionId: opts.identity.unionId,
73825
+ loginPass: r2.loginPass ? "issued" : "absent"
73834
73826
  });
73827
+ return r2;
73835
73828
  } catch (err) {
73836
73829
  logger.warn("device-binding upsert failed", {
73837
73830
  deviceId: opts.deviceId,
@@ -74238,9 +74231,7 @@ async function startDaemon(config) {
74238
74231
  const larkBotStore = createLarkBotBindingStore(import_node_path79.default.join(config.dataDir, "personas"));
74239
74232
  const larkBotCloud = createLarkBotCloudClient({
74240
74233
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
74241
- apiKey: resolveClawdTicketsApiKey(),
74242
- deviceId: authFile.deviceId,
74243
- displayName: ownerDisplayName
74234
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null
74244
74235
  });
74245
74236
  let larkDraining = false;
74246
74237
  const larkChatRouter = createLarkChatRouter({
@@ -74280,17 +74271,8 @@ async function startDaemon(config) {
74280
74271
  buildOnboardingHandlers({
74281
74272
  dataDir: config.dataDir,
74282
74273
  // 现读(登录/登出热切换);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
74274
+ getLoginPass: () => ownerIdentityStore.read()?.loginPass ?? null,
74275
+ apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API
74294
74276
  })
74295
74277
  );
74296
74278
  const authResolver = new AuthContextResolver({
@@ -74533,21 +74515,6 @@ async function startDaemon(config) {
74533
74515
  stateMgr.write(stateSnapshot);
74534
74516
  currentTunnelUrl = r2.url;
74535
74517
  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
74518
  const connectUrl = resolvedAuthToken ? `${r2.url}#token=${resolvedAuthToken}` : r2.url;
74552
74519
  const lines = [
74553
74520
  `Tunnel: ${r2.url}`,
@@ -74592,9 +74559,23 @@ ${bar}
74592
74559
  logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
74593
74560
  }
74594
74561
  }
74595
- void reportDevice();
74562
+ void reportDevice().then(async () => {
74563
+ void runOrgMesh();
74564
+ if (!currentTunnelUrl || larkBotDeps.listBoundPersonaIds().length === 0) return;
74565
+ void refreshTunnelUrlOnStartup(larkBotDeps);
74566
+ try {
74567
+ const items = await larkBotCloud.claimEvents();
74568
+ routeClaimedEvents(items, {
74569
+ router: larkChatRouter,
74570
+ dedupe: larkEventDedupe,
74571
+ nowSec: Math.floor(Date.now() / 1e3)
74572
+ });
74573
+ if (items.length > 0) logger.info("larkBot pending events claimed", { count: items.length });
74574
+ } catch (err) {
74575
+ logger.warn("larkBot pending events claim failed", { err: err.message });
74576
+ }
74577
+ });
74596
74578
  void fetchServerKey();
74597
- void runOrgMesh();
74598
74579
  const tickAttachmentGc = () => {
74599
74580
  try {
74600
74581
  runAttachmentGc({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.523",
3
+ "version": "0.2.525",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",