@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
@@ -1,10 +1,9 @@
1
1
  const CLOUDFLARE_AUTH_URL = 'https://dash.cloudflare.com/oauth2/auth';
2
2
  const CLOUDFLARE_TOKEN_URL = 'https://dash.cloudflare.com/oauth2/token';
3
3
 
4
- // Identity-only login should not request the full resource-access scope set
5
- // used elsewhere for the Local Studio Cloudflare *connection* (deploy/d1/r2/etc).
6
- // account-settings.read is enough to resolve the account for userinfo.
7
- const CLOUDFLARE_LOGIN_SCOPE = 'account-settings.read offline_access';
4
+ // Identity-only login: account identity + user details (email/name via /v4/user).
5
+ // Do NOT request the resource-access scope set used by the Cloudflare *connection* flow.
6
+ const CLOUDFLARE_LOGIN_SCOPE = 'account-settings.read user-details.read offline_access';
8
7
 
9
8
  /** @param {import('../../provider-contract.js').OAuthAuthorizeInput} input */
10
9
  export function getCloudflareAuthUrl({ clientId, redirectUri, state, codeChallenge, scope } = {}) {
@@ -1,26 +1,61 @@
1
1
  const CLOUDFLARE_USERINFO_URL = 'https://dash.cloudflare.com/oauth2/userinfo';
2
+ const CLOUDFLARE_API_USER_URL = 'https://api.cloudflare.com/client/v4/user';
2
3
 
3
4
  /**
4
5
  * Fetch the authenticated Cloudflare user's identity.
5
- * Response shape not fully verified against a live token yet — this reads
6
- * defensively (sub/id/user_id, email, name) rather than assuming Cloudflare's
7
- * exact OIDC field names. Confirm the real shape on first live login and
8
- * tighten this if fields differ.
6
+ *
7
+ * OIDC userinfo (`/oauth2/userinfo`) structurally returns `sub` only.
8
+ * Email/name require `user-details.read` and a second call to `/client/v4/user`
9
+ * (`data.result.*`, not top-level fields).
10
+ *
9
11
  * @param {string} accessToken
12
+ * @param {{ fetchImpl?: typeof fetch }} [opts]
10
13
  */
11
- export async function fetchCloudflareProfile(accessToken) {
14
+ export async function fetchCloudflareProfile(accessToken, opts = {}) {
15
+ const token = String(accessToken || '').trim();
16
+ if (!token) return null;
17
+ const fetchImpl = opts.fetchImpl || fetch;
12
18
  try {
13
- const res = await fetch(CLOUDFLARE_USERINFO_URL, {
14
- headers: { Authorization: `Bearer ${accessToken}` },
19
+ const userinfoRes = await fetchImpl(CLOUDFLARE_USERINFO_URL, {
20
+ headers: { Authorization: `Bearer ${token}` },
15
21
  });
16
- if (!res.ok) return null;
17
- const data = await res.json();
18
- if (!data) return null;
22
+ if (!userinfoRes.ok) return null;
23
+ const userinfo = await userinfoRes.json().catch(() => null);
24
+ if (!userinfo) return null;
25
+
26
+ const sub = userinfo.sub || userinfo.id || userinfo.user_id || null;
27
+ let email = typeof userinfo.email === 'string' ? userinfo.email : null;
28
+ let name = typeof userinfo.name === 'string'
29
+ ? userinfo.name
30
+ : (typeof userinfo.display_name === 'string' ? userinfo.display_name : null);
31
+
32
+ // Cloudflare userinfo does not return email/name — enrich from /v4/user.
33
+ if (!email || !name) {
34
+ try {
35
+ const apiRes = await fetchImpl(CLOUDFLARE_API_USER_URL, {
36
+ headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
37
+ });
38
+ if (apiRes.ok) {
39
+ const body = await apiRes.json().catch(() => null);
40
+ const result = body?.result && typeof body.result === 'object' ? body.result : null;
41
+ if (result) {
42
+ if (!email && typeof result.email === 'string') email = result.email;
43
+ if (!name && typeof result.first_name === 'string' && typeof result.last_name === 'string') {
44
+ name = `${result.first_name} ${result.last_name}`.trim() || null;
45
+ }
46
+ if (!name && typeof result.name === 'string') name = result.name;
47
+ }
48
+ }
49
+ } catch {
50
+ // Non-fatal: identity can proceed with sub alone.
51
+ }
52
+ }
53
+
19
54
  return {
20
- sub: data.sub || data.id || data.user_id || data.account_id || null,
21
- email: data.email || null,
22
- name: data.name || data.display_name || null,
23
- raw: data,
55
+ sub: sub || null,
56
+ email,
57
+ name,
58
+ raw: userinfo,
24
59
  };
25
60
  } catch {
26
61
  return null;
@@ -9,13 +9,16 @@ import {
9
9
  } from './oauth.js';
10
10
  import { fetchIamProfile } from './profile.js';
11
11
  import { normalizeIamIdentity } from './mapper.js';
12
- import { DEFAULT_IAM_ORIGIN } from '../../contracts/auth-config.js';
12
+ import { resolveIamIssuer } from '../../contracts/auth-config.js';
13
13
 
14
14
  /**
15
- * @param {string} [origin]
15
+ * @param {string} origin Absolute IAM issuer origin (required — no DEFAULT_*).
16
16
  */
17
- export function createIamIdentityProvider(origin = DEFAULT_IAM_ORIGIN) {
18
- const resolvedOrigin = String(origin || DEFAULT_IAM_ORIGIN).replace(/\/+$/, '');
17
+ export function createIamIdentityProvider(origin) {
18
+ const resolvedOrigin = String(origin || '').replace(/\/+$/, '');
19
+ if (!resolvedOrigin) {
20
+ throw new Error('IAM_OAUTH_ISSUER is required to create the IAM identity provider');
21
+ }
19
22
  return createIdentityProvider({
20
23
  id: 'iam',
21
24
  authorizeUrl: (input) => getIamAuthUrl({ ...input, origin: resolvedOrigin }),
@@ -25,7 +28,26 @@ export function createIamIdentityProvider(origin = DEFAULT_IAM_ORIGIN) {
25
28
  });
26
29
  }
27
30
 
28
- export const IamProvider = createIamIdentityProvider();
31
+ /**
32
+ * Registry singleton — resolves IAM_OAUTH_ISSUER from env/input at call time.
33
+ * No baked-in product host.
34
+ */
35
+ export const IamProvider = createIdentityProvider({
36
+ id: 'iam',
37
+ authorizeUrl: (input) => {
38
+ const origin = resolveIamIssuer(input?.env || process.env, input?.origin || input?.issuer || '');
39
+ return getIamAuthUrl({ ...input, origin });
40
+ },
41
+ exchangeCode: (input) => {
42
+ const origin = resolveIamIssuer(input?.env || process.env, input?.origin || input?.issuer || '');
43
+ return exchangeIamCode({ ...input, origin });
44
+ },
45
+ getProfile: (accessToken, input = {}) => {
46
+ const origin = resolveIamIssuer(input?.env || process.env, input?.origin || input?.issuer || '');
47
+ return fetchIamProfile({ origin, accessToken });
48
+ },
49
+ normalizeIdentity: normalizeIamIdentity,
50
+ });
29
51
 
30
52
  export {
31
53
  getIamAuthUrl,
@@ -61,6 +61,11 @@ export function createIdentityService(config) {
61
61
  return match ? decodeURIComponent(match[1]) : null;
62
62
  }
63
63
 
64
+ function parseBearerSessionId(authorization) {
65
+ const match = String(authorization || '').match(/^Bearer\s+([^\s]+)$/i);
66
+ return match ? match[1] : null;
67
+ }
68
+
64
69
  return Object.freeze({
65
70
  cookieName,
66
71
  app,
@@ -108,7 +113,8 @@ export function createIdentityService(config) {
108
113
  },
109
114
 
110
115
  async sessionFromRequest(request) {
111
- const sessionId = parseSessionId(request.headers.get('Cookie'));
116
+ const sessionId = parseBearerSessionId(request.headers.get('Authorization'))
117
+ || parseSessionId(request.headers.get('Cookie'));
112
118
  if (!sessionId) return null;
113
119
  const session = await adapter.getSession(sessionId);
114
120
  if (!session) return null;
@@ -118,7 +124,8 @@ export function createIdentityService(config) {
118
124
  },
119
125
 
120
126
  async logout(request) {
121
- const sessionId = parseSessionId(request.headers.get('Cookie'));
127
+ const sessionId = parseBearerSessionId(request.headers.get('Authorization'))
128
+ || parseSessionId(request.headers.get('Cookie'));
122
129
  if (sessionId) await adapter.revokeSession(sessionId);
123
130
  return { ok: true };
124
131
  },
@@ -152,6 +159,28 @@ export function createIdentityService(config) {
152
159
  return { ok: true, authUserId: user.id, sessionId: session.id, session };
153
160
  },
154
161
 
162
+ isNativeSessionRequest(request) {
163
+ const marker = String(request?.headers?.get?.('X-AgentSam-Native-Client') || '').toLowerCase();
164
+ return marker === '1' || marker === 'true' || marker === 'native';
165
+ },
166
+
167
+ buildNativeLoginSuccessResponse(result, nextPath) {
168
+ const redirect = resolvePostLoginPath(nextPath);
169
+ return jsonResponse({
170
+ ok: true,
171
+ redirect,
172
+ session_id: result.sessionId,
173
+ expires_at: result.session?.expires_at ?? null,
174
+ user: result.user
175
+ ? {
176
+ id: result.user.id,
177
+ email: result.user.email,
178
+ displayName: result.user.display_name ?? null,
179
+ }
180
+ : null,
181
+ });
182
+ },
183
+
155
184
  buildLoginSuccessResponse(request, sessionId, nextPath) {
156
185
  const redirect = resolvePostLoginPath(nextPath);
157
186
  return jsonResponse(
@@ -12,6 +12,7 @@ import { fetchCloudflareProfile } from '../providers/cloudflare/profile.js';
12
12
  import { IDENTITY_ROUTE_IDS } from '../contracts/route-ids.js';
13
13
  import { IdentityRoutingError } from '../contracts/identity-store.js';
14
14
  import { resolveOAuthCredentialLane } from '../oauth/credentials.js';
15
+ import { handleGoogleDesktopExchangeRequest } from '../oauth/google-desktop-exchange.js';
15
16
  import { iamPlatformOAuthCallback, iamPlatformOAuthStart } from '../oauth/iam-platform.js';
16
17
  import { pkceChallenge, pkceVerifier, randomOAuthState } from '../oauth/pkce.js';
17
18
 
@@ -23,6 +24,10 @@ function escapeHtml(value) {
23
24
  .replace(/"/g, '"');
24
25
  }
25
26
 
27
+ function resolveLoginPath(identity) {
28
+ return identity.routeRegistry.resolve(identity.app.id, IDENTITY_ROUTE_IDS.LOGIN) || '/auth/login';
29
+ }
30
+
26
31
  function resolveKv(env) {
27
32
  return env?.SESSION_CACHE || env?.KV || null;
28
33
  }
@@ -115,6 +120,9 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
115
120
  await adapter.logAuthEvent({
116
121
  userId: result.user.id, eventType: 'login', status: 'ok', provider: 'email', request,
117
122
  });
123
+ if (identity.isNativeSessionRequest(request)) {
124
+ return identity.buildNativeLoginSuccessResponse(result, body.next);
125
+ }
118
126
  return identity.buildLoginSuccessResponse(request, result.sessionId, body.next);
119
127
  }
120
128
 
@@ -128,6 +136,9 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
128
136
  if (!result.ok) {
129
137
  return jsonResponse({ ok: false, error: result.error }, 400);
130
138
  }
139
+ if (identity.isNativeSessionRequest(request)) {
140
+ return identity.buildNativeLoginSuccessResponse(result, body.next);
141
+ }
131
142
  return identity.buildLoginSuccessResponse(request, result.sessionId, body.next);
132
143
  }
133
144
 
@@ -218,9 +229,13 @@ export async function handleIdentityWorkerRequest(request, env, options = {}) {
218
229
  }
219
230
 
220
231
  // ── OAuth ────────────────────────────────────────────────────────────────
221
- // Default: IAM_CLIENT_* (minted). Developer BYOK GOOGLE_*/GITHUB_* take the
222
- // matching /api/oauth/{provider}/start button when set.
232
+ // Default: IAM_CLIENT_* (minted). Developer BYOK GOOGLE_*/GITHUB_*/CLOUDFLARE_*
233
+ // take the matching /api/oauth/{provider}/start button when set.
234
+ // Desktop PKCE: GOOGLE_DESKTOP_CLIENT_ID → POST /api/oauth/google/desktop-exchange.
223
235
  // Canonical platform id = inneranimalmedia (legacy /api/oauth/iam/* still accepted).
236
+ if (path === '/api/oauth/google/desktop-exchange') {
237
+ return handleGoogleDesktopExchangeRequest(request, env);
238
+ }
224
239
  if (path === '/api/oauth/inneranimalmedia/callback' || path === '/api/oauth/iam/callback') {
225
240
  if (method === 'GET') {
226
241
  return iamPlatformOAuthCallback(request, env, adapter, identity);
@@ -367,91 +382,106 @@ async function oauthStart(request, env, identity, adapter, provider, creds) {
367
382
 
368
383
  async function oauthCallback(request, env, identity, adapter, provider, creds) {
369
384
  const url = new URL(request.url);
370
- const code = url.searchParams.get('code');
371
- const state = url.searchParams.get('state');
372
- const err = url.searchParams.get('error');
373
- if (err || !code || !state) {
374
- await adapter.logAuthEvent({ eventType: 'login', status: 'failed', provider, request, metadata: { reason: 'oauth_failed' } });
375
- return Response.redirect(`${url.origin}${loginPath()}?error=oauth_failed`, 302);
376
- }
377
- const saved = await adapter.consumeOAuthState(state);
378
- if (!saved || saved.provider !== provider) {
379
- await adapter.logAuthEvent({ eventType: 'login', status: 'failed', provider, request, metadata: { reason: 'state_mismatch' } });
380
- return Response.redirect(`${url.origin}${loginPath()}?error=state_mismatch`, 302);
381
- }
382
- const redirectUri = `${url.origin}/api/oauth/${provider}/callback`;
383
- let token;
384
- let profile;
385
- if (provider === 'google') {
386
- token = await exchangeGoogleCode({
387
- code,
388
- codeVerifier: saved.code_verifier,
389
- clientId: creds.clientId,
390
- clientSecret: creds.clientSecret,
391
- redirectUri,
392
- });
393
- if (!token?.access_token) {
394
- return Response.redirect(`${url.origin}${loginPath()}?error=token_exchange_failed`, 302);
385
+ const login = resolveLoginPath(identity);
386
+ try {
387
+ const code = url.searchParams.get('code');
388
+ const state = url.searchParams.get('state');
389
+ const err = url.searchParams.get('error');
390
+ if (err || !code || !state) {
391
+ await adapter.logAuthEvent({ eventType: 'login', status: 'failed', provider, request, metadata: { reason: 'oauth_failed' } });
392
+ return Response.redirect(`${url.origin}${login}?error=oauth_failed`, 302);
395
393
  }
396
- profile = await fetchGoogleProfile(token.access_token);
397
- } else if (provider === 'cloudflare') {
398
- token = await exchangeCloudflareCode({
399
- code,
400
- codeVerifier: saved.code_verifier,
401
- clientId: creds.clientId,
402
- clientSecret: creds.clientSecret,
403
- redirectUri,
404
- });
405
- if (!token?.access_token) {
406
- return Response.redirect(`${url.origin}${loginPath()}?error=token_exchange_failed`, 302);
394
+ const saved = await adapter.consumeOAuthState(state);
395
+ if (!saved || saved.provider !== provider) {
396
+ await adapter.logAuthEvent({ eventType: 'login', status: 'failed', provider, request, metadata: { reason: 'state_mismatch' } });
397
+ return Response.redirect(`${url.origin}${login}?error=state_mismatch`, 302);
407
398
  }
408
- profile = await fetchCloudflareProfile(token.access_token);
409
- } else {
410
- token = await exchangeGithubCode({
411
- code,
412
- codeVerifier: saved.code_verifier,
413
- clientId: creds.clientId,
414
- clientSecret: creds.clientSecret,
415
- redirectUri,
416
- });
417
- if (!token?.access_token) {
418
- return Response.redirect(`${url.origin}${loginPath()}?error=token_exchange_failed`, 302);
399
+ const redirectUri = `${url.origin}/api/oauth/${provider}/callback`;
400
+ let token;
401
+ let profile;
402
+ if (provider === 'google') {
403
+ token = await exchangeGoogleCode({
404
+ code,
405
+ codeVerifier: saved.code_verifier,
406
+ clientId: creds.clientId,
407
+ clientSecret: creds.clientSecret,
408
+ redirectUri,
409
+ });
410
+ if (!token?.access_token) {
411
+ return Response.redirect(`${url.origin}${login}?error=token_exchange_failed`, 302);
412
+ }
413
+ profile = await fetchGoogleProfile(token.access_token);
414
+ } else if (provider === 'cloudflare') {
415
+ token = await exchangeCloudflareCode({
416
+ code,
417
+ codeVerifier: saved.code_verifier,
418
+ clientId: creds.clientId,
419
+ clientSecret: creds.clientSecret,
420
+ redirectUri,
421
+ });
422
+ if (!token?.access_token) {
423
+ return Response.redirect(`${url.origin}${login}?error=token_exchange_failed`, 302);
424
+ }
425
+ profile = await fetchCloudflareProfile(token.access_token);
426
+ } else {
427
+ token = await exchangeGithubCode({
428
+ code,
429
+ codeVerifier: saved.code_verifier,
430
+ clientId: creds.clientId,
431
+ clientSecret: creds.clientSecret,
432
+ redirectUri,
433
+ });
434
+ if (!token?.access_token) {
435
+ return Response.redirect(`${url.origin}${login}?error=token_exchange_failed`, 302);
436
+ }
437
+ profile = await fetchGithubProfile(token.access_token);
438
+ }
439
+ if (!profile) {
440
+ return Response.redirect(`${url.origin}${login}?error=userinfo_failed`, 302);
419
441
  }
420
- profile = await fetchGithubProfile(token.access_token);
421
- }
422
- if (!profile) {
423
- return Response.redirect(`${url.origin}${loginPath()}?error=userinfo_failed`, 302);
424
- }
425
442
 
426
- let normalized;
427
- if (provider === 'google') {
428
- normalized = { subject: profile.sub, email: profile.email, name: profile.name };
429
- } else if (provider === 'cloudflare') {
430
- normalized = { subject: String(profile.sub), email: profile.email, name: profile.name || profile.email };
431
- } else {
432
- normalized = { subject: String(profile.id), email: profile.email, name: profile.name || profile.login };
433
- }
443
+ let normalized;
444
+ if (provider === 'google') {
445
+ normalized = { subject: profile.sub, email: profile.email, name: profile.name };
446
+ } else if (provider === 'cloudflare') {
447
+ const subject = profile.sub || profile.email;
448
+ if (!subject) {
449
+ return Response.redirect(`${url.origin}${login}?error=userinfo_incomplete`, 302);
450
+ }
451
+ normalized = { subject: String(subject), email: profile.email, name: profile.name || profile.email };
452
+ } else {
453
+ normalized = { subject: String(profile.id), email: profile.email, name: profile.name || profile.login };
454
+ }
434
455
 
435
- const result = await identity.provisionOAuthUser({
436
- provider,
437
- providerSubject: normalized.subject,
438
- email: normalized.email,
439
- displayName: normalized.name,
440
- });
441
- await adapter.logAuthEvent({
442
- userId: result.authUserId, eventType: 'login', status: 'ok', provider, request,
443
- });
456
+ const result = await identity.provisionOAuthUser({
457
+ provider,
458
+ providerSubject: normalized.subject,
459
+ email: normalized.email,
460
+ displayName: normalized.name,
461
+ });
462
+ await adapter.logAuthEvent({
463
+ userId: result.authUserId, eventType: 'login', status: 'ok', provider, request,
464
+ });
444
465
 
445
- const redirectTo = identity.resolvePostLoginPath(saved.redirect_to);
446
- const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
447
- const globeUrl = `${url.origin}${loginPath()}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
448
- return new Response(null, {
449
- status: 302,
450
- headers: {
451
- Location: globeUrl,
452
- 'Set-Cookie': res.headers.get('Set-Cookie') || '',
453
- },
454
- });
466
+ const redirectTo = identity.resolvePostLoginPath(saved.redirect_to);
467
+ const res = identity.buildLoginSuccessResponse(request, result.sessionId, redirectTo);
468
+ const globeUrl = `${url.origin}${login}?globe_exit=1&next=${encodeURIComponent(redirectTo)}`;
469
+ return new Response(null, {
470
+ status: 302,
471
+ headers: {
472
+ Location: globeUrl,
473
+ 'Set-Cookie': res.headers.get('Set-Cookie') || '',
474
+ },
475
+ });
476
+ } catch (error) {
477
+ const message = String(error?.message || error || 'oauth_callback_failed');
478
+ console.error('oauth_callback_failed', provider, message);
479
+ const loginUrl = new URL(login, url.origin);
480
+ loginUrl.searchParams.set('error', 'oauth_callback_failed');
481
+ loginUrl.searchParams.set('provider', provider);
482
+ loginUrl.searchParams.set('detail', message.slice(0, 120));
483
+ return Response.redirect(loginUrl.toString(), 302);
484
+ }
455
485
  }
456
486
 
457
487
  export { createIdentityService, createCloudflareD1Adapter };
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
2
2
  import { describe, it } from 'node:test';
3
3
  import {
4
4
  AGENTSAM_AUTH_CONTRACT,
5
+ PLATFORM_ACCOUNT_ISSUER,
5
6
  buildApiAuthorizationHeaders,
6
7
  resolveApiKey,
7
8
  resolveBridgeKey,
@@ -23,8 +24,8 @@ describe('auth configuration contract', () => {
23
24
  assert.equal(resolveIamOrigin({ IAM_ORIGIN: 'https://legacy.example.test/' }), 'https://legacy.example.test');
24
25
  });
25
26
 
26
- it('defaults issuer when neither IAM_OAUTH_ISSUER nor IAM_ORIGIN exists', () => {
27
- assert.equal(resolveIamOrigin({}), 'https://inneranimalmedia.com');
27
+ it('falls back to the canonical platform account issuer when env is unset', () => {
28
+ assert.equal(resolveIamOrigin({}), PLATFORM_ACCOUNT_ISSUER);
28
29
  });
29
30
 
30
31
  it('resolves only AGENTSAM_API_KEY for delegated account API auth', () => {
@@ -16,7 +16,10 @@ describe('identity init scaffold', () => {
16
16
  assert.ok(files['app/frontend/auth/signup.html'].includes('/api/auth/signup'));
17
17
  assert.ok(files['backend/src/index.js'].includes('handleIdentityWorkerRequest'));
18
18
  assert.ok(files['migrations/0001_identity_core.sql'].includes('CREATE TABLE IF NOT EXISTS company'));
19
+ assert.ok(files['migrations/0001_identity_core.sql'].includes('CREATE TABLE IF NOT EXISTS accounts'));
19
20
  assert.ok(files['migrations/0001_identity_core.sql'].includes('INSERT OR IGNORE INTO company'));
21
+ assert.ok(files['.env.example'].includes('GOOGLE_DESKTOP_CLIENT_ID'));
22
+ assert.ok(files['.env.example'].includes('CLOUDFLARE_OAUTH_CLIENT_ID'));
20
23
  assert.ok(files['app/frontend/shared/company-branding.js'].includes('/api/company'));
21
24
 
22
25
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'identity-init-'));
@@ -128,6 +128,22 @@ describe('identity service', () => {
128
128
  });
129
129
  assert.equal(login.ok, true);
130
130
 
131
+ const nativeRequest = new Request('https://example.test/api/auth/login', {
132
+ headers: { 'X-AgentSam-Native-Client': '1' },
133
+ });
134
+ assert.equal(identity.isNativeSessionRequest(nativeRequest), true);
135
+ const nativeResponse = identity.buildNativeLoginSuccessResponse(login, '/agentsam');
136
+ const nativeBody = await nativeResponse.json();
137
+ assert.equal(nativeBody.ok, true);
138
+ assert.equal(nativeBody.session_id, login.sessionId);
139
+ assert.equal(nativeBody.user.email, 'user@example.com');
140
+ assert.equal(nativeResponse.headers.get('set-cookie'), null);
141
+
142
+ const bearerCtx = await identity.sessionFromRequest(new Request('https://example.test/api/auth/me', {
143
+ headers: { Authorization: `Bearer ${login.sessionId}` },
144
+ }));
145
+ assert.equal(bearerCtx?.user?.email, 'user@example.com');
146
+
131
147
  const bad = await identity.loginWithPassword({
132
148
  email: 'user@example.com',
133
149
  password: 'wrong',
@@ -0,0 +1,64 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+ import { handleIdentityWorkerRequest } from '../src/server/worker-router.js';
4
+
5
+ function fakeDb() {
6
+ return {
7
+ prepare() {
8
+ return {
9
+ bind() {
10
+ return {
11
+ async run() { return { success: true }; },
12
+ async first() { return null; },
13
+ async all() { return { results: [] }; },
14
+ };
15
+ },
16
+ };
17
+ },
18
+ };
19
+ }
20
+
21
+ test('native login returns bearer session JSON instead of cookie response', async () => {
22
+ let nativeBranch = false;
23
+ const identity = {
24
+ app: { id: 'local-studio' },
25
+ routeRegistry: { resolve: () => '/agentsam' },
26
+ async loginWithPassword() {
27
+ return {
28
+ ok: true,
29
+ user: { id: 'au_test', email: 'user@example.com', display_name: 'User' },
30
+ sessionId: 'sess_native_123',
31
+ session: { expires_at: 1234567890 },
32
+ };
33
+ },
34
+ isNativeSessionRequest(request) {
35
+ return request.headers.get('X-AgentSam-Native-Client') === '1';
36
+ },
37
+ buildNativeLoginSuccessResponse(result) {
38
+ nativeBranch = true;
39
+ return Response.json({ ok: true, session_id: result.sessionId, user: result.user });
40
+ },
41
+ buildLoginSuccessResponse() {
42
+ throw new Error('cookie_login_response_should_not_run');
43
+ },
44
+ };
45
+
46
+ const response = await handleIdentityWorkerRequest(
47
+ new Request('https://example.test/api/auth/login', {
48
+ method: 'POST',
49
+ headers: {
50
+ 'content-type': 'application/json',
51
+ 'X-AgentSam-Native-Client': '1',
52
+ },
53
+ body: JSON.stringify({ email: 'user@example.com', password: 'secret-pass' }),
54
+ }),
55
+ { DB: fakeDb() },
56
+ { identity },
57
+ );
58
+
59
+ assert.equal(response.status, 200);
60
+ assert.equal(nativeBranch, true);
61
+ assert.equal(response.headers.get('set-cookie'), null);
62
+ const body = await response.json();
63
+ assert.equal(body.session_id, 'sess_native_123');
64
+ });