apex-code 0.0.1-alpha.7 → 0.0.1-alpha.8

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 (256) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +5 -0
  3. package/dist/cli/args.d.ts +3 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +15 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/concurrent-session.d.ts +3 -0
  8. package/dist/cli/concurrent-session.d.ts.map +1 -0
  9. package/dist/cli/concurrent-session.js +13 -0
  10. package/dist/cli/concurrent-session.js.map +1 -0
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +1 -2
  13. package/dist/cli.js.map +1 -1
  14. package/dist/core/agent-session-services.d.ts.map +1 -1
  15. package/dist/core/agent-session-services.js +20 -0
  16. package/dist/core/agent-session-services.js.map +1 -1
  17. package/dist/core/agent-session.d.ts +15 -2
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +9 -6
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/auth-storage.d.ts +4 -1
  22. package/dist/core/auth-storage.d.ts.map +1 -1
  23. package/dist/core/auth-storage.js +6 -2
  24. package/dist/core/auth-storage.js.map +1 -1
  25. package/dist/core/bash-executor.d.ts.map +1 -1
  26. package/dist/core/bash-executor.js +10 -9
  27. package/dist/core/bash-executor.js.map +1 -1
  28. package/dist/core/experimental.d.ts +4 -0
  29. package/dist/core/experimental.d.ts.map +1 -1
  30. package/dist/core/experimental.js +4 -0
  31. package/dist/core/experimental.js.map +1 -1
  32. package/dist/core/extensions/types.d.ts +4 -0
  33. package/dist/core/extensions/types.d.ts.map +1 -1
  34. package/dist/core/extensions/types.js.map +1 -1
  35. package/dist/core/http-dispatcher.d.ts +1 -2
  36. package/dist/core/http-dispatcher.d.ts.map +1 -1
  37. package/dist/core/http-dispatcher.js +2 -17
  38. package/dist/core/http-dispatcher.js.map +1 -1
  39. package/dist/core/http-idle-timeout.d.ts +12 -0
  40. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  41. package/dist/core/http-idle-timeout.js +27 -0
  42. package/dist/core/http-idle-timeout.js.map +1 -0
  43. package/dist/core/keybindings.d.ts +24 -0
  44. package/dist/core/keybindings.d.ts.map +1 -1
  45. package/dist/core/lsp/client.d.ts +2 -0
  46. package/dist/core/lsp/client.d.ts.map +1 -1
  47. package/dist/core/lsp/client.js +27 -2
  48. package/dist/core/lsp/client.js.map +1 -1
  49. package/dist/core/model-config.d.ts +5 -0
  50. package/dist/core/model-config.d.ts.map +1 -1
  51. package/dist/core/model-config.js +1 -0
  52. package/dist/core/model-config.js.map +1 -1
  53. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  54. package/dist/core/sandbox/child-entry.js +8 -1
  55. package/dist/core/sandbox/child-entry.js.map +1 -1
  56. package/dist/core/sandbox/cli-launch.d.ts +3 -0
  57. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  58. package/dist/core/sandbox/cli-launch.js +25 -4
  59. package/dist/core/sandbox/cli-launch.js.map +1 -1
  60. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  61. package/dist/core/sandbox/cli-supervisor.js +53 -13
  62. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  63. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  64. package/dist/core/sandbox/linux-backend.js +15 -0
  65. package/dist/core/sandbox/linux-backend.js.map +1 -1
  66. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  67. package/dist/core/sandbox/macos-backend.js +24 -1
  68. package/dist/core/sandbox/macos-backend.js.map +1 -1
  69. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  70. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  71. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  72. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  73. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  74. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  75. package/dist/core/sandbox/rpc/credential-proxy.js +381 -0
  76. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  77. package/dist/core/sandbox/supervisor.d.ts +11 -0
  78. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  79. package/dist/core/sandbox/supervisor.js.map +1 -1
  80. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  81. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  82. package/dist/core/sandbox/terminal-size.js +105 -0
  83. package/dist/core/sandbox/terminal-size.js.map +1 -0
  84. package/dist/core/sdk.d.ts +6 -3
  85. package/dist/core/sdk.d.ts.map +1 -1
  86. package/dist/core/sdk.js +16 -4
  87. package/dist/core/sdk.js.map +1 -1
  88. package/dist/core/session-lease.d.ts +31 -0
  89. package/dist/core/session-lease.d.ts.map +1 -0
  90. package/dist/core/session-lease.js +138 -0
  91. package/dist/core/session-lease.js.map +1 -0
  92. package/dist/core/session-manager.d.ts +5 -0
  93. package/dist/core/session-manager.d.ts.map +1 -1
  94. package/dist/core/session-manager.js +3 -3
  95. package/dist/core/session-manager.js.map +1 -1
  96. package/dist/core/settings-manager.d.ts +26 -0
  97. package/dist/core/settings-manager.d.ts.map +1 -1
  98. package/dist/core/settings-manager.js +42 -3
  99. package/dist/core/settings-manager.js.map +1 -1
  100. package/dist/core/slash-commands.d.ts.map +1 -1
  101. package/dist/core/slash-commands.js +2 -0
  102. package/dist/core/slash-commands.js.map +1 -1
  103. package/dist/core/system-prompt.d.ts +2 -2
  104. package/dist/core/system-prompt.d.ts.map +1 -1
  105. package/dist/core/system-prompt.js +47 -62
  106. package/dist/core/system-prompt.js.map +1 -1
  107. package/dist/core/tools/bash.d.ts.map +1 -1
  108. package/dist/core/tools/bash.js +2 -0
  109. package/dist/core/tools/bash.js.map +1 -1
  110. package/dist/core/tools/delegate.d.ts.map +1 -1
  111. package/dist/core/tools/delegate.js +24 -0
  112. package/dist/core/tools/delegate.js.map +1 -1
  113. package/dist/core/tools/edit.d.ts.map +1 -1
  114. package/dist/core/tools/edit.js +2 -0
  115. package/dist/core/tools/edit.js.map +1 -1
  116. package/dist/core/tools/find.d.ts.map +1 -1
  117. package/dist/core/tools/find.js +1 -1
  118. package/dist/core/tools/find.js.map +1 -1
  119. package/dist/core/tools/grep.d.ts +2 -0
  120. package/dist/core/tools/grep.d.ts.map +1 -1
  121. package/dist/core/tools/grep.js +35 -10
  122. package/dist/core/tools/grep.js.map +1 -1
  123. package/dist/core/tools/ls.d.ts +2 -0
  124. package/dist/core/tools/ls.d.ts.map +1 -1
  125. package/dist/core/tools/ls.js +5 -1
  126. package/dist/core/tools/ls.js.map +1 -1
  127. package/dist/core/tools/read.d.ts.map +1 -1
  128. package/dist/core/tools/read.js +2 -0
  129. package/dist/core/tools/read.js.map +1 -1
  130. package/dist/core/tools/web-search-exa.d.ts +33 -0
  131. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  132. package/dist/core/tools/web-search-exa.js +128 -0
  133. package/dist/core/tools/web-search-exa.js.map +1 -0
  134. package/dist/core/tools/write.d.ts.map +1 -1
  135. package/dist/core/tools/write.js +2 -0
  136. package/dist/core/tools/write.js.map +1 -1
  137. package/dist/core/web-search-provider.d.ts +64 -0
  138. package/dist/core/web-search-provider.d.ts.map +1 -0
  139. package/dist/core/web-search-provider.js +149 -0
  140. package/dist/core/web-search-provider.js.map +1 -0
  141. package/dist/index.d.ts +1 -1
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.js.map +1 -1
  144. package/dist/main.d.ts +1 -0
  145. package/dist/main.d.ts.map +1 -1
  146. package/dist/main.js +20 -1
  147. package/dist/main.js.map +1 -1
  148. package/dist/modes/interactive/components/custom-editor.d.ts +23 -0
  149. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  150. package/dist/modes/interactive/components/custom-editor.js +105 -2
  151. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  152. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  153. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  155. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  156. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  157. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/extension-selector.js +24 -6
  159. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  160. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/first-time-setup.js +6 -3
  162. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  163. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  164. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  165. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  166. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  167. package/dist/modes/interactive/components/footer.d.ts +12 -0
  168. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  169. package/dist/modes/interactive/components/footer.js +128 -9
  170. package/dist/modes/interactive/components/footer.js.map +1 -1
  171. package/dist/modes/interactive/components/index.d.ts +1 -0
  172. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  173. package/dist/modes/interactive/components/index.js +1 -0
  174. package/dist/modes/interactive/components/index.js.map +1 -1
  175. package/dist/modes/interactive/components/model-selector.d.ts +2 -0
  176. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/model-selector.js +20 -5
  178. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  179. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  180. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  181. package/dist/modes/interactive/components/settings-selector.js +10 -0
  182. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  183. package/dist/modes/interactive/components/splash-header.d.ts +29 -4
  184. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -1
  185. package/dist/modes/interactive/components/splash-header.js +58 -23
  186. package/dist/modes/interactive/components/splash-header.js.map +1 -1
  187. package/dist/modes/interactive/components/tool-execution.d.ts +14 -0
  188. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  189. package/dist/modes/interactive/components/tool-execution.js +113 -35
  190. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  191. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  192. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  193. package/dist/modes/interactive/components/tool-panel.js +130 -0
  194. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  195. package/dist/modes/interactive/interactive-mode.d.ts +44 -9
  196. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/dist/modes/interactive/interactive-mode.js +282 -70
  198. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  200. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  201. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  202. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  203. package/dist/modes/interactive/theme/apex.json +62 -63
  204. package/dist/modes/interactive/theme/dark.json +2 -0
  205. package/dist/modes/interactive/theme/light.json +2 -0
  206. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  207. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  208. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  209. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  210. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  211. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  212. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  213. package/dist/modes/interactive/theme/theme.js +55 -6
  214. package/dist/modes/interactive/theme/theme.js.map +1 -1
  215. package/dist/modes/json-event.d.ts +4 -1
  216. package/dist/modes/json-event.d.ts.map +1 -1
  217. package/dist/modes/json-event.js +5 -2
  218. package/dist/modes/json-event.js.map +1 -1
  219. package/dist/server/create-harness.d.ts.map +1 -1
  220. package/dist/server/create-harness.js +2 -0
  221. package/dist/server/create-harness.js.map +1 -1
  222. package/dist/themes/apex-logo.d.ts +36 -16
  223. package/dist/themes/apex-logo.d.ts.map +1 -1
  224. package/dist/themes/apex-logo.js +32 -28
  225. package/dist/themes/apex-logo.js.map +1 -1
  226. package/dist/utils/tools-manager.d.ts +10 -1
  227. package/dist/utils/tools-manager.d.ts.map +1 -1
  228. package/dist/utils/tools-manager.js +15 -20
  229. package/dist/utils/tools-manager.js.map +1 -1
  230. package/docs/compaction.md +3 -3
  231. package/docs/custom-provider.md +1 -1
  232. package/docs/environment-variables.md +1 -0
  233. package/docs/extensions.md +4 -0
  234. package/docs/json.md +6 -4
  235. package/docs/keybindings.md +8 -2
  236. package/docs/rpc.md +15 -4
  237. package/docs/sdk.md +9 -0
  238. package/docs/settings.md +63 -0
  239. package/docs/terminal-setup.md +23 -2
  240. package/docs/themes.md +24 -3
  241. package/docs/tui.md +2 -2
  242. package/docs/usage.md +3 -2
  243. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  244. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  245. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  246. package/examples/extensions/gondolin/package-lock.json +2 -2
  247. package/examples/extensions/gondolin/package.json +1 -1
  248. package/examples/extensions/sandbox/package-lock.json +2 -2
  249. package/examples/extensions/sandbox/package.json +1 -1
  250. package/examples/extensions/subagent/README.md +2 -0
  251. package/examples/extensions/subagent/agents.ts +40 -9
  252. package/examples/extensions/subagent/index.ts +21 -3
  253. package/examples/extensions/with-deps/package-lock.json +2 -2
  254. package/examples/extensions/with-deps/package.json +1 -1
  255. package/npm-shrinkwrap.json +27 -78
  256. package/package.json +6 -6
@@ -24,8 +24,11 @@ export declare class FileAuthStorageBackend implements AuthStorageBackend {
24
24
  }
25
25
  export declare class ReadOnlyAuthStorage implements CredentialStore {
26
26
  private readonly authPath;
27
+ private readonly resolveCommands;
27
28
  private data;
28
- constructor(authPath?: string);
29
+ constructor(authPath?: string, options?: {
30
+ resolveCommands?: boolean;
31
+ });
29
32
  private load;
30
33
  read(providerId: string, options?: AuthOperationOptions): Promise<Credential | undefined>;
31
34
  list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CredentialStore implementation backed by auth.json.\n * Provider auth orchestration belongs to ModelRuntime and pi-ai Models.\n */\n\nimport type { AuthOperationOptions, Credential, CredentialInfo, CredentialStore } from \"@earendil-works/pi-ai\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { setTimeout as sleep } from \"timers/promises\";\nimport { getAuthPath } from \"../config.ts\";\nimport { raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { getFileRevision, normalizePath } from \"../utils/paths.ts\";\nimport { isCommandConfigValue, resolveConfigValue } from \"./resolve-config-value.ts\";\n\ntype AuthStorageData = Record<string, Credential>;\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\ntype AuthFileReload = {\n\tcontroller: AbortController;\n\tpromise: Promise<AuthStorageData>;\n\treaders: number;\n};\n\ntype AuthFileReadState = {\n\tdata: AuthStorageData;\n\trevision?: string;\n\treload?: AuthFileReload;\n};\n\nlet sharedAuthFileReadState: { authPath: string; readState: AuthFileReadState } | undefined;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async acquireLockAsync(\n\t\tsignal: AbortSignal | undefined,\n\t\tonCompromised: (error: Error) => void,\n\t): Promise<() => Promise<void>> {\n\t\tconst staleMs = 30_000;\n\t\tconst maxDelayMs = 2_000;\n\t\tconst deadline = Date.now() + staleMs;\n\t\tlet retry = 0;\n\t\twhile (true) {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\ttry {\n\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tretries: 0,\n\t\t\t\t\tstale: staleMs,\n\t\t\t\t\tonCompromised,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\tif (code !== \"ELOCKED\" || remainingMs <= 0) throw error;\n\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry, maxDelayMs / 2);\n\t\t\t\tretry++;\n\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\tif (signal) await sleep(delayMs, undefined, { signal });\n\t\t\t\telse await sleep(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tawait release();\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t}\n\t\t\treturn release;\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await this.acquireLockAsync(options?.signal, (error) => {\n\t\t\t\tlockCompromised = true;\n\t\t\t\tlockCompromisedError = error;\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class ReadOnlyAuthStorage implements CredentialStore {\n\tprivate readonly authPath: string;\n\tprivate data: AuthStorageData | undefined;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate load(): AuthStorageData {\n\t\tif (this.data) return this.data;\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(this.authPath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tthis.data = {};\n\t\t\t\treturn this.data;\n\t\t\t}\n\t\t\tthrow new Error(`Failed to read auth.json: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Invalid auth.json: expected an object\");\n\t\t}\n\t\tfor (const [providerId, credential] of Object.entries(parsed)) {\n\t\t\tif (typeof credential !== \"object\" || credential === null || Array.isArray(credential)) {\n\t\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t\t}\n\t\t\tconst value = credential as Record<string, unknown>;\n\t\t\tif (value.type === \"api_key\") {\n\t\t\t\tconst validKey = value.key === undefined || typeof value.key === \"string\";\n\t\t\t\tconst validEnv =\n\t\t\t\t\tvalue.env === undefined ||\n\t\t\t\t\t(typeof value.env === \"object\" &&\n\t\t\t\t\t\tvalue.env !== null &&\n\t\t\t\t\t\t!Array.isArray(value.env) &&\n\t\t\t\t\t\tObject.values(value.env).every((entry) => typeof entry === \"string\"));\n\t\t\t\tif (validKey && validEnv) continue;\n\t\t\t} else if (\n\t\t\t\tvalue.type === \"oauth\" &&\n\t\t\t\ttypeof value.access === \"string\" &&\n\t\t\t\ttypeof value.refresh === \"string\" &&\n\t\t\t\ttypeof value.expires === \"number\" &&\n\t\t\t\tNumber.isFinite(value.expires)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t}\n\n\t\tthis.data = parsed as AuthStorageData;\n\t\treturn this.data;\n\t}\n\n\tasync read(providerId: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credential = this.load()[providerId];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!credential) return undefined;\n\t\tif (credential.type !== \"api_key\" || !credential.key || isCommandConfigValue(credential.key)) {\n\t\t\treturn structuredClone(credential);\n\t\t}\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credentials = Object.entries(this.load()).map(([providerId, credential]) => ({\n\t\t\tproviderId,\n\t\t\ttype: credential.type,\n\t\t}));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn credentials;\n\t}\n\n\tasync modify(\n\t\t_providerId: string,\n\t\t_fn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\t_options?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n\n\tasync delete(_providerId: string, _options?: AuthOperationOptions): Promise<void> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate asyncChain: Promise<unknown> = Promise.resolve();\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\tconst previous = this.asyncChain;\n\t\tconst operation = (async () => {\n\t\t\tawait previous.catch(() => {});\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t})();\n\t\tthis.asyncChain = operation.catch(() => {});\n\t\treturn raceWithAbortSignal(operation, options?.signal);\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage implements CredentialStore {\n\tprivate storage: AuthStorageBackend;\n\tprivate authPath: string | undefined;\n\tprivate readState: AuthFileReadState;\n\n\tprivate constructor(storage: AuthStorageBackend, authPath?: string) {\n\t\tthis.storage = storage;\n\t\tthis.authPath = authPath;\n\t\tthis.readState =\n\t\t\tauthPath && sharedAuthFileReadState?.authPath === authPath ? sharedAuthFileReadState.readState : { data: {} };\n\t\tif (authPath && !sharedAuthFileReadState) {\n\t\t\tsharedAuthFileReadState = { authPath, readState: this.readState };\n\t\t}\n\t\tif (authPath) {\n\t\t\tconst revision = getFileRevision(authPath);\n\t\t\tif (revision !== undefined && revision === this.readState.revision) return;\n\t\t}\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath: string = getAuthPath()): AuthStorage {\n\t\tconst normalizedAuthPath = normalizePath(authPath);\n\t\treturn new AuthStorage(new FileAuthStorageBackend(normalizedAuthPath), normalizedAuthPath);\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\tprivate updateReadState(data: AuthStorageData, revision?: string): void {\n\t\tthis.readState.data = data;\n\t\tthis.readState.revision = revision;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\tlet revision: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.updateReadState(this.parseStorageData(content), revision);\n\t\t} catch {\n\t\t\t// Preserve the last valid in-memory snapshot.\n\t\t}\n\t}\n\n\tprivate async reloadFromStorageAsync(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\treturn this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst revision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\tthis.updateReadState(currentData, revision);\n\t\t\treturn { result: currentData };\n\t\t}, options);\n\t}\n\n\tprivate async readLatestData(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!this.authPath) {\n\t\t\tconst reload = this.reloadFromStorageAsync(options);\n\t\t\treturn options?.signal ? reload : reload.catch(() => this.readState.data);\n\t\t}\n\t\tconst revision = getFileRevision(this.authPath);\n\t\tif (revision !== undefined && revision === this.readState.revision) return this.readState.data;\n\t\tif (!this.readState.reload) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst reload: AuthFileReload = {\n\t\t\t\tcontroller,\n\t\t\t\tpromise: this.reloadFromStorageAsync({ signal: controller.signal }),\n\t\t\t\treaders: 0,\n\t\t\t};\n\t\t\tthis.readState.reload = reload;\n\t\t\tvoid reload.promise.then(\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst reload = this.readState.reload;\n\t\treload.readers++;\n\t\ttry {\n\t\t\tconst result = raceWithAbortSignal(reload.promise, options?.signal);\n\t\t\treturn options?.signal ? await result : await result.catch(() => this.readState.data);\n\t\t} finally {\n\t\t\treload.readers--;\n\t\t\tif (reload.readers === 0 && this.readState.reload === reload) {\n\t\t\t\tthis.readState.reload = undefined;\n\t\t\t\treload.controller.abort();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync read(provider: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\tconst credential = (await this.readLatestData(options))[provider];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (credential?.type !== \"api_key\") return credential;\n\t\tif (credential.key === undefined) return credential;\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync modify(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tlet latestData = this.readState.data;\n\t\tlet revision: string | undefined;\n\t\tconst result = await this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst next = await fn(currentData[provider]);\n\t\t\tif (next === undefined) {\n\t\t\t\tlatestData = currentData;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: currentData[provider] };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = { ...currentData, [provider]: next };\n\t\t\tlatestData = merged;\n\t\t\treturn { result: next, next: JSON.stringify(merged, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData, revision);\n\t\treturn result;\n\t}\n\n\tasync delete(provider: string, options?: AuthOperationOptions): Promise<void> {\n\t\tlet latestData = this.readState.data;\n\t\tawait this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tdelete currentData[provider];\n\t\t\tlatestData = currentData;\n\t\t\treturn { result: undefined, next: JSON.stringify(currentData, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData);\n\t}\n\n\t/** List credential metadata without resolving configured key values. */\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\tconst entries = Object.entries(await this.readLatestData(options));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn entries.map(([providerId, credential]) => ({ providerId, type: credential.type }));\n\t}\n}\n\n/**\n * One-off synchronous read of a stored credential from an auth.json file,\n * without instantiating a store or resolving configured key values.\n */\nexport function readStoredCredential(providerId: string, authPath: string = getAuthPath()): Credential | undefined {\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(normalizePath(authPath), \"utf-8\")) as AuthStorageData;\n\t\treturn data[providerId];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CredentialStore implementation backed by auth.json.\n * Provider auth orchestration belongs to ModelRuntime and pi-ai Models.\n */\n\nimport type { AuthOperationOptions, Credential, CredentialInfo, CredentialStore } from \"@earendil-works/pi-ai\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { setTimeout as sleep } from \"timers/promises\";\nimport { getAuthPath } from \"../config.ts\";\nimport { raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { getFileRevision, normalizePath } from \"../utils/paths.ts\";\nimport { isCommandConfigValue, resolveConfigValue } from \"./resolve-config-value.ts\";\n\ntype AuthStorageData = Record<string, Credential>;\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\ntype AuthFileReload = {\n\tcontroller: AbortController;\n\tpromise: Promise<AuthStorageData>;\n\treaders: number;\n};\n\ntype AuthFileReadState = {\n\tdata: AuthStorageData;\n\trevision?: string;\n\treload?: AuthFileReload;\n};\n\nlet sharedAuthFileReadState: { authPath: string; readState: AuthFileReadState } | undefined;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async acquireLockAsync(\n\t\tsignal: AbortSignal | undefined,\n\t\tonCompromised: (error: Error) => void,\n\t): Promise<() => Promise<void>> {\n\t\tconst staleMs = 30_000;\n\t\tconst maxDelayMs = 2_000;\n\t\tconst deadline = Date.now() + staleMs;\n\t\tlet retry = 0;\n\t\twhile (true) {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\ttry {\n\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tretries: 0,\n\t\t\t\t\tstale: staleMs,\n\t\t\t\t\tonCompromised,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\tif (code !== \"ELOCKED\" || remainingMs <= 0) throw error;\n\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry, maxDelayMs / 2);\n\t\t\t\tretry++;\n\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\tif (signal) await sleep(delayMs, undefined, { signal });\n\t\t\t\telse await sleep(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tawait release();\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t}\n\t\t\treturn release;\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await this.acquireLockAsync(options?.signal, (error) => {\n\t\t\t\tlockCompromised = true;\n\t\t\t\tlockCompromisedError = error;\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class ReadOnlyAuthStorage implements CredentialStore {\n\tprivate readonly authPath: string;\n\tprivate readonly resolveCommands: boolean;\n\tprivate data: AuthStorageData | undefined;\n\n\tconstructor(authPath: string = getAuthPath(), options: { resolveCommands?: boolean } = {}) {\n\t\tthis.authPath = normalizePath(authPath);\n\t\tthis.resolveCommands = options.resolveCommands ?? false;\n\t}\n\n\tprivate load(): AuthStorageData {\n\t\tif (this.data) return this.data;\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(this.authPath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tthis.data = {};\n\t\t\t\treturn this.data;\n\t\t\t}\n\t\t\tthrow new Error(`Failed to read auth.json: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Invalid auth.json: expected an object\");\n\t\t}\n\t\tfor (const [providerId, credential] of Object.entries(parsed)) {\n\t\t\tif (typeof credential !== \"object\" || credential === null || Array.isArray(credential)) {\n\t\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t\t}\n\t\t\tconst value = credential as Record<string, unknown>;\n\t\t\tif (value.type === \"api_key\") {\n\t\t\t\tconst validKey = value.key === undefined || typeof value.key === \"string\";\n\t\t\t\tconst validEnv =\n\t\t\t\t\tvalue.env === undefined ||\n\t\t\t\t\t(typeof value.env === \"object\" &&\n\t\t\t\t\t\tvalue.env !== null &&\n\t\t\t\t\t\t!Array.isArray(value.env) &&\n\t\t\t\t\t\tObject.values(value.env).every((entry) => typeof entry === \"string\"));\n\t\t\t\tif (validKey && validEnv) continue;\n\t\t\t} else if (\n\t\t\t\tvalue.type === \"oauth\" &&\n\t\t\t\ttypeof value.access === \"string\" &&\n\t\t\t\ttypeof value.refresh === \"string\" &&\n\t\t\t\ttypeof value.expires === \"number\" &&\n\t\t\t\tNumber.isFinite(value.expires)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t}\n\n\t\tthis.data = parsed as AuthStorageData;\n\t\treturn this.data;\n\t}\n\n\tasync read(providerId: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credential = this.load()[providerId];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!credential) return undefined;\n\t\tif (\n\t\t\tcredential.type !== \"api_key\" ||\n\t\t\tcredential.key === undefined ||\n\t\t\t(!this.resolveCommands && isCommandConfigValue(credential.key))\n\t\t) {\n\t\t\treturn structuredClone(credential);\n\t\t}\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credentials = Object.entries(this.load()).map(([providerId, credential]) => ({\n\t\t\tproviderId,\n\t\t\ttype: credential.type,\n\t\t}));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn credentials;\n\t}\n\n\tasync modify(\n\t\t_providerId: string,\n\t\t_fn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\t_options?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n\n\tasync delete(_providerId: string, _options?: AuthOperationOptions): Promise<void> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate asyncChain: Promise<unknown> = Promise.resolve();\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\tconst previous = this.asyncChain;\n\t\tconst operation = (async () => {\n\t\t\tawait previous.catch(() => {});\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t})();\n\t\tthis.asyncChain = operation.catch(() => {});\n\t\treturn raceWithAbortSignal(operation, options?.signal);\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage implements CredentialStore {\n\tprivate storage: AuthStorageBackend;\n\tprivate authPath: string | undefined;\n\tprivate readState: AuthFileReadState;\n\n\tprivate constructor(storage: AuthStorageBackend, authPath?: string) {\n\t\tthis.storage = storage;\n\t\tthis.authPath = authPath;\n\t\tthis.readState =\n\t\t\tauthPath && sharedAuthFileReadState?.authPath === authPath ? sharedAuthFileReadState.readState : { data: {} };\n\t\tif (authPath && !sharedAuthFileReadState) {\n\t\t\tsharedAuthFileReadState = { authPath, readState: this.readState };\n\t\t}\n\t\tif (authPath) {\n\t\t\tconst revision = getFileRevision(authPath);\n\t\t\tif (revision !== undefined && revision === this.readState.revision) return;\n\t\t}\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath: string = getAuthPath()): AuthStorage {\n\t\tconst normalizedAuthPath = normalizePath(authPath);\n\t\treturn new AuthStorage(new FileAuthStorageBackend(normalizedAuthPath), normalizedAuthPath);\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\tprivate updateReadState(data: AuthStorageData, revision?: string): void {\n\t\tthis.readState.data = data;\n\t\tthis.readState.revision = revision;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\tlet revision: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.updateReadState(this.parseStorageData(content), revision);\n\t\t} catch {\n\t\t\t// Preserve the last valid in-memory snapshot.\n\t\t}\n\t}\n\n\tprivate async reloadFromStorageAsync(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\treturn this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst revision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\tthis.updateReadState(currentData, revision);\n\t\t\treturn { result: currentData };\n\t\t}, options);\n\t}\n\n\tprivate async readLatestData(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!this.authPath) {\n\t\t\tconst reload = this.reloadFromStorageAsync(options);\n\t\t\treturn options?.signal ? reload : reload.catch(() => this.readState.data);\n\t\t}\n\t\tconst revision = getFileRevision(this.authPath);\n\t\tif (revision !== undefined && revision === this.readState.revision) return this.readState.data;\n\t\tif (!this.readState.reload) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst reload: AuthFileReload = {\n\t\t\t\tcontroller,\n\t\t\t\tpromise: this.reloadFromStorageAsync({ signal: controller.signal }),\n\t\t\t\treaders: 0,\n\t\t\t};\n\t\t\tthis.readState.reload = reload;\n\t\t\tvoid reload.promise.then(\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst reload = this.readState.reload;\n\t\treload.readers++;\n\t\ttry {\n\t\t\tconst result = raceWithAbortSignal(reload.promise, options?.signal);\n\t\t\treturn options?.signal ? await result : await result.catch(() => this.readState.data);\n\t\t} finally {\n\t\t\treload.readers--;\n\t\t\tif (reload.readers === 0 && this.readState.reload === reload) {\n\t\t\t\tthis.readState.reload = undefined;\n\t\t\t\treload.controller.abort();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync read(provider: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\tconst credential = (await this.readLatestData(options))[provider];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (credential?.type !== \"api_key\") return credential;\n\t\tif (credential.key === undefined) return credential;\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync modify(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tlet latestData = this.readState.data;\n\t\tlet revision: string | undefined;\n\t\tconst result = await this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst next = await fn(currentData[provider]);\n\t\t\tif (next === undefined) {\n\t\t\t\tlatestData = currentData;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: currentData[provider] };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = { ...currentData, [provider]: next };\n\t\t\tlatestData = merged;\n\t\t\treturn { result: next, next: JSON.stringify(merged, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData, revision);\n\t\treturn result;\n\t}\n\n\tasync delete(provider: string, options?: AuthOperationOptions): Promise<void> {\n\t\tlet latestData = this.readState.data;\n\t\tawait this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tdelete currentData[provider];\n\t\t\tlatestData = currentData;\n\t\t\treturn { result: undefined, next: JSON.stringify(currentData, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData);\n\t}\n\n\t/** List credential metadata without resolving configured key values. */\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\tconst entries = Object.entries(await this.readLatestData(options));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn entries.map(([providerId, credential]) => ({ providerId, type: credential.type }));\n\t}\n}\n\n/**\n * One-off synchronous read of a stored credential from an auth.json file,\n * without instantiating a store or resolving configured key values.\n */\nexport function readStoredCredential(providerId: string, authPath: string = getAuthPath()): Credential | undefined {\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(normalizePath(authPath), \"utf-8\")) as AuthStorageData;\n\t\treturn data[providerId];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -157,9 +157,11 @@ export class FileAuthStorageBackend {
157
157
  }
158
158
  export class ReadOnlyAuthStorage {
159
159
  authPath;
160
+ resolveCommands;
160
161
  data;
161
- constructor(authPath = getAuthPath()) {
162
+ constructor(authPath = getAuthPath(), options = {}) {
162
163
  this.authPath = normalizePath(authPath);
164
+ this.resolveCommands = options.resolveCommands ?? false;
163
165
  }
164
166
  load() {
165
167
  if (this.data)
@@ -211,7 +213,9 @@ export class ReadOnlyAuthStorage {
211
213
  options?.signal?.throwIfAborted();
212
214
  if (!credential)
213
215
  return undefined;
214
- if (credential.type !== "api_key" || !credential.key || isCommandConfigValue(credential.key)) {
216
+ if (credential.type !== "api_key" ||
217
+ credential.key === undefined ||
218
+ (!this.resolveCommands && isCommandConfigValue(credential.key))) {
215
219
  return structuredClone(credential);
216
220
  }
217
221
  return { ...credential, key: resolveConfigValue(credential.key, credential.env) };
@@ -1 +1 @@
1
- {"version":3,"file":"auth-storage.js","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CredentialStore implementation backed by auth.json.\n * Provider auth orchestration belongs to ModelRuntime and pi-ai Models.\n */\n\nimport type { AuthOperationOptions, Credential, CredentialInfo, CredentialStore } from \"@earendil-works/pi-ai\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { setTimeout as sleep } from \"timers/promises\";\nimport { getAuthPath } from \"../config.ts\";\nimport { raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { getFileRevision, normalizePath } from \"../utils/paths.ts\";\nimport { isCommandConfigValue, resolveConfigValue } from \"./resolve-config-value.ts\";\n\ntype AuthStorageData = Record<string, Credential>;\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\ntype AuthFileReload = {\n\tcontroller: AbortController;\n\tpromise: Promise<AuthStorageData>;\n\treaders: number;\n};\n\ntype AuthFileReadState = {\n\tdata: AuthStorageData;\n\trevision?: string;\n\treload?: AuthFileReload;\n};\n\nlet sharedAuthFileReadState: { authPath: string; readState: AuthFileReadState } | undefined;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async acquireLockAsync(\n\t\tsignal: AbortSignal | undefined,\n\t\tonCompromised: (error: Error) => void,\n\t): Promise<() => Promise<void>> {\n\t\tconst staleMs = 30_000;\n\t\tconst maxDelayMs = 2_000;\n\t\tconst deadline = Date.now() + staleMs;\n\t\tlet retry = 0;\n\t\twhile (true) {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\ttry {\n\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tretries: 0,\n\t\t\t\t\tstale: staleMs,\n\t\t\t\t\tonCompromised,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\tif (code !== \"ELOCKED\" || remainingMs <= 0) throw error;\n\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry, maxDelayMs / 2);\n\t\t\t\tretry++;\n\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\tif (signal) await sleep(delayMs, undefined, { signal });\n\t\t\t\telse await sleep(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tawait release();\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t}\n\t\t\treturn release;\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await this.acquireLockAsync(options?.signal, (error) => {\n\t\t\t\tlockCompromised = true;\n\t\t\t\tlockCompromisedError = error;\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class ReadOnlyAuthStorage implements CredentialStore {\n\tprivate readonly authPath: string;\n\tprivate data: AuthStorageData | undefined;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate load(): AuthStorageData {\n\t\tif (this.data) return this.data;\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(this.authPath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tthis.data = {};\n\t\t\t\treturn this.data;\n\t\t\t}\n\t\t\tthrow new Error(`Failed to read auth.json: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Invalid auth.json: expected an object\");\n\t\t}\n\t\tfor (const [providerId, credential] of Object.entries(parsed)) {\n\t\t\tif (typeof credential !== \"object\" || credential === null || Array.isArray(credential)) {\n\t\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t\t}\n\t\t\tconst value = credential as Record<string, unknown>;\n\t\t\tif (value.type === \"api_key\") {\n\t\t\t\tconst validKey = value.key === undefined || typeof value.key === \"string\";\n\t\t\t\tconst validEnv =\n\t\t\t\t\tvalue.env === undefined ||\n\t\t\t\t\t(typeof value.env === \"object\" &&\n\t\t\t\t\t\tvalue.env !== null &&\n\t\t\t\t\t\t!Array.isArray(value.env) &&\n\t\t\t\t\t\tObject.values(value.env).every((entry) => typeof entry === \"string\"));\n\t\t\t\tif (validKey && validEnv) continue;\n\t\t\t} else if (\n\t\t\t\tvalue.type === \"oauth\" &&\n\t\t\t\ttypeof value.access === \"string\" &&\n\t\t\t\ttypeof value.refresh === \"string\" &&\n\t\t\t\ttypeof value.expires === \"number\" &&\n\t\t\t\tNumber.isFinite(value.expires)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t}\n\n\t\tthis.data = parsed as AuthStorageData;\n\t\treturn this.data;\n\t}\n\n\tasync read(providerId: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credential = this.load()[providerId];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!credential) return undefined;\n\t\tif (credential.type !== \"api_key\" || !credential.key || isCommandConfigValue(credential.key)) {\n\t\t\treturn structuredClone(credential);\n\t\t}\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credentials = Object.entries(this.load()).map(([providerId, credential]) => ({\n\t\t\tproviderId,\n\t\t\ttype: credential.type,\n\t\t}));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn credentials;\n\t}\n\n\tasync modify(\n\t\t_providerId: string,\n\t\t_fn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\t_options?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n\n\tasync delete(_providerId: string, _options?: AuthOperationOptions): Promise<void> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate asyncChain: Promise<unknown> = Promise.resolve();\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\tconst previous = this.asyncChain;\n\t\tconst operation = (async () => {\n\t\t\tawait previous.catch(() => {});\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t})();\n\t\tthis.asyncChain = operation.catch(() => {});\n\t\treturn raceWithAbortSignal(operation, options?.signal);\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage implements CredentialStore {\n\tprivate storage: AuthStorageBackend;\n\tprivate authPath: string | undefined;\n\tprivate readState: AuthFileReadState;\n\n\tprivate constructor(storage: AuthStorageBackend, authPath?: string) {\n\t\tthis.storage = storage;\n\t\tthis.authPath = authPath;\n\t\tthis.readState =\n\t\t\tauthPath && sharedAuthFileReadState?.authPath === authPath ? sharedAuthFileReadState.readState : { data: {} };\n\t\tif (authPath && !sharedAuthFileReadState) {\n\t\t\tsharedAuthFileReadState = { authPath, readState: this.readState };\n\t\t}\n\t\tif (authPath) {\n\t\t\tconst revision = getFileRevision(authPath);\n\t\t\tif (revision !== undefined && revision === this.readState.revision) return;\n\t\t}\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath: string = getAuthPath()): AuthStorage {\n\t\tconst normalizedAuthPath = normalizePath(authPath);\n\t\treturn new AuthStorage(new FileAuthStorageBackend(normalizedAuthPath), normalizedAuthPath);\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\tprivate updateReadState(data: AuthStorageData, revision?: string): void {\n\t\tthis.readState.data = data;\n\t\tthis.readState.revision = revision;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\tlet revision: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.updateReadState(this.parseStorageData(content), revision);\n\t\t} catch {\n\t\t\t// Preserve the last valid in-memory snapshot.\n\t\t}\n\t}\n\n\tprivate async reloadFromStorageAsync(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\treturn this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst revision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\tthis.updateReadState(currentData, revision);\n\t\t\treturn { result: currentData };\n\t\t}, options);\n\t}\n\n\tprivate async readLatestData(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!this.authPath) {\n\t\t\tconst reload = this.reloadFromStorageAsync(options);\n\t\t\treturn options?.signal ? reload : reload.catch(() => this.readState.data);\n\t\t}\n\t\tconst revision = getFileRevision(this.authPath);\n\t\tif (revision !== undefined && revision === this.readState.revision) return this.readState.data;\n\t\tif (!this.readState.reload) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst reload: AuthFileReload = {\n\t\t\t\tcontroller,\n\t\t\t\tpromise: this.reloadFromStorageAsync({ signal: controller.signal }),\n\t\t\t\treaders: 0,\n\t\t\t};\n\t\t\tthis.readState.reload = reload;\n\t\t\tvoid reload.promise.then(\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst reload = this.readState.reload;\n\t\treload.readers++;\n\t\ttry {\n\t\t\tconst result = raceWithAbortSignal(reload.promise, options?.signal);\n\t\t\treturn options?.signal ? await result : await result.catch(() => this.readState.data);\n\t\t} finally {\n\t\t\treload.readers--;\n\t\t\tif (reload.readers === 0 && this.readState.reload === reload) {\n\t\t\t\tthis.readState.reload = undefined;\n\t\t\t\treload.controller.abort();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync read(provider: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\tconst credential = (await this.readLatestData(options))[provider];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (credential?.type !== \"api_key\") return credential;\n\t\tif (credential.key === undefined) return credential;\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync modify(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tlet latestData = this.readState.data;\n\t\tlet revision: string | undefined;\n\t\tconst result = await this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst next = await fn(currentData[provider]);\n\t\t\tif (next === undefined) {\n\t\t\t\tlatestData = currentData;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: currentData[provider] };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = { ...currentData, [provider]: next };\n\t\t\tlatestData = merged;\n\t\t\treturn { result: next, next: JSON.stringify(merged, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData, revision);\n\t\treturn result;\n\t}\n\n\tasync delete(provider: string, options?: AuthOperationOptions): Promise<void> {\n\t\tlet latestData = this.readState.data;\n\t\tawait this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tdelete currentData[provider];\n\t\t\tlatestData = currentData;\n\t\t\treturn { result: undefined, next: JSON.stringify(currentData, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData);\n\t}\n\n\t/** List credential metadata without resolving configured key values. */\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\tconst entries = Object.entries(await this.readLatestData(options));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn entries.map(([providerId, credential]) => ({ providerId, type: credential.type }));\n\t}\n}\n\n/**\n * One-off synchronous read of a stored credential from an auth.json file,\n * without instantiating a store or resolving configured key values.\n */\nexport function readStoredCredential(providerId: string, authPath: string = getAuthPath()): Credential | undefined {\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(normalizePath(authPath), \"utf-8\")) as AuthStorageData;\n\t\treturn data[providerId];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"auth-storage.js","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CredentialStore implementation backed by auth.json.\n * Provider auth orchestration belongs to ModelRuntime and pi-ai Models.\n */\n\nimport type { AuthOperationOptions, Credential, CredentialInfo, CredentialStore } from \"@earendil-works/pi-ai\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { setTimeout as sleep } from \"timers/promises\";\nimport { getAuthPath } from \"../config.ts\";\nimport { raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { getFileRevision, normalizePath } from \"../utils/paths.ts\";\nimport { isCommandConfigValue, resolveConfigValue } from \"./resolve-config-value.ts\";\n\ntype AuthStorageData = Record<string, Credential>;\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\ntype AuthFileReload = {\n\tcontroller: AbortController;\n\tpromise: Promise<AuthStorageData>;\n\treaders: number;\n};\n\ntype AuthFileReadState = {\n\tdata: AuthStorageData;\n\trevision?: string;\n\treload?: AuthFileReload;\n};\n\nlet sharedAuthFileReadState: { authPath: string; readState: AuthFileReadState } | undefined;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = getAuthPath()) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async acquireLockAsync(\n\t\tsignal: AbortSignal | undefined,\n\t\tonCompromised: (error: Error) => void,\n\t): Promise<() => Promise<void>> {\n\t\tconst staleMs = 30_000;\n\t\tconst maxDelayMs = 2_000;\n\t\tconst deadline = Date.now() + staleMs;\n\t\tlet retry = 0;\n\t\twhile (true) {\n\t\t\tsignal?.throwIfAborted();\n\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\ttry {\n\t\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tretries: 0,\n\t\t\t\t\tstale: staleMs,\n\t\t\t\t\tonCompromised,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst remainingMs = deadline - Date.now();\n\t\t\t\tif (code !== \"ELOCKED\" || remainingMs <= 0) throw error;\n\t\t\t\tconst baseDelayMs = Math.min(10 * 2 ** retry, maxDelayMs / 2);\n\t\t\t\tretry++;\n\t\t\t\tconst delayMs = Math.min(Math.round(baseDelayMs * (1 + Math.random())), remainingMs);\n\t\t\t\tif (signal) await sleep(delayMs, undefined, { signal });\n\t\t\t\telse await sleep(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tawait release();\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t}\n\t\t\treturn release;\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await this.acquireLockAsync(options?.signal, (error) => {\n\t\t\t\tlockCompromised = true;\n\t\t\t\tlockCompromisedError = error;\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class ReadOnlyAuthStorage implements CredentialStore {\n\tprivate readonly authPath: string;\n\tprivate readonly resolveCommands: boolean;\n\tprivate data: AuthStorageData | undefined;\n\n\tconstructor(authPath: string = getAuthPath(), options: { resolveCommands?: boolean } = {}) {\n\t\tthis.authPath = normalizePath(authPath);\n\t\tthis.resolveCommands = options.resolveCommands ?? false;\n\t}\n\n\tprivate load(): AuthStorageData {\n\t\tif (this.data) return this.data;\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(this.authPath, \"utf-8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tthis.data = {};\n\t\t\t\treturn this.data;\n\t\t\t}\n\t\t\tthrow new Error(`Failed to read auth.json: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Invalid auth.json: expected an object\");\n\t\t}\n\t\tfor (const [providerId, credential] of Object.entries(parsed)) {\n\t\t\tif (typeof credential !== \"object\" || credential === null || Array.isArray(credential)) {\n\t\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t\t}\n\t\t\tconst value = credential as Record<string, unknown>;\n\t\t\tif (value.type === \"api_key\") {\n\t\t\t\tconst validKey = value.key === undefined || typeof value.key === \"string\";\n\t\t\t\tconst validEnv =\n\t\t\t\t\tvalue.env === undefined ||\n\t\t\t\t\t(typeof value.env === \"object\" &&\n\t\t\t\t\t\tvalue.env !== null &&\n\t\t\t\t\t\t!Array.isArray(value.env) &&\n\t\t\t\t\t\tObject.values(value.env).every((entry) => typeof entry === \"string\"));\n\t\t\t\tif (validKey && validEnv) continue;\n\t\t\t} else if (\n\t\t\t\tvalue.type === \"oauth\" &&\n\t\t\t\ttypeof value.access === \"string\" &&\n\t\t\t\ttypeof value.refresh === \"string\" &&\n\t\t\t\ttypeof value.expires === \"number\" &&\n\t\t\t\tNumber.isFinite(value.expires)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Invalid auth.json credential for provider \"${providerId}\"`);\n\t\t}\n\n\t\tthis.data = parsed as AuthStorageData;\n\t\treturn this.data;\n\t}\n\n\tasync read(providerId: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credential = this.load()[providerId];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!credential) return undefined;\n\t\tif (\n\t\t\tcredential.type !== \"api_key\" ||\n\t\t\tcredential.key === undefined ||\n\t\t\t(!this.resolveCommands && isCommandConfigValue(credential.key))\n\t\t) {\n\t\t\treturn structuredClone(credential);\n\t\t}\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tconst credentials = Object.entries(this.load()).map(([providerId, credential]) => ({\n\t\t\tproviderId,\n\t\t\ttype: credential.type,\n\t\t}));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn credentials;\n\t}\n\n\tasync modify(\n\t\t_providerId: string,\n\t\t_fn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\t_options?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n\n\tasync delete(_providerId: string, _options?: AuthOperationOptions): Promise<void> {\n\t\tthrow new Error(\"Read-only credential storage cannot modify auth.json\");\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\tprivate asyncChain: Promise<unknown> = Promise.resolve();\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\twithLockAsync<T>(\n\t\tfn: (current: string | undefined) => Promise<LockResult<T>>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<T> {\n\t\tconst previous = this.asyncChain;\n\t\tconst operation = (async () => {\n\t\t\tawait previous.catch(() => {});\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tconst { result, next } = await fn(this.value);\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (next !== undefined) {\n\t\t\t\tthis.value = next;\n\t\t\t}\n\t\t\treturn result;\n\t\t})();\n\t\tthis.asyncChain = operation.catch(() => {});\n\t\treturn raceWithAbortSignal(operation, options?.signal);\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage implements CredentialStore {\n\tprivate storage: AuthStorageBackend;\n\tprivate authPath: string | undefined;\n\tprivate readState: AuthFileReadState;\n\n\tprivate constructor(storage: AuthStorageBackend, authPath?: string) {\n\t\tthis.storage = storage;\n\t\tthis.authPath = authPath;\n\t\tthis.readState =\n\t\t\tauthPath && sharedAuthFileReadState?.authPath === authPath ? sharedAuthFileReadState.readState : { data: {} };\n\t\tif (authPath && !sharedAuthFileReadState) {\n\t\t\tsharedAuthFileReadState = { authPath, readState: this.readState };\n\t\t}\n\t\tif (authPath) {\n\t\t\tconst revision = getFileRevision(authPath);\n\t\t\tif (revision !== undefined && revision === this.readState.revision) return;\n\t\t}\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath: string = getAuthPath()): AuthStorage {\n\t\tconst normalizedAuthPath = normalizePath(authPath);\n\t\treturn new AuthStorage(new FileAuthStorageBackend(normalizedAuthPath), normalizedAuthPath);\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\tprivate updateReadState(data: AuthStorageData, revision?: string): void {\n\t\tthis.readState.data = data;\n\t\tthis.readState.revision = revision;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\tlet revision: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.updateReadState(this.parseStorageData(content), revision);\n\t\t} catch {\n\t\t\t// Preserve the last valid in-memory snapshot.\n\t\t}\n\t}\n\n\tprivate async reloadFromStorageAsync(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\treturn this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst revision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\tthis.updateReadState(currentData, revision);\n\t\t\treturn { result: currentData };\n\t\t}, options);\n\t}\n\n\tprivate async readLatestData(options?: AuthOperationOptions): Promise<AuthStorageData> {\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (!this.authPath) {\n\t\t\tconst reload = this.reloadFromStorageAsync(options);\n\t\t\treturn options?.signal ? reload : reload.catch(() => this.readState.data);\n\t\t}\n\t\tconst revision = getFileRevision(this.authPath);\n\t\tif (revision !== undefined && revision === this.readState.revision) return this.readState.data;\n\t\tif (!this.readState.reload) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst reload: AuthFileReload = {\n\t\t\t\tcontroller,\n\t\t\t\tpromise: this.reloadFromStorageAsync({ signal: controller.signal }),\n\t\t\t\treaders: 0,\n\t\t\t};\n\t\t\tthis.readState.reload = reload;\n\t\t\tvoid reload.promise.then(\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tif (this.readState.reload === reload) this.readState.reload = undefined;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst reload = this.readState.reload;\n\t\treload.readers++;\n\t\ttry {\n\t\t\tconst result = raceWithAbortSignal(reload.promise, options?.signal);\n\t\t\treturn options?.signal ? await result : await result.catch(() => this.readState.data);\n\t\t} finally {\n\t\t\treload.readers--;\n\t\t\tif (reload.readers === 0 && this.readState.reload === reload) {\n\t\t\t\tthis.readState.reload = undefined;\n\t\t\t\treload.controller.abort();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync read(provider: string, options?: AuthOperationOptions): Promise<Credential | undefined> {\n\t\tconst credential = (await this.readLatestData(options))[provider];\n\t\toptions?.signal?.throwIfAborted();\n\t\tif (credential?.type !== \"api_key\") return credential;\n\t\tif (credential.key === undefined) return credential;\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync modify(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t\toptions?: AuthOperationOptions,\n\t): Promise<Credential | undefined> {\n\t\tlet latestData = this.readState.data;\n\t\tlet revision: string | undefined;\n\t\tconst result = await this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst next = await fn(currentData[provider]);\n\t\t\tif (next === undefined) {\n\t\t\t\tlatestData = currentData;\n\t\t\t\trevision = this.authPath ? getFileRevision(this.authPath) : undefined;\n\t\t\t\treturn { result: currentData[provider] };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = { ...currentData, [provider]: next };\n\t\t\tlatestData = merged;\n\t\t\treturn { result: next, next: JSON.stringify(merged, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData, revision);\n\t\treturn result;\n\t}\n\n\tasync delete(provider: string, options?: AuthOperationOptions): Promise<void> {\n\t\tlet latestData = this.readState.data;\n\t\tawait this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tdelete currentData[provider];\n\t\t\tlatestData = currentData;\n\t\t\treturn { result: undefined, next: JSON.stringify(currentData, null, 2) };\n\t\t}, options);\n\t\tthis.updateReadState(latestData);\n\t}\n\n\t/** List credential metadata without resolving configured key values. */\n\tasync list(options?: AuthOperationOptions): Promise<readonly CredentialInfo[]> {\n\t\tconst entries = Object.entries(await this.readLatestData(options));\n\t\toptions?.signal?.throwIfAborted();\n\t\treturn entries.map(([providerId, credential]) => ({ providerId, type: credential.type }));\n\t}\n}\n\n/**\n * One-off synchronous read of a stored credential from an auth.json file,\n * without instantiating a store or resolving configured key values.\n */\nexport function readStoredCredential(providerId: string, authPath: string = getAuthPath()): Credential | undefined {\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(normalizePath(authPath), \"utf-8\")) as AuthStorageData;\n\t\treturn data[providerId];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAqGrB","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `apex-code-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
1
+ {"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAqGrB","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { finished } from \"node:stream/promises\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `apex-code-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\tconst closeTempFile = async () => {\n\t\tif (!tempFileStream) return;\n\t\ttempFileStream.end();\n\t\tawait finished(tempFileStream);\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tawait closeTempFile();\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tawait closeTempFile();\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tawait closeTempFile();\n\n\t\tthrow err;\n\t}\n}\n"]}
@@ -9,6 +9,7 @@ import { randomBytes } from "node:crypto";
9
9
  import { createWriteStream } from "node:fs";
10
10
  import { tmpdir } from "node:os";
11
11
  import { join } from "node:path";
12
+ import { finished } from "node:stream/promises";
12
13
  import { stripAnsi } from "../utils/ansi.js";
13
14
  import { sanitizeBinaryOutput } from "../utils/shell.js";
14
15
  import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
@@ -61,6 +62,12 @@ export async function executeBashWithOperations(command, cwd, operations, option
61
62
  options.onChunk(text);
62
63
  }
63
64
  };
65
+ const closeTempFile = async () => {
66
+ if (!tempFileStream)
67
+ return;
68
+ tempFileStream.end();
69
+ await finished(tempFileStream);
70
+ };
64
71
  try {
65
72
  const result = await operations.exec(command, cwd, {
66
73
  onData,
@@ -71,9 +78,7 @@ export async function executeBashWithOperations(command, cwd, operations, option
71
78
  if (truncationResult.truncated) {
72
79
  ensureTempFile();
73
80
  }
74
- if (tempFileStream) {
75
- tempFileStream.end();
76
- }
81
+ await closeTempFile();
77
82
  const cancelled = options?.signal?.aborted ?? false;
78
83
  return {
79
84
  output: truncationResult.truncated ? truncationResult.content : fullOutput,
@@ -91,9 +96,7 @@ export async function executeBashWithOperations(command, cwd, operations, option
91
96
  if (truncationResult.truncated) {
92
97
  ensureTempFile();
93
98
  }
94
- if (tempFileStream) {
95
- tempFileStream.end();
96
- }
99
+ await closeTempFile();
97
100
  return {
98
101
  output: truncationResult.truncated ? truncationResult.content : fullOutput,
99
102
  exitCode: undefined,
@@ -102,9 +105,7 @@ export async function executeBashWithOperations(command, cwd, operations, option
102
105
  fullOutputPath: tempFilePath,
103
106
  };
104
107
  }
105
- if (tempFileStream) {
106
- tempFileStream.end();
107
- }
108
+ await closeTempFile();
108
109
  throw err;
109
110
  }
110
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `apex-code-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
1
+ {"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { finished } from \"node:stream/promises\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `apex-code-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\tconst closeTempFile = async () => {\n\t\tif (!tempFileStream) return;\n\t\ttempFileStream.end();\n\t\tawait finished(tempFileStream);\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tawait closeTempFile();\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tawait closeTempFile();\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tawait closeTempFile();\n\n\t\tthrow err;\n\t}\n}\n"]}
@@ -1,2 +1,6 @@
1
1
  export declare function areExperimentalFeaturesEnabled(): boolean;
2
+ export declare function getExperimentalToolSampling(): {
3
+ readonly type: "json_schema";
4
+ readonly strict: "prefer";
5
+ } | undefined;
2
6
  //# sourceMappingURL=experimental.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../../src/core/experimental.ts"],"names":[],"mappings":"AAAA,wBAAgB,8BAA8B,IAAI,OAAO,CAExD","sourcesContent":["export function areExperimentalFeaturesEnabled(): boolean {\n\treturn getApexEnvironment(\"APEX_CODE_EXPERIMENTAL\") === \"1\";\n}\n\nimport { getApexEnvironment } from \"./environment.ts\";\n"]}
1
+ {"version":3,"file":"experimental.d.ts","sourceRoot":"","sources":["../../src/core/experimental.ts"],"names":[],"mappings":"AAEA,wBAAgB,8BAA8B,IAAI,OAAO,CAExD;AAID,wBAAgB,2BAA2B;;;cAE1C","sourcesContent":["const PREFER_STRICT_TOOL_SAMPLING = { type: \"json_schema\", strict: \"prefer\" } as const;\n\nexport function areExperimentalFeaturesEnabled(): boolean {\n\treturn getApexEnvironment(\"APEX_CODE_EXPERIMENTAL\") === \"1\";\n}\n\nimport { getApexEnvironment } from \"./environment.ts\";\n\nexport function getExperimentalToolSampling() {\n\treturn areExperimentalFeaturesEnabled() ? PREFER_STRICT_TOOL_SAMPLING : undefined;\n}\n"]}
@@ -1,5 +1,9 @@
1
+ const PREFER_STRICT_TOOL_SAMPLING = { type: "json_schema", strict: "prefer" };
1
2
  export function areExperimentalFeaturesEnabled() {
2
3
  return getApexEnvironment("APEX_CODE_EXPERIMENTAL") === "1";
3
4
  }
4
5
  import { getApexEnvironment } from "./environment.js";
6
+ export function getExperimentalToolSampling() {
7
+ return areExperimentalFeaturesEnabled() ? PREFER_STRICT_TOOL_SAMPLING : undefined;
8
+ }
5
9
  //# sourceMappingURL=experimental.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"experimental.js","sourceRoot":"","sources":["../../src/core/experimental.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,8BAA8B,GAAY;IACzD,OAAO,kBAAkB,CAAC,wBAAwB,CAAC,KAAK,GAAG,CAAC;AAAA,CAC5D;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["export function areExperimentalFeaturesEnabled(): boolean {\n\treturn getApexEnvironment(\"APEX_CODE_EXPERIMENTAL\") === \"1\";\n}\n\nimport { getApexEnvironment } from \"./environment.ts\";\n"]}
1
+ {"version":3,"file":"experimental.js","sourceRoot":"","sources":["../../src/core/experimental.ts"],"names":[],"mappings":"AAAA,MAAM,2BAA2B,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAW,CAAC;AAEvF,MAAM,UAAU,8BAA8B,GAAY;IACzD,OAAO,kBAAkB,CAAC,wBAAwB,CAAC,KAAK,GAAG,CAAC;AAAA,CAC5D;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,UAAU,2BAA2B,GAAG;IAC7C,OAAO,8BAA8B,EAAE,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAClF","sourcesContent":["const PREFER_STRICT_TOOL_SAMPLING = { type: \"json_schema\", strict: \"prefer\" } as const;\n\nexport function areExperimentalFeaturesEnabled(): boolean {\n\treturn getApexEnvironment(\"APEX_CODE_EXPERIMENTAL\") === \"1\";\n}\n\nimport { getApexEnvironment } from \"./environment.ts\";\n\nexport function getExperimentalToolSampling() {\n\treturn areExperimentalFeaturesEnabled() ? PREFER_STRICT_TOOL_SAMPLING : undefined;\n}\n"]}
@@ -301,6 +301,7 @@ export interface ReplacedSessionContext extends ExtensionCommandContext {
301
301
  }): Promise<void>;
302
302
  sendUserMessage(content: string | (TextContent | ImageContent)[], options?: {
303
303
  deliverAs?: "steer" | "followUp";
304
+ expandPromptTemplates?: boolean;
304
305
  }): Promise<void>;
305
306
  }
306
307
  /** Rendering options for tool results */
@@ -928,9 +929,11 @@ export interface ExtensionAPI {
928
929
  /**
929
930
  * Send a user message to the agent. Always triggers a turn.
930
931
  * When the agent is streaming, use deliverAs to specify how to queue the message.
932
+ * Set expandPromptTemplates to dispatch extension commands and expand skill commands and prompt templates.
931
933
  */
932
934
  sendUserMessage(content: string | (TextContent | ImageContent)[], options?: {
933
935
  deliverAs?: "steer" | "followUp";
936
+ expandPromptTemplates?: boolean;
934
937
  }): void;
935
938
  /** Append a custom entry to the session for state persistence (not sent to LLM). */
936
939
  appendEntry<T = unknown>(customType: string, data?: T): void;
@@ -1133,6 +1136,7 @@ export type SendMessageHandler = <T = unknown>(message: Pick<CustomMessage<T>, "
1133
1136
  }) => void;
1134
1137
  export type SendUserMessageHandler = (content: string | (TextContent | ImageContent)[], options?: {
1135
1138
  deliverAs?: "steer" | "followUp";
1139
+ expandPromptTemplates?: boolean;
1136
1140
  }) => void;
1137
1141
  export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
1138
1142
  export type SetSessionNameHandler = (name: string) => void;