@clawos-dev/clawd 0.2.415 → 0.2.417

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
@@ -57076,6 +57076,21 @@ async function authenticate(token, deps) {
57076
57076
  if (result.reason === "WRONG_AUDIENCE") return { ok: false, code: "TOKEN_REVOKED" };
57077
57077
  return { ok: false, code: "BAD_TOKEN" };
57078
57078
  }
57079
+ if (deps.checkPrincipalActive) {
57080
+ let active;
57081
+ try {
57082
+ active = await deps.checkPrincipalActive(result.provider, result.subOwner);
57083
+ } catch (e) {
57084
+ deps.onRevokeCheckFailed?.({
57085
+ provider: result.provider,
57086
+ ownerId: result.subOwner,
57087
+ subDevice: result.subDevice,
57088
+ reason: e.message
57089
+ });
57090
+ active = true;
57091
+ }
57092
+ if (!active) return { ok: false, code: "PRINCIPAL_REVOKED" };
57093
+ }
57079
57094
  return {
57080
57095
  ok: true,
57081
57096
  context: connectGuestContext(result.subDevice, result.subOwner, result.provider, result.displayName)
@@ -59859,6 +59874,16 @@ async function centralUpsertDevice(opts) {
59859
59874
  }
59860
59875
  return { ok: true };
59861
59876
  }
59877
+ async function centralPrincipalStatus(opts) {
59878
+ const query = `provider=${encodeURIComponent(opts.provider)}&ownerId=${encodeURIComponent(opts.ownerId)}`;
59879
+ const body = await centralRequest(opts, `/api/clawd-identity/principal-status?${query}`, {
59880
+ method: "GET"
59881
+ });
59882
+ if (typeof body.active !== "boolean") {
59883
+ throw new CentralClientError("BAD_RESPONSE", "principal-status: missing active flag");
59884
+ }
59885
+ return { active: body.active };
59886
+ }
59862
59887
  async function centralListDevices(opts) {
59863
59888
  const body = await centralRequest(
59864
59889
  opts,
@@ -60395,8 +60420,16 @@ function buildSessionHandlers(deps) {
60395
60420
  );
60396
60421
  }
60397
60422
  };
60423
+ const assertNotMasterPersona = (personaId2) => {
60424
+ if (personaId2 !== MASTER_PERSONA_ID) return;
60425
+ throw new ClawdError(
60426
+ ERROR_CODES.FORBIDDEN,
60427
+ `${MASTER_PERSONA_ID} \u7684\u4F1A\u8BDD\u7531\u8BDD\u9898\u521B\u5EFA\uFF0C\u4E0D\u80FD\u76F4\u63A5\u5EFA\uFF1A\u8BF7\u5728\u9891\u9053\u91CC\u65B0\u5EFA\u8BDD\u9898`
60428
+ );
60429
+ };
60398
60430
  const create = async (frame, _client, ctx) => {
60399
60431
  const args = SessionCreateArgs.parse(frame);
60432
+ assertNotMasterPersona(args.ownerPersonaId);
60400
60433
  let tool = args.tool;
60401
60434
  let model = args.model;
60402
60435
  let effort = args.effort;
@@ -60436,6 +60469,7 @@ function buildSessionHandlers(deps) {
60436
60469
  );
60437
60470
  }
60438
60471
  const args = SessionImportExternalArgs.parse(frame);
60472
+ assertNotMasterPersona(args.ownerPersonaId);
60439
60473
  if (args.ownerPersonaId) {
60440
60474
  const persona = deps.personaRegistry.get(args.ownerPersonaId);
60441
60475
  if (!persona) {
@@ -62278,7 +62312,7 @@ function computeMethodAccess(args) {
62278
62312
  }
62279
62313
 
62280
62314
  // src/version.ts
62281
- var version = "0.2.415".length > 0 ? "0.2.415" : "dev";
62315
+ var version = "0.2.417".length > 0 ? "0.2.417" : "dev";
62282
62316
 
62283
62317
  // src/cli-probe/probe.ts
62284
62318
  var fs65 = __toESM(require("fs"), 1);
@@ -66208,6 +66242,31 @@ async function startDaemon(config) {
66208
66242
  publicKeyPem,
66209
66243
  expectedDeviceId: authFile.deviceId
66210
66244
  });
66245
+ },
66246
+ // 验票第二段(spec 2026-08-28):本地验签只证明"票是中心签的",这一步问中心
66247
+ // "持票人还在职吗"。token 不再过期,撤销全靠它。端点无鉴权——不带本机 ttcJwt,
66248
+ // 否则飞书过期又会牵连互联。
66249
+ checkPrincipalActive: async (provider, ownerId2) => {
66250
+ const r = await centralPrincipalStatus({
66251
+ api: config.clawosApi ?? DEFAULT_CLAWOS_API,
66252
+ provider,
66253
+ ownerId: ownerId2,
66254
+ // 必须短于 AuthGate 的握手 timer(默认 5s,transport/auth.ts)。那个 timer 只看
66255
+ // st.authed、**不看 st.pending**,所以 authenticate 跑太久会被它 1008 关掉——
66256
+ // 那是"连接被关",不是我们要的 fail-open 放行。centralRequest 默认 15s 远超
66257
+ // 预算,这里压到 3s:中心慢时先抛超时 → 走 fail-open → 放行 + warn。
66258
+ timeoutMs: 3e3
66259
+ });
66260
+ return r.active;
66261
+ },
66262
+ // fail-open 的可观测性出口:连接放行了,但撤销状态这次没查到。
66263
+ onRevokeCheckFailed: (f) => {
66264
+ logger.warn("principal revoke check failed (fail-open: connection allowed)", {
66265
+ provider: f.provider,
66266
+ ownerId: f.ownerId,
66267
+ subDevice: f.subDevice,
66268
+ reason: f.reason
66269
+ });
66211
66270
  }
66212
66271
  } : {}
66213
66272
  });
@@ -66220,7 +66279,8 @@ async function startDaemon(config) {
66220
66279
  // owner 路径 constantTimeEqual 防侧信道;guest 路径走 verifyConnectToken (Ed25519).
66221
66280
  expectedToken: resolvedAuthToken,
66222
66281
  // 纯验票(spec 2026-07-22 双向确认建联):guest 入站 = 自包含 connect token
66223
- // **本地验签**(Ed25519 公钥 + aud === 本机 deviceId + 未过期),零网络调用。
66282
+ // 本地验签(Ed25519 公钥 + aud === 本机 deviceId + 未过期)+ 中心撤销查询
66283
+ // (spec 2026-08-28,查不通则 fail-open 放行并 warn)。
66224
66284
  // 反向落库不再挂在验票副作用上——落库唯一路径 = contact:handshake handler。
66225
66285
  authenticate: async (t) => authenticate(t, buildConnectAuthDeps()),
66226
66286
  onAuthed: (h, ctx) => wsServer?.attachClientContext(h.id, ctx),