@inneranimalmedia/agentsam-sdk 2.6.4 → 2.6.5

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 (418) hide show
  1. package/AGENTSAM.md +11 -0
  2. package/README.md +15 -9
  3. package/apps/client-cms-editor/AGENTS.md +12 -0
  4. package/apps/client-cms-editor/IMPORT_PROVENANCE.json +8 -0
  5. package/apps/client-cms-editor/README.md +72 -0
  6. package/apps/client-cms-editor/agentsam.app.json +57 -0
  7. package/apps/client-cms-editor/backend/README.md +5 -0
  8. package/apps/client-cms-editor/backend/package.json +22 -0
  9. package/apps/client-cms-editor/backend/src/api/client.ts +200 -0
  10. package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +145 -0
  11. package/apps/client-cms-editor/backend/src/index.ts +6 -0
  12. package/apps/client-cms-editor/backend/src/model.ts +115 -0
  13. package/apps/client-cms-editor/backend/src/preview/bridge.ts +79 -0
  14. package/apps/client-cms-editor/backend/src/preview/urls.ts +55 -0
  15. package/apps/client-cms-editor/backend/src/routing/cms-route.js +293 -0
  16. package/apps/client-cms-editor/backend/src/routing/index.js +10 -0
  17. package/apps/client-cms-editor/backend/src/storefront.ts +52 -0
  18. package/apps/client-cms-editor/backend/tsconfig.json +15 -0
  19. package/apps/client-cms-editor/backend/worker/index.js +34 -0
  20. package/apps/client-cms-editor/backend/wrangler.jsonc +11 -0
  21. package/apps/client-cms-editor/bin/agentsam-cms.mjs +229 -0
  22. package/apps/client-cms-editor/frontend/index.html +12 -0
  23. package/apps/client-cms-editor/frontend/package.json +33 -0
  24. package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +188 -0
  25. package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +191 -0
  26. package/apps/client-cms-editor/frontend/src/CmsEditor.tsx +1345 -0
  27. package/apps/client-cms-editor/frontend/src/env.d.ts +14 -0
  28. package/apps/client-cms-editor/frontend/src/index.ts +8 -0
  29. package/apps/client-cms-editor/frontend/src/lib/content-safety.ts +63 -0
  30. package/apps/client-cms-editor/frontend/src/main.tsx +17 -0
  31. package/apps/client-cms-editor/frontend/src/mount.tsx +51 -0
  32. package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +214 -0
  33. package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +1739 -0
  34. package/apps/client-cms-editor/frontend/src/styles/studio.css +106 -0
  35. package/apps/client-cms-editor/frontend/src/surfaces/CmsDashboard.tsx +313 -0
  36. package/apps/client-cms-editor/frontend/src/surfaces/CmsGuidedChatHero.tsx +121 -0
  37. package/apps/client-cms-editor/frontend/src/surfaces/CmsHubPage.tsx +132 -0
  38. package/apps/client-cms-editor/frontend/src/surfaces/CmsSiteSwitcher.tsx +176 -0
  39. package/apps/client-cms-editor/frontend/src/surfaces/index.ts +5 -0
  40. package/apps/client-cms-editor/frontend/src/surfaces/types.ts +48 -0
  41. package/apps/client-cms-editor/frontend/tsconfig.json +25 -0
  42. package/apps/client-cms-editor/frontend/vite.config.ts +13 -0
  43. package/apps/client-cms-editor/package-lock.json +3832 -0
  44. package/apps/client-cms-editor/package.json +23 -0
  45. package/apps/client-cms-editor/reference/DONOR_README.md +52 -0
  46. package/apps/client-cms-editor/reference/DONOR_package-lock.json +1253 -0
  47. package/apps/client-cms-editor/reference/DONOR_tsconfig.json +15 -0
  48. package/apps/client-cms-editor/shared/cms/package.json +21 -0
  49. package/apps/client-cms-editor/shared/cms/src/agent-context.ts +35 -0
  50. package/apps/client-cms-editor/shared/cms/src/cloudflare-bindings.ts +51 -0
  51. package/apps/client-cms-editor/shared/cms/src/editor-types.ts +58 -0
  52. package/apps/client-cms-editor/shared/cms/src/index.ts +5 -0
  53. package/apps/client-cms-editor/shared/cms/src/publication.ts +24 -0
  54. package/apps/client-cms-editor/shared/cms/src/routes.ts +11 -0
  55. package/apps/client-cms-editor/shared/cms/tsconfig.json +12 -0
  56. package/bin/agentsam-rapid-rust +36 -0
  57. package/docs/AUTH_IDENTITY_CONTRACT.md +2 -0
  58. package/docs/MACHINE_INTELLIGENCE.md +120 -0
  59. package/docs/WINDOWS_RUNTIME_INSTALL.md +30 -0
  60. package/docs/architecture/AGENTSAM_APPLICATION_CONTRACT.md +188 -0
  61. package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +151 -0
  62. package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +12 -4
  63. package/docs/architecture/R2_SLUG_NAMESPACE.md +20 -0
  64. package/docs/architecture/WORK_PRODUCT_EXTRACTION.md +97 -0
  65. package/docs/brand/AGENT_SAM_ICON_GRAMMAR.md +70 -0
  66. package/docs/content-studio/HOST_MOUNT_CHECKLIST.md +71 -0
  67. package/docs/content-studio/REVISION_GATE_2026-09-27.md +501 -0
  68. package/docs/contracts/capability-install-planner.md +67 -0
  69. package/docs/contracts/environment-vocabulary.md +195 -0
  70. package/docs/contracts/google-cloud-oauth-setup.md +199 -0
  71. package/docs/local-studio/WORKMODE_DONOR_NOTES.md +1 -1
  72. package/docs/plans/LOCAL-STUDIO-GRADUATION-2026-09-26.md +290 -0
  73. package/docs/plans/LOCAL-STUDIO-REAL-APP-CONTRACT-ADDENDUM-2026-09-27.md +394 -0
  74. package/docs/plans/MACHINE-FIRST-APPS-TUI-2026-09-24.md +227 -0
  75. package/docs/plans/TOMORROW-EXTENSIVE-TODO-2026-09-26.md +221 -0
  76. package/docs/plans/TOMORROW-MACHINE-FIRST-BRIEFING.md +145 -0
  77. package/docs/plans/UNIVERSAL-INGEST-MACHINE-FIRST-2026-09-24.md +215 -0
  78. package/docs/plugins.md +2 -2
  79. package/docs/product/AGENTSAM_PRODUCT_EXPERIENCE_STANDARD.md +280 -0
  80. package/docs/product/LOCAL_STUDIO_DESKTOP_EXPERIENCE.md +139 -0
  81. package/docs/product/README.md +8 -0
  82. package/docs/rapid-rust-wasm.md +35 -0
  83. package/migrations/d1/0016_cloudflare_oauth_user_oauth_tokens_ssot.sql +14 -0
  84. package/package.json +73 -14
  85. package/packages/agentsam-brand/README.md +106 -11
  86. package/packages/agentsam-brand/bin/agentsam-brand.mjs +9 -0
  87. package/packages/agentsam-brand/package.json +39 -7
  88. package/packages/agentsam-brand/src/adapters/cloudflare-images.js +453 -0
  89. package/packages/agentsam-brand/src/adapters/cloudflare-r2.js +74 -0
  90. package/packages/agentsam-brand/src/adapters/d1-registry.js +26 -0
  91. package/packages/agentsam-brand/src/adapters/filesystem.js +59 -0
  92. package/packages/agentsam-brand/src/adapters/memory.js +31 -0
  93. package/packages/agentsam-brand/src/cli/controller.js +503 -0
  94. package/packages/agentsam-brand/src/cli/wizard.js +526 -0
  95. package/packages/agentsam-brand/src/core/capabilities.js +201 -0
  96. package/packages/agentsam-brand/src/core/derivatives.js +211 -0
  97. package/packages/agentsam-brand/src/core/errors.js +12 -0
  98. package/packages/agentsam-brand/src/core/ingest.js +195 -0
  99. package/packages/agentsam-brand/src/core/inspect.js +109 -0
  100. package/packages/agentsam-brand/src/core/keys.js +83 -0
  101. package/packages/agentsam-brand/src/core/naming.js +142 -0
  102. package/packages/agentsam-brand/src/core/optimize.js +316 -0
  103. package/packages/agentsam-brand/src/core/pack.js +305 -0
  104. package/packages/agentsam-brand/src/core/plan.js +276 -0
  105. package/packages/agentsam-brand/src/core/preview.js +159 -0
  106. package/packages/agentsam-brand/src/core/processors/cloudflare.js +68 -0
  107. package/packages/agentsam-brand/src/core/processors/contract.js +203 -0
  108. package/packages/agentsam-brand/src/core/processors/index.js +148 -0
  109. package/packages/agentsam-brand/src/core/processors/native.js +82 -0
  110. package/packages/agentsam-brand/src/core/processors/sharp.js +113 -0
  111. package/packages/agentsam-brand/src/core/processors/squoosh-binary.js +195 -0
  112. package/packages/agentsam-brand/src/core/promote.js +288 -0
  113. package/packages/agentsam-brand/src/core/receipts.js +155 -0
  114. package/packages/agentsam-brand/src/core/v2/compile.js +280 -0
  115. package/packages/agentsam-brand/src/core/v2/derivatives-semantic.js +207 -0
  116. package/packages/agentsam-brand/src/core/v2/ingest-graph.js +387 -0
  117. package/packages/agentsam-brand/src/core/v2/roles.js +180 -0
  118. package/packages/agentsam-brand/src/core/v2/schema.js +209 -0
  119. package/packages/agentsam-brand/src/core/v2/templates.js +114 -0
  120. package/packages/agentsam-brand/src/index.js +193 -36
  121. package/packages/agentsam-brand/src/presets/app-icon.json +16 -0
  122. package/packages/agentsam-brand/src/presets/favicon.json +17 -0
  123. package/packages/agentsam-brand/src/presets/hero.json +12 -0
  124. package/packages/agentsam-brand/src/presets/index.js +40 -0
  125. package/packages/agentsam-brand/src/presets/logo.json +12 -0
  126. package/packages/agentsam-brand/src/presets/model.json +8 -0
  127. package/packages/agentsam-brand/src/presets/og-image.json +10 -0
  128. package/packages/agentsam-brand/src/presets/product-shot.json +11 -0
  129. package/packages/agentsam-brand/src/presets/social-image.json +10 -0
  130. package/packages/agentsam-brand/src/presets/web-image.json +10 -0
  131. package/packages/agentsam-contracts/src/artifacts.ts +41 -0
  132. package/packages/agentsam-knowledge/src/autorag/index.js +1 -1
  133. package/packages/agentsam-knowledge/src/providers/index.js +6 -4
  134. package/packages/agentsam-knowledge/src/providers/ollama-endpoint.js +31 -0
  135. package/packages/agentsam-knowledge/test/autorag.test.mjs +22 -1
  136. package/packages/agentsam-nav/package.json +2 -2
  137. package/packages/agentsam-rapid-rust/Cargo.lock +344 -0
  138. package/packages/agentsam-rapid-rust/Cargo.toml +18 -0
  139. package/packages/agentsam-rapid-rust/README.md +5 -0
  140. package/packages/agentsam-rapid-rust/src/main.rs +718 -0
  141. package/packages/agentsam-rapid-rust/templates/api/Cargo.toml.tmpl +18 -0
  142. package/packages/agentsam-rapid-rust/templates/api/src/lib.rs.tmpl +17 -0
  143. package/packages/agentsam-rapid-rust/templates/common/README.md.tmpl +22 -0
  144. package/packages/agentsam-rapid-rust/templates/common/package.json.tmpl +12 -0
  145. package/packages/agentsam-rapid-rust/templates/common/wrangler.toml.tmpl +6 -0
  146. package/packages/agentsam-rapid-rust/templates/crypto-auth/Cargo.toml.tmpl +21 -0
  147. package/packages/agentsam-rapid-rust/templates/crypto-auth/src/lib.rs.tmpl +32 -0
  148. package/packages/agentsam-rapid-rust/templates/data-processor/Cargo.toml.tmpl +18 -0
  149. package/packages/agentsam-rapid-rust/templates/data-processor/src/lib.rs.tmpl +41 -0
  150. package/packages/agentsam-rapid-rust/templates/shared-core/Cargo.toml.tmpl +9 -0
  151. package/packages/agentsam-rapid-rust/templates/shared-core/core/Cargo.toml.tmpl +7 -0
  152. package/packages/agentsam-rapid-rust/templates/shared-core/core/src/lib.rs.tmpl +23 -0
  153. package/packages/agentsam-rapid-rust/templates/shared-core/worker/Cargo.toml.tmpl +12 -0
  154. package/packages/agentsam-rapid-rust/templates/shared-core/worker/src/lib.rs.tmpl +16 -0
  155. package/packages/agentsam-rapid-rust/templates/shared-core/wrangler.toml.tmpl +6 -0
  156. package/packages/agentsam-repository/src/index.js +1 -0
  157. package/packages/agentsam-repository/src/merkle/semantic.js +3 -10
  158. package/packages/agentsam-repository/src/source-types.js +85 -0
  159. package/packages/agentsam-vault/README.md +96 -0
  160. package/packages/agentsam-vault/package.json +32 -0
  161. package/packages/agentsam-vault/src/contracts/credential.d.ts +78 -0
  162. package/packages/agentsam-vault/src/contracts/credential.js +132 -0
  163. package/packages/agentsam-vault/src/crypto/aes-gcm.d.ts +30 -0
  164. package/packages/agentsam-vault/src/crypto/aes-gcm.js +107 -0
  165. package/packages/agentsam-vault/src/crypto/oauth-envelope.js +150 -0
  166. package/packages/agentsam-vault/src/index.d.ts +9 -0
  167. package/packages/agentsam-vault/src/index.js +27 -0
  168. package/packages/agentsam-vault/src/providers/registry.js +90 -0
  169. package/packages/agentsam-vault/src/runtime/resolver.js +77 -0
  170. package/packages/agentsam-workbench/README.md +24 -0
  171. package/packages/agentsam-workbench/package.json +38 -0
  172. package/packages/agentsam-workbench/src/agent/AgentPanel.tsx +41 -0
  173. package/packages/agentsam-workbench/src/agent/ArtifactCard.tsx +9 -0
  174. package/packages/agentsam-workbench/src/agent/Composer.tsx +102 -0
  175. package/packages/agentsam-workbench/src/agent/ConnectedAgentPanel.tsx +194 -0
  176. package/packages/agentsam-workbench/src/agent/ContextualComposer.tsx +788 -0
  177. package/packages/agentsam-workbench/src/agent/GoalStatusStrip.tsx +38 -0
  178. package/packages/agentsam-workbench/src/agent/MiniAgentSam.tsx +86 -0
  179. package/packages/agentsam-workbench/src/agent/ModelSelect.tsx +19 -0
  180. package/packages/agentsam-workbench/src/agent/Thread.tsx +56 -0
  181. package/packages/agentsam-workbench/src/agent/ToolReceipt.tsx +11 -0
  182. package/packages/agentsam-workbench/src/agent/contextual-composer.css +611 -0
  183. package/packages/agentsam-workbench/src/agent/index.ts +10 -0
  184. package/packages/agentsam-workbench/src/agent/mini-agentsam.css +12 -0
  185. package/packages/agentsam-workbench/src/browser/BrowserFrame.tsx +37 -0
  186. package/packages/agentsam-workbench/src/browser/index.ts +1 -0
  187. package/packages/agentsam-workbench/src/index.ts +6 -0
  188. package/packages/agentsam-workbench/src/project-control.css +160 -0
  189. package/packages/agentsam-workbench/src/projects/ProjectsSurface.tsx +131 -0
  190. package/packages/agentsam-workbench/src/projects/index.ts +1 -0
  191. package/packages/agentsam-workbench/src/shell/Breadcrumb.tsx +35 -0
  192. package/packages/agentsam-workbench/src/shell/ResizableStage.tsx +99 -0
  193. package/packages/agentsam-workbench/src/shell/SidePanel.tsx +5 -0
  194. package/packages/agentsam-workbench/src/shell/index.ts +4 -0
  195. package/packages/agentsam-workbench/src/shell/surfaceRegistry.ts +29 -0
  196. package/packages/agentsam-workbench/src/terminal/TerminalSurface.tsx +5 -0
  197. package/packages/agentsam-workbench/src/terminal/index.ts +1 -0
  198. package/packages/agentsam-workbench/src/timeline/GanttBoard.tsx +258 -0
  199. package/packages/agentsam-workbench/src/timeline/index.ts +1 -0
  200. package/packages/agentsam-workbench/tsconfig.json +13 -0
  201. package/packages/connectors/cloudflare/OAUTH_STORAGE_AUDIT.md +53 -0
  202. package/packages/connectors/cloudflare/package.json +3 -1
  203. package/packages/connectors/cloudflare/src/api-client.js +279 -0
  204. package/packages/connectors/cloudflare/src/capabilities.js +585 -0
  205. package/packages/connectors/cloudflare/src/credential.js +318 -0
  206. package/packages/connectors/cloudflare/src/families/brand-protection.js +123 -0
  207. package/packages/connectors/cloudflare/src/families/keyless.js +114 -0
  208. package/packages/connectors/cloudflare/src/families/pages.js +151 -0
  209. package/packages/connectors/cloudflare/src/families/scanner.js +123 -0
  210. package/packages/connectors/cloudflare/src/families/snippets.js +207 -0
  211. package/packages/connectors/cloudflare/src/families/tag-gateway.js +46 -0
  212. package/packages/connectors/cloudflare/src/families/tags.js +124 -0
  213. package/packages/connectors/cloudflare/src/families/token-validation.js +95 -0
  214. package/packages/connectors/cloudflare/src/families/workflows.js +156 -0
  215. package/packages/connectors/cloudflare/src/index.js +102 -265
  216. package/packages/connectors/cloudflare/src/oauth-persist.js +336 -0
  217. package/packages/connectors/cloudflare/src/owner.js +42 -12
  218. package/packages/connectors/cloudflare/src/permission-options.js +1064 -0
  219. package/packages/connectors/cloudflare/src/routes.js +309 -144
  220. package/packages/connectors/cloudflare/src/scope-catalog.js +345 -0
  221. package/packages/connectors/cloudflare/src/vault.js +2 -2
  222. package/packages/connectors/cloudflare/tests/capabilities.test.mjs +276 -0
  223. package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -30
  224. package/packages/connectors/cloudflare/tests/oauth-persist.test.mjs +125 -0
  225. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +22 -2
  226. package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +10 -4
  227. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +20 -0
  228. package/packages/identity/README.md +1 -1
  229. package/packages/identity/backend/README.md +1 -1
  230. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +3 -2
  231. package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +48 -0
  232. package/packages/identity/package.json +4 -3
  233. package/packages/identity/scripts/local-identity-bridge.mjs +184 -0
  234. package/packages/identity/scripts/preview-auth-portal.mjs +7 -7
  235. package/packages/identity/src/adapters/cloudflare-d1/index.js +31 -2
  236. package/packages/identity/src/adapters/sqlite/index.js +4 -1
  237. package/packages/identity/src/contracts/auth-config.js +28 -8
  238. package/packages/identity/src/contracts/external-identity.js +1 -1
  239. package/packages/identity/src/contracts/provider.js +1 -0
  240. package/packages/identity/src/frontend/auth-portal/README.md +2 -1
  241. package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -5
  242. package/packages/identity/src/frontend/auth-portal/pages/reset.html +6 -5
  243. package/packages/identity/src/frontend/auth-portal/pages/signup.html +6 -5
  244. package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
  245. package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +207 -0
  246. package/packages/identity/src/index.js +16 -0
  247. package/packages/identity/src/migrations/0001_identity_core.sql +19 -2
  248. package/packages/identity/src/migrations/0003_accounts_ssot.sql +20 -0
  249. package/packages/identity/src/migrations/README.md +5 -2
  250. package/packages/identity/src/oauth/README.md +10 -5
  251. package/packages/identity/src/oauth/consent-view.js +388 -0
  252. package/packages/identity/src/oauth/credentials.js +31 -11
  253. package/packages/identity/src/oauth/google-desktop-exchange.js +173 -0
  254. package/packages/identity/src/providers/cloudflare/oauth.js +3 -4
  255. package/packages/identity/src/providers/cloudflare/profile.js +49 -14
  256. package/packages/identity/src/providers/iam/index.js +27 -5
  257. package/packages/identity/src/server/identity-service.js +31 -2
  258. package/packages/identity/src/server/worker-router.js +111 -81
  259. package/packages/identity/tests/auth-config.test.mjs +3 -2
  260. package/packages/identity/tests/identity-init-scaffold.test.mjs +3 -0
  261. package/packages/identity/tests/identity-service.test.mjs +16 -0
  262. package/packages/identity/tests/native-session-worker.test.mjs +64 -0
  263. package/packages/identity/tests/oauth-consent-view.test.mjs +106 -0
  264. package/packages/identity/tests/oauth-credentials.test.mjs +17 -4
  265. package/packages/identity/tests/oauth-presets-accounts-ssot.test.mjs +58 -0
  266. package/packages/identity/tests/preview-auth-portal.test.mjs +3 -3
  267. package/packages/runtime-protocol/src/index.js +154 -0
  268. package/packages/theme-scenes/README.md +164 -0
  269. package/packages/theme-scenes/package.json +53 -0
  270. package/packages/theme-scenes/src/blocks/index.js +30 -0
  271. package/packages/theme-scenes/src/contracts/index.js +286 -0
  272. package/packages/theme-scenes/src/demos/index.js +65 -0
  273. package/packages/theme-scenes/src/index.js +75 -0
  274. package/packages/theme-scenes/src/presets/index.js +263 -0
  275. package/packages/theme-scenes/src/runtime/compile.js +119 -0
  276. package/packages/theme-scenes/src/scenes/index.js +146 -0
  277. package/packages/theme-scenes/src/shell/index.js +47 -0
  278. package/packages/theme-scenes/src/themes/index.js +129 -0
  279. package/packages/theme-scenes/src/visuals/index.js +41 -0
  280. package/packages/theme-scenes/test/theme-scenes.test.mjs +179 -0
  281. package/packages/work-graph/src/index.js +34 -1
  282. package/packages/work-graph/src/renderers/gantt-model.js +24 -0
  283. package/packages/work-graph/test/work-graph.test.mjs +27 -7
  284. package/protocol/README.md +15 -7
  285. package/protocol/apps/agentsam.app.v1.schema.json +89 -7
  286. package/protocol/apps/agentsam.host-install.v1.schema.json +36 -0
  287. package/protocol/assets/README.md +9 -0
  288. package/protocol/assets/asset-record.v1.schema.json +64 -0
  289. package/protocol/runtime/agentsam.runtime.v1.schema.json +75 -0
  290. package/protocol/scoring/README.md +10 -0
  291. package/protocol/scoring/scorecard.v1.schema.json +65 -0
  292. package/protocol/ui/icon-registry.mjs +1 -0
  293. package/registry/cms-rust-machine-20260928/installable-products.json +31 -0
  294. package/registry/cms-rust-machine-20260928/manifest.json +22 -0
  295. package/registry/cms-rust-machine-20260928/migration.sql +237 -0
  296. package/registry/cms-rust-machine-20260928/receipt.json +21 -0
  297. package/registry/cms-rust-machine-20260928/receipt.sql +27 -0
  298. package/scripts/build_rapid_rust_cli.py +83 -0
  299. package/src/attachments/index.js +6 -0
  300. package/src/attachments/intake.js +235 -0
  301. package/src/capabilities/repository-snapshot.js +19 -2
  302. package/src/cli/command-catalog.js +8 -0
  303. package/src/cli.js +182 -26
  304. package/src/commands/account-auth.js +94 -8
  305. package/src/commands/app.js +307 -3
  306. package/src/commands/billing.js +127 -0
  307. package/src/commands/brand.js +74 -60
  308. package/src/commands/cheat-sheet.js +154 -0
  309. package/src/commands/cloudflare-login.js +222 -0
  310. package/src/commands/cloudflare.js +453 -23
  311. package/src/commands/codebaseindex.js +6 -1
  312. package/src/commands/compute.js +4 -0
  313. package/src/commands/connections.js +11 -5
  314. package/src/commands/credentials.js +116 -0
  315. package/src/commands/gcloud-auth.js +427 -0
  316. package/src/commands/google-cloud.js +375 -0
  317. package/src/commands/identity-init.js +2 -2
  318. package/src/commands/mcp.js +226 -33
  319. package/src/commands/ollama.js +3 -2
  320. package/src/commands/plugins.js +7 -2
  321. package/src/commands/product.js +36 -0
  322. package/src/commands/runtime-status.js +28 -9
  323. package/src/commands/runtime.js +313 -0
  324. package/src/commands/rust.js +24 -0
  325. package/src/commands/setup.js +279 -0
  326. package/src/commands/shell.js +16 -3
  327. package/src/commands/start-local.js +2 -2
  328. package/src/commands/terminal.js +171 -0
  329. package/src/commands/update.js +140 -0
  330. package/src/commands/whoami.js +329 -65
  331. package/src/features/resolve.js +1 -0
  332. package/src/index.js +4 -0
  333. package/src/indexing/ingest/inventory.js +254 -19
  334. package/src/indexing/ingest/materials.js +3 -9
  335. package/src/indexing/ingest/style-evidence.js +181 -0
  336. package/src/knowledge/source.js +3 -3
  337. package/src/lib/account-session.js +11 -4
  338. package/src/lib/app-authority.js +172 -0
  339. package/src/lib/auth.js +94 -21
  340. package/src/lib/core-client.js +5 -6
  341. package/src/lib/credentials-audit.js +451 -0
  342. package/src/lib/deploy/health.js +24 -6
  343. package/src/lib/detect-context.js +7 -1
  344. package/src/lib/google-cloud-connection.js +270 -0
  345. package/src/lib/google-cloud-inventory.js +385 -0
  346. package/src/lib/google-desktop-oauth.js +372 -0
  347. package/src/lib/google-studio-brokered-oauth.js +158 -0
  348. package/src/lib/identity-scaffold.js +24 -6
  349. package/src/lib/init-options.js +192 -0
  350. package/src/lib/local-scaffold.js +48 -16
  351. package/src/lib/provider-command-remediation.js +308 -0
  352. package/src/lib/provider-credentials.js +8 -0
  353. package/src/lib/scaffold/index.js +8 -2
  354. package/src/lib/scaffold/mcp-server.js +43 -0
  355. package/src/lib/scaffold/templates/mcp-server/index.js +397 -0
  356. package/src/lib/scaffold/templates/mcp-server/presets.js +39 -0
  357. package/src/lib/scaffold/wizards/cms.js +93 -0
  358. package/src/lib/scaffold/wizards/mcp-server.js +61 -0
  359. package/src/lib/setup/contract.js +26 -0
  360. package/src/lib/setup/discover.js +116 -0
  361. package/src/lib/setup/execute.js +205 -0
  362. package/src/lib/setup/planner.js +134 -0
  363. package/src/lib/setup/recipes.js +235 -0
  364. package/src/lib/setup/runtime-persist.js +197 -0
  365. package/src/lib/setup/runtime.js +394 -0
  366. package/src/lib/slash-commands.js +2 -0
  367. package/src/lib/terminal/machine-identity.js +95 -0
  368. package/src/lib/terminal-local.js +164 -0
  369. package/src/lib/terminal-scopes.js +52 -0
  370. package/src/lib/whoami-capabilities.js +184 -0
  371. package/src/local-pty/server.js +34 -5
  372. package/src/mcp/authority.js +58 -108
  373. package/src/mcp/client-adapters.js +68 -144
  374. package/src/mcp/client.js +142 -122
  375. package/src/mcp/cloudflare-bundles.js +225 -0
  376. package/src/mcp/index.js +12 -0
  377. package/src/plugins/cloudflare.js +1 -1
  378. package/src/providers/ollama-chat.js +2 -1
  379. package/src/sam/types.js +27 -2
  380. package/src/security/inventory.js +12 -2
  381. package/src/security/local-vault.js +2 -0
  382. package/src/security/scan.js +1 -1
  383. package/test/account-session.test.mjs +3 -3
  384. package/test/app-authority-context.test.mjs +32 -0
  385. package/test/cli/codebaseindex-plan-ux.test.mjs +1 -1
  386. package/test/cli/mcp-client.test.mjs +21 -10
  387. package/test/cli/runtime-persist.test.mjs +56 -0
  388. package/test/cli/rust-command.test.mjs +21 -0
  389. package/test/credentials-audit.test.mjs +113 -0
  390. package/test/deploy-health-scan.test.mjs +29 -1
  391. package/test/gcloud-auth.test.mjs +111 -0
  392. package/test/google-cloud-connection.test.mjs +83 -0
  393. package/test/google-cloud-inventory.test.mjs +64 -0
  394. package/test/init-options.test.mjs +35 -0
  395. package/test/integration/app-command.test.mjs +30 -0
  396. package/test/integration/cad-project.test.mjs +4 -1
  397. package/test/integration/cms-app-scaffold.test.mjs +71 -0
  398. package/test/integration/cms-site-tenancy-contract.test.mjs +3 -2
  399. package/test/integration/codebaseindex-ingest.test.mjs +48 -0
  400. package/test/integration/features-resolve.test.mjs +30 -11
  401. package/test/integration/inspect-path-cli.test.mjs +46 -0
  402. package/test/integration/inventory-evidence.test.mjs +125 -0
  403. package/test/integration/runtime-status.integration.test.mjs +2 -1
  404. package/test/knowledge.test.mjs +35 -1
  405. package/test/loopback-oauth.test.mjs +53 -0
  406. package/test/ollama.test.mjs +22 -0
  407. package/test/security.test.mjs +23 -0
  408. package/test/setup-planner.test.mjs +74 -0
  409. package/test/setup-runtime.test.mjs +51 -0
  410. package/test/terminal/local-pty.mock.test.mjs +3 -1
  411. package/test/terminal-machine-identity.test.mjs +88 -0
  412. package/test/whoami-resume.test.mjs +27 -6
  413. package/packages/agentsam-brand/test/brand-scan.test.mjs +0 -106
  414. package/packages/work-graph/prototype/README.md +0 -32
  415. package/packages/work-graph/prototype/data/demo-workgraph.js +0 -47
  416. package/packages/work-graph/prototype/renderers/timeline-model.js +0 -21
  417. package/packages/work-graph/prototype/renderers/workgraph-runtime.js +0 -38
  418. package/packages/work-graph/prototype-gnantt.html +0 -655
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Canonical Cloudflare connection writer → user_oauth_tokens (SSOT).
3
+ *
4
+ * One row per (user_id, provider=cloudflare, account_identifier).
5
+ * Scopes are merged as a union on every write so concurrent grants from
6
+ * different Cloudflare OAuth clients cannot silently downgrade each other.
7
+ * Provenance is the real OAuth client_id only — no invented app-name enum.
8
+ *
9
+ * Status / account / scopes (legacy agentsam_cloudflare_connections fields):
10
+ * - status → is_active + revoked_at (connected | revoked | superseded)
11
+ * - cloudflare_account_id → account_identifier + metadata_json.cloudflare_account_id
12
+ * - scopes → scopes / scope columns
13
+ */
14
+
15
+ import { oauthAad, sealOauthToken } from '../../../agentsam-vault/src/crypto/oauth-envelope.js';
16
+
17
+ const PROVIDER = 'cloudflare';
18
+
19
+ function clean(value) {
20
+ return value == null ? '' : String(value).trim();
21
+ }
22
+
23
+ function splitScopes(value) {
24
+ if (Array.isArray(value)) return value.map(clean).filter(Boolean);
25
+ return clean(value).split(/[\s,]+/).filter(Boolean);
26
+ }
27
+
28
+ /** @param {string[]} a @param {string[]} b */
29
+ export function unionScopes(a = [], b = []) {
30
+ return [...new Set([...splitScopes(a), ...splitScopes(b)])];
31
+ }
32
+
33
+ function parseMeta(raw) {
34
+ if (!raw) return {};
35
+ if (typeof raw === 'object') return { ...raw };
36
+ try {
37
+ const parsed = JSON.parse(String(raw));
38
+ return parsed && typeof parsed === 'object' ? parsed : {};
39
+ } catch {
40
+ return {};
41
+ }
42
+ }
43
+
44
+ function vaultMaterial(env) {
45
+ return clean(env?.VAULT_MASTER_KEY || env?.VAULT_KEY);
46
+ }
47
+
48
+ /**
49
+ * Resolve Cloudflare account id from token via /v4/accounts (best-effort).
50
+ * @param {string} accessToken
51
+ * @param {{ fetchImpl?: typeof fetch }} [opts]
52
+ */
53
+ export async function resolveCloudflareAccountId(accessToken, opts = {}) {
54
+ const token = clean(accessToken);
55
+ if (!token) return null;
56
+ const fetchImpl = opts.fetchImpl || fetch;
57
+ try {
58
+ const res = await fetchImpl('https://api.cloudflare.com/client/v4/accounts?per_page=50', {
59
+ headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
60
+ });
61
+ if (!res.ok) return null;
62
+ const body = await res.json().catch(() => null);
63
+ const accounts = (Array.isArray(body?.result) ? body.result : [])
64
+ .map((account) => ({
65
+ id: clean(account?.id),
66
+ name: clean(account?.name) || null,
67
+ }))
68
+ .filter((account) => /^[a-f0-9]{32}$/i.test(account.id));
69
+ if (accounts.length === 1) return accounts[0];
70
+ if (accounts.length > 1) {
71
+ return {
72
+ id: null,
73
+ name: null,
74
+ error: 'cloudflare_account_selection_required',
75
+ accounts,
76
+ };
77
+ }
78
+ return null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Mark other Cloudflare grants for this user inactive (single-connection model).
86
+ * Mirrors legacy agentsam_cloudflare_connections status='superseded'.
87
+ */
88
+
89
+ /**
90
+ * Upsert Cloudflare grant into user_oauth_tokens with scope union + client_id provenance.
91
+ * When VAULT_MASTER_KEY is set, stores connector-sealed ciphertext and clears plaintext
92
+ * (encrypted-only at rest for Cloudflare).
93
+ *
94
+ * @param {object} env
95
+ * @param {object} input
96
+ * @param {string} input.userId
97
+ * @param {string} input.accessToken
98
+ * @param {string} [input.refreshToken]
99
+ * @param {string|string[]} [input.scopes]
100
+ * @param {string} [input.accountId]
101
+ * @param {string} [input.accountDisplay]
102
+ * @param {number|null} [input.expiresAt]
103
+ * @param {string} [input.clientId] — real Cloudflare OAuth client_id used for this grant
104
+ * @param {string[]} [input.capabilitySet]
105
+ */
106
+ export async function upsertCloudflareUserOauthToken(env, input = {}) {
107
+ if (!env?.DB?.prepare) {
108
+ return { ok: false, error: 'db_unavailable' };
109
+ }
110
+ const userId = clean(input.userId);
111
+ const accessToken = clean(input.accessToken);
112
+ if (!userId || !accessToken) {
113
+ return { ok: false, error: 'user_id_and_access_token_required' };
114
+ }
115
+
116
+ let accountId = clean(input.accountId);
117
+ let accountDisplay = clean(input.accountDisplay) || null;
118
+ if (!accountId) {
119
+ const resolved = await resolveCloudflareAccountId(accessToken);
120
+ if (resolved?.id) {
121
+ accountId = resolved.id;
122
+ accountDisplay = accountDisplay || resolved.name || null;
123
+ } else if (resolved?.error) {
124
+ return {
125
+ ok: false,
126
+ error: resolved.error,
127
+ accounts: Array.isArray(resolved.accounts) ? resolved.accounts : [],
128
+ };
129
+ }
130
+ }
131
+ if (!/^[a-f0-9]{32}$/i.test(accountId)) {
132
+ return { ok: false, error: 'cloudflare_account_identifier_required' };
133
+ }
134
+
135
+ const now = Math.floor(Date.now() / 1000);
136
+ const incomingScopes = splitScopes(input.scopes);
137
+ let existing = null;
138
+ try {
139
+ existing = await env.DB.prepare(
140
+ `SELECT scopes, scope, metadata_json, access_token, refresh_token,
141
+ access_token_encrypted, refresh_token_encrypted, created_at
142
+ FROM user_oauth_tokens
143
+ WHERE user_id = ? AND LOWER(provider) = ? AND account_identifier = ?
144
+ LIMIT 1`,
145
+ ).bind(userId, PROVIDER, accountId).first();
146
+ } catch (err) {
147
+ return { ok: false, error: 'user_oauth_tokens_unavailable', detail: String(err?.message || err) };
148
+ }
149
+
150
+ const priorScopes = splitScopes(existing?.scopes || existing?.scope);
151
+ const mergedScopes = unionScopes(priorScopes, incomingScopes);
152
+ const scopesStr = mergedScopes.join(' ') || null;
153
+
154
+ const priorMeta = parseMeta(existing?.metadata_json);
155
+ const clientId = clean(input.clientId) || clean(priorMeta.connected_via_client_id) || null;
156
+ const capabilitySet = Array.isArray(input.capabilitySet)
157
+ ? input.capabilitySet
158
+ : (Array.isArray(priorMeta.granted_at_capability_set) ? priorMeta.granted_at_capability_set : []);
159
+
160
+ let metadataJson = null;
161
+ try {
162
+ const next = {
163
+ ...priorMeta,
164
+ cloudflare_account_id: accountId,
165
+ status: 'connected',
166
+ last_connected_at: now,
167
+ };
168
+ if (clientId) next.connected_via_client_id = clientId;
169
+ if (capabilitySet.length) next.granted_at_capability_set = capabilitySet;
170
+ delete next.connected_via_app;
171
+ metadataJson = JSON.stringify(next);
172
+ } catch {
173
+ metadataJson = JSON.stringify({
174
+ cloudflare_account_id: accountId,
175
+ status: 'connected',
176
+ ...(clientId ? { connected_via_client_id: clientId } : {}),
177
+ });
178
+ }
179
+
180
+ const expiresAt = input.expiresAt != null ? Number(input.expiresAt) : null;
181
+ const refreshToken = clean(input.refreshToken) || clean(existing?.refresh_token) || null;
182
+
183
+ const material = vaultMaterial(env);
184
+ if (!material) return { ok: false, error: 'vault_required' };
185
+
186
+ let accessEnc = null;
187
+ let refreshEnc = null;
188
+ try {
189
+ const aad = oauthAad(PROVIDER, userId, accountId);
190
+ accessEnc = await sealOauthToken(material, accessToken, aad);
191
+ refreshEnc = refreshToken ? await sealOauthToken(material, refreshToken, aad) : null;
192
+ } catch (err) {
193
+ return { ok: false, error: err?.code || 'vault_unavailable', detail: String(err?.message || err) };
194
+ }
195
+
196
+ // user_oauth_tokens never stores plaintext provider tokens.
197
+ const accessPlain = null;
198
+ const refreshPlain = null;
199
+
200
+ // Keep other Cloudflare account rows active. The composite PK already
201
+ // supports one row per (user, provider, account_identifier).
202
+
203
+ try {
204
+ await env.DB.prepare(
205
+ `INSERT INTO user_oauth_tokens (
206
+ user_id, tenant_id, person_uuid, provider, account_identifier,
207
+ access_token, refresh_token, access_token_encrypted, refresh_token_encrypted,
208
+ scope, scopes, expires_at,
209
+ account_display, metadata_json, is_active, created_at, updated_at,
210
+ revoked_at, refresh_failure_count
211
+ ) VALUES (?, '', '', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, NULL, 0)
212
+ ON CONFLICT(user_id, provider, account_identifier) DO UPDATE SET
213
+ access_token = excluded.access_token,
214
+ refresh_token = COALESCE(excluded.refresh_token, user_oauth_tokens.refresh_token),
215
+ access_token_encrypted = excluded.access_token_encrypted,
216
+ refresh_token_encrypted = COALESCE(excluded.refresh_token_encrypted, user_oauth_tokens.refresh_token_encrypted),
217
+ scope = excluded.scope,
218
+ scopes = excluded.scopes,
219
+ expires_at = excluded.expires_at,
220
+ account_display = COALESCE(excluded.account_display, user_oauth_tokens.account_display),
221
+ metadata_json = excluded.metadata_json,
222
+ is_active = 1,
223
+ revoked_at = NULL,
224
+ last_refresh_error_code = NULL,
225
+ refresh_failure_count = 0,
226
+ last_refresh_at = NULL,
227
+ updated_at = excluded.updated_at`,
228
+ ).bind(
229
+ userId,
230
+ PROVIDER,
231
+ accountId,
232
+ accessPlain,
233
+ refreshPlain,
234
+ accessEnc,
235
+ refreshEnc,
236
+ scopesStr,
237
+ scopesStr,
238
+ expiresAt,
239
+ accountDisplay,
240
+ metadataJson,
241
+ now,
242
+ now,
243
+ ).run();
244
+ } catch (err) {
245
+ try {
246
+ await env.DB.prepare(
247
+ `INSERT OR REPLACE INTO user_oauth_tokens (
248
+ user_id, tenant_id, person_uuid, provider, account_identifier,
249
+ access_token, refresh_token, access_token_encrypted, refresh_token_encrypted,
250
+ scope, scopes, expires_at,
251
+ account_display, metadata_json, is_active, created_at, updated_at
252
+ ) VALUES (?, '', '', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`,
253
+ ).bind(
254
+ userId,
255
+ PROVIDER,
256
+ accountId,
257
+ accessPlain,
258
+ refreshPlain,
259
+ accessEnc,
260
+ refreshEnc,
261
+ scopesStr,
262
+ scopesStr,
263
+ expiresAt,
264
+ accountDisplay,
265
+ metadataJson,
266
+ existing ? Number(existing.created_at) || now : now,
267
+ now,
268
+ ).run();
269
+ } catch (err2) {
270
+ return { ok: false, error: 'upsert_failed', detail: String(err2?.message || err2 || err?.message || err) };
271
+ }
272
+ }
273
+
274
+ return {
275
+ ok: true,
276
+ accountId,
277
+ scopes: mergedScopes,
278
+ scope_union: true,
279
+ connectionId: accountId,
280
+ status: 'connected',
281
+ provenance: {
282
+ connected_via_client_id: clientId,
283
+ granted_at_capability_set: capabilitySet,
284
+ },
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Revoke Cloudflare grant(s) in user_oauth_tokens for an owner.
290
+ * @returns {{ ok: boolean, row?: object, error?: string }}
291
+ */
292
+ export async function revokeCloudflareUserOauthTokens(env, ownerId) {
293
+ const userId = clean(ownerId);
294
+ if (!env?.DB?.prepare || !userId) return { ok: false, error: 'db_unavailable' };
295
+ const now = Math.floor(Date.now() / 1000);
296
+ let row = null;
297
+ try {
298
+ row = await env.DB.prepare(
299
+ `SELECT user_id, account_identifier, access_token, access_token_encrypted,
300
+ scopes, scope, expires_at, metadata_json
301
+ FROM user_oauth_tokens
302
+ WHERE user_id = ? AND LOWER(provider) = ?
303
+ AND COALESCE(is_active, 1) = 1
304
+ AND (revoked_at IS NULL OR revoked_at = 0)
305
+ ORDER BY updated_at DESC LIMIT 1`,
306
+ ).bind(userId, PROVIDER).first();
307
+ } catch (err) {
308
+ return { ok: false, error: 'user_oauth_tokens_unavailable', detail: String(err?.message || err) };
309
+ }
310
+ try {
311
+ await env.DB.prepare(
312
+ `UPDATE user_oauth_tokens
313
+ SET is_active = 0,
314
+ revoked_at = ?,
315
+ access_token = NULL,
316
+ refresh_token = NULL,
317
+ access_token_encrypted = NULL,
318
+ refresh_token_encrypted = NULL,
319
+ updated_at = ?,
320
+ metadata_json = json_set(
321
+ COALESCE(NULLIF(TRIM(metadata_json), ''), '{}'),
322
+ '$.status', 'revoked'
323
+ )
324
+ WHERE user_id = ? AND LOWER(provider) = ?
325
+ AND COALESCE(is_active, 1) = 1`,
326
+ ).bind(now, now, userId, PROVIDER).run();
327
+ } catch {
328
+ await env.DB.prepare(
329
+ `UPDATE user_oauth_tokens
330
+ SET is_active = 0, revoked_at = ?, updated_at = ?,
331
+ access_token = NULL, refresh_token = NULL
332
+ WHERE user_id = ? AND LOWER(provider) = ?`,
333
+ ).bind(now, now, userId, PROVIDER).run();
334
+ }
335
+ return { ok: true, row };
336
+ }
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * Cloudflare connection ownership is derived from the REAL AgentSam identity
3
3
  * session (packages/identity — the same `session` cookie set by
4
- * /api/auth/login and every OAuth login callback). This previously
5
- * hand-rolled its own cookie name (`agentsam_session`) and session table
6
- * names (`agentsam_sessions`/`sessions`/`identity_sessions`) that never
7
- * matched the actual identity system (cookie name `session`, sessions
8
- * resolved via identity.sessionFromRequest against the real adapter) — so
9
- * this connector could never actually authenticate anyone. Fixed to call
10
- * the real identity service instead of guessing at its storage shape.
4
+ * /api/auth/login and every OAuth login callback).
5
+ *
6
+ * Host MUST pass `app` + `routeRegistry` (APP contract). createIdentityService
7
+ * is fail-closed without them (AUTH_APP_UNRESOLVED).
11
8
  *
12
9
  * Browser-submitted account_id / user_id / owner_id / X-User-Id are never authority.
13
10
  */
@@ -37,7 +34,14 @@ export function rejectUntrustedOwnerHints(request, url, body = {}) {
37
34
  return { ignoredXUserId: Boolean(headerUser) };
38
35
  }
39
36
 
40
- export async function resolveAuthenticatedOwner(request, env, url, body) {
37
+ /**
38
+ * @param {Request} request
39
+ * @param {object} env
40
+ * @param {URL} url
41
+ * @param {object} body
42
+ * @param {{ app?: { id: string }, routeRegistry?: object }} [options]
43
+ */
44
+ export async function resolveAuthenticatedOwner(request, env, url, body, options = {}) {
41
45
  rejectUntrustedOwnerHints(request, url, body);
42
46
  // Test/portable adapters may expose an in-memory session map. Keep this
43
47
  // compatibility lane deliberately opt-in; production authority is D1.
@@ -52,16 +56,42 @@ export async function resolveAuthenticatedOwner(request, env, url, body) {
52
56
  err.code = 'unauthenticated';
53
57
  throw err;
54
58
  }
59
+
60
+ const app = options.app || null;
61
+ const routeRegistry = options.routeRegistry || null;
62
+ if (!app?.id || !routeRegistry) {
63
+ const err = new Error('AUTH_APP_UNRESOLVED');
64
+ err.code = 'AUTH_APP_UNRESOLVED';
65
+ err.message = 'Cloudflare connection owner resolution requires host app + routeRegistry';
66
+ throw err;
67
+ }
68
+
55
69
  const adapter = createCloudflareD1Adapter(env.DB);
56
- const identity = createIdentityService({ adapter, env });
70
+ const identity = createIdentityService({ adapter, env, app, routeRegistry });
57
71
  const ctx = await identity.sessionFromRequest(request);
58
72
  if (ctx?.user?.id) return String(ctx.user.id);
59
73
 
60
- // Native CLI OAuth returns a bearer access token rather than a browser
61
- // cookie. Resolve that token through IAM userinfo, then map the verified
62
- // IAM subject to the local auth user. No caller-supplied owner id is used.
74
+ // Native CLI OAuth / platform access token — resolve via IAM userinfo, then
75
+ // map verified IAM subject to the local auth user. No caller-supplied owner.
63
76
  const bearer = (request.headers.get('authorization') || '').replace(/^Bearer\s+/i, '').trim();
64
77
  if (bearer) {
78
+ // Reusable aak_* account API keys are verified by hash in D1 (same table as whoami).
79
+ if (bearer.startsWith('aak_')) {
80
+ try {
81
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(bearer));
82
+ const hash = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('');
83
+ const row = await env.DB.prepare(
84
+ `SELECT account_id FROM agentsam_api_credentials
85
+ WHERE credential_hash = ?
86
+ AND revoked_at_unix IS NULL
87
+ AND (expires_at_unix IS NULL OR expires_at_unix > ?)
88
+ LIMIT 1`,
89
+ ).bind(hash, Math.floor(Date.now() / 1000)).first();
90
+ if (row?.account_id) return String(row.account_id);
91
+ } catch {
92
+ /* fall through to IAM profile */
93
+ }
94
+ }
65
95
  const profile = await fetchIamProfile({
66
96
  issuer: resolveIamIssuer(env),
67
97
  accessToken: bearer,