@namzu/sdk 58.1.0 → 59.0.0

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 (90) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/account/binding.d.ts +20 -0
  3. package/dist/account/binding.d.ts.map +1 -0
  4. package/dist/account/binding.js +27 -0
  5. package/dist/account/binding.js.map +1 -0
  6. package/dist/account/index.d.ts +9 -0
  7. package/dist/account/index.d.ts.map +1 -0
  8. package/dist/account/index.js +5 -0
  9. package/dist/account/index.js.map +1 -0
  10. package/dist/account/lock.d.ts +33 -0
  11. package/dist/account/lock.d.ts.map +1 -0
  12. package/dist/account/lock.js +170 -0
  13. package/dist/account/lock.js.map +1 -0
  14. package/dist/account/registry.d.ts +21 -0
  15. package/dist/account/registry.d.ts.map +1 -0
  16. package/dist/account/registry.js +340 -0
  17. package/dist/account/registry.js.map +1 -0
  18. package/dist/account/types.d.ts +124 -0
  19. package/dist/account/types.d.ts.map +1 -0
  20. package/dist/account/types.js +35 -0
  21. package/dist/account/types.js.map +1 -0
  22. package/dist/pals/communication/ingress-schema.d.ts +38 -38
  23. package/dist/peers/record.d.ts +98 -0
  24. package/dist/peers/record.d.ts.map +1 -1
  25. package/dist/peers/record.js +17 -0
  26. package/dist/peers/record.js.map +1 -1
  27. package/dist/peers/registry.d.ts.map +1 -1
  28. package/dist/peers/registry.js +2 -2
  29. package/dist/peers/registry.js.map +1 -1
  30. package/dist/provider/errors.d.ts.map +1 -1
  31. package/dist/provider/errors.js +25 -2
  32. package/dist/provider/errors.js.map +1 -1
  33. package/dist/provider/token-budget.d.ts.map +1 -1
  34. package/dist/provider/token-budget.js +9 -1
  35. package/dist/provider/token-budget.js.map +1 -1
  36. package/dist/public-runtime.d.ts +1 -0
  37. package/dist/public-runtime.d.ts.map +1 -1
  38. package/dist/public-runtime.js +1 -0
  39. package/dist/public-runtime.js.map +1 -1
  40. package/dist/public-types.d.ts +2 -0
  41. package/dist/public-types.d.ts.map +1 -1
  42. package/dist/runtime/query/executor.d.ts +7 -2
  43. package/dist/runtime/query/executor.d.ts.map +1 -1
  44. package/dist/runtime/query/executor.js +10 -2
  45. package/dist/runtime/query/executor.js.map +1 -1
  46. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +7 -7
  47. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -1
  48. package/dist/runtime/query/iteration/phases/compaction-breaker.js +7 -7
  49. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -1
  50. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  51. package/dist/runtime/query/tool-output-budget.js +5 -3
  52. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  53. package/dist/tools/text-window.d.ts.map +1 -1
  54. package/dist/tools/text-window.js +4 -3
  55. package/dist/tools/text-window.js.map +1 -1
  56. package/dist/types/errors/catalog.d.ts.map +1 -1
  57. package/dist/types/errors/catalog.js +6 -0
  58. package/dist/types/errors/catalog.js.map +1 -1
  59. package/dist/types/provider/errors.d.ts +7 -0
  60. package/dist/types/provider/errors.d.ts.map +1 -1
  61. package/dist/types/provider/errors.js +27 -3
  62. package/dist/types/provider/errors.js.map +1 -1
  63. package/dist/types/session/records.d.ts +215 -1
  64. package/dist/types/session/records.d.ts.map +1 -1
  65. package/dist/types/session/records.js +13 -0
  66. package/dist/types/session/records.js.map +1 -1
  67. package/dist/utils/copy-string.d.ts +10 -0
  68. package/dist/utils/copy-string.d.ts.map +1 -0
  69. package/dist/utils/copy-string.js +12 -0
  70. package/dist/utils/copy-string.js.map +1 -0
  71. package/package.json +1 -1
  72. package/src/account/binding.ts +43 -0
  73. package/src/account/index.ts +32 -0
  74. package/src/account/lock.ts +184 -0
  75. package/src/account/registry.ts +413 -0
  76. package/src/account/types.ts +137 -0
  77. package/src/peers/record.ts +18 -0
  78. package/src/peers/registry.ts +2 -2
  79. package/src/provider/errors.ts +27 -1
  80. package/src/provider/token-budget.ts +9 -3
  81. package/src/public-runtime.ts +17 -0
  82. package/src/public-types.ts +15 -0
  83. package/src/runtime/query/executor.ts +10 -2
  84. package/src/runtime/query/iteration/phases/compaction-breaker.ts +7 -7
  85. package/src/runtime/query/tool-output-budget.ts +8 -4
  86. package/src/tools/text-window.ts +4 -3
  87. package/src/types/errors/catalog.ts +6 -0
  88. package/src/types/provider/errors.ts +40 -3
  89. package/src/types/session/records.ts +16 -0
  90. package/src/utils/copy-string.ts +11 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 59.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 153dd6f: Breaking: a spent allowance is no longer retried, and two unions gain a member. `ProviderErrorCode` gains `usage_limit`, and the classifier now maps the codes `insufficient_quota`, `usage_limit_reached`, `usage_limit_exceeded` and `billing_error` to it, and a `ProviderRequestError` that names a spent allowance (by provider code, or by an exhausted credit balance or quota in its own words) is `usage_limit` and not retried, where these were a retryable `rate_limit`. A caller that retried or waited on `rate_limit` for a spent allowance now gets `usage_limit` and no retry: handle it by switching account or telling the user the allowance is spent. An exhaustive `switch` over `ProviderErrorCode`, or over `SessionRecord` types (which gains `account_bound`), needs a new case to keep compiling.
8
+
9
+ Also adds an account registry: several credentials for one provider, each session bound to one. `createAccountRegistry` keeps labelled API-key accounts (each with an optional endpoint and starting model) and private Codex-home accounts (the codex provider only) in a new `accounts.json` under `NAMZU_HOME`; today's sign-in is always the provider's `default` account and `credentials.json` and `api-keys.json` are read as before. A session log that contains `account_bound` is refused by earlier builds; a session that stays on the default account never writes one.
10
+
11
+ ### Minor Changes
12
+
13
+ - 7a472bb: A live-session registry record can now carry five optional fields: `hostSlot`, `conversationId`, `account`, `limited` and `limitedUntil` (when a spent allowance is expected back). A spaces host uses them to match a terminal to its sidebar row and to show which conversation, account and allowance state it is on. Records without them are unchanged and still valid. A session started inside a spaces host writes them; a plain `namzu` session does not, so it stays visible to older builds. Builds before this release skip a record that carries any of them (they list the terminal as absent from `list_sessions` and peer messaging), so upgrade every build that shares a registry before starting a spaces host.
14
+
15
+ ### Patch Changes
16
+
17
+ - ea5da1d: A tool-result guardrail's `halt` verdict now ends the turn instead of reaching the model as an ordinary failed tool result.
18
+
19
+ `ToolExecutor` caught every exception from a tool call and turned it into a failed result, including the `ToolResultHalted` that a `halt` verdict throws. The model then read it as a rejected call and tried another way, which is what `refuse` means and `halt` must not. The executor now lets `ToolResultHalted` through, so the turn stops with `stopReason: 'error'` and the model is not asked again.
20
+
21
+ This only affects hosts that configure a guardrail returning `{ action: 'halt' }`. Nothing to change unless you relied on a `halt` being retried; return `{ action: 'refuse' }` to keep that behaviour.
22
+
23
+ - 153dd6f: A live-session registry record that carries a field this build does not know is now read instead of dropped, so a session started by a newer build stays visible to `list_sessions` in an older one. Writing a record is still strict.
24
+
25
+ ## 58.1.1
26
+
27
+ ### Patch Changes
28
+
29
+ - 60ca139: Prevent bounded tool previews and delegated-agent activity rows from retaining the backing storage of much larger source strings. Visible text, output limits and retained full-output recovery are unchanged; copied previews preserve all UTF-16 code units.
30
+
31
+ Explain that repeated compaction refills can result from little reclaimed space even with small outputs. Suggest checking the model context window instead of diagnosing an oversized file from the refill count alone. The compaction safety limits remain unchanged.
32
+
3
33
  ## 58.1.0
4
34
 
5
35
  ### Minor Changes
@@ -0,0 +1,20 @@
1
+ import type { AccountBoundRecord } from '../types/session/records.js';
2
+ import { type AccountId, type AccountRegistry } from './types.js';
3
+ export type AccountBoundReason = AccountBoundRecord['reason'];
4
+ /** The fields of an `account_bound` record that a writer supplies; the log adds the envelope. */
5
+ export declare function accountBoundPayload(id: AccountId, provider: string, reason: AccountBoundReason): Pick<AccountBoundRecord, 'type' | 'accountId' | 'provider' | 'reason'>;
6
+ export interface BoundAccount {
7
+ readonly accountId: AccountId;
8
+ /** The recorded account was removed; the session continues on `<provider>:default`. */
9
+ readonly fellBack: boolean;
10
+ }
11
+ /**
12
+ * The account a session continues under, from its replayed records: the last
13
+ * `account_bound` for `provider`, else that provider's `default`. When the
14
+ * recorded account has since been removed the result is the default with
15
+ * `fellBack` set, so the caller can say so and append a `user_switch` record.
16
+ */
17
+ export declare function resolveBoundAccount(records: readonly {
18
+ readonly type: string;
19
+ }[], provider: string, registry: Pick<AccountRegistry, 'get'>): BoundAccount;
20
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/account/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAErE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAyB,MAAM,YAAY,CAAA;AAExF,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;AAE7D,iGAAiG;AACjG,wBAAgB,mBAAmB,CAClC,EAAE,EAAE,SAAS,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACxB,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC,CAExE;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,EAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,GACpC,YAAY,CAWd"}
@@ -0,0 +1,27 @@
1
+ import { accountId } from './registry.js';
2
+ import { DEFAULT_ACCOUNT_LABEL } from './types.js';
3
+ /** The fields of an `account_bound` record that a writer supplies; the log adds the envelope. */
4
+ export function accountBoundPayload(id, provider, reason) {
5
+ return { type: 'account_bound', accountId: id, provider, reason };
6
+ }
7
+ /**
8
+ * The account a session continues under, from its replayed records: the last
9
+ * `account_bound` for `provider`, else that provider's `default`. When the
10
+ * recorded account has since been removed the result is the default with
11
+ * `fellBack` set, so the caller can say so and append a `user_switch` record.
12
+ */
13
+ export function resolveBoundAccount(records, provider, registry) {
14
+ const fallback = accountId(provider, DEFAULT_ACCOUNT_LABEL);
15
+ let last;
16
+ for (const record of records) {
17
+ if (record.type === 'account_bound' && record.provider === provider) {
18
+ last = record;
19
+ }
20
+ }
21
+ if (!last)
22
+ return { accountId: fallback, fellBack: false };
23
+ if (registry.get(last.accountId))
24
+ return { accountId: last.accountId, fellBack: false };
25
+ return { accountId: fallback, fellBack: last.accountId !== fallback };
26
+ }
27
+ //# sourceMappingURL=binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/account/binding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAwC,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIxF,iGAAiG;AACjG,MAAM,UAAU,mBAAmB,CAClC,EAAa,EACb,QAAgB,EAChB,MAA0B;IAE1B,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AAClE,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,OAA6C,EAC7C,QAAgB,EAChB,QAAsC;IAEtC,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAA;IAC3D,IAAI,IAAoC,CAAA;IACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,IAAK,MAA6B,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC7F,IAAI,GAAG,MAA4B,CAAA;QACpC,CAAC;IACF,CAAC;IACD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IAC1D,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACvF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAA;AACtE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { ACCOUNTS_FILE_NAME, ACCOUNTS_FILE_VERSION, accountId, createAccountRegistry, parseAccountId, } from './registry.js';
2
+ export type { AccountRegistryOptions } from './registry.js';
3
+ export { codexRefreshLockPath, withAccountLock, withAccountLockSync, withAccountRefreshLock, } from './lock.js';
4
+ export type { AccountLockOptions, AccountLockSyncOptions } from './lock.js';
5
+ export { accountBoundPayload, resolveBoundAccount } from './binding.js';
6
+ export type { AccountBoundReason, BoundAccount } from './binding.js';
7
+ export { AccountLockError, AccountRegistryError, CredentialWithdrawnError, DEFAULT_ACCOUNT_LABEL, } from './types.js';
8
+ export type { Account, AccountCredentialReaders, AccountId, AccountRegistry, AccountSource, NewAccount, ResolvedAccountCredential, } from './types.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/account/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,qBAAqB,EACrB,SAAS,EACT,qBAAqB,EACrB,cAAc,GACd,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,GACtB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EACN,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,GACrB,MAAM,YAAY,CAAA;AACnB,YAAY,EACX,OAAO,EACP,wBAAwB,EACxB,SAAS,EACT,eAAe,EACf,aAAa,EACb,UAAU,EACV,yBAAyB,GACzB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { ACCOUNTS_FILE_NAME, ACCOUNTS_FILE_VERSION, accountId, createAccountRegistry, parseAccountId, } from './registry.js';
2
+ export { codexRefreshLockPath, withAccountLock, withAccountLockSync, withAccountRefreshLock, } from './lock.js';
3
+ export { accountBoundPayload, resolveBoundAccount } from './binding.js';
4
+ export { AccountLockError, AccountRegistryError, CredentialWithdrawnError, DEFAULT_ACCOUNT_LABEL, } from './types.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/account/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,qBAAqB,EACrB,SAAS,EACT,qBAAqB,EACrB,cAAc,GACd,MAAM,eAAe,CAAA;AAEtB,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,GACtB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvE,OAAO,EACN,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,GACrB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The lock file beside a Codex account's `auth.json`. One lock per account
3
+ * directory, so a stuck account never blocks another.
4
+ */
5
+ export declare function codexRefreshLockPath(dir: string): string;
6
+ export interface AccountLockOptions {
7
+ /** How many times to try before refusing. Default 1: a held lock is refused at once. */
8
+ readonly attempts?: number;
9
+ /** Waits between attempts; injected so a test never depends on the clock. */
10
+ readonly wait?: (attempt: number) => Promise<void>;
11
+ }
12
+ /**
13
+ * Run `operation` while holding the lock file at `lockPath`. The lock is held
14
+ * across local read/write work, never across a user prompt. A lock whose
15
+ * recorded process has exited is reclaimed; a lock held by a live process is a
16
+ * refusal (or a wait, when attempts and a wait function are given).
17
+ */
18
+ export declare function withAccountLock<T>(lockPath: string, operation: () => Promise<T> | T, options?: AccountLockOptions): Promise<T>;
19
+ export interface AccountLockSyncOptions {
20
+ /** How many times to try before refusing. Default 1. */
21
+ readonly attempts?: number;
22
+ /** Blocks between attempts; injected so a test never depends on the clock. */
23
+ readonly wait?: (attempt: number) => void;
24
+ }
25
+ /** Synchronous form for short store mutations. */
26
+ export declare function withAccountLockSync<T>(lockPath: string, operation: () => T, options?: AccountLockSyncOptions): T;
27
+ /**
28
+ * Serialise a Codex account's read-refresh-write. A reader that implements
29
+ * `AccountCredentialReaders.codexHome` takes this around the whole cycle and
30
+ * compares the file's tokens with what it read before writing back.
31
+ */
32
+ export declare function withAccountRefreshLock<T>(dir: string, operation: () => Promise<T> | T, options?: AccountLockOptions): Promise<T>;
33
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/account/lock.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,MAAM,WAAW,kBAAkB;IAClC,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAoGD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACtC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC/B,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED,MAAM,WAAW,sBAAsB;IACtC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACzC;AAED,kDAAkD;AAClD,wBAAgB,mBAAmB,CAAC,CAAC,EACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,EAClB,OAAO,GAAE,sBAA2B,GAClC,CAAC,CAcH;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACvC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC/B,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,CAAC,CAAC,CAEZ"}
@@ -0,0 +1,170 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { closeSync, mkdirSync, openSync, readFileSync, rmSync, writeSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { AccountLockError } from './types.js';
5
+ /**
6
+ * The lock file beside a Codex account's `auth.json`. One lock per account
7
+ * directory, so a stuck account never blocks another.
8
+ */
9
+ export function codexRefreshLockPath(dir) {
10
+ return join(dir, 'auth.json.namzu.lock');
11
+ }
12
+ function acquire(lockPath) {
13
+ const token = `${process.pid}:${Date.now()}:${randomBytes(12).toString('hex')}`;
14
+ mkdirSync(dirname(lockPath), { recursive: true, mode: 0o700 });
15
+ for (let pass = 0; pass < 2; pass++) {
16
+ let fd;
17
+ try {
18
+ fd = openSync(lockPath, 'wx', 0o600);
19
+ }
20
+ catch (error) {
21
+ if (error.code !== 'EEXIST')
22
+ throw error;
23
+ if (pass === 0 && reclaimIfStale(lockPath))
24
+ continue;
25
+ return undefined;
26
+ }
27
+ try {
28
+ writeSync(fd, token);
29
+ }
30
+ finally {
31
+ closeSync(fd);
32
+ }
33
+ return token;
34
+ }
35
+ return undefined;
36
+ }
37
+ function pidOf(token) {
38
+ const pid = Number.parseInt(token.split(':', 1)[0] ?? '', 10);
39
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
40
+ }
41
+ function processIsDead(pid) {
42
+ try {
43
+ process.kill(pid, 0);
44
+ return false;
45
+ }
46
+ catch (error) {
47
+ // EPERM means the process exists but belongs to someone else: alive.
48
+ return error.code === 'ESRCH';
49
+ }
50
+ }
51
+ /**
52
+ * Remove a lock whose recorded owner process no longer exists. Reclaimers are
53
+ * serialised by a guard file, and the lock is re-read under the guard, so a
54
+ * lock that was replaced by a live owner in the meantime is never removed.
55
+ */
56
+ function reclaimIfStale(lockPath) {
57
+ let observed;
58
+ try {
59
+ observed = readFileSync(lockPath, 'utf8');
60
+ }
61
+ catch {
62
+ return true; // already gone; the caller may simply try again
63
+ }
64
+ const pid = pidOf(observed);
65
+ if (pid === undefined || !processIsDead(pid))
66
+ return false;
67
+ const guard = `${lockPath}.reclaim`;
68
+ let fd;
69
+ try {
70
+ fd = openSync(guard, 'wx', 0o600);
71
+ }
72
+ catch (error) {
73
+ if (error.code !== 'EEXIST')
74
+ throw error;
75
+ try {
76
+ const holder = pidOf(readFileSync(guard, 'utf8'));
77
+ if (holder !== undefined && processIsDead(holder))
78
+ rmSync(guard, { force: true });
79
+ }
80
+ catch {
81
+ // The guard vanished or is unreadable; the next attempt decides.
82
+ }
83
+ return false;
84
+ }
85
+ try {
86
+ writeSync(fd, `${process.pid}`);
87
+ closeSync(fd);
88
+ let again;
89
+ try {
90
+ again = readFileSync(lockPath, 'utf8');
91
+ }
92
+ catch {
93
+ return true;
94
+ }
95
+ if (again !== observed)
96
+ return false;
97
+ rmSync(lockPath, { force: true });
98
+ return true;
99
+ }
100
+ finally {
101
+ rmSync(guard, { force: true });
102
+ }
103
+ }
104
+ function release(lockPath, token) {
105
+ // Never remove a successor's lock if something else replaced the file.
106
+ try {
107
+ if (readFileSync(lockPath, 'utf8') === token)
108
+ rmSync(lockPath, { force: true });
109
+ }
110
+ catch {
111
+ // The work already has its result; a missing lock is not its failure.
112
+ }
113
+ }
114
+ function refusal(lockPath) {
115
+ return new AccountLockError(lockPath, `another owner holds the account lock ${lockPath}. The lock is reclaimed on its own when the process that recorded it has exited; if that process is still running, wait for it to finish.`);
116
+ }
117
+ /**
118
+ * Run `operation` while holding the lock file at `lockPath`. The lock is held
119
+ * across local read/write work, never across a user prompt. A lock whose
120
+ * recorded process has exited is reclaimed; a lock held by a live process is a
121
+ * refusal (or a wait, when attempts and a wait function are given).
122
+ */
123
+ export async function withAccountLock(lockPath, operation, options = {}) {
124
+ const attempts = Math.max(1, options.attempts ?? 1);
125
+ let token;
126
+ for (let attempt = 0; attempt < attempts; attempt++) {
127
+ token = acquire(lockPath);
128
+ if (token !== undefined)
129
+ break;
130
+ if (attempt + 1 < attempts && options.wait)
131
+ await options.wait(attempt);
132
+ }
133
+ if (token === undefined)
134
+ throw refusal(lockPath);
135
+ try {
136
+ return await operation();
137
+ }
138
+ finally {
139
+ release(lockPath, token);
140
+ }
141
+ }
142
+ /** Synchronous form for short store mutations. */
143
+ export function withAccountLockSync(lockPath, operation, options = {}) {
144
+ const attempts = Math.max(1, options.attempts ?? 1);
145
+ let token;
146
+ for (let attempt = 0; attempt < attempts; attempt++) {
147
+ token = acquire(lockPath);
148
+ if (token !== undefined)
149
+ break;
150
+ if (attempt + 1 < attempts && options.wait)
151
+ options.wait(attempt);
152
+ }
153
+ if (token === undefined)
154
+ throw refusal(lockPath);
155
+ try {
156
+ return operation();
157
+ }
158
+ finally {
159
+ release(lockPath, token);
160
+ }
161
+ }
162
+ /**
163
+ * Serialise a Codex account's read-refresh-write. A reader that implements
164
+ * `AccountCredentialReaders.codexHome` takes this around the whole cycle and
165
+ * compares the file's tokens with what it read before writing back.
166
+ */
167
+ export function withAccountRefreshLock(dir, operation, options = {}) {
168
+ return withAccountLock(codexRefreshLockPath(dir), operation, options);
169
+ }
170
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/account/lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type AccountCredentialReaders, type AccountId, type AccountRegistry } from './types.js';
2
+ export declare const ACCOUNTS_FILE_NAME = "accounts.json";
3
+ export declare const ACCOUNTS_FILE_VERSION = 1;
4
+ export interface AccountRegistryOptions {
5
+ /** The Namzu home. Defaults to `resolveNamzuHome()`. */
6
+ readonly home?: string;
7
+ /** Providers that always have a synthesised `default` account, even with no stored one. */
8
+ readonly defaultProviders?: readonly string[];
9
+ readonly readers?: AccountCredentialReaders;
10
+ readonly now?: () => Date;
11
+ /** Blocks between attempts to take the store lock. Defaults to a short real sleep. */
12
+ readonly lockWait?: (attempt: number) => void;
13
+ }
14
+ export declare function accountId(provider: string, label: string): AccountId;
15
+ /** Split an id into provider and label, or `undefined` if it is not shaped `<provider>:<label>`. */
16
+ export declare function parseAccountId(id: string): {
17
+ provider: string;
18
+ label: string;
19
+ } | undefined;
20
+ export declare function createAccountRegistry(options?: AccountRegistryOptions): AccountRegistry;
21
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/account/registry.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEN,KAAK,wBAAwB,EAC7B,KAAK,SAAS,EACd,KAAK,eAAe,EAOpB,MAAM,YAAY,CAAA;AAEnB,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AACjD,eAAO,MAAM,qBAAqB,IAAI,CAAA;AA+BtC,MAAM,WAAW,sBAAsB;IACtC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,2FAA2F;IAC3F,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,wBAAwB,CAAA;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7C;AAMD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAEpE;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI1F;AAuBD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA8S3F"}