@inneranimalmedia/agentsam-sdk 2.6.5 → 2.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/AGENTSAM.md +29 -0
  2. package/README.md +37 -0
  3. package/apps/README.md +5 -0
  4. package/apps/cad-creator/README.md +4 -4
  5. package/apps/cad-creator/shared/cad/package.json +1 -1
  6. package/apps/client-cms-editor/AGENTS.md +12 -7
  7. package/apps/client-cms-editor/README.md +76 -48
  8. package/apps/client-cms-editor/agentsam.app.json +26 -9
  9. package/apps/client-cms-editor/backend/package.json +20 -3
  10. package/apps/client-cms-editor/backend/src/api/client.ts +134 -101
  11. package/apps/client-cms-editor/backend/src/index.ts +1 -1
  12. package/apps/client-cms-editor/backend/src/model.ts +11 -115
  13. package/apps/client-cms-editor/backend/src/preview/bridge.ts +1 -1
  14. package/apps/client-cms-editor/backend/tsconfig.json +6 -1
  15. package/apps/client-cms-editor/bin/agentsam-cms.js +262 -0
  16. package/apps/client-cms-editor/bin/agentsam-cms.mjs +2 -228
  17. package/apps/client-cms-editor/frontend/package.json +16 -7
  18. package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +9 -41
  19. package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +19 -174
  20. package/apps/client-cms-editor/frontend/src/adapters/heuristic.ts +29 -0
  21. package/apps/client-cms-editor/frontend/src/adapters/http.ts +628 -0
  22. package/apps/client-cms-editor/frontend/src/adapters/memory.ts +510 -0
  23. package/apps/client-cms-editor/frontend/src/editor/CmsEditor.tsx +213 -0
  24. package/apps/client-cms-editor/frontend/src/editor/CmsEditorProvider.tsx +20 -0
  25. package/apps/client-cms-editor/frontend/src/editor/canvas/PreviewCanvas.tsx +53 -0
  26. package/apps/client-cms-editor/frontend/src/{CmsEditor.tsx → editor/legacy/CmsEditorPrototype.tsx} +52 -23
  27. package/apps/client-cms-editor/frontend/src/editor/useCmsEditorController.ts +218 -0
  28. package/apps/client-cms-editor/frontend/src/index.ts +19 -5
  29. package/apps/client-cms-editor/frontend/src/lib/agent-host.ts +24 -0
  30. package/apps/client-cms-editor/frontend/src/main.tsx +82 -8
  31. package/apps/client-cms-editor/frontend/src/mount.tsx +92 -30
  32. package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +1 -1
  33. package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +8 -8
  34. package/apps/client-cms-editor/frontend/src/styles/studio.css +55 -3
  35. package/apps/client-cms-editor/frontend/tsconfig.json +12 -1
  36. package/apps/client-cms-editor/frontend/vite.config.ts +12 -0
  37. package/apps/client-cms-editor/package-lock.json +973 -134
  38. package/apps/client-cms-editor/package.json +118 -6
  39. package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +201 -0
  40. package/apps/client-cms-editor/reference/harvest/PROVENANCE_HISTORY.txt +10 -0
  41. package/apps/client-cms-editor/reference/harvest/README.md +8 -0
  42. package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +135 -0
  43. package/apps/client-cms-editor/reference/harvest/plans/CHATGPT-IDENTITY-HARVEST.md +83 -0
  44. package/apps/client-cms-editor/reference/harvest/plans/CMS-EDITOR-HARVEST.md +166 -0
  45. package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +196 -0
  46. package/apps/client-cms-editor/reference/harvest/plans/INNERANIMALS-CONTENT-001.md +137 -0
  47. package/apps/client-cms-editor/reference/harvest/plans/NO-IMPLICIT-SIDE-EFFECTS.md +87 -0
  48. package/apps/client-cms-editor/reference/harvest/plans/STOREFRONT-SURFACE-MAP.md +140 -0
  49. package/apps/client-cms-editor/reference/harvest/plans/THEME-INNERANIMALS-HARVEST.md +137 -0
  50. package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +63 -0
  51. package/apps/client-cms-editor/reference/harvest/plans/harvest-manifest.json +97 -0
  52. package/apps/client-cms-editor/reference/harvest/plans/local-cms-harvest-schema.sql +328 -0
  53. package/apps/client-cms-editor/shared/cms/package.json +17 -7
  54. package/apps/client-cms-editor/shared/cms/src/adapter.ts +204 -0
  55. package/apps/client-cms-editor/shared/cms/src/agent-context.ts +7 -5
  56. package/apps/client-cms-editor/shared/cms/src/editor-types.ts +32 -0
  57. package/apps/client-cms-editor/shared/cms/src/host.ts +82 -0
  58. package/apps/client-cms-editor/shared/cms/src/index.ts +4 -0
  59. package/apps/client-cms-editor/shared/cms/src/map.ts +115 -0
  60. package/apps/client-cms-editor/shared/cms/src/starter-pack.ts +158 -0
  61. package/contracts/source-types.v1.json +353 -0
  62. package/docs/CMS_CONTEXTUAL_COMPOSER.md +1 -1
  63. package/docs/PRODUCT_LIFECYCLE.md +566 -0
  64. package/docs/REPO_FILE_MAP.txt +132 -0
  65. package/docs/THEME_REFINERY.md +2 -2
  66. package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +40 -20
  67. package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
  68. package/docs/plans/LOCAL_STUDIO_DESKTOP_RELEASE_2026-09-29.md +150 -0
  69. package/migrations/d1/0017_identity_native_oauth.sql +21 -0
  70. package/migrations/d1/0018_goap_control_plane_remaster.sql +40 -0
  71. package/migrations/d1/0019_agentsam_agent_run_plan_todo.sql +14 -0
  72. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +20 -9
  73. package/migrations/runtime/0002_goap_blackboard_v1.sql +15 -0
  74. package/migrations/runtime/0004_agent_run_plan_linkage.sql +8 -0
  75. package/native/agentsam-machine/Cargo.lock +426 -0
  76. package/native/agentsam-machine/Cargo.toml +9 -0
  77. package/native/agentsam-machine/cli/Cargo.toml +21 -0
  78. package/native/agentsam-machine/cli/README.md +21 -0
  79. package/native/agentsam-machine/cli/src/main.rs +77 -0
  80. package/native/agentsam-machine/core/Cargo.toml +22 -0
  81. package/native/agentsam-machine/core/README.md +14 -0
  82. package/native/agentsam-machine/core/build.rs +33 -0
  83. package/native/agentsam-machine/core/contracts/source-types.v1.json +353 -0
  84. package/native/agentsam-machine/core/src/assets.rs +790 -0
  85. package/native/agentsam-machine/core/src/bound.rs +298 -0
  86. package/native/agentsam-machine/core/src/enrich.rs +706 -0
  87. package/native/agentsam-machine/core/src/frontend.rs +442 -0
  88. package/native/agentsam-machine/core/src/lib.rs +1092 -0
  89. package/native/agentsam-machine/core/src/liquid.rs +528 -0
  90. package/native/agentsam-machine/core/src/source_types.rs +78 -0
  91. package/package.json +64 -24
  92. package/packages/agentsam-brand/README.md +1 -1
  93. package/packages/agentsam-brand/package.json +4 -4
  94. package/packages/agentsam-brand/src/index.js +1 -1
  95. package/packages/agentsam-contracts/package.json +13 -3
  96. package/packages/agentsam-contracts/src/tools.ts +22 -0
  97. package/packages/agentsam-errors/package.json +13 -3
  98. package/packages/agentsam-errors/src/index.js +1 -0
  99. package/packages/agentsam-errors/src/tool.js +56 -0
  100. package/packages/agentsam-errors/test/tool.test.mjs +58 -0
  101. package/packages/agentsam-goap/README.md +52 -0
  102. package/packages/agentsam-goap/package.json +37 -0
  103. package/packages/agentsam-goap/src/adapters/d1-sqlite.js +570 -0
  104. package/packages/agentsam-goap/src/adapters/memory.js +199 -0
  105. package/packages/agentsam-goap/src/contracts.js +226 -0
  106. package/packages/agentsam-goap/src/control-plane.js +112 -0
  107. package/packages/agentsam-goap/src/errors.js +157 -0
  108. package/packages/agentsam-goap/src/index.js +5 -0
  109. package/packages/agentsam-goap/src/world.js +202 -0
  110. package/packages/agentsam-goap/test/goap-control-plane.test.mjs +245 -0
  111. package/packages/agentsam-goap/test/world.test.mjs +79 -0
  112. package/packages/agentsam-knowledge/package.json +18 -4
  113. package/packages/agentsam-knowledge/src/autorag/index.js +10 -3
  114. package/packages/agentsam-knowledge/src/backends/index.js +33 -1
  115. package/packages/agentsam-knowledge/test/autorag.test.mjs +60 -1
  116. package/packages/agentsam-nav/dist/browser.global.js +21 -18
  117. package/packages/agentsam-nav/dist/browser.js +6807 -130
  118. package/packages/agentsam-nav/dist/index.js +6799 -122
  119. package/packages/agentsam-nav/package.json +24 -6
  120. package/packages/agentsam-queue-control/README.md +1 -1
  121. package/packages/agentsam-queue-control/package.json +7 -2
  122. package/packages/agentsam-rapid-rust/src/main.rs +1 -1
  123. package/packages/agentsam-repository/package.json +13 -3
  124. package/packages/agentsam-repository/src/goap.js +13 -14
  125. package/packages/agentsam-repository/src/source-types.js +8 -0
  126. package/packages/agentsam-repository/src/tickets.js +15 -22
  127. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +19 -8
  128. package/packages/agentsam-vault/package.json +14 -4
  129. package/packages/agentsam-workbench/package.json +15 -4
  130. package/packages/agentsam-workbench/src/agent/ToolPermissionRequest.tsx +94 -0
  131. package/packages/agentsam-workbench/src/agent/index.ts +2 -0
  132. package/packages/agentsam-workbench/src/agent/tool-permission.css +134 -0
  133. package/packages/connectors/cloudflare/package.json +15 -3
  134. package/packages/connectors/cloudflare/src/api-client.js +3 -2
  135. package/packages/connectors/cloudflare/src/families/vectorize.js +127 -0
  136. package/packages/connectors/cloudflare/src/index.js +1 -0
  137. package/packages/heuristic-theme/README.md +2 -2
  138. package/packages/heuristic-theme/package.json +8 -3
  139. package/packages/heuristic-theme/src/registry.js +2 -2
  140. package/packages/heuristic-theme/src/theme.js +1 -1
  141. package/packages/heuristic-theme/test/theme.test.mjs +1 -1
  142. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  143. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +27 -7
  144. package/packages/identity/README.md +2 -2
  145. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +18 -15
  146. package/packages/identity/migrations/apply-portable.js +109 -0
  147. package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +1 -1
  148. package/packages/identity/migrations/sqlite/005_identity_company_native.sql +52 -0
  149. package/packages/identity/package.json +27 -6
  150. package/packages/identity/schema/agentsam.identity/manifest.json +101 -0
  151. package/packages/identity/scripts/local-identity-bridge.mjs +13 -7
  152. package/packages/identity/scripts/preview-auth-portal.mjs +6 -6
  153. package/packages/identity/src/adapters/cloudflare-d1/index.js +8 -401
  154. package/packages/identity/src/adapters/iam-compat/index.js +540 -0
  155. package/packages/identity/src/adapters/portable-d1/index.js +18 -0
  156. package/packages/identity/src/adapters/portable-store.js +479 -0
  157. package/packages/identity/src/adapters/sqlite/index.js +13 -312
  158. package/packages/identity/src/contracts/auth-config.js +0 -1
  159. package/packages/identity/src/contracts/identity-store.js +56 -5
  160. package/packages/identity/src/contracts/provider.js +1 -0
  161. package/packages/identity/src/contracts/sign-in-options.js +104 -0
  162. package/packages/identity/src/core/constants.js +1 -1
  163. package/packages/identity/src/core/session-policy.js +31 -0
  164. package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -6
  165. package/packages/identity/src/frontend/auth-portal/pages/reset.html +5 -5
  166. package/packages/identity/src/frontend/auth-portal/pages/signup.html +5 -5
  167. package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
  168. package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +6 -37
  169. package/packages/identity/src/index.js +28 -1
  170. package/packages/identity/src/migrations/README.md +23 -23
  171. package/packages/identity/src/oauth/README.md +1 -1
  172. package/packages/identity/src/oauth/credentials.js +10 -11
  173. package/packages/identity/src/oauth/google-desktop-exchange.js +195 -27
  174. package/packages/identity/src/oauth/iam-platform.js +21 -9
  175. package/packages/identity/src/oauth/native-handoff.js +136 -0
  176. package/packages/identity/src/providers/chatgpt/headers.js +65 -0
  177. package/packages/identity/src/providers/chatgpt/index.js +86 -0
  178. package/packages/identity/src/providers/chatgpt/mapper.js +43 -0
  179. package/packages/identity/src/providers/chatgpt/paths.js +45 -0
  180. package/packages/identity/src/providers/github/oauth.js +1 -2
  181. package/packages/identity/src/providers/github/profile.js +16 -2
  182. package/packages/identity/src/providers/index.js +11 -3
  183. package/packages/identity/src/server/identity-service.js +2 -1
  184. package/packages/identity/src/server/worker-router.js +71 -18
  185. package/packages/identity/tests/chatgpt-provider.test.mjs +75 -0
  186. package/packages/identity/tests/google-desktop-identity-login.test.mjs +210 -0
  187. package/packages/identity/tests/identity-init-scaffold.test.mjs +15 -4
  188. package/packages/identity/tests/lane1-identity-store-parity.test.mjs +527 -0
  189. package/packages/identity/tests/lane1-packed-consumer.test.mjs +175 -0
  190. package/packages/identity/tests/native-oauth-handoff.test.mjs +292 -0
  191. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  192. package/packages/identity/tests/preview-auth-portal.test.mjs +4 -2
  193. package/packages/identity/tests/session-routes-policy.test.mjs +13 -1
  194. package/packages/identity/tests/worker-runtime-boundary.test.mjs +11 -0
  195. package/packages/providers/completeful/package.json +7 -2
  196. package/packages/theme-church-site/package.json +17 -6
  197. package/packages/theme-church-site/src/harvest.json +4 -2
  198. package/packages/theme-church-site/src/index.js +7 -3
  199. package/packages/theme-church-site/test/theme.test.mjs +4 -2
  200. package/packages/theme-companions-site/package.json +17 -6
  201. package/packages/theme-companions-site/src/harvest.json +4 -2
  202. package/packages/theme-companions-site/src/index.js +7 -3
  203. package/packages/theme-companions-site/test/theme.test.mjs +4 -2
  204. package/packages/theme-floors-site/package.json +17 -6
  205. package/packages/theme-floors-site/src/harvest.json +4 -2
  206. package/packages/theme-floors-site/src/index.js +7 -3
  207. package/packages/theme-floors-site/test/theme.test.mjs +4 -2
  208. package/packages/theme-fuelnfree-site/package.json +17 -6
  209. package/packages/theme-fuelnfree-site/src/harvest.json +4 -2
  210. package/packages/theme-fuelnfree-site/src/index.js +7 -3
  211. package/packages/theme-fuelnfree-site/test/theme.test.mjs +4 -2
  212. package/packages/theme-handyman-site/package.json +17 -6
  213. package/packages/theme-handyman-site/src/harvest.json +4 -2
  214. package/packages/theme-handyman-site/src/index.js +7 -3
  215. package/packages/theme-handyman-site/test/theme.test.mjs +4 -2
  216. package/packages/theme-insurance-site/package.json +17 -6
  217. package/packages/theme-insurance-site/src/harvest.json +4 -2
  218. package/packages/theme-insurance-site/src/index.js +7 -3
  219. package/packages/theme-insurance-site/test/theme.test.mjs +4 -2
  220. package/packages/theme-scenes/README.md +1 -1
  221. package/packages/theme-scenes/package.json +4 -3
  222. package/packages/theme-scenes/src/registry.js +187 -0
  223. package/packages/theme-shinshu-site/package.json +17 -6
  224. package/packages/theme-shinshu-site/src/harvest.json +4 -2
  225. package/packages/theme-shinshu-site/src/index.js +7 -3
  226. package/packages/theme-shinshu-site/test/theme.test.mjs +4 -2
  227. package/packages/work-graph/package.json +14 -4
  228. package/protocol/FILEMETA_V1.md +2 -2
  229. package/protocol/capabilities/manifest.json +1 -1
  230. package/protocol/database/agentsam.schema-pack.v1.schema.json +72 -0
  231. package/protocol/presets/catalog.json +2 -2
  232. package/protocol/theme-refinery/lineage-registry.json +105 -0
  233. package/protocol/theme-refinery/product-metadata.schema.json +21 -1
  234. package/python/agentsam_sdk/repository/intelligence/discovery.py +27 -2
  235. package/python/agentsam_sdk/repository/intelligence/git.py +4 -0
  236. package/python/tests/test_repository_intelligence.py +30 -0
  237. package/src/cli/command-catalog.js +48 -5
  238. package/src/cli/dispatch.js +1 -0
  239. package/src/cli.js +20 -1
  240. package/src/cloudflare/runtime-status.js +2 -1
  241. package/src/commands/autorag.js +67 -13
  242. package/src/commands/cheat-sheet.js +29 -4
  243. package/src/commands/codebaseindex.js +45 -2
  244. package/src/commands/goap-world.js +134 -0
  245. package/src/commands/goap.js +11 -4
  246. package/src/commands/knowledge.js +59 -2
  247. package/src/commands/machine-binary.js +109 -0
  248. package/src/commands/machine-runtime.js +266 -0
  249. package/src/commands/machine.js +208 -0
  250. package/src/commands/package.js +530 -0
  251. package/src/commands/plan-ledger.js +370 -0
  252. package/src/commands/plan.js +183 -0
  253. package/src/commands/security.js +67 -4
  254. package/src/commands/setup.js +33 -16
  255. package/src/commands/skill.js +78 -17
  256. package/src/commands/status.js +8 -1
  257. package/src/features/resolve.js +1 -0
  258. package/src/knowledge/engine.js +21 -2
  259. package/src/knowledge/runtime-discovery.js +127 -0
  260. package/src/knowledge/source.js +1 -1
  261. package/src/lib/identity-scaffold.js +53 -13
  262. package/src/lib/setup/recipes.js +1 -1
  263. package/src/lib/slash-commands.js +1 -1
  264. package/src/local/runtime-store.js +24 -2
  265. package/src/local/sqlite.js +17 -0
  266. package/src/models/discovery.js +40 -22
  267. package/src/models/inventory-core.d.ts +105 -0
  268. package/src/models/inventory-core.js +54 -6
  269. package/src/sam/operations/package-audit.js +41 -0
  270. package/src/sam/seed.js +2 -0
  271. package/src/security/logs.js +3 -0
  272. package/src/ui/cli/help.js +41 -0
  273. package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +0 -145
  274. package/test/account-session.test.mjs +0 -101
  275. package/test/agent-capabilities.test.mjs +0 -67
  276. package/test/app-authority-context.test.mjs +0 -32
  277. package/test/app-building-skills.test.mjs +0 -61
  278. package/test/apps-scaffold-contract.test.mjs +0 -114
  279. package/test/blender-cad.test.mjs +0 -146
  280. package/test/cad-docker.test.mjs +0 -96
  281. package/test/capabilities.test.mjs +0 -133
  282. package/test/cli/api-key-env-whoami.test.mjs +0 -129
  283. package/test/cli/codebaseindex-plan-ux.test.mjs +0 -37
  284. package/test/cli/eval-live.test.mjs +0 -219
  285. package/test/cli/go.test.mjs +0 -128
  286. package/test/cli/local-vault-and-provider-setup.test.mjs +0 -268
  287. package/test/cli/mcp-client.test.mjs +0 -256
  288. package/test/cli/preferences-runtime.test.mjs +0 -11
  289. package/test/cli/runtime-persist.test.mjs +0 -56
  290. package/test/cli/runtime-ui.test.mjs +0 -141
  291. package/test/cli/rust-command.test.mjs +0 -21
  292. package/test/cli/skill-npm-and-env.test.mjs +0 -52
  293. package/test/cli/status-actions.test.mjs +0 -88
  294. package/test/cli/wireframes-go-registry.test.mjs +0 -139
  295. package/test/cli-preferences.test.mjs +0 -46
  296. package/test/cloudflare-connector.test.mjs +0 -93
  297. package/test/cloudflare-runtime.test.mjs +0 -82
  298. package/test/context.test.mjs +0 -159
  299. package/test/credentials-audit.test.mjs +0 -113
  300. package/test/deploy-health-scan.test.mjs +0 -95
  301. package/test/deploy-receipt.test.mjs +0 -91
  302. package/test/dockerize.test.mjs +0 -93
  303. package/test/error-diagnostics.test.mjs +0 -124
  304. package/test/eval-context.test.mjs +0 -37
  305. package/test/execution-approvals.test.mjs +0 -27
  306. package/test/fixtures/cad/courtyard-house.json +0 -208
  307. package/test/fixtures/knowledge-rpc-worker.mjs +0 -16
  308. package/test/gcloud-auth.test.mjs +0 -111
  309. package/test/go/build-source-identity.test.mjs +0 -31
  310. package/test/go/cloudflare-probe.test.mjs +0 -317
  311. package/test/go/distribution.test.mjs +0 -28
  312. package/test/google-cloud-connection.test.mjs +0 -83
  313. package/test/google-cloud-inventory.test.mjs +0 -64
  314. package/test/indexing-provider.test.mjs +0 -29
  315. package/test/init-options.test.mjs +0 -35
  316. package/test/integration/ai-access-onboarding.test.mjs +0 -47
  317. package/test/integration/app-command.test.mjs +0 -85
  318. package/test/integration/cad-discovery.test.mjs +0 -158
  319. package/test/integration/cad-docker-executor.test.mjs +0 -249
  320. package/test/integration/cad-execution.test.mjs +0 -184
  321. package/test/integration/cad-project-app.test.mjs +0 -31
  322. package/test/integration/cad-project-d1.test.mjs +0 -25
  323. package/test/integration/cad-project-native.test.mjs +0 -24
  324. package/test/integration/cad-project-workflow.test.mjs +0 -9
  325. package/test/integration/cad-project.test.mjs +0 -85
  326. package/test/integration/cli-help.test.mjs +0 -41
  327. package/test/integration/cms-app-scaffold.test.mjs +0 -71
  328. package/test/integration/cms-site-tenancy-contract.test.mjs +0 -257
  329. package/test/integration/codebaseindex-ingest.test.mjs +0 -214
  330. package/test/integration/core-client.integration.test.mjs +0 -55
  331. package/test/integration/error-runtime-rpc.test.mjs +0 -71
  332. package/test/integration/features-resolve.test.mjs +0 -147
  333. package/test/integration/icon-registry.test.mjs +0 -67
  334. package/test/integration/ingest-discover-models.test.mjs +0 -30
  335. package/test/integration/inspect-path-cli.test.mjs +0 -46
  336. package/test/integration/install-script.test.mjs +0 -110
  337. package/test/integration/inventory-core.test.mjs +0 -53
  338. package/test/integration/inventory-evidence.test.mjs +0 -125
  339. package/test/integration/knowledge-rpc.test.mjs +0 -112
  340. package/test/integration/local-fs.test.mjs +0 -113
  341. package/test/integration/merkle-cli.test.mjs +0 -61
  342. package/test/integration/merkle-persistence-identity.test.mjs +0 -48
  343. package/test/integration/plugins-cloudflare.integration.test.mjs +0 -28
  344. package/test/integration/provider-env-cli.test.mjs +0 -50
  345. package/test/integration/provider-factory.test.mjs +0 -197
  346. package/test/integration/repository-company-graph.test.mjs +0 -90
  347. package/test/integration/runtime-migrations.test.mjs +0 -87
  348. package/test/integration/runtime-status.integration.test.mjs +0 -212
  349. package/test/integration/sam-activity-recovery.test.mjs +0 -147
  350. package/test/integration/sam-decision.test.mjs +0 -584
  351. package/test/integration/sam-kernel.test.mjs +0 -99
  352. package/test/integration/sam-planning-astar.test.mjs +0 -279
  353. package/test/integration/scaffold-cloudflare.test.mjs +0 -46
  354. package/test/integration/skill-runtime.test.mjs +0 -240
  355. package/test/integration/storage-architecture.test.mjs +0 -154
  356. package/test/integration/store-discovery.test.mjs +0 -44
  357. package/test/integration/studio-fs-pty-e2e.test.mjs +0 -294
  358. package/test/integration/terminal-exec.integration.test.mjs +0 -86
  359. package/test/integration/tool-runtime.test.mjs +0 -124
  360. package/test/integration/tool-schema.test.mjs +0 -59
  361. package/test/jr-dev-skill.test.mjs +0 -26
  362. package/test/knowledge-context-pack.test.mjs +0 -19
  363. package/test/knowledge-postgres.test.mjs +0 -40
  364. package/test/knowledge-service.test.mjs +0 -110
  365. package/test/knowledge.test.mjs +0 -206
  366. package/test/live/terminal-transport.live.test.mjs +0 -24
  367. package/test/local-sessions.test.mjs +0 -57
  368. package/test/local-studio-deploy.test.mjs +0 -83
  369. package/test/loopback-oauth.test.mjs +0 -53
  370. package/test/merkle-render.test.mjs +0 -31
  371. package/test/mini.test.mjs +0 -140
  372. package/test/model-catalog.test.mjs +0 -70
  373. package/test/models.test.mjs +0 -309
  374. package/test/npm10-lock.test.mjs +0 -29
  375. package/test/ollama.test.mjs +0 -137
  376. package/test/open-url.test.mjs +0 -64
  377. package/test/openai-responses.test.mjs +0 -155
  378. package/test/portable-context.test.mjs +0 -94
  379. package/test/project-config.test.mjs +0 -81
  380. package/test/project-rules.test.mjs +0 -44
  381. package/test/provider-credentials.test.mjs +0 -106
  382. package/test/rehydrate.test.mjs +0 -25
  383. package/test/release-hygiene.test.mjs +0 -42
  384. package/test/repository-snapshot-view.test.mjs +0 -112
  385. package/test/responses-runner.test.mjs +0 -465
  386. package/test/runtime-activity.test.mjs +0 -98
  387. package/test/sdk-worker-contract.test.mjs +0 -72
  388. package/test/security.test.mjs +0 -252
  389. package/test/setup-planner.test.mjs +0 -74
  390. package/test/setup-runtime.test.mjs +0 -51
  391. package/test/shell.test.mjs +0 -281
  392. package/test/skills.test.mjs +0 -22
  393. package/test/smoke.mjs +0 -122
  394. package/test/telemetry.test.mjs +0 -79
  395. package/test/terminal/local-pty.mock.test.mjs +0 -153
  396. package/test/terminal-machine-identity.test.mjs +0 -88
  397. package/test/theme-portability.test.mjs +0 -14
  398. package/test/tools-search.test.mjs +0 -73
  399. package/test/unit/website-assets-bindings.test.mjs +0 -60
  400. package/test/whoami-resume.test.mjs +0 -157
@@ -0,0 +1,540 @@
1
+ import { SESSION_POLICY, shouldRenewDesktopSession } from '../../core/constants.js';
2
+ import { NATIVE_HANDOFF_TTL_SECONDS, SESSION_TYPES } from '../../core/session-policy.js';
3
+ import { IdentitySchemaError } from '../../contracts/identity-store.js';
4
+ import { IDENTITY_STORE_SCHEMA_VERSION } from '../../contracts/identity-store.js';
5
+ import { newAccountIdentityId, newAuthUserId, newSessionId, newAuthEventId, nowUnix } from '../cloudflare-d1/ids.js';
6
+ import { DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, normalizeCompanyRow } from '../../contracts/company.js';
7
+
8
+
9
+ /**
10
+ * @typedef {object} D1Database
11
+ * @property {(sql: string) => { bind: (...args: unknown[]) => { first: () => Promise<unknown>, run: () => Promise<unknown>, all: () => Promise<{ results?: unknown[] }> } }} prepare
12
+ */
13
+
14
+ /**
15
+ * IAM compatibility IdentityStore — maps contract semantics onto hosted/IAM-shaped
16
+ * tables (accounts, auth_users, auth_sessions, identity_oauth_states, company, …).
17
+ * This is NOT the portable identity_* customer schema.
18
+ *
19
+ * @param {D1Database} db
20
+ * @param {{ sessionTtlSeconds?: number }} [options]
21
+ */
22
+ async function hashValue(value) {
23
+ const v = String(value || '').trim();
24
+ if (!v) return null;
25
+ const bytes = new TextEncoder().encode(v);
26
+ const digest = await crypto.subtle.digest('SHA-256', bytes);
27
+ return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
28
+ }
29
+
30
+ export function createIamCompatIdentityAdapter(db, options = {}) {
31
+ if (!db?.prepare) {
32
+ throw new Error('iam_compat_identity_adapter_requires_db_binding');
33
+ }
34
+ const sessionTtlSeconds = options.sessionTtlSeconds ?? SESSION_POLICY.browser.ttlSeconds;
35
+
36
+ return Object.freeze({
37
+ schemaVersion: IDENTITY_STORE_SCHEMA_VERSION,
38
+ sessionTtlSeconds,
39
+ backend: 'iam-compat',
40
+
41
+ /**
42
+ * Append-only login/logout/failed-attempt audit trail (auth_event_log).
43
+ * Never throws — an observability write must not break a real auth flow.
44
+ * status remains the event outcome only ('ok' | 'failed').
45
+ * Richer context (session, capabilities, activity, client) goes in metadata_json.
46
+ * ip/userAgent are hashed (SHA-256) — raw values are never persisted.
47
+ */
48
+ async logAuthEvent({
49
+ userId,
50
+ accountId,
51
+ eventType,
52
+ status = 'ok',
53
+ provider,
54
+ session,
55
+ capabilities,
56
+ activity,
57
+ client,
58
+ metadata,
59
+ request,
60
+ }) {
61
+ try {
62
+ const ip = request?.headers?.get?.('cf-connecting-ip') || request?.headers?.get?.('x-forwarded-for') || '';
63
+ const ua = request?.headers?.get?.('user-agent') || '';
64
+ const [ipHash, uaHash] = await Promise.all([hashValue(ip), hashValue(ua)]);
65
+ const envelope = {
66
+ ...(metadata && typeof metadata === 'object' ? metadata : {}),
67
+ ...(accountId ? { accountId } : {}),
68
+ ...(session ? { session } : {}),
69
+ ...(capabilities ? { capabilities } : {}),
70
+ ...(activity ? { activity } : {}),
71
+ ...(client ? { client } : {}),
72
+ };
73
+ await db.prepare(
74
+ `INSERT INTO auth_event_log
75
+ (id, user_id, event_type, status, provider, metadata_json, ip_hash, user_agent_hash)
76
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
77
+ ).bind(
78
+ newAuthEventId(),
79
+ userId || null,
80
+ eventType,
81
+ status,
82
+ provider || null,
83
+ JSON.stringify(envelope),
84
+ ipHash,
85
+ uaHash,
86
+ ).run();
87
+ } catch {
88
+ // Never let audit logging break a real login/OAuth flow.
89
+ }
90
+ },
91
+
92
+ /**
93
+ * Account/session activity snapshot for auth receipts (not event outcome).
94
+ * @param {string} userId
95
+ */
96
+ async countAuthActivity(userId) {
97
+ if (!userId) {
98
+ return { loginCount: 0, activeSessionCount: 0, lastLoginAt: null };
99
+ }
100
+ try {
101
+ const logins = await db.prepare(
102
+ `SELECT COUNT(*) AS c, MAX(created_at) AS last_at
103
+ FROM auth_event_log
104
+ WHERE user_id = ? AND event_type = 'login' AND status = 'ok'`,
105
+ ).bind(userId).first();
106
+ const sessions = await db.prepare(
107
+ `SELECT COUNT(*) AS c FROM auth_sessions
108
+ WHERE user_id = ? AND revoked_at IS NULL AND expires_at > ?`,
109
+ ).bind(userId, nowUnix()).first();
110
+ return {
111
+ loginCount: Number(logins?.c || 0),
112
+ activeSessionCount: Number(sessions?.c || 0),
113
+ lastLoginAt: logins?.last_at != null ? Number(logins.last_at) : null,
114
+ };
115
+ } catch {
116
+ return { loginCount: 0, activeSessionCount: 0, lastLoginAt: null };
117
+ }
118
+ },
119
+
120
+ async findUserByEmail(email) {
121
+ const row = await db.prepare(
122
+ `SELECT id, email, display_name, password_hash, salt, status, created_at, updated_at
123
+ FROM auth_users WHERE email = ? COLLATE NOCASE LIMIT 1`,
124
+ ).bind(String(email || '').trim().toLowerCase()).first();
125
+ return row || null;
126
+ },
127
+
128
+ async findUserById(userId) {
129
+ const row = await db.prepare(
130
+ `SELECT id, email, display_name, password_hash, salt, status, created_at, updated_at
131
+ FROM auth_users WHERE id = ? LIMIT 1`,
132
+ ).bind(userId).first();
133
+ return row || null;
134
+ },
135
+
136
+ async createUser({ email, passwordHash, salt, displayName }) {
137
+ const id = newAuthUserId();
138
+ const ts = nowUnix();
139
+ const normalizedEmail = String(email || '').trim().toLowerCase();
140
+ // accounts = SSOT; auth_users = login principal (1:1 id in portable scaffold).
141
+ await db.prepare(
142
+ `INSERT INTO accounts (id, email, display_name, status, created_at, updated_at)
143
+ VALUES (?, ?, ?, 'active', ?, ?)`,
144
+ ).bind(id, normalizedEmail, displayName || null, ts, ts).run();
145
+ await db.prepare(
146
+ `INSERT INTO auth_users (id, email, display_name, password_hash, salt, status, created_at, updated_at)
147
+ VALUES (?, ?, ?, ?, ?, 'active', ?, ?)`,
148
+ ).bind(id, normalizedEmail, displayName || null, passwordHash || null, salt || null, ts, ts).run();
149
+ return this.findUserById(id);
150
+ },
151
+
152
+ async updateUserPassword(userId, passwordHash, salt) {
153
+ const ts = nowUnix();
154
+ await db.prepare(
155
+ `UPDATE auth_users SET password_hash = ?, salt = ?, updated_at = ? WHERE id = ?`,
156
+ ).bind(passwordHash, salt, ts, userId).run();
157
+ await db.prepare(
158
+ `UPDATE accounts SET updated_at = ? WHERE id = ?`,
159
+ ).bind(ts, userId).run().catch(() => {});
160
+ },
161
+
162
+ async findUserByProvider(provider, providerSubject) {
163
+ const row = await db.prepare(
164
+ `SELECT u.id, u.email, u.display_name, u.password_hash, u.salt, u.status, u.created_at, u.updated_at
165
+ FROM account_identities ai
166
+ JOIN accounts a ON a.id = ai.account_id
167
+ JOIN auth_users u ON u.id = a.id
168
+ WHERE ai.provider = ? AND ai.provider_subject = ?
169
+ AND COALESCE(a.status, 'active') = 'active'
170
+ LIMIT 1`,
171
+ ).bind(provider, providerSubject).first();
172
+ return row || null;
173
+ },
174
+
175
+ async upsertProviderIdentity({ accountId, provider, providerSubject, email }) {
176
+ // Ensure SSOT account row exists (OAuth may create identity before password user).
177
+ const ts = nowUnix();
178
+ const account = await db.prepare(
179
+ `SELECT id FROM accounts WHERE id = ? LIMIT 1`,
180
+ ).bind(accountId).first();
181
+ if (!account?.id) {
182
+ const authUser = await this.findUserById(accountId);
183
+ if (!authUser) throw new Error('account_ssot_missing');
184
+ await db.prepare(
185
+ `INSERT INTO accounts (id, email, display_name, status, created_at, updated_at)
186
+ VALUES (?, ?, ?, 'active', ?, ?)`,
187
+ ).bind(
188
+ accountId,
189
+ authUser.email,
190
+ authUser.display_name || null,
191
+ authUser.created_at || ts,
192
+ ts,
193
+ ).run();
194
+ }
195
+
196
+ const existing = await db.prepare(
197
+ `SELECT id FROM account_identities WHERE provider = ? AND provider_subject = ? LIMIT 1`,
198
+ ).bind(provider, providerSubject).first();
199
+ if (existing?.id) {
200
+ await db.prepare(
201
+ `UPDATE account_identities SET account_id = ?, email = ?, updated_at = ? WHERE id = ?`,
202
+ ).bind(accountId, email || null, ts, existing.id).run();
203
+ return existing.id;
204
+ }
205
+ const id = newAccountIdentityId();
206
+ await db.prepare(
207
+ `INSERT INTO account_identities (id, account_id, provider, provider_subject, email, created_at, updated_at)
208
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
209
+ ).bind(id, accountId, provider, providerSubject, email || null, ts, ts).run();
210
+ return id;
211
+ },
212
+
213
+ async createSession({ userId, email, provider, providerSubject, displayName, type }) {
214
+ const id = newSessionId();
215
+ const ts = nowUnix();
216
+ const desktop = type === SESSION_TYPES.DESKTOP;
217
+ const expiresAt = ts + (desktop ? SESSION_POLICY.desktop.ttlSeconds : sessionTtlSeconds);
218
+ // Browser inserts stay column-identical to before (auth_sessions.type defaults
219
+ // to 'browser'), so this is safe on databases that predate the type column.
220
+ const columns = ['id', 'user_id', 'email', 'provider', 'provider_subject', 'display_name', 'expires_at', 'created_at', 'last_active_at'];
221
+ const values = [id, userId, email || null, provider || 'email', providerSubject || null, displayName || null, expiresAt, ts, ts];
222
+ if (desktop) {
223
+ columns.push('type');
224
+ values.push(SESSION_TYPES.DESKTOP);
225
+ }
226
+ await db.prepare(
227
+ `INSERT INTO auth_sessions (${columns.join(', ')})
228
+ VALUES (${columns.map(() => '?').join(', ')})`,
229
+ ).bind(...values).run();
230
+ return {
231
+ id,
232
+ type: desktop ? SESSION_TYPES.DESKTOP : SESSION_TYPES.BROWSER,
233
+ user_id: userId,
234
+ email,
235
+ provider: provider || 'email',
236
+ provider_subject: providerSubject || null,
237
+ display_name: displayName || null,
238
+ expires_at: expiresAt,
239
+ created_at: ts,
240
+ };
241
+ },
242
+
243
+ async getSession(sessionId) {
244
+ const row = await db.prepare(
245
+ `SELECT id, user_id, email, provider, provider_subject, display_name, expires_at, revoked_at, created_at, last_active_at, type
246
+ FROM auth_sessions WHERE id = ? LIMIT 1`,
247
+ ).bind(sessionId).first();
248
+ if (!row) return null;
249
+ if (row.revoked_at) return null;
250
+ const now = nowUnix();
251
+ if (row.expires_at <= now) return null;
252
+ if (shouldRenewDesktopSession(row, now)) {
253
+ const renewedExpiresAt = now + SESSION_POLICY.desktop.ttlSeconds;
254
+ await db.prepare(
255
+ `UPDATE auth_sessions
256
+ SET expires_at = ?, last_active_at = ?
257
+ WHERE id = ? AND revoked_at IS NULL AND expires_at > ?`,
258
+ ).bind(renewedExpiresAt, now, sessionId, now).run();
259
+ row.expires_at = renewedExpiresAt;
260
+ row.last_active_at = now;
261
+ }
262
+ return row;
263
+ },
264
+
265
+ async revokeSession(sessionId, reason = 'logout') {
266
+ const ts = nowUnix();
267
+ await db.prepare(
268
+ `UPDATE auth_sessions SET revoked_at = ?, last_active_at = ? WHERE id = ? AND revoked_at IS NULL`,
269
+ ).bind(ts, ts, sessionId).run();
270
+ return { ok: true, reason };
271
+ },
272
+
273
+ async createOAuthTransaction({ state, provider, codeVerifier, returnTo, redirectTo, appId, ttlSeconds = 600, clientType, nativeChallenge, nativeRedirect }) {
274
+ if (!appId) {
275
+ throw new IdentitySchemaError(
276
+ 'OAUTH_TRANSACTION_APP_ID_REQUIRED',
277
+ 'OAuth transactions must include app_id',
278
+ );
279
+ }
280
+ const ts = nowUnix();
281
+ const expiresAt = ts + ttlSeconds;
282
+ try {
283
+ const columns = ['state', 'provider', 'code_verifier', 'redirect_to', 'app_id', 'expires_at', 'created_at'];
284
+ const values = [state, provider, codeVerifier, returnTo ?? redirectTo ?? null, appId, expiresAt, ts];
285
+ // Native columns (migration 0017) are written only for native transactions,
286
+ // so browser OAuth is unaffected until/unless 0017 is applied.
287
+ if (clientType === SESSION_TYPES.DESKTOP) {
288
+ columns.push('client_type', 'native_challenge', 'native_redirect');
289
+ values.push(clientType, nativeChallenge ?? null, nativeRedirect ?? null);
290
+ }
291
+ await db.prepare(
292
+ `INSERT INTO identity_oauth_states (${columns.join(', ')})
293
+ VALUES (${columns.map(() => '?').join(', ')})`,
294
+ ).bind(...values).run();
295
+ } catch (err) {
296
+ throw new IdentitySchemaError(
297
+ 'IDENTITY_SCHEMA_MIGRATION_REQUIRED',
298
+ 'identity_oauth_states.app_id required — apply migration 0015 (no silent pre-app_id fallback)',
299
+ { cause: String(err?.message || err) },
300
+ );
301
+ }
302
+ },
303
+
304
+ /** @deprecated Use createOAuthTransaction */
305
+ async saveOAuthState(input) {
306
+ return this.createOAuthTransaction(input);
307
+ },
308
+
309
+ async consumeOAuthTransaction(state) {
310
+ let row;
311
+ try {
312
+ row = await db.prepare(
313
+ `SELECT * FROM identity_oauth_states WHERE state = ? LIMIT 1`,
314
+ ).bind(state).first();
315
+ } catch (err) {
316
+ throw new IdentitySchemaError(
317
+ 'IDENTITY_SCHEMA_MIGRATION_REQUIRED',
318
+ 'identity_oauth_states.app_id column missing — apply migration 0015',
319
+ { cause: String(err?.message || err) },
320
+ );
321
+ }
322
+ if (!row) return null;
323
+ await db.prepare(`DELETE FROM identity_oauth_states WHERE state = ?`).bind(state).run();
324
+ if (row.expires_at <= nowUnix()) return null;
325
+ if (!row.app_id) {
326
+ throw new IdentitySchemaError(
327
+ 'IDENTITY_SCHEMA_MIGRATION_REQUIRED',
328
+ 'OAuth transaction missing app_id — refuse silent pre-app_id semantics',
329
+ );
330
+ }
331
+ return {
332
+ state: row.state,
333
+ provider: row.provider,
334
+ code_verifier: row.code_verifier,
335
+ redirect_to: row.redirect_to,
336
+ return_to: row.redirect_to,
337
+ app_id: row.app_id,
338
+ client_type: row.client_type ?? null,
339
+ native_challenge: row.native_challenge ?? null,
340
+ native_redirect: row.native_redirect ?? null,
341
+ expires_at: row.expires_at,
342
+ created_at: row.created_at,
343
+ };
344
+ },
345
+
346
+ /** @deprecated Use consumeOAuthTransaction */
347
+ async consumeOAuthState(state) {
348
+ return this.consumeOAuthTransaction(state);
349
+ },
350
+
351
+ /** Single-use desktop session pickup (migration 0017). Only the code's sha256 is stored. */
352
+ async createNativeHandoff({ handoffHash, sessionId, challenge, ttlSeconds = NATIVE_HANDOFF_TTL_SECONDS }) {
353
+ const ts = nowUnix();
354
+ await db.prepare(
355
+ `INSERT INTO identity_native_handoffs (handoff_hash, session_id, challenge, expires_at, created_at)
356
+ VALUES (?, ?, ?, ?, ?)`,
357
+ ).bind(handoffHash, sessionId, challenge, ts + ttlSeconds, ts).run();
358
+ },
359
+
360
+ /** Atomically claim an unexpired, unconsumed handoff; null if none. */
361
+ async consumeNativeHandoff(handoffHash) {
362
+ const ts = nowUnix();
363
+ const row = await db.prepare(
364
+ `UPDATE identity_native_handoffs SET consumed_at = ?
365
+ WHERE handoff_hash = ? AND consumed_at IS NULL AND expires_at > ?
366
+ RETURNING session_id, challenge`,
367
+ ).bind(ts, handoffHash, ts).first();
368
+ return row || null;
369
+ },
370
+
371
+ async getCompanyBySlug(slug = DEFAULT_COMPANY_SLUG) {
372
+ const row = await db.prepare(
373
+ `SELECT id, slug, name, legal_name, logo_url, favicon_url, primary_color, auth_bg_color,
374
+ support_email, website_url, tagline, meta_json, created_at, updated_at
375
+ FROM company WHERE slug = ? LIMIT 1`,
376
+ ).bind(slug).first();
377
+ return normalizeCompanyRow(row);
378
+ },
379
+
380
+ /**
381
+ * Resolve portal branding by request host against company.meta.hosts (or website_url host).
382
+ * Product-agnostic — each tenant lists their own hosts in D1.
383
+ * @param {string | null | undefined} hostname
384
+ */
385
+ async getCompanyByHost(hostname) {
386
+ const host = String(hostname || '')
387
+ .trim()
388
+ .toLowerCase()
389
+ .split(':')[0];
390
+ if (!host) return null;
391
+ const { results } = await db.prepare(
392
+ `SELECT id, slug, name, legal_name, logo_url, favicon_url, primary_color, auth_bg_color,
393
+ support_email, website_url, tagline, meta_json, created_at, updated_at
394
+ FROM company`,
395
+ ).all();
396
+ for (const row of results || []) {
397
+ const company = normalizeCompanyRow(row);
398
+ if (!company) continue;
399
+ const hosts = Array.isArray(company.meta?.hosts)
400
+ ? company.meta.hosts.map((h) => String(h || '').trim().toLowerCase()).filter(Boolean)
401
+ : [];
402
+ if (hosts.includes(host)) return company;
403
+ try {
404
+ if (company.websiteUrl) {
405
+ const siteHost = new URL(company.websiteUrl).hostname.toLowerCase();
406
+ if (siteHost && siteHost === host) return company;
407
+ }
408
+ } catch {
409
+ // ignore invalid website_url
410
+ }
411
+ }
412
+ return null;
413
+ },
414
+
415
+ async getDefaultCompany() {
416
+ const bySlug = await this.getCompanyBySlug(DEFAULT_COMPANY_SLUG);
417
+ if (bySlug) return bySlug;
418
+ const row = await db.prepare(
419
+ `SELECT id, slug, name, legal_name, logo_url, favicon_url, primary_color, auth_bg_color,
420
+ support_email, website_url, tagline, meta_json, created_at, updated_at
421
+ FROM company WHERE id = ? LIMIT 1`,
422
+ ).bind(DEFAULT_COMPANY_ID).first();
423
+ return normalizeCompanyRow(row);
424
+ },
425
+
426
+ async upsertCompany(input) {
427
+ const ts = nowUnix();
428
+ const id = input.id || DEFAULT_COMPANY_ID;
429
+ const slug = input.slug || DEFAULT_COMPANY_SLUG;
430
+ const existing = await db.prepare(`SELECT id FROM company WHERE id = ? OR slug = ? LIMIT 1`)
431
+ .bind(id, slug).first();
432
+ const metaJson = input.meta ? JSON.stringify(input.meta) : (input.metaJson ?? null);
433
+ if (existing?.id) {
434
+ await db.prepare(
435
+ `UPDATE company SET
436
+ slug = ?, name = ?, legal_name = ?, logo_url = ?, favicon_url = ?,
437
+ primary_color = ?, auth_bg_color = ?, support_email = ?, website_url = ?,
438
+ tagline = ?, meta_json = ?, updated_at = ?
439
+ WHERE id = ?`,
440
+ ).bind(
441
+ slug,
442
+ input.name,
443
+ input.legalName ?? null,
444
+ input.logoUrl ?? null,
445
+ input.faviconUrl ?? null,
446
+ input.primaryColor ?? null,
447
+ input.authBgColor ?? null,
448
+ input.supportEmail ?? null,
449
+ input.websiteUrl ?? null,
450
+ input.tagline ?? null,
451
+ metaJson,
452
+ ts,
453
+ existing.id,
454
+ ).run();
455
+ return this.getCompanyBySlug(slug);
456
+ }
457
+ await db.prepare(
458
+ `INSERT INTO company
459
+ (id, slug, name, legal_name, logo_url, favicon_url, primary_color, auth_bg_color,
460
+ support_email, website_url, tagline, meta_json, created_at, updated_at)
461
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
462
+ ).bind(
463
+ id,
464
+ slug,
465
+ input.name,
466
+ input.legalName ?? null,
467
+ input.logoUrl ?? null,
468
+ input.faviconUrl ?? null,
469
+ input.primaryColor ?? null,
470
+ input.authBgColor ?? null,
471
+ input.supportEmail ?? null,
472
+ input.websiteUrl ?? null,
473
+ input.tagline ?? null,
474
+ metaJson,
475
+ ts,
476
+ ts,
477
+ ).run();
478
+ return this.getCompanyBySlug(slug);
479
+ },
480
+
481
+ /**
482
+ * Provider connection / config-ref. Fixture or hosted DB must provide
483
+ * identity_provider_connections (credential_ref only — never raw secrets).
484
+ */
485
+ async upsertProviderConnection({
486
+ userId, provider, credentialRef, grantedScopes = [], expiresAt = null, refreshable = false,
487
+ }) {
488
+ if (!credentialRef) {
489
+ throw new IdentitySchemaError(
490
+ 'PROVIDER_CREDENTIAL_REF_REQUIRED',
491
+ 'Provider connections store credential_ref only — never raw secrets',
492
+ );
493
+ }
494
+ const existing = await db.prepare(
495
+ `SELECT id FROM identity_provider_connections WHERE user_id = ? AND provider = ? LIMIT 1`,
496
+ ).bind(userId, provider).first();
497
+ const ts = nowUnix();
498
+ const scopesJson = JSON.stringify(Array.isArray(grantedScopes) ? grantedScopes : []);
499
+ if (existing?.id) {
500
+ await db.prepare(
501
+ `UPDATE identity_provider_connections
502
+ SET granted_scopes_json = ?, credential_ref = ?, expires_at = ?, refreshable = ?, updated_at = ?
503
+ WHERE id = ?`,
504
+ ).bind(scopesJson, credentialRef, expiresAt, refreshable ? 1 : 0, ts, existing.id).run();
505
+ return existing.id;
506
+ }
507
+ const id = `ipc_${crypto.randomUUID().replace(/-/g, '')}`;
508
+ await db.prepare(
509
+ `INSERT INTO identity_provider_connections
510
+ (id, user_id, provider, granted_scopes_json, credential_ref, expires_at, refreshable, created_at, updated_at)
511
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
512
+ ).bind(id, userId, provider, scopesJson, credentialRef, expiresAt, refreshable ? 1 : 0, ts, ts).run();
513
+ return id;
514
+ },
515
+
516
+ async getProviderConnection(userId, provider) {
517
+ const row = await db.prepare(
518
+ `SELECT id, user_id, provider, granted_scopes_json, credential_ref, expires_at, refreshable, created_at, updated_at
519
+ FROM identity_provider_connections WHERE user_id = ? AND provider = ? LIMIT 1`,
520
+ ).bind(userId, provider).first();
521
+ if (!row) return null;
522
+ let grantedScopes = [];
523
+ try { grantedScopes = JSON.parse(row.granted_scopes_json || '[]'); } catch { grantedScopes = []; }
524
+ return {
525
+ id: row.id,
526
+ user_id: row.user_id,
527
+ provider: row.provider,
528
+ granted_scopes: grantedScopes,
529
+ credential_ref: row.credential_ref,
530
+ expires_at: row.expires_at,
531
+ refreshable: Boolean(row.refreshable),
532
+ created_at: row.created_at,
533
+ updated_at: row.updated_at,
534
+ };
535
+ },
536
+ });
537
+ }
538
+
539
+ /** @deprecated Prefer createIamCompatIdentityAdapter — stable alias for existing call sites. */
540
+ export const createCloudflareD1Adapter = createIamCompatIdentityAdapter;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Portable D1 IdentityStore — SAME portable identity_* schema as SQLite.
3
+ * Clean customer D1 (user123). Does NOT query accounts/auth_users/company/etc.
4
+ *
5
+ * Implementation shares prepare/bind CRUD with SQLite only because both targets
6
+ * already speak the same D1-shaped statement API — not because D1 was forced
7
+ * through a Node sqlite emulator.
8
+ */
9
+ import { createPortableIdentityStore } from '../portable-store.js';
10
+
11
+ /**
12
+ * @param {{ prepare: Function }} db Cloudflare D1 binding or D1-shaped fixture
13
+ * @param {{ sessionTtlSeconds?: number, skipSchemaCheck?: boolean }} [options]
14
+ */
15
+ export function createPortableD1IdentityAdapter(db, options = {}) {
16
+ if (!db?.prepare) throw new Error('portable_d1_identity_adapter_requires_db');
17
+ return createPortableIdentityStore(db, { ...options, backend: 'portable-d1' });
18
+ }