toolcraft 0.0.354 → 0.0.356

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 (21) hide show
  1. package/composition.json +3 -3
  2. package/dist/composition.json +3 -3
  3. package/node_modules/auth-store/README.md +2 -1
  4. package/node_modules/auth-store/dist/encrypted-file-store.js +1 -1
  5. package/node_modules/auth-store/dist/transaction-lock.js +1 -1
  6. package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
  7. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +15 -4
  8. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +3 -6
  9. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +23 -12
  10. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +1 -1
  11. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/persistence-options.d.ts +3 -0
  12. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/persistence-options.js +10 -0
  13. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js +2 -2
  14. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-grant.js +1 -1
  15. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +1 -0
  16. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +1 -0
  17. package/node_modules/tiny-http-mcp-server/package.json +1 -1
  18. package/node_modules/tiny-mcp-client/dist/index.js +58 -23
  19. package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
  20. package/node_modules/toolcraft-schema/package.json +1 -1
  21. package/package.json +2 -2
package/composition.json CHANGED
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.85",
101
+ "version": "0.1.88",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.354",
116
+ "version": "0.0.356",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.354",
126
+ "version": "0.0.356",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.85",
101
+ "version": "0.1.88",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.354",
116
+ "version": "0.0.356",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.354",
126
+ "version": "0.0.356",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -35,7 +35,8 @@ instances and processes serialize the same encrypted file or Keychain
35
35
  service/account; unrelated identities proceed independently. The default
36
36
  acquisition timeout is 30 seconds. Cancellation during acquisition does not
37
37
  release the active owner's lock. Each acquisition retains its original signal
38
- and timeout before path checks wait; replacing the caller's option handles cannot
38
+ and timeout before path checks wait, including nonenumerable declared options;
39
+ replacing the caller's option handles cannot
39
40
  bypass original cancellation or introduce another signal's cancellation. Individual `get`, `set` and `delete` calls do
40
41
  not implicitly acquire it.
41
42
 
@@ -81,7 +81,7 @@ export class EncryptedFileStore {
81
81
  }
82
82
  }
83
83
  async withLock(operation, options = {}) {
84
- options = { ...options };
84
+ options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
85
85
  await this.assertCredentialPathHasNoSymbolicLinks(`${this.filePath}.lock`);
86
86
  if (this.fs.readdir === undefined)
87
87
  throw new Error("Secret-store transaction locks require filesystem readdir support");
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { hasOwnErrorCode } from "./error-codes.js";
4
4
  /** Filesystem bakery lock: unique claims allow dead-owner cleanup without deleting a replacement owner's lock. */
5
5
  export async function withSecretStoreFileLock(fs, lockDirectory, operation, options = {}) {
6
- options = { ...options };
6
+ options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
7
7
  const timeoutMs = options.timeoutMs ?? 30_000;
8
8
  if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647)
9
9
  throw new Error("Invalid secret-store transaction lock timeout");
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "name": "tiny-http-mcp-server",
21
- "version": "0.1.85",
21
+ "version": "0.1.88",
22
22
  "license": "MIT"
23
23
  },
24
24
  {
@@ -66,6 +66,9 @@ alone do not establish either native error type.
66
66
 
67
67
  The default provider captures client, interaction, callback, landing-page and
68
68
  lock policies when created. Create a new provider to change those settings.
69
+ Initial grants and the selected clock are captured before native persistence
70
+ callbacks. Clock methods retain their original host receiver and private class
71
+ state; later clock state still determines whether a grant has expired.
69
72
  Selected callbacks, stores, clocks and AbortSignals remain live host dependencies;
70
73
  aborting the original signal still cancels authorization.
71
74
  An invalid clock fails before attaching or refreshing a grant with a known
@@ -74,7 +77,11 @@ unlimited secret lifetimes do not require a clock read. Valid negative epoch
74
77
  times remain supported.
75
78
  Native session and client persistence factories also capture file paths, salts,
76
79
  Keychain identities, lock locations and selected filesystem/command handles.
77
- Mutating these settings cannot redirect a later read or write.
80
+ Mutating these settings cannot redirect a later read or write. Declared options
81
+ retain nonenumerable values; selected filesystem, machine/home/random callbacks
82
+ and Keychain runners keep their original host receiver, including private class
83
+ state. `snapshotOAuthPersistenceOptions(options)` exposes this capture boundary
84
+ for integrations that defer native store construction.
78
85
  The factories copy these settings before reading the selected backend environment
79
86
  variable, so its getter cannot replace them. They resolve that variable once at creation;
80
87
  later environment changes cannot move a transaction between file and Keychain.
@@ -124,6 +131,8 @@ A browser callback cannot redirect a later code exchange by changing caller
124
131
  metadata. Metadata loaded from a host session store is copied before clock or
125
132
  fetch callbacks, retaining the original refresh endpoint and persisted extensions. Explicit authentication owns metadata when lazy discovery returns;
126
133
  the selected discovery method retains its original receiver and live host state.
134
+ Browser and standalone loopback snapshots also retain declared landing-page
135
+ title/body values regardless of enumerability; served text remains HTML escaped.
127
136
  Browser and standalone loopback snapshots retain declared redirects, deadlines,
128
137
  cancellation and selected host hooks regardless of property enumerability.
129
138
  Browser callback hooks retain their original receiver and live host state.
@@ -161,8 +170,8 @@ corrupt or undecryptable records without reading their old contents. It atomical
161
170
  retires the identity's grant and registrations and writes a marker that suppresses
162
171
  stale initial grants. The default lock wait is 30 seconds. Other names/profiles
163
172
  are untouched, and symlink paths are still refused. Native reset, import and
164
- transaction callbacks retain their original signal while locks or reconciliation
165
- wait; replacing a caller handle cannot change subsequent cancellation checks.
173
+ transaction callbacks retain their original signal and acquisition limit, including
174
+ nonenumerable declared options, while locks or reconciliation wait; replacing a caller handle cannot change subsequent cancellation checks.
166
175
  Cancellation after a completed identity write prevents the transaction callback
167
176
  from running and retains that already persisted identity.
168
177
 
@@ -210,7 +219,9 @@ also be valid epoch milliseconds; adding the lifetime cannot make an invalid
210
219
  issuance clock acceptable. This applies to raw-grant parsing and token responses.
211
220
  For a raw OAuth response, `parseOAuthTokenGrant(response, { issuedAt, expiresAt })`
212
221
  returns normalized `StoredOAuthTokens`. Timing options are captured before the
213
- host clock runs, so it cannot bypass their earlier validation. The parser accepts
222
+ host clock runs, so it cannot bypass their earlier validation. Declared timing
223
+ options retain nonenumerable values, and a selected clock method retains its
224
+ original host receiver. The parser accepts
214
225
  `access_token`, `refresh_token`,
215
226
  `token_type`, `scope`, `expires_in` (seconds), `expires_at` (epoch seconds), and
216
227
  `expiresAt` (epoch milliseconds). Numeric absolute expiry wins over relative
@@ -1,3 +1,4 @@
1
+ import { snapshotOAuthPersistenceOptions } from "./persistence-options.js";
1
2
  import { normalizeStoredOAuthClient } from "./client-registration.js";
2
3
  import crypto from "node:crypto";
3
4
  import path from "node:path";
@@ -82,16 +83,12 @@ export function createAuthStoreClientStore(options, namespace) {
82
83
  };
83
84
  }
84
85
  function snapshotPersistenceOptions(options) {
85
- const snapshot = {
86
- ...options,
87
- ...(options.fileStore === undefined ? {} : { fileStore: { ...options.fileStore } }),
88
- ...(options.keychainStore === undefined ? {} : { keychainStore: { ...options.keychainStore,
89
- ...(options.keychainStore.lock === undefined ? {} : { lock: { ...options.keychainStore.lock } }) } })
90
- };
86
+ const snapshot = snapshotOAuthPersistenceOptions(options);
91
87
  snapshot.backend = resolveSecretStoreBackend(snapshot);
92
88
  return snapshot;
93
89
  }
94
90
  export function createNamedSecretStore(key, options, defaults, namespace) {
91
+ options = snapshotOAuthPersistenceOptions(options);
95
92
  const hash = crypto.createHash("sha256").update(namespace === undefined ? key : JSON.stringify([namespace, key])).digest("hex");
96
93
  const configuredFilePath = options.fileStore?.filePath;
97
94
  const parsedFilePath = configuredFilePath === undefined ? null : path.parse(configuredFilePath);
@@ -1,3 +1,4 @@
1
+ import { snapshotOAuthPersistenceOptions } from "./persistence-options.js";
1
2
  import { createResourceBoundOAuthStores } from "./resource-bound-store.js";
2
3
  import { normalizeStoredOAuthClient, parseOAuthClientRegistration, registrationMatchesRedirect } from "./client-registration.js";
3
4
  import { normalizeOAuthScope } from "./scope.js";
@@ -21,6 +22,7 @@ export function createOAuthClientProvider(options) {
21
22
  return createDefaultOAuthClientProvider(options);
22
23
  }
23
24
  export function createDefaultOAuthClientProvider(options) {
25
+ const clock = options.now?.bind(options) ?? Date.now;
24
26
  const browser = snapshotLoopbackAuthorizationOptions(options.browser);
25
27
  const clientMode = options.client.mode;
26
28
  const interactiveEnabled = options.allowInteractive !== false;
@@ -32,14 +34,12 @@ export function createDefaultOAuthClientProvider(options) {
32
34
  const configuredClient = normalizeConfiguredClient(options.client);
33
35
  const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
34
36
  const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
35
- if (options.resourceIdentity !== undefined && options.sessionStore !== undefined)
37
+ const resourceIdentity = options.resourceIdentity, persistenceNamespace = options.persistenceNamespace, selectedSessionStore = options.sessionStore;
38
+ const suppliedGrant = options.initialGrant;
39
+ const capturedGrant = suppliedGrant === undefined ? undefined : { resource: suppliedGrant.resource, tokens: snapshotInitialGrantTokens(suppliedGrant.tokens) };
40
+ const authStore = options.authStore === undefined ? undefined : snapshotOAuthPersistenceOptions(options.authStore);
41
+ if (resourceIdentity !== undefined && selectedSessionStore !== undefined)
36
42
  throw new Error("OAuth resourceIdentity requires native-owned persistence; custom stores own their resource trust policy");
37
- const resourceStores = options.resourceIdentity === undefined ? undefined :
38
- createResourceBoundOAuthStores(options.authStore ?? {}, options.persistenceNamespace, options.resourceIdentity);
39
- const sessionStore = resourceStores?.sessionStore ?? options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
40
- const clientStore = resourceStores?.clientStore ??
41
- (options.authStore === undefined ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace));
42
- const clock = options.now ?? Date.now;
43
43
  const now = () => {
44
44
  const timestamp = clock();
45
45
  if (!Number.isSafeInteger(timestamp) || Math.abs(timestamp) > MAX_JS_DATE_MS)
@@ -47,10 +47,15 @@ export function createDefaultOAuthClientProvider(options) {
47
47
  return timestamp;
48
48
  };
49
49
  const registeredClients = new Map();
50
- if (options.initialGrant !== undefined) {
50
+ const resourceStores = resourceIdentity === undefined ? undefined :
51
+ createResourceBoundOAuthStores(authStore ?? {}, persistenceNamespace, resourceIdentity);
52
+ const sessionStore = resourceStores?.sessionStore ?? selectedSessionStore ?? createAuthStoreSessionStore(authStore, persistenceNamespace);
53
+ const clientStore = resourceStores?.clientStore ??
54
+ (authStore === undefined ? null : createAuthStoreClientStore(authStore, persistenceNamespace));
55
+ if (capturedGrant !== undefined) {
51
56
  let resource;
52
57
  try {
53
- resource = new URL(options.initialGrant.resource);
58
+ resource = new URL(capturedGrant.resource);
54
59
  }
55
60
  catch {
56
61
  throw new Error("OAuth initial grant resource must be an absolute HTTP URL");
@@ -58,9 +63,9 @@ export function createDefaultOAuthClientProvider(options) {
58
63
  if ((resource.protocol !== "http:" && resource.protocol !== "https:") || resource.username || resource.password || resource.href.includes("#"))
59
64
  throw new Error("OAuth initial grant resource must be an HTTP URL without credentials or fragments");
60
65
  }
61
- const initialGrant = options.initialGrant === undefined ? undefined : {
62
- resource: canonicalizeResourceIndicator(options.initialGrant.resource),
63
- tokens: normalizeImportedTokens(options.initialGrant.tokens, now),
66
+ const initialGrant = capturedGrant === undefined ? undefined : {
67
+ resource: canonicalizeResourceIndicator(capturedGrant.resource),
68
+ tokens: normalizeImportedTokens(capturedGrant.tokens, now),
64
69
  client: configuredClient
65
70
  };
66
71
  if (initialGrant !== undefined && (initialGrant.tokens === undefined || initialGrant.client === null))
@@ -606,6 +611,12 @@ function normalizeLoadedSession(session) {
606
611
  }
607
612
  return { ...session, discovery, client, tokens };
608
613
  }
614
+ function snapshotInitialGrantTokens(value) {
615
+ if (!isObjectRecord(value))
616
+ return value;
617
+ const expiresAt = getOwnEntry(value, "expiresAt"), expiresIn = getOwnEntry(value, "expiresIn"), issuedAt = getOwnEntry(value, "issuedAt");
618
+ return { ...value, expiresAt, expiresIn, issuedAt };
619
+ }
609
620
  function normalizeImportedTokens(value, now) {
610
621
  if (!isObjectRecord(value))
611
622
  return undefined;
@@ -6,7 +6,7 @@ const oauthCallbackParameters = ["code", "state", "iss", "error", "error_descrip
6
6
  export function snapshotLoopbackAuthorizationOptions(options) {
7
7
  return { ...options, openBrowser: options.openBrowser?.bind(options), readLine: options.readLine?.bind(options), createServer: options.createServer?.bind(options),
8
8
  callbackPath: options.callbackPath, redirectUri: options.redirectUri, signal: options.signal, timeoutMs: options.timeoutMs,
9
- landingPage: options.landingPage === undefined ? undefined : { ...options.landingPage } };
9
+ landingPage: options.landingPage === undefined ? undefined : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body } };
10
10
  }
11
11
  /** Authorization callback denial, retaining the provider diagnostic for host observers. */
12
12
  export class OAuthAuthorizationError extends Error {
@@ -0,0 +1,3 @@
1
+ import type { CreateSecretStoreInput } from "auth-store";
2
+ /** Own scalar policies while retaining selected live host dependencies. */
3
+ export declare function snapshotOAuthPersistenceOptions(options: CreateSecretStoreInput): CreateSecretStoreInput;
@@ -0,0 +1,10 @@
1
+ /** Own scalar policies while retaining selected live host dependencies. */
2
+ export function snapshotOAuthPersistenceOptions(options) {
3
+ const file = options.fileStore, keychain = options.keychainStore, lock = keychain?.lock;
4
+ return { ...options, backend: options.backend, env: options.env, platform: options.platform, backendEnvVar: options.backendEnvVar,
5
+ ...(file === undefined ? {} : { fileStore: { ...file, fs: file.fs, filePath: file.filePath, salt: file.salt,
6
+ defaultDirectory: file.defaultDirectory, defaultFileName: file.defaultFileName, throwOnInvalidDocument: file.throwOnInvalidDocument,
7
+ getHomeDirectory: file.getHomeDirectory?.bind(file), getMachineIdentity: file.getMachineIdentity?.bind(file), getRandomBytes: file.getRandomBytes?.bind(file) } }),
8
+ ...(keychain === undefined ? {} : { keychainStore: { ...keychain, service: keychain.service, account: keychain.account,
9
+ runCommand: keychain.runCommand?.bind(keychain), ...(lock === undefined ? {} : { lock: { ...lock, fs: lock.fs, directory: lock.directory } }) } }) };
10
+ }
@@ -52,7 +52,7 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
52
52
  }
53
53
  };
54
54
  async function replace(record, options) {
55
- options = { ...options };
55
+ options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
56
56
  options.signal?.throwIfAborted();
57
57
  const timeoutMs = options.timeoutMs ?? 30_000;
58
58
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
@@ -126,7 +126,7 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
126
126
  }
127
127
  result.sessionStore = {
128
128
  async withLock(resource, operation, options) {
129
- options = { ...options };
129
+ options = { ...options, signal: options?.signal, timeoutMs: options?.timeoutMs };
130
130
  if (store.withLock === undefined)
131
131
  throw new Error("OAuth resource identity backend must support transaction locks");
132
132
  return store.withLock(async () => {
@@ -2,7 +2,7 @@ import { copyBoundedOAuthJson } from "./bounded-json.js";
2
2
  import { normalizeOAuthScope } from "./scope.js";
3
3
  /** Validate a raw RFC token response and anchor its lifetime once for persistence. */
4
4
  export function parseOAuthTokenGrant(value, options = {}) {
5
- options = { ...options };
5
+ options = { ...options, expiresAt: options.expiresAt, issuedAt: options.issuedAt, now: options.now?.bind(options) };
6
6
  const invalid = () => new Error("Invalid OAuth token grant");
7
7
  const result = copyBoundedOAuthJson(value, "Invalid OAuth token grant");
8
8
  if (typeof result !== "object" || result === null || Array.isArray(result))
@@ -1,3 +1,4 @@
1
+ export { snapshotOAuthPersistenceOptions } from "./client/persistence-options.js";
1
2
  export { normalizeOAuthScope } from "./client/scope.js";
2
3
  export { waitForOAuthOperation } from "./client/cancellable-operation.js";
3
4
  export { parseOAuthTokenGrant } from "./client/token-grant.js";
@@ -1,3 +1,4 @@
1
+ export { snapshotOAuthPersistenceOptions } from "./client/persistence-options.js";
1
2
  export { normalizeOAuthScope } from "./client/scope.js";
2
3
  export { waitForOAuthOperation } from "./client/cancellable-operation.js";
3
4
  export { parseOAuthTokenGrant } from "./client/token-grant.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-http-mcp-server",
3
- "version": "0.1.85",
3
+ "version": "0.1.88",
4
4
  "description": "Minimal MCP server over HTTP built on tiny-stdio-mcp-server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3395,6 +3395,37 @@ var SubscriptionManager = class {
3395
3395
  }
3396
3396
  };
3397
3397
 
3398
+ // ../mcp-oauth/dist/client/persistence-options.js
3399
+ function snapshotOAuthPersistenceOptions(options) {
3400
+ const file = options.fileStore, keychain = options.keychainStore, lock = keychain?.lock;
3401
+ return {
3402
+ ...options,
3403
+ backend: options.backend,
3404
+ env: options.env,
3405
+ platform: options.platform,
3406
+ backendEnvVar: options.backendEnvVar,
3407
+ ...file === void 0 ? {} : { fileStore: {
3408
+ ...file,
3409
+ fs: file.fs,
3410
+ filePath: file.filePath,
3411
+ salt: file.salt,
3412
+ defaultDirectory: file.defaultDirectory,
3413
+ defaultFileName: file.defaultFileName,
3414
+ throwOnInvalidDocument: file.throwOnInvalidDocument,
3415
+ getHomeDirectory: file.getHomeDirectory?.bind(file),
3416
+ getMachineIdentity: file.getMachineIdentity?.bind(file),
3417
+ getRandomBytes: file.getRandomBytes?.bind(file)
3418
+ } },
3419
+ ...keychain === void 0 ? {} : { keychainStore: {
3420
+ ...keychain,
3421
+ service: keychain.service,
3422
+ account: keychain.account,
3423
+ runCommand: keychain.runCommand?.bind(keychain),
3424
+ ...lock === void 0 ? {} : { lock: { ...lock, fs: lock.fs, directory: lock.directory } }
3425
+ } }
3426
+ };
3427
+ }
3428
+
3398
3429
  // ../mcp-oauth/dist/client/scope.js
3399
3430
  function normalizeOAuthScope(scope) {
3400
3431
  if (scope === void 0)
@@ -3528,7 +3559,7 @@ function snapshotLoopbackAuthorizationOptions(options) {
3528
3559
  redirectUri: options.redirectUri,
3529
3560
  signal: options.signal,
3530
3561
  timeoutMs: options.timeoutMs,
3531
- landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage }
3562
+ landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body }
3532
3563
  };
3533
3564
  }
3534
3565
  var OAuthAuthorizationError = class extends Error {
@@ -3954,7 +3985,7 @@ function hasOwnErrorCode(error, code) {
3954
3985
  import { randomUUID } from "node:crypto";
3955
3986
  import path from "node:path";
3956
3987
  async function withSecretStoreFileLock(fs2, lockDirectory, operation, options = {}) {
3957
- options = { ...options };
3988
+ options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
3958
3989
  const timeoutMs = options.timeoutMs ?? 3e4;
3959
3990
  if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647)
3960
3991
  throw new Error("Invalid secret-store transaction lock timeout");
@@ -4174,7 +4205,7 @@ var EncryptedFileStore = class {
4174
4205
  }
4175
4206
  }
4176
4207
  async withLock(operation, options = {}) {
4177
- options = { ...options };
4208
+ options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
4178
4209
  await this.assertCredentialPathHasNoSymbolicLinks(`${this.filePath}.lock`);
4179
4210
  if (this.fs.readdir === void 0)
4180
4211
  throw new Error("Secret-store transaction locks require filesystem readdir support");
@@ -4687,18 +4718,12 @@ function createAuthStoreClientStore(options, namespace) {
4687
4718
  };
4688
4719
  }
4689
4720
  function snapshotPersistenceOptions(options) {
4690
- const snapshot = {
4691
- ...options,
4692
- ...options.fileStore === void 0 ? {} : { fileStore: { ...options.fileStore } },
4693
- ...options.keychainStore === void 0 ? {} : { keychainStore: {
4694
- ...options.keychainStore,
4695
- ...options.keychainStore.lock === void 0 ? {} : { lock: { ...options.keychainStore.lock } }
4696
- } }
4697
- };
4721
+ const snapshot = snapshotOAuthPersistenceOptions(options);
4698
4722
  snapshot.backend = resolveSecretStoreBackend(snapshot);
4699
4723
  return snapshot;
4700
4724
  }
4701
4725
  function createNamedSecretStore(key2, options, defaults, namespace) {
4726
+ options = snapshotOAuthPersistenceOptions(options);
4702
4727
  const hash = crypto2.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
4703
4728
  const configuredFilePath = options.fileStore?.filePath;
4704
4729
  const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
@@ -4842,7 +4867,7 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
4842
4867
  }
4843
4868
  };
4844
4869
  async function replace(record2, options2) {
4845
- options2 = { ...options2 };
4870
+ options2 = { ...options2, signal: options2.signal, timeoutMs: options2.timeoutMs };
4846
4871
  options2.signal?.throwIfAborted();
4847
4872
  const timeoutMs = options2.timeoutMs ?? 3e4;
4848
4873
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
@@ -4908,7 +4933,7 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
4908
4933
  }
4909
4934
  result.sessionStore = {
4910
4935
  async withLock(resource, operation, options2) {
4911
- options2 = { ...options2 };
4936
+ options2 = { ...options2, signal: options2?.signal, timeoutMs: options2?.timeoutMs };
4912
4937
  if (store.withLock === void 0)
4913
4938
  throw new Error("OAuth resource identity backend must support transaction locks");
4914
4939
  return store.withLock(async () => {
@@ -5327,6 +5352,7 @@ function createOAuthClientProvider(options) {
5327
5352
  return createDefaultOAuthClientProvider(options);
5328
5353
  }
5329
5354
  function createDefaultOAuthClientProvider(options) {
5355
+ const clock = options.now?.bind(options) ?? Date.now;
5330
5356
  const browser = snapshotLoopbackAuthorizationOptions(options.browser);
5331
5357
  const clientMode = options.client.mode;
5332
5358
  const interactiveEnabled = options.allowInteractive !== false;
@@ -5338,12 +5364,12 @@ function createDefaultOAuthClientProvider(options) {
5338
5364
  const configuredClient = normalizeConfiguredClient(options.client);
5339
5365
  const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
5340
5366
  const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
5341
- if (options.resourceIdentity !== void 0 && options.sessionStore !== void 0)
5367
+ const resourceIdentity = options.resourceIdentity, persistenceNamespace = options.persistenceNamespace, selectedSessionStore = options.sessionStore;
5368
+ const suppliedGrant = options.initialGrant;
5369
+ const capturedGrant = suppliedGrant === void 0 ? void 0 : { resource: suppliedGrant.resource, tokens: snapshotInitialGrantTokens(suppliedGrant.tokens) };
5370
+ const authStore = options.authStore === void 0 ? void 0 : snapshotOAuthPersistenceOptions(options.authStore);
5371
+ if (resourceIdentity !== void 0 && selectedSessionStore !== void 0)
5342
5372
  throw new Error("OAuth resourceIdentity requires native-owned persistence; custom stores own their resource trust policy");
5343
- const resourceStores = options.resourceIdentity === void 0 ? void 0 : createResourceBoundOAuthStores(options.authStore ?? {}, options.persistenceNamespace, options.resourceIdentity);
5344
- const sessionStore = resourceStores?.sessionStore ?? options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
5345
- const clientStore = resourceStores?.clientStore ?? (options.authStore === void 0 ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace));
5346
- const clock = options.now ?? Date.now;
5347
5373
  const now = () => {
5348
5374
  const timestamp = clock();
5349
5375
  if (!Number.isSafeInteger(timestamp) || Math.abs(timestamp) > MAX_JS_DATE_MS3)
@@ -5351,19 +5377,22 @@ function createDefaultOAuthClientProvider(options) {
5351
5377
  return timestamp;
5352
5378
  };
5353
5379
  const registeredClients = /* @__PURE__ */ new Map();
5354
- if (options.initialGrant !== void 0) {
5380
+ const resourceStores = resourceIdentity === void 0 ? void 0 : createResourceBoundOAuthStores(authStore ?? {}, persistenceNamespace, resourceIdentity);
5381
+ const sessionStore = resourceStores?.sessionStore ?? selectedSessionStore ?? createAuthStoreSessionStore(authStore, persistenceNamespace);
5382
+ const clientStore = resourceStores?.clientStore ?? (authStore === void 0 ? null : createAuthStoreClientStore(authStore, persistenceNamespace));
5383
+ if (capturedGrant !== void 0) {
5355
5384
  let resource;
5356
5385
  try {
5357
- resource = new URL2(options.initialGrant.resource);
5386
+ resource = new URL2(capturedGrant.resource);
5358
5387
  } catch {
5359
5388
  throw new Error("OAuth initial grant resource must be an absolute HTTP URL");
5360
5389
  }
5361
5390
  if (resource.protocol !== "http:" && resource.protocol !== "https:" || resource.username || resource.password || resource.href.includes("#"))
5362
5391
  throw new Error("OAuth initial grant resource must be an HTTP URL without credentials or fragments");
5363
5392
  }
5364
- const initialGrant = options.initialGrant === void 0 ? void 0 : {
5365
- resource: canonicalizeResourceIndicator(options.initialGrant.resource),
5366
- tokens: normalizeImportedTokens(options.initialGrant.tokens, now),
5393
+ const initialGrant = capturedGrant === void 0 ? void 0 : {
5394
+ resource: canonicalizeResourceIndicator(capturedGrant.resource),
5395
+ tokens: normalizeImportedTokens(capturedGrant.tokens, now),
5367
5396
  client: configuredClient
5368
5397
  };
5369
5398
  if (initialGrant !== void 0 && (initialGrant.tokens === void 0 || initialGrant.client === null))
@@ -5891,6 +5920,12 @@ function normalizeLoadedSession(session) {
5891
5920
  }
5892
5921
  return { ...session, discovery, client, tokens };
5893
5922
  }
5923
+ function snapshotInitialGrantTokens(value) {
5924
+ if (!isObjectRecord3(value))
5925
+ return value;
5926
+ const expiresAt = getOwnEntry6(value, "expiresAt"), expiresIn = getOwnEntry6(value, "expiresIn"), issuedAt = getOwnEntry6(value, "issuedAt");
5927
+ return { ...value, expiresAt, expiresIn, issuedAt };
5928
+ }
5894
5929
  function normalizeImportedTokens(value, now) {
5895
5930
  if (!isObjectRecord3(value))
5896
5931
  return void 0;
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.354",
11
+ "version": "0.0.356",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.354",
3
+ "version": "0.0.356",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.354",
3
+ "version": "0.0.356",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -158,7 +158,7 @@
158
158
  "yaml"
159
159
  ],
160
160
  "optionalDependencies": {
161
- "toolcraft-schema": "0.0.354",
161
+ "toolcraft-schema": "0.0.356",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",