@inneranimalmedia/agentsam-sdk 2.6.5 → 2.6.7

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 (371) hide show
  1. package/AGENTSAM.md +29 -0
  2. package/apps/README.md +5 -0
  3. package/apps/cad-creator/README.md +4 -4
  4. package/apps/cad-creator/shared/cad/package.json +1 -1
  5. package/apps/client-cms-editor/AGENTS.md +12 -7
  6. package/apps/client-cms-editor/README.md +76 -48
  7. package/apps/client-cms-editor/agentsam.app.json +26 -9
  8. package/apps/client-cms-editor/backend/package.json +20 -3
  9. package/apps/client-cms-editor/backend/src/api/client.ts +134 -101
  10. package/apps/client-cms-editor/backend/src/index.ts +1 -1
  11. package/apps/client-cms-editor/backend/src/model.ts +11 -115
  12. package/apps/client-cms-editor/backend/src/preview/bridge.ts +1 -1
  13. package/apps/client-cms-editor/backend/tsconfig.json +6 -1
  14. package/apps/client-cms-editor/bin/agentsam-cms.js +262 -0
  15. package/apps/client-cms-editor/bin/agentsam-cms.mjs +2 -228
  16. package/apps/client-cms-editor/frontend/package.json +16 -7
  17. package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +9 -41
  18. package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +19 -174
  19. package/apps/client-cms-editor/frontend/src/adapters/heuristic.ts +29 -0
  20. package/apps/client-cms-editor/frontend/src/adapters/http.ts +628 -0
  21. package/apps/client-cms-editor/frontend/src/adapters/memory.ts +510 -0
  22. package/apps/client-cms-editor/frontend/src/editor/CmsEditor.tsx +213 -0
  23. package/apps/client-cms-editor/frontend/src/editor/CmsEditorProvider.tsx +20 -0
  24. package/apps/client-cms-editor/frontend/src/editor/canvas/PreviewCanvas.tsx +53 -0
  25. package/apps/client-cms-editor/frontend/src/{CmsEditor.tsx → editor/legacy/CmsEditorPrototype.tsx} +52 -23
  26. package/apps/client-cms-editor/frontend/src/editor/useCmsEditorController.ts +218 -0
  27. package/apps/client-cms-editor/frontend/src/index.ts +19 -5
  28. package/apps/client-cms-editor/frontend/src/lib/agent-host.ts +24 -0
  29. package/apps/client-cms-editor/frontend/src/main.tsx +82 -8
  30. package/apps/client-cms-editor/frontend/src/mount.tsx +92 -30
  31. package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +1 -1
  32. package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +8 -8
  33. package/apps/client-cms-editor/frontend/src/styles/studio.css +55 -3
  34. package/apps/client-cms-editor/frontend/tsconfig.json +12 -1
  35. package/apps/client-cms-editor/frontend/vite.config.ts +12 -0
  36. package/apps/client-cms-editor/package-lock.json +973 -134
  37. package/apps/client-cms-editor/package.json +118 -6
  38. package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +201 -0
  39. package/apps/client-cms-editor/reference/harvest/PROVENANCE_HISTORY.txt +10 -0
  40. package/apps/client-cms-editor/reference/harvest/README.md +8 -0
  41. package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +135 -0
  42. package/apps/client-cms-editor/reference/harvest/plans/CHATGPT-IDENTITY-HARVEST.md +83 -0
  43. package/apps/client-cms-editor/reference/harvest/plans/CMS-EDITOR-HARVEST.md +166 -0
  44. package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +196 -0
  45. package/apps/client-cms-editor/reference/harvest/plans/INNERANIMALS-CONTENT-001.md +137 -0
  46. package/apps/client-cms-editor/reference/harvest/plans/NO-IMPLICIT-SIDE-EFFECTS.md +87 -0
  47. package/apps/client-cms-editor/reference/harvest/plans/STOREFRONT-SURFACE-MAP.md +140 -0
  48. package/apps/client-cms-editor/reference/harvest/plans/THEME-INNERANIMALS-HARVEST.md +137 -0
  49. package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +63 -0
  50. package/apps/client-cms-editor/reference/harvest/plans/harvest-manifest.json +97 -0
  51. package/apps/client-cms-editor/reference/harvest/plans/local-cms-harvest-schema.sql +328 -0
  52. package/apps/client-cms-editor/shared/cms/package.json +17 -7
  53. package/apps/client-cms-editor/shared/cms/src/adapter.ts +204 -0
  54. package/apps/client-cms-editor/shared/cms/src/agent-context.ts +7 -5
  55. package/apps/client-cms-editor/shared/cms/src/editor-types.ts +32 -0
  56. package/apps/client-cms-editor/shared/cms/src/host.ts +82 -0
  57. package/apps/client-cms-editor/shared/cms/src/index.ts +4 -0
  58. package/apps/client-cms-editor/shared/cms/src/map.ts +115 -0
  59. package/apps/client-cms-editor/shared/cms/src/starter-pack.ts +158 -0
  60. package/contracts/source-types.v1.json +353 -0
  61. package/docs/CMS_CONTEXTUAL_COMPOSER.md +1 -1
  62. package/docs/PRODUCT_LIFECYCLE.md +566 -0
  63. package/docs/THEME_REFINERY.md +2 -2
  64. package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +40 -20
  65. package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
  66. package/docs/plans/LOCAL_STUDIO_DESKTOP_RELEASE_2026-09-29.md +150 -0
  67. package/migrations/d1/0017_identity_native_oauth.sql +21 -0
  68. package/native/agentsam-machine/Cargo.lock +426 -0
  69. package/native/agentsam-machine/Cargo.toml +9 -0
  70. package/native/agentsam-machine/cli/Cargo.toml +14 -0
  71. package/native/agentsam-machine/cli/src/main.rs +77 -0
  72. package/native/agentsam-machine/core/Cargo.toml +15 -0
  73. package/native/agentsam-machine/core/src/assets.rs +790 -0
  74. package/native/agentsam-machine/core/src/bound.rs +298 -0
  75. package/native/agentsam-machine/core/src/enrich.rs +706 -0
  76. package/native/agentsam-machine/core/src/frontend.rs +442 -0
  77. package/native/agentsam-machine/core/src/lib.rs +1086 -0
  78. package/native/agentsam-machine/core/src/liquid.rs +528 -0
  79. package/native/agentsam-machine/core/src/source_types.rs +78 -0
  80. package/package.json +51 -21
  81. package/packages/agentsam-brand/README.md +1 -1
  82. package/packages/agentsam-brand/package.json +4 -4
  83. package/packages/agentsam-brand/src/index.js +1 -1
  84. package/packages/agentsam-contracts/package.json +13 -3
  85. package/packages/agentsam-contracts/src/tools.ts +22 -0
  86. package/packages/agentsam-errors/package.json +13 -3
  87. package/packages/agentsam-knowledge/package.json +18 -4
  88. package/packages/agentsam-knowledge/src/autorag/index.js +10 -3
  89. package/packages/agentsam-knowledge/src/backends/index.js +33 -1
  90. package/packages/agentsam-knowledge/test/autorag.test.mjs +60 -1
  91. package/packages/agentsam-nav/package.json +24 -6
  92. package/packages/agentsam-queue-control/README.md +1 -1
  93. package/packages/agentsam-queue-control/package.json +7 -2
  94. package/packages/agentsam-rapid-rust/src/main.rs +1 -1
  95. package/packages/agentsam-repository/package.json +13 -3
  96. package/packages/agentsam-repository/src/source-types.js +8 -0
  97. package/packages/agentsam-vault/package.json +14 -4
  98. package/packages/agentsam-workbench/package.json +15 -4
  99. package/packages/agentsam-workbench/src/agent/ToolPermissionRequest.tsx +94 -0
  100. package/packages/agentsam-workbench/src/agent/index.ts +2 -0
  101. package/packages/agentsam-workbench/src/agent/tool-permission.css +134 -0
  102. package/packages/connectors/cloudflare/package.json +13 -3
  103. package/packages/connectors/cloudflare/src/api-client.js +3 -2
  104. package/packages/connectors/cloudflare/src/families/vectorize.js +127 -0
  105. package/packages/connectors/cloudflare/src/index.js +1 -0
  106. package/packages/heuristic-theme/README.md +2 -2
  107. package/packages/heuristic-theme/package.json +8 -3
  108. package/packages/heuristic-theme/src/registry.js +2 -2
  109. package/packages/heuristic-theme/src/theme.js +1 -1
  110. package/packages/heuristic-theme/test/theme.test.mjs +1 -1
  111. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  112. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +27 -7
  113. package/packages/identity/README.md +2 -2
  114. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +18 -15
  115. package/packages/identity/migrations/apply-portable.js +109 -0
  116. package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +1 -1
  117. package/packages/identity/migrations/sqlite/005_identity_company_native.sql +52 -0
  118. package/packages/identity/package.json +27 -6
  119. package/packages/identity/schema/agentsam.identity/manifest.json +101 -0
  120. package/packages/identity/scripts/local-identity-bridge.mjs +13 -7
  121. package/packages/identity/scripts/preview-auth-portal.mjs +6 -6
  122. package/packages/identity/src/adapters/cloudflare-d1/index.js +8 -401
  123. package/packages/identity/src/adapters/iam-compat/index.js +540 -0
  124. package/packages/identity/src/adapters/portable-d1/index.js +18 -0
  125. package/packages/identity/src/adapters/portable-store.js +479 -0
  126. package/packages/identity/src/adapters/sqlite/index.js +13 -312
  127. package/packages/identity/src/contracts/auth-config.js +0 -1
  128. package/packages/identity/src/contracts/identity-store.js +56 -5
  129. package/packages/identity/src/contracts/provider.js +1 -0
  130. package/packages/identity/src/contracts/sign-in-options.js +104 -0
  131. package/packages/identity/src/core/constants.js +1 -1
  132. package/packages/identity/src/core/session-policy.js +31 -0
  133. package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -6
  134. package/packages/identity/src/frontend/auth-portal/pages/reset.html +5 -5
  135. package/packages/identity/src/frontend/auth-portal/pages/signup.html +5 -5
  136. package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
  137. package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +6 -37
  138. package/packages/identity/src/index.js +28 -1
  139. package/packages/identity/src/migrations/README.md +23 -23
  140. package/packages/identity/src/oauth/README.md +1 -1
  141. package/packages/identity/src/oauth/credentials.js +10 -11
  142. package/packages/identity/src/oauth/google-desktop-exchange.js +195 -27
  143. package/packages/identity/src/oauth/iam-platform.js +21 -9
  144. package/packages/identity/src/oauth/native-handoff.js +136 -0
  145. package/packages/identity/src/providers/chatgpt/headers.js +65 -0
  146. package/packages/identity/src/providers/chatgpt/index.js +86 -0
  147. package/packages/identity/src/providers/chatgpt/mapper.js +43 -0
  148. package/packages/identity/src/providers/chatgpt/paths.js +45 -0
  149. package/packages/identity/src/providers/github/oauth.js +1 -2
  150. package/packages/identity/src/providers/github/profile.js +16 -2
  151. package/packages/identity/src/providers/index.js +11 -3
  152. package/packages/identity/src/server/identity-service.js +2 -1
  153. package/packages/identity/src/server/worker-router.js +71 -18
  154. package/packages/identity/tests/chatgpt-provider.test.mjs +75 -0
  155. package/packages/identity/tests/google-desktop-identity-login.test.mjs +210 -0
  156. package/packages/identity/tests/identity-init-scaffold.test.mjs +15 -4
  157. package/packages/identity/tests/lane1-identity-store-parity.test.mjs +527 -0
  158. package/packages/identity/tests/lane1-packed-consumer.test.mjs +175 -0
  159. package/packages/identity/tests/native-oauth-handoff.test.mjs +292 -0
  160. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  161. package/packages/identity/tests/preview-auth-portal.test.mjs +4 -2
  162. package/packages/identity/tests/session-routes-policy.test.mjs +13 -1
  163. package/packages/identity/tests/worker-runtime-boundary.test.mjs +11 -0
  164. package/packages/providers/completeful/package.json +7 -2
  165. package/packages/theme-church-site/package.json +17 -6
  166. package/packages/theme-church-site/src/harvest.json +4 -2
  167. package/packages/theme-church-site/src/index.js +7 -3
  168. package/packages/theme-church-site/test/theme.test.mjs +4 -2
  169. package/packages/theme-companions-site/package.json +17 -6
  170. package/packages/theme-companions-site/src/harvest.json +4 -2
  171. package/packages/theme-companions-site/src/index.js +7 -3
  172. package/packages/theme-companions-site/test/theme.test.mjs +4 -2
  173. package/packages/theme-floors-site/package.json +17 -6
  174. package/packages/theme-floors-site/src/harvest.json +4 -2
  175. package/packages/theme-floors-site/src/index.js +7 -3
  176. package/packages/theme-floors-site/test/theme.test.mjs +4 -2
  177. package/packages/theme-fuelnfree-site/package.json +17 -6
  178. package/packages/theme-fuelnfree-site/src/harvest.json +4 -2
  179. package/packages/theme-fuelnfree-site/src/index.js +7 -3
  180. package/packages/theme-fuelnfree-site/test/theme.test.mjs +4 -2
  181. package/packages/theme-handyman-site/package.json +17 -6
  182. package/packages/theme-handyman-site/src/harvest.json +4 -2
  183. package/packages/theme-handyman-site/src/index.js +7 -3
  184. package/packages/theme-handyman-site/test/theme.test.mjs +4 -2
  185. package/packages/theme-insurance-site/package.json +17 -6
  186. package/packages/theme-insurance-site/src/harvest.json +4 -2
  187. package/packages/theme-insurance-site/src/index.js +7 -3
  188. package/packages/theme-insurance-site/test/theme.test.mjs +4 -2
  189. package/packages/theme-scenes/README.md +1 -1
  190. package/packages/theme-scenes/package.json +4 -3
  191. package/packages/theme-scenes/src/registry.js +187 -0
  192. package/packages/theme-shinshu-site/package.json +17 -6
  193. package/packages/theme-shinshu-site/src/harvest.json +4 -2
  194. package/packages/theme-shinshu-site/src/index.js +7 -3
  195. package/packages/theme-shinshu-site/test/theme.test.mjs +4 -2
  196. package/packages/work-graph/package.json +14 -4
  197. package/protocol/FILEMETA_V1.md +2 -2
  198. package/protocol/capabilities/manifest.json +1 -1
  199. package/protocol/database/agentsam.schema-pack.v1.schema.json +72 -0
  200. package/protocol/presets/catalog.json +2 -2
  201. package/protocol/theme-refinery/lineage-registry.json +105 -0
  202. package/protocol/theme-refinery/product-metadata.schema.json +21 -1
  203. package/skills/grok-auth/SKILL.md +110 -0
  204. package/skills/grok-auth/agentsam.skill.json +25 -0
  205. package/skills/grok-auth/references/grok-identity.md +64 -0
  206. package/skills/grok-auth/references/per-user-data.md +49 -0
  207. package/skills/grok-auth/references/prewired-and-env.md +54 -0
  208. package/skills/grok-auth/references/session-ui.md +133 -0
  209. package/skills/grok-auth/references/sign-in-methods.md +55 -0
  210. package/skills/grok-auth/references/wiring.md +71 -0
  211. package/skills/grok-building-games/SKILL.md +213 -0
  212. package/skills/grok-building-games/agentsam.skill.json +25 -0
  213. package/skills/grok-building-games/references/3d-libs.md +64 -0
  214. package/skills/grok-building-games/references/ai-pathfinding.md +122 -0
  215. package/skills/grok-building-games/references/audio.md +129 -0
  216. package/skills/grok-building-games/references/babylon.md +202 -0
  217. package/skills/grok-building-games/references/collision-physics.md +122 -0
  218. package/skills/grok-building-games/references/ecs-architecture.md +111 -0
  219. package/skills/grok-building-games/references/game-feel-juice.md +110 -0
  220. package/skills/grok-building-games/references/genres/board-card-chess.md +89 -0
  221. package/skills/grok-building-games/references/genres/endless-runner.md +83 -0
  222. package/skills/grok-building-games/references/genres/fps.md +105 -0
  223. package/skills/grok-building-games/references/genres/platformer-2d.md +97 -0
  224. package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +131 -0
  225. package/skills/grok-building-games/references/genres/racing-kart.md +106 -0
  226. package/skills/grok-building-games/references/genres/topdown-twin-stick.md +97 -0
  227. package/skills/grok-building-games/references/genres/tower-defense.md +84 -0
  228. package/skills/grok-building-games/references/genres/voxel-minecraft.md +89 -0
  229. package/skills/grok-building-games/references/input.md +139 -0
  230. package/skills/grok-building-games/references/phaser.md +277 -0
  231. package/skills/grok-building-games/references/procedural-generation.md +141 -0
  232. package/skills/grok-building-games/references/save-persistence.md +108 -0
  233. package/skills/grok-building-games/references/threejs-foundational.md +148 -0
  234. package/skills/grok-controls/SKILL.md +274 -0
  235. package/skills/grok-controls/agentsam.skill.json +25 -0
  236. package/skills/grok-design-ui/SKILL.md +122 -0
  237. package/skills/grok-design-ui/agentsam.skill.json +25 -0
  238. package/skills/grok-design-ui/references/animations.md +379 -0
  239. package/skills/grok-design-ui/references/performance.md +88 -0
  240. package/skills/grok-design-ui/references/refined-ui.md +384 -0
  241. package/skills/grok-design-ui/references/surfaces.md +247 -0
  242. package/skills/grok-design-ui/references/typography.md +123 -0
  243. package/skills/grok-game-animation-frames/SKILL.md +84 -0
  244. package/skills/grok-game-animation-frames/agentsam.skill.json +25 -0
  245. package/skills/grok-game-asset-core/SKILL.md +69 -0
  246. package/skills/grok-game-asset-core/agentsam.skill.json +25 -0
  247. package/skills/grok-game-character-consistency/SKILL.md +69 -0
  248. package/skills/grok-game-character-consistency/agentsam.skill.json +25 -0
  249. package/skills/grok-game-tilesets/SKILL.md +65 -0
  250. package/skills/grok-game-tilesets/agentsam.skill.json +25 -0
  251. package/skills/grok-game-ui-icons/SKILL.md +56 -0
  252. package/skills/grok-game-ui-icons/agentsam.skill.json +25 -0
  253. package/skills/grok-generate2dmap/LICENSE +21 -0
  254. package/skills/grok-generate2dmap/SKILL.md +120 -0
  255. package/skills/grok-generate2dmap/SOURCE.md +6 -0
  256. package/skills/grok-generate2dmap/agentsam.skill.json +25 -0
  257. package/skills/grok-generate2dmap/references/deliverables.md +101 -0
  258. package/skills/grok-generate2dmap/references/layered-map-contract.md +209 -0
  259. package/skills/grok-generate2dmap/references/map-strategies.md +341 -0
  260. package/skills/grok-generate2dmap/references/object-production-gate.md +24 -0
  261. package/skills/grok-generate2dmap/references/pipeline.md +149 -0
  262. package/skills/grok-generate2dmap/references/prop-pack-contract.md +174 -0
  263. package/skills/grok-generate2dmap/references/side-scroll-stages.md +29 -0
  264. package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +157 -0
  265. package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +369 -0
  266. package/skills/grok-generate2dsprite/LICENSE +21 -0
  267. package/skills/grok-generate2dsprite/SKILL.md +313 -0
  268. package/skills/grok-generate2dsprite/SOURCE.md +6 -0
  269. package/skills/grok-generate2dsprite/agentsam.skill.json +25 -0
  270. package/skills/grok-generate2dsprite/references/modes.md +124 -0
  271. package/skills/grok-generate2dsprite/references/prompt-rules.md +315 -0
  272. package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +1031 -0
  273. package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +109 -0
  274. package/skills/grok-imagine/SKILL.md +201 -0
  275. package/skills/grok-imagine/agentsam.skill.json +25 -0
  276. package/skills/grok-multiplayer-p2p/SKILL.md +111 -0
  277. package/skills/grok-multiplayer-p2p/agentsam.skill.json +25 -0
  278. package/skills/grok-multiplayer-p2p/references/react-binding.md +144 -0
  279. package/skills/grok-multiplayer-p2p/references/signaling-relay.md +269 -0
  280. package/skills/grok-neon/SKILL.md +157 -0
  281. package/skills/grok-neon/agentsam.skill.json +25 -0
  282. package/skills/grok-og/SKILL.md +102 -0
  283. package/skills/grok-og/agentsam.skill.json +25 -0
  284. package/skills/grok-og/references/brand-pass.md +53 -0
  285. package/skills/grok-og/references/custom-card.md +107 -0
  286. package/skills/grok-og/references/favicon-and-icons.md +48 -0
  287. package/skills/grok-og/references/og-type-contract.md +18 -0
  288. package/skills/grok-og/references/placeholder-card.md +21 -0
  289. package/skills/grok-og/references/x-banner.md +41 -0
  290. package/skills/grok-threejs/SKILL.md +67 -0
  291. package/skills/grok-threejs/agentsam.skill.json +25 -0
  292. package/skills/grok-threejs/references/llms-full.txt +2780 -0
  293. package/skills/grok-video2dsprite/LICENSE +21 -0
  294. package/skills/grok-video2dsprite/SKILL.md +236 -0
  295. package/skills/grok-video2dsprite/SOURCE.md +6 -0
  296. package/skills/grok-video2dsprite/agentsam.skill.json +25 -0
  297. package/skills/grok-video2dsprite/references/pipeline.md +90 -0
  298. package/skills/grok-video2dsprite/references/prompt-rules.md +63 -0
  299. package/skills/grok-video2dsprite/scripts/video2dsprite.py +490 -0
  300. package/skills/grok-xai-api/SKILL.md +152 -0
  301. package/skills/grok-xai-api/agentsam.skill.json +25 -0
  302. package/src/cli/command-catalog.js +46 -4
  303. package/src/cli/dispatch.js +1 -0
  304. package/src/cli.js +10 -0
  305. package/src/cloudflare/runtime-status.js +2 -1
  306. package/src/commands/autorag.js +67 -13
  307. package/src/commands/cheat-sheet.js +29 -4
  308. package/src/commands/codebaseindex.js +45 -2
  309. package/src/commands/knowledge.js +59 -2
  310. package/src/commands/machine-binary.js +103 -0
  311. package/src/commands/machine.js +137 -0
  312. package/src/commands/security.js +67 -4
  313. package/src/commands/setup.js +33 -16
  314. package/src/commands/status.js +8 -1
  315. package/src/features/resolve.js +1 -0
  316. package/src/knowledge/engine.js +21 -2
  317. package/src/knowledge/runtime-discovery.js +127 -0
  318. package/src/lib/identity-scaffold.js +53 -13
  319. package/src/lib/setup/recipes.js +1 -1
  320. package/src/models/discovery.js +40 -22
  321. package/src/models/inventory-core.d.ts +105 -0
  322. package/src/models/inventory-core.js +54 -6
  323. package/src/security/logs.js +3 -0
  324. package/src/ui/cli/help.js +41 -0
  325. package/test/apps-scaffold-contract.test.mjs +3 -2
  326. package/test/cli/api-key-env-whoami.test.mjs +2 -2
  327. package/test/cli/catalog-authority.test.mjs +64 -0
  328. package/test/cli/machine-command.test.mjs +34 -0
  329. package/test/fixtures/project-authority/customer-app/.agentsam/app.json +5 -0
  330. package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +1 -0
  331. package/test/fixtures/project-authority/customer-app/wrangler.toml +17 -0
  332. package/test/integration/autorag-project-authority.integration.test.mjs +51 -0
  333. package/test/integration/autorag-runtime-discovery.test.mjs +107 -0
  334. package/test/integration/cms-app-scaffold.test.mjs +37 -31
  335. package/test/integration/cms-site-tenancy-contract.test.mjs +28 -17
  336. package/test/integration/codebaseindex-ingest.test.mjs +41 -0
  337. package/test/integration/database-hosted-honesty.test.mjs +49 -0
  338. package/test/integration/desktop-keychain-boundary.test.mjs +45 -0
  339. package/test/integration/hosted-work-host-authority.test.mjs +47 -0
  340. package/test/integration/inventory-eligibility.test.mjs +59 -0
  341. package/test/integration/local-provider-bridge.test.mjs +65 -0
  342. package/test/integration/local-studio-settings-production.test.mjs +40 -0
  343. package/test/integration/lockfile-sync.test.mjs +26 -0
  344. package/test/integration/provider-env-cli.test.mjs +13 -3
  345. package/test/integration/runtime-status.integration.test.mjs +16 -1
  346. package/test/integration/theme-package-registry.test.mjs +31 -0
  347. package/test/repository-snapshot-view.test.mjs +2 -2
  348. package/test/security.test.mjs +14 -0
  349. package/test/terminal-machine-identity.test.mjs +2 -2
  350. package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +0 -145
  351. package/packages/agentsam-nav/dist/Nav.d.ts +0 -43
  352. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +0 -10
  353. package/packages/agentsam-nav/dist/NavContent.d.ts +0 -2
  354. package/packages/agentsam-nav/dist/NavFooter.d.ts +0 -2
  355. package/packages/agentsam-nav/dist/NavGroup.d.ts +0 -3
  356. package/packages/agentsam-nav/dist/NavHeader.d.ts +0 -2
  357. package/packages/agentsam-nav/dist/NavMenu.d.ts +0 -22
  358. package/packages/agentsam-nav/dist/NavProvider.d.ts +0 -35
  359. package/packages/agentsam-nav/dist/NavTrigger.d.ts +0 -2
  360. package/packages/agentsam-nav/dist/Surfaces.d.ts +0 -41
  361. package/packages/agentsam-nav/dist/browser.d.ts +0 -11
  362. package/packages/agentsam-nav/dist/browser.global.js +0 -132
  363. package/packages/agentsam-nav/dist/browser.js +0 -613
  364. package/packages/agentsam-nav/dist/contracts.d.ts +0 -76
  365. package/packages/agentsam-nav/dist/index.d.ts +0 -11
  366. package/packages/agentsam-nav/dist/index.js +0 -614
  367. package/packages/agentsam-nav/dist/legacy.css +0 -83
  368. package/packages/agentsam-nav/dist/shell.css +0 -113
  369. package/packages/agentsam-nav/dist/theme.css +0 -113
  370. package/packages/agentsam-nav/dist/utils.d.ts +0 -1
  371. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +0 -670
@@ -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
+ }