@m1a0rz/agent-identity 0.5.1 → 0.5.2

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/index.d.ts CHANGED
@@ -10,5 +10,7 @@
10
10
  * - Tools: identity_whoami, identity_logout
11
11
  */
12
12
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
13
- export default function register(api: OpenClawPluginApi): void;
13
+ declare function registerImpl(api: OpenClawPluginApi): void;
14
+ declare const _default: typeof registerImpl;
15
+ export default _default;
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAgB,MAAM,qBAAqB,CAAC;AAqF3E,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QA6iBtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAgB,MAAM,qBAAqB,CAAC;AAkG3E,iBAAS,YAAY,CAAC,GAAG,EAAE,iBAAiB,QAqjB3C;wBAMwC,OAAO,YAAY;AAA5D,wBAA6D"}
package/dist/index.js CHANGED
@@ -75,7 +75,19 @@ function hasAnyIdentityConfig(identity) {
75
75
  identity.audience?.length ||
76
76
  identity.durationSeconds);
77
77
  }
78
- export default function register(api) {
78
+ /**
79
+ * Guard against jiti module duplication.
80
+ *
81
+ * OpenClaw ≥ v2026.3.28 may create multiple jiti instances with independent
82
+ * module caches. If this file is loaded twice, module-level state (Maps,
83
+ * flags, cached promises) in transitive imports splits into two copies and
84
+ * silently diverges. By pinning the register function on globalThis, the
85
+ * second load re-exports the first load's function, whose closures reference
86
+ * the first module graph — keeping all 27+ mutable module-level variables in
87
+ * a single, consistent copy.
88
+ */
89
+ const PLUGIN_REGISTER_KEY = Symbol.for("openclaw-identity.pluginRegister");
90
+ function registerImpl(api) {
79
91
  const pluginConfig = (api.pluginConfig ?? {});
80
92
  const storeDir = api.resolvePath(PLUGIN_STORE_DIR);
81
93
  initEncryptionKey(storeDir);
@@ -492,10 +504,13 @@ export default function register(api) {
492
504
  api.registerGatewayMethod("identity.session.get", createSessionGetHandler(sessionMethodDeps));
493
505
  logInfo(api.logger, "gateway methods: identity.session.put, identity.session.get (webchat session exchange)");
494
506
  }
495
- // Preflight: run async after register() returns so startup is never blocked.
496
- // On any failure, set pluginState.degraded so hooks skip all interception.
507
+ // Preflight: deferred to gateway_start hook so all subsystems (gateway,
508
+ // plugins, channels, …) have finished their synchronous plugin loading.
509
+ // Running inside register() would race against the next subsystem's sync
510
+ // load, whose event-loop blocking delays our fetch response callbacks and
511
+ // causes false timeouts (observed: ListUserPools 6836 ms → 55 ms once warm).
497
512
  const authzEnabled = !!(authz?.agentCheck || authz?.toolCheck || authz?.requireRiskApproval);
498
- runPluginPreflight({
513
+ const preflightDeps = {
499
514
  pluginConfig,
500
515
  identityClient,
501
516
  hasIdentity,
@@ -517,13 +532,18 @@ export default function register(api) {
517
532
  authzEnabled,
518
533
  namespaceName: authz?.namespaceName ?? "default",
519
534
  logger: api.logger,
520
- }).then((result) => {
521
- if (!result.ok) {
522
- pluginState.degraded = true;
523
- pluginState.failures = result.failures;
535
+ };
536
+ api.on("gateway_start", async () => {
537
+ try {
538
+ const result = await runPluginPreflight(preflightDeps);
539
+ if (!result.ok) {
540
+ pluginState.degraded = true;
541
+ pluginState.failures = result.failures;
542
+ }
543
+ }
544
+ catch (err) {
545
+ logWarn(api.logger, `[identity] preflight threw unexpectedly: ${String(err)}`);
524
546
  }
525
- }).catch((err) => {
526
- logWarn(api.logger, `[identity] preflight threw unexpectedly: ${String(err)}`);
527
547
  });
528
548
  // Local UDS server: lets other processes on the same machine retrieve
529
549
  // TIP tokens via HTTP-over-UDS (no network exposure, 0600 socket).
@@ -551,3 +571,8 @@ export default function register(api) {
551
571
  });
552
572
  }
553
573
  }
574
+ const g = globalThis;
575
+ if (!g[PLUGIN_REGISTER_KEY]) {
576
+ g[PLUGIN_REGISTER_KEY] = registerImpl;
577
+ }
578
+ export default g[PLUGIN_REGISTER_KEY];
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/local-server/handlers.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AASrD,oEAAoE;AACpE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrH,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,iBAAiB,KAAK,cAAc,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE;QACN,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC9B,CAAC;IACF,oEAAoE;IACpE,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACvD,CAAC;AAwBF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,IAc1C,KAAK,eAAe,EAAE,KAAK,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC,CA2IxE"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/local-server/handlers.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAUrD,oEAAoE;AACpE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrH,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,iBAAiB,KAAK,cAAc,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE;QACN,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC9B,CAAC;IACF,oEAAoE;IACpE,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACvD,CAAC;AAwBF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,IAc1C,KAAK,eAAe,EAAE,KAAK,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC,CA+IxE"}
@@ -17,6 +17,7 @@ import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
17
17
  import { getSessionWithRefresh } from "../services/session-refresh.js";
18
18
  import { getAllTIPTokens } from "../store/tip-store.js";
19
19
  import { isPersonalSessionModeEnabled, PERSONAL_SESSION_STORAGE_KEY, } from "../store/sender-session-store.js";
20
+ import { pluginState } from "../preflight/plugin-state.js";
20
21
  const MAIN_SESSION_KEY = "agent:main:main";
21
22
  function resolveMainSessionKey() {
22
23
  if (isPersonalSessionModeEnabled())
@@ -129,7 +130,11 @@ export function createRequestHandler(deps) {
129
130
  ttlSec: Math.max(0, Math.floor((entry.expiresAt - now) / 1000)),
130
131
  }));
131
132
  json(res, 200, {
132
- ok: true,
133
+ ok: !pluginState.degraded,
134
+ degraded: pluginState.degraded,
135
+ failures: pluginState.failures.length > 0
136
+ ? pluginState.failures.map((f) => ({ check: f.check, reason: f.reason }))
137
+ : undefined,
133
138
  personalSessionMode: isPersonalSessionModeEnabled(),
134
139
  mainSessionKey: resolveMainSessionKey(),
135
140
  activeSessions: sessions.length,
@@ -61,5 +61,14 @@ export type PreflightDeps = {
61
61
  info?: (msg: string) => void;
62
62
  };
63
63
  };
64
+ /**
65
+ * Run preflight checks.
66
+ *
67
+ * Expected to be called from the `gateway_start` hook, which fires after
68
+ * ALL subsystems (gateway, plugins, channels, …) have finished their
69
+ * synchronous plugin loading and the server is listening. This guarantees
70
+ * the event loop is free and network requests complete promptly — no yield
71
+ * loops or retry hacks needed.
72
+ */
64
73
  export declare function runPluginPreflight(deps: PreflightDeps): Promise<PreflightResult>;
65
74
  //# sourceMappingURL=plugin-preflight.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-preflight.d.ts","sourceRoot":"","sources":["../../../src/preflight/plugin-preflight.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAG9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,4CAA4C,CAC1D,GAAG,EAAE,YAAY,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0BxC;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,qFAAqF;IACrF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,+CAA+C;IAC/C,cAAc,EAAE,uBAAuB,CAAC;IACxC,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,0CAA0C;IAC1C,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,SAAS,GAAG,UAAU,CAAC;QAC7B,mBAAmB;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,oBAAoB;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AA4JF,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAkEtF"}
1
+ {"version":3,"file":"plugin-preflight.d.ts","sourceRoot":"","sources":["../../../src/preflight/plugin-preflight.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAG9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,4CAA4C,CAC1D,GAAG,EAAE,YAAY,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0BxC;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,qFAAqF;IACrF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,+CAA+C;IAC/C,cAAc,EAAE,uBAAuB,CAAC;IACxC,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,0CAA0C;IAC1C,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,SAAS,GAAG,UAAU,CAAC;QAC7B,mBAAmB;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,oBAAoB;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AA4JF;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAwEtF"}
@@ -192,11 +192,18 @@ async function checkNamespace(deps) {
192
192
  }
193
193
  }
194
194
  // ─── Main entry ───────────────────────────────────────────────────────────────
195
+ /**
196
+ * Run preflight checks.
197
+ *
198
+ * Expected to be called from the `gateway_start` hook, which fires after
199
+ * ALL subsystems (gateway, plugins, channels, …) have finished their
200
+ * synchronous plugin loading and the server is listening. This guarantees
201
+ * the event loop is free and network requests complete promptly — no yield
202
+ * loops or retry hacks needed.
203
+ */
195
204
  export async function runPluginPreflight(deps) {
196
205
  const logger = deps.logger;
197
- const warnFailed = (failures) => {
198
- logger?.warn?.(`[identity] preflight failed (${failures.length} issue(s)): ${failures.map((f) => `${f.check}: ${f.reason}`).join(" | ")}`);
199
- };
206
+ // Quick config sanity check.
200
207
  if (deps.pluginConfig) {
201
208
  const hit = findFirstUnrenderedPlaceholderInPluginConfig(deps.pluginConfig);
202
209
  if (hit) {
@@ -206,7 +213,7 @@ export async function runPluginPreflight(deps) {
206
213
  reason: `Configuration contains an unrendered environment variable placeholder ${hit.match} at ${hit.path}`,
207
214
  },
208
215
  ];
209
- warnFailed(failures);
216
+ logger?.warn?.(`[identity] preflight failed (${failures.length} issue(s)): ${failures.map((f) => `${f.check}: ${f.reason}`).join(" | ")}`);
210
217
  return { ok: false, failures };
211
218
  }
212
219
  }
@@ -215,21 +222,19 @@ export async function runPluginPreflight(deps) {
215
222
  { name: "userpool", run: () => checkUserpool(deps) },
216
223
  { name: "namespace", run: () => checkNamespace(deps) },
217
224
  ];
218
- return new Promise((resolve) => {
225
+ const result = await new Promise((resolve) => {
219
226
  let pending = steps.length;
220
227
  let settled = false;
221
228
  const finishOk = () => {
222
229
  if (settled)
223
230
  return;
224
231
  settled = true;
225
- logger?.info?.("[identity] preflight passed");
226
232
  resolve({ ok: true, failures: [] });
227
233
  };
228
234
  const finishFail = (failures) => {
229
235
  if (settled)
230
236
  return;
231
237
  settled = true;
232
- warnFailed(failures);
233
238
  resolve({ ok: false, failures });
234
239
  };
235
240
  for (const { name, run } of steps) {
@@ -246,8 +251,17 @@ export async function runPluginPreflight(deps) {
246
251
  finishOk();
247
252
  })
248
253
  .catch((err) => {
249
- finishFail([{ check: name, reason: `Unexpected preflight error: ${errMsg(err)}` }]);
254
+ finishFail([
255
+ { check: name, reason: `Unexpected preflight error: ${errMsg(err)}` },
256
+ ]);
250
257
  });
251
258
  }
252
259
  });
260
+ if (result.ok) {
261
+ logger?.info?.("[identity] preflight passed");
262
+ }
263
+ else {
264
+ logger?.warn?.(`[identity] preflight failed (${result.failures.length} issue(s)): ${result.failures.map((f) => `${f.check}: ${f.reason}`).join(" | ")}`);
265
+ }
266
+ return result;
253
267
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-state.d.ts","sourceRoot":"","sources":["../../../src/preflight/plugin-state.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,aAAa,GACb,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,WAGzB,CAAC"}
1
+ {"version":3,"file":"plugin-state.d.ts","sourceRoot":"","sources":["../../../src/preflight/plugin-state.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,aAAa,GACb,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAWF,eAAO,MAAM,WAAW,EAA2B,WAAW,CAAC"}
@@ -13,7 +13,13 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export const pluginState = {
17
- degraded: false,
18
- failures: [],
19
- };
16
+ // Share across jiti module duplicates. Two subsystems each run preflight
17
+ // independently — the first may time out (sets degraded=true) while the
18
+ // second succeeds. Without sharing, hooks from the first instance stay
19
+ // degraded even though the process-wide state is healthy.
20
+ const PLUGIN_STATE_KEY = Symbol.for("openclaw-identity.pluginState");
21
+ const _g = globalThis;
22
+ if (!_g[PLUGIN_STATE_KEY]) {
23
+ _g[PLUGIN_STATE_KEY] = { degraded: false, failures: [] };
24
+ }
25
+ export const pluginState = _g[PLUGIN_STATE_KEY];
@@ -1 +1 @@
1
- {"version":3,"file":"identity-credentials.d.ts","sourceRoot":"","sources":["../../../src/services/identity-credentials.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AASF,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kHAAkH;IAClH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC,CAAC;AAoEF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,mBAAmB,CAAC,CAgE9B"}
1
+ {"version":3,"file":"identity-credentials.d.ts","sourceRoot":"","sources":["../../../src/services/identity-credentials.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AASF,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kHAAkH;IAClH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC,CAAC;AA8DF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,mBAAmB,CAAC,CAgE9B"}
@@ -38,17 +38,14 @@ const REMOTE_METADATA_REFRESH_BUFFER_SEC = 300;
38
38
  * Returns the raw AK/SK/Token from the response, or null on failure.
39
39
  */
40
40
  async function fetchRemoteMetadata(fullUrl) {
41
- stsDebugLog("metadata fetch", { url: fullUrl });
42
41
  let res;
43
42
  try {
44
43
  res = await fetch(fullUrl);
45
44
  }
46
- catch (err) {
47
- stsDebugLog("metadata fetch failed", { error: String(err) });
45
+ catch {
48
46
  return null;
49
47
  }
50
48
  const resText = await res.text();
51
- stsDebugLog("metadata response", { status: res.status, body: resText });
52
49
  if (!res.ok)
53
50
  return null;
54
51
  let json;
@@ -77,7 +74,6 @@ async function fetchRemoteMetadataThenAssumeRole(fullUrl, roleTrn) {
77
74
  const creds = await fetchRemoteMetadata(fullUrl);
78
75
  if (!creds)
79
76
  return null;
80
- stsDebugLog("metadata creds ok, calling AssumeRole", { roleTrn });
81
77
  return assumeRole({
82
78
  accessKeyId: creds.accessKeyId,
83
79
  secretAccessKey: creds.secretAccessKey,
@@ -169,13 +165,6 @@ async function loadCredentialsFromFile(path) {
169
165
  };
170
166
  }
171
167
  const STS_ENDPOINT = "https://sts.volcengineapi.com";
172
- const STS_DEBUG = process.env["IDENTITY_STS_DEBUG"] === "1" || process.env["IDENTITY_STS_DEBUG"] === "true";
173
- function stsDebugLog(msg, data) {
174
- if (STS_DEBUG) {
175
- const payload = data !== undefined ? ` ${JSON.stringify(data)}` : "";
176
- console.warn(`[identity-credentials] STS AssumeRole: ${msg}${payload}`);
177
- }
178
- }
179
168
  /**
180
169
  * Call STS AssumeRole to get temporary credentials.
181
170
  * Uses GET with params in query per volcengine-nodejs-sdk (metaPath: get/text_plain).
@@ -221,22 +210,11 @@ async function assumeRole(params) {
221
210
  Accept: "application/json",
222
211
  ...signedHeaders,
223
212
  };
224
- stsDebugLog("request", {
225
- method: "GET",
226
- url: url.toString(),
227
- query,
228
- headers: { ...headers, Authorization: "(redacted)" },
229
- });
230
213
  const res = await fetch(url.toString(), {
231
214
  method: "GET",
232
215
  headers,
233
216
  });
234
217
  const resText = await res.text();
235
- stsDebugLog("response", {
236
- status: res.status,
237
- statusText: res.statusText,
238
- body: resText,
239
- });
240
218
  if (!res.ok) {
241
219
  throw new Error(`STS AssumeRole failed ${res.status}: ${resText}`);
242
220
  }
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/store/encryption.ts"],"names":[],"mappings":"AAgEA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIxD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAExD;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/store/encryption.ts"],"names":[],"mappings":"AA2EA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIxD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAExD;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
@@ -33,8 +33,12 @@ const SALT_LEN = 32;
33
33
  const SALT_FILENAME = ".key-salt";
34
34
  const PEPPER = "openclaw-identity-plugin-v1-AES256GCM-7f3a9c";
35
35
  const HKDF_INFO = "openclaw-identity-enc";
36
- let cachedKey = null;
37
- let initPromise = null;
36
+ const ENC_STATE_KEY = Symbol.for("openclaw-identity.encryptionState");
37
+ const _g = globalThis;
38
+ if (!_g[ENC_STATE_KEY]) {
39
+ _g[ENC_STATE_KEY] = { cachedKey: null, initPromise: null };
40
+ }
41
+ const encState = _g[ENC_STATE_KEY];
38
42
  async function readOrCreateSalt(storeDir) {
39
43
  const saltPath = path.join(storeDir, SALT_FILENAME);
40
44
  try {
@@ -60,10 +64,10 @@ async function deriveKey(storeDir) {
60
64
  * is fine — the returned promise is awaited lazily by getEncryptionKey).
61
65
  */
62
66
  export function initEncryptionKey(storeDir) {
63
- if (cachedKey || initPromise)
67
+ if (encState.cachedKey || encState.initPromise)
64
68
  return;
65
- initPromise = deriveKey(storeDir).then((key) => {
66
- cachedKey = key;
69
+ encState.initPromise = deriveKey(storeDir).then((key) => {
70
+ encState.cachedKey = key;
67
71
  return key;
68
72
  });
69
73
  }
@@ -72,10 +76,10 @@ export function initEncryptionKey(storeDir) {
72
76
  * completed yet. Callers in async hooks simply `await getEncryptionKey()`.
73
77
  */
74
78
  export async function getEncryptionKey() {
75
- if (cachedKey)
76
- return cachedKey;
77
- if (initPromise)
78
- return initPromise;
79
+ if (encState.cachedKey)
80
+ return encState.cachedKey;
81
+ if (encState.initPromise)
82
+ return encState.initPromise;
79
83
  throw new Error("Encryption key not initialized — call initEncryptionKey first");
80
84
  }
81
85
  export function encrypt(key, plaintext) {
@@ -142,6 +146,6 @@ export function isEncryptedField(value) {
142
146
  }
143
147
  /** Reset cached key and init promise (for testing only). */
144
148
  export function __resetCachedKey() {
145
- cachedKey = null;
146
- initPromise = null;
149
+ encState.cachedKey = null;
150
+ encState.initPromise = null;
147
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1a0rz/agent-identity",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Agent Identity: UserPool (用户池) login, TIP token (工作负载令牌), credential hosting (凭据托管 OAuth2/API key), optional tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",