@clawos-dev/clawd 0.2.217-beta.428.894efa9 → 0.2.218-beta.429.c31b635

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
@@ -54191,6 +54191,22 @@ var LoginFlow = class {
54191
54191
  } catch (err) {
54192
54192
  return { ok: false, reason: `user_info failed: ${err.message}` };
54193
54193
  }
54194
+ if (!identity.unionId) {
54195
+ return { ok: false, reason: "union_id missing from TTC user_info" };
54196
+ }
54197
+ try {
54198
+ await this.deps.upsertBinding({
54199
+ deviceId: this.deps.getDeviceId(),
54200
+ unionId: identity.unionId,
54201
+ displayName: identity.displayName,
54202
+ avatarUrl: identity.avatarUrl ?? null
54203
+ });
54204
+ } catch (err) {
54205
+ return {
54206
+ ok: false,
54207
+ reason: `device_binding upsert failed: ${err.message}`
54208
+ };
54209
+ }
54194
54210
  const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
54195
54211
  this.deps.store.write({
54196
54212
  identity,
@@ -54359,6 +54375,57 @@ async function centralListDevices(opts) {
54359
54375
  return out;
54360
54376
  }
54361
54377
 
54378
+ // src/feishu-auth/device-binding-client.ts
54379
+ var DeviceBindingClientError = class extends Error {
54380
+ constructor(code, message, cause) {
54381
+ super(message);
54382
+ this.code = code;
54383
+ this.cause = cause;
54384
+ this.name = "DeviceBindingClientError";
54385
+ }
54386
+ code;
54387
+ cause;
54388
+ };
54389
+ var DEFAULT_TIMEOUT_MS4 = 8e3;
54390
+ async function upsertDeviceBinding(opts) {
54391
+ const doFetch = opts.fetchImpl ?? fetch;
54392
+ const ac = new AbortController();
54393
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS4);
54394
+ const body = { unionId: opts.unionId };
54395
+ if (opts.avatarUrl != null) body.avatarUrl = opts.avatarUrl;
54396
+ let res;
54397
+ try {
54398
+ res = await doFetch(`${opts.apiUrl}/api/device-bindings/self`, {
54399
+ method: "PUT",
54400
+ signal: ac.signal,
54401
+ headers: {
54402
+ authorization: `Bearer ${opts.apiKey}`,
54403
+ "x-did": encodeURIComponent(opts.deviceId),
54404
+ "x-name": encodeURIComponent(opts.displayName),
54405
+ "content-type": "application/json"
54406
+ },
54407
+ body: JSON.stringify(body)
54408
+ });
54409
+ } catch (err) {
54410
+ clearTimeout(timer);
54411
+ throw new DeviceBindingClientError(
54412
+ "NETWORK",
54413
+ `network error: ${err.message}`,
54414
+ err
54415
+ );
54416
+ } finally {
54417
+ clearTimeout(timer);
54418
+ }
54419
+ if (!res.ok) {
54420
+ let detail = "";
54421
+ try {
54422
+ detail = JSON.stringify(await res.json());
54423
+ } catch {
54424
+ }
54425
+ throw new DeviceBindingClientError("API_ERROR", `HTTP ${res.status}: ${detail}`);
54426
+ }
54427
+ }
54428
+
54362
54429
  // src/feishu-auth/verify-token.ts
54363
54430
  var crypto14 = __toESM(require("crypto"), 1);
54364
54431
  var CONNECT_TOKEN_PREFIX = "clawdtk1";
@@ -59528,7 +59595,16 @@ async function startDaemon(config) {
59528
59595
  store: ownerIdentityStore,
59529
59596
  fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
59530
59597
  ttcAuthBase: TTC_HOSTS.auth,
59531
- getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
59598
+ getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`,
59599
+ // device_bindings upsert(did ↔ 飞书 unionId 映射):
59600
+ // deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;apiKey 复用 CLAWD_TICKETS_API_KEY。
59601
+ // 失败即登录失败(fail-hard,login-flow.spec §upsert 失败)。
59602
+ getDeviceId: () => authFile.deviceId,
59603
+ upsertBinding: (opts) => upsertDeviceBinding({
59604
+ apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
59605
+ apiKey: resolveClawdTicketsApiKey(),
59606
+ ...opts
59607
+ })
59532
59608
  });
59533
59609
  const visitorStore = createVisitorStore({ dir: config.dataDir });
59534
59610
  const exchangeVisitorToken = buildVisitorLogin({