@agent-native/core 0.96.0 → 0.97.0

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 (450) hide show
  1. package/README.md +6 -0
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +39 -0
  4. package/corpus/core/docs/content/audit-log.mdx +2 -1
  5. package/corpus/core/docs/content/automations.mdx +7 -7
  6. package/corpus/core/docs/content/creating-templates.mdx +1 -1
  7. package/corpus/core/docs/content/internationalization.mdx +2 -1
  8. package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  9. package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
  10. package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  11. package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  12. package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  13. package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  14. package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
  15. package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
  16. package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
  17. package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
  18. package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
  19. package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  20. package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  21. package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
  22. package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
  23. package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
  24. package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
  25. package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
  26. package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  27. package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  28. package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
  29. package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  30. package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
  31. package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  32. package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  33. package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  34. package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  35. package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
  36. package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  37. package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
  38. package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  39. package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  40. package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  41. package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  42. package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
  43. package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  44. package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
  45. package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  46. package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  47. package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  48. package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  49. package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
  50. package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  51. package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
  52. package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  53. package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  54. package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  55. package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  56. package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
  57. package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  58. package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
  59. package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  60. package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  61. package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  62. package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  63. package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
  64. package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  65. package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
  66. package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  67. package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  68. package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  69. package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  70. package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
  71. package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  72. package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
  73. package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  74. package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  75. package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  76. package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  77. package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
  78. package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
  79. package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
  80. package/corpus/core/package.json +9 -2
  81. package/corpus/core/src/a2a/auth-policy.ts +33 -0
  82. package/corpus/core/src/agent/durable-background.ts +15 -7
  83. package/corpus/core/src/agent/run-store.ts +13 -4
  84. package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
  85. package/corpus/core/src/audit/store.ts +8 -3
  86. package/corpus/core/src/audit/types.ts +3 -0
  87. package/corpus/core/src/cli/doctor.ts +396 -0
  88. package/corpus/core/src/cli/index.ts +56 -0
  89. package/corpus/core/src/cli/info.ts +3 -3
  90. package/corpus/core/src/cli/package-lifecycle.ts +821 -0
  91. package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
  92. package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
  93. package/corpus/core/src/guards/index.ts +23 -0
  94. package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
  95. package/corpus/core/src/guards/no-env-credentials.ts +229 -0
  96. package/corpus/core/src/guards/no-env-mutation.ts +114 -0
  97. package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
  98. package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
  99. package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
  100. package/corpus/core/src/guards/scan-utils.ts +105 -0
  101. package/corpus/core/src/guards/types.ts +30 -0
  102. package/corpus/core/src/org/accept-pending.ts +9 -1
  103. package/corpus/core/src/org/migrations.ts +41 -0
  104. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  105. package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
  106. package/corpus/core/src/provider-api/custom-registry.ts +56 -0
  107. package/corpus/core/src/provider-api/index.ts +2 -0
  108. package/corpus/core/src/secrets/storage.ts +47 -27
  109. package/corpus/core/src/server/action-discovery.ts +4 -0
  110. package/corpus/core/src/server/agent-chat-plugin.ts +54 -12
  111. package/corpus/core/src/server/core-routes-plugin.ts +30 -14
  112. package/corpus/core/src/server/csrf.ts +24 -5
  113. package/corpus/core/src/server/framework-request-handler.ts +34 -0
  114. package/corpus/core/src/server/google-realtime-session.ts +1 -36
  115. package/corpus/core/src/server/poll.ts +24 -2
  116. package/corpus/core/src/server/realtime-voice.ts +9 -0
  117. package/corpus/core/src/server/request-origin.ts +63 -0
  118. package/corpus/core/src/server/transcribe-voice.ts +1 -50
  119. package/corpus/core/src/templates/default/package.json +2 -2
  120. package/corpus/core/src/templates/headless/package.json +0 -1
  121. package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  122. package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  123. package/corpus/core/src/templates/workspace-core/package.json +0 -1
  124. package/corpus/core/src/templates/workspace-root/package.json +0 -1
  125. package/corpus/core/src/triggers/actions.ts +11 -3
  126. package/corpus/core/src/triggers/types.ts +7 -1
  127. package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
  128. package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
  129. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
  130. package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
  131. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
  133. package/corpus/templates/analytics/package.json +1 -2
  134. package/corpus/templates/analytics/server/lib/bigquery.ts +3 -2
  135. package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
  136. package/corpus/templates/assets/package.json +1 -2
  137. package/corpus/templates/brain/package.json +1 -2
  138. package/corpus/templates/calendar/package.json +1 -2
  139. package/corpus/templates/chat/package.json +1 -2
  140. package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
  141. package/corpus/templates/clips/chrome-extension/package.json +3 -2
  142. package/corpus/templates/clips/desktop/package.json +6 -4
  143. package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
  144. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
  145. package/corpus/templates/clips/package.json +1 -2
  146. package/corpus/templates/clips/server/db/schema.ts +29 -17
  147. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  148. package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
  149. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
  150. package/corpus/templates/content/AGENTS.md +72 -62
  151. package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
  152. package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
  153. package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
  154. package/corpus/templates/content/actions/_position-utils.ts +76 -0
  155. package/corpus/templates/content/actions/_property-utils.ts +31 -20
  156. package/corpus/templates/content/actions/add-database-item.ts +52 -39
  157. package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
  158. package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
  159. package/corpus/templates/content/actions/configure-document-property.ts +36 -27
  160. package/corpus/templates/content/actions/create-content-database.ts +40 -30
  161. package/corpus/templates/content/actions/create-document.ts +39 -32
  162. package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
  163. package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
  164. package/corpus/templates/content/actions/move-document.ts +64 -48
  165. package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
  166. package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
  167. package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
  168. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
  169. package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
  170. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
  171. package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
  172. package/corpus/templates/content/app/i18n-data.ts +40 -0
  173. package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
  174. package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
  175. package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
  176. package/corpus/templates/content/package.json +1 -2
  177. package/corpus/templates/content/parity/matrix.md +25 -25
  178. package/corpus/templates/content/parity/matrix.ts +1 -0
  179. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
  180. package/corpus/templates/content/shared/api.ts +16 -1
  181. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
  182. package/corpus/templates/design/AGENTS.md +12 -2
  183. package/corpus/templates/design/DEVELOPING.md +10 -6
  184. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
  185. package/corpus/templates/design/actions/duplicate-design.ts +31 -25
  186. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
  187. package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
  188. package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
  189. package/corpus/templates/design/package.json +1 -1
  190. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
  191. package/corpus/templates/dispatch/package.json +1 -2
  192. package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
  193. package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
  194. package/corpus/templates/forms/package.json +1 -2
  195. package/corpus/templates/macros/package.json +1 -2
  196. package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
  197. package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
  198. package/corpus/templates/mail/package.json +1 -2
  199. package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
  200. package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
  201. package/corpus/templates/plan/package.json +1 -2
  202. package/corpus/templates/plan/server/plans.ts +9 -2
  203. package/corpus/templates/slides/actions/list-decks.ts +28 -0
  204. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
  205. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
  206. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
  207. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
  208. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
  209. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
  210. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
  211. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
  212. package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
  213. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
  214. package/corpus/templates/slides/app/pages/Index.tsx +2 -3
  215. package/corpus/templates/slides/app/root.tsx +0 -5
  216. package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
  217. package/corpus/templates/slides/package.json +1 -2
  218. package/dist/a2a/auth-policy.d.ts +19 -0
  219. package/dist/a2a/auth-policy.d.ts.map +1 -1
  220. package/dist/a2a/auth-policy.js +32 -0
  221. package/dist/a2a/auth-policy.js.map +1 -1
  222. package/dist/agent/durable-background.d.ts +8 -5
  223. package/dist/agent/durable-background.d.ts.map +1 -1
  224. package/dist/agent/durable-background.js +15 -8
  225. package/dist/agent/durable-background.js.map +1 -1
  226. package/dist/agent/run-store.d.ts.map +1 -1
  227. package/dist/agent/run-store.js +9 -4
  228. package/dist/agent/run-store.js.map +1 -1
  229. package/dist/audit/actions/export-audit-events.d.ts +27 -0
  230. package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
  231. package/dist/audit/actions/export-audit-events.js +144 -0
  232. package/dist/audit/actions/export-audit-events.js.map +1 -0
  233. package/dist/audit/store.d.ts +1 -0
  234. package/dist/audit/store.d.ts.map +1 -1
  235. package/dist/audit/store.js +8 -3
  236. package/dist/audit/store.js.map +1 -1
  237. package/dist/audit/types.d.ts +3 -0
  238. package/dist/audit/types.d.ts.map +1 -1
  239. package/dist/audit/types.js.map +1 -1
  240. package/dist/cli/doctor.d.ts +83 -0
  241. package/dist/cli/doctor.d.ts.map +1 -0
  242. package/dist/cli/doctor.js +277 -0
  243. package/dist/cli/doctor.js.map +1 -0
  244. package/dist/cli/index.js +50 -0
  245. package/dist/cli/index.js.map +1 -1
  246. package/dist/cli/info.js +3 -3
  247. package/dist/cli/info.js.map +1 -1
  248. package/dist/cli/package-lifecycle.d.ts +50 -0
  249. package/dist/cli/package-lifecycle.d.ts.map +1 -0
  250. package/dist/cli/package-lifecycle.js +560 -0
  251. package/dist/cli/package-lifecycle.js.map +1 -0
  252. package/dist/client/agent-chat-adapter.js +7 -3
  253. package/dist/client/agent-chat-adapter.js.map +1 -1
  254. package/dist/collab/struct-routes.d.ts +1 -1
  255. package/dist/guards/db-tool-scoping.d.ts +38 -0
  256. package/dist/guards/db-tool-scoping.d.ts.map +1 -0
  257. package/dist/guards/db-tool-scoping.js +114 -0
  258. package/dist/guards/db-tool-scoping.js.map +1 -0
  259. package/dist/guards/index.d.ts +23 -0
  260. package/dist/guards/index.d.ts.map +1 -0
  261. package/dist/guards/index.js +19 -0
  262. package/dist/guards/index.js.map +1 -0
  263. package/dist/guards/no-drizzle-push.d.ts +14 -0
  264. package/dist/guards/no-drizzle-push.d.ts.map +1 -0
  265. package/dist/guards/no-drizzle-push.js +97 -0
  266. package/dist/guards/no-drizzle-push.js.map +1 -0
  267. package/dist/guards/no-env-credentials.d.ts +36 -0
  268. package/dist/guards/no-env-credentials.d.ts.map +1 -0
  269. package/dist/guards/no-env-credentials.js +204 -0
  270. package/dist/guards/no-env-credentials.js.map +1 -0
  271. package/dist/guards/no-env-mutation.d.ts +23 -0
  272. package/dist/guards/no-env-mutation.d.ts.map +1 -0
  273. package/dist/guards/no-env-mutation.js +90 -0
  274. package/dist/guards/no-env-mutation.js.map +1 -0
  275. package/dist/guards/no-localhost-fallback.d.ts +30 -0
  276. package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
  277. package/dist/guards/no-localhost-fallback.js +105 -0
  278. package/dist/guards/no-localhost-fallback.js.map +1 -0
  279. package/dist/guards/no-unscoped-credentials.d.ts +28 -0
  280. package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
  281. package/dist/guards/no-unscoped-credentials.js +199 -0
  282. package/dist/guards/no-unscoped-credentials.js.map +1 -0
  283. package/dist/guards/no-unscoped-queries.d.ts +32 -0
  284. package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
  285. package/dist/guards/no-unscoped-queries.js +720 -0
  286. package/dist/guards/no-unscoped-queries.js.map +1 -0
  287. package/dist/guards/scan-utils.d.ts +28 -0
  288. package/dist/guards/scan-utils.d.ts.map +1 -0
  289. package/dist/guards/scan-utils.js +92 -0
  290. package/dist/guards/scan-utils.js.map +1 -0
  291. package/dist/guards/types.d.ts +28 -0
  292. package/dist/guards/types.d.ts.map +1 -0
  293. package/dist/guards/types.js +11 -0
  294. package/dist/guards/types.js.map +1 -0
  295. package/dist/org/accept-pending.d.ts.map +1 -1
  296. package/dist/org/accept-pending.js +9 -1
  297. package/dist/org/accept-pending.js.map +1 -1
  298. package/dist/org/migrations.d.ts +7 -2
  299. package/dist/org/migrations.d.ts.map +1 -1
  300. package/dist/org/migrations.js +41 -0
  301. package/dist/org/migrations.js.map +1 -1
  302. package/dist/package-lifecycle/index.d.ts +2 -0
  303. package/dist/package-lifecycle/index.d.ts.map +1 -0
  304. package/dist/package-lifecycle/index.js +2 -0
  305. package/dist/package-lifecycle/index.js.map +1 -0
  306. package/dist/package-lifecycle/manifest.d.ts +24 -0
  307. package/dist/package-lifecycle/manifest.d.ts.map +1 -0
  308. package/dist/package-lifecycle/manifest.js +81 -0
  309. package/dist/package-lifecycle/manifest.js.map +1 -0
  310. package/dist/progress/routes.d.ts +1 -1
  311. package/dist/provider-api/custom-registry.d.ts +32 -1
  312. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  313. package/dist/provider-api/custom-registry.js +41 -1
  314. package/dist/provider-api/custom-registry.js.map +1 -1
  315. package/dist/provider-api/index.d.ts +1 -1
  316. package/dist/provider-api/index.d.ts.map +1 -1
  317. package/dist/provider-api/index.js +1 -1
  318. package/dist/provider-api/index.js.map +1 -1
  319. package/dist/secrets/storage.d.ts.map +1 -1
  320. package/dist/secrets/storage.js +45 -27
  321. package/dist/secrets/storage.js.map +1 -1
  322. package/dist/server/action-discovery.d.ts.map +1 -1
  323. package/dist/server/action-discovery.js +4 -0
  324. package/dist/server/action-discovery.js.map +1 -1
  325. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  326. package/dist/server/agent-chat-plugin.js +47 -13
  327. package/dist/server/agent-chat-plugin.js.map +1 -1
  328. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  329. package/dist/server/core-routes-plugin.js +23 -12
  330. package/dist/server/core-routes-plugin.js.map +1 -1
  331. package/dist/server/csrf.d.ts.map +1 -1
  332. package/dist/server/csrf.js +18 -5
  333. package/dist/server/csrf.js.map +1 -1
  334. package/dist/server/framework-request-handler.d.ts.map +1 -1
  335. package/dist/server/framework-request-handler.js +34 -0
  336. package/dist/server/framework-request-handler.js.map +1 -1
  337. package/dist/server/google-realtime-session.d.ts.map +1 -1
  338. package/dist/server/google-realtime-session.js +2 -34
  339. package/dist/server/google-realtime-session.js.map +1 -1
  340. package/dist/server/poll.d.ts.map +1 -1
  341. package/dist/server/poll.js +24 -2
  342. package/dist/server/poll.js.map +1 -1
  343. package/dist/server/realtime-voice.d.ts.map +1 -1
  344. package/dist/server/realtime-voice.js +9 -0
  345. package/dist/server/realtime-voice.js.map +1 -1
  346. package/dist/server/request-origin.d.ts +10 -0
  347. package/dist/server/request-origin.d.ts.map +1 -0
  348. package/dist/server/request-origin.js +60 -0
  349. package/dist/server/request-origin.js.map +1 -0
  350. package/dist/server/transcribe-voice.d.ts.map +1 -1
  351. package/dist/server/transcribe-voice.js +2 -48
  352. package/dist/server/transcribe-voice.js.map +1 -1
  353. package/dist/templates/default/package.json +2 -2
  354. package/dist/templates/headless/package.json +0 -1
  355. package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  356. package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  357. package/dist/templates/workspace-core/package.json +0 -1
  358. package/dist/templates/workspace-root/package.json +0 -1
  359. package/dist/triggers/actions.d.ts.map +1 -1
  360. package/dist/triggers/actions.js +8 -3
  361. package/dist/triggers/actions.js.map +1 -1
  362. package/dist/triggers/types.d.ts +7 -1
  363. package/dist/triggers/types.d.ts.map +1 -1
  364. package/dist/triggers/types.js.map +1 -1
  365. package/docs/content/audit-log.mdx +2 -1
  366. package/docs/content/automations.mdx +7 -7
  367. package/docs/content/creating-templates.mdx +1 -1
  368. package/docs/content/internationalization.mdx +2 -1
  369. package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  370. package/docs/content/locales/ar-SA/automations.mdx +7 -7
  371. package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  372. package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  373. package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  374. package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  375. package/docs/content/locales/ar-SA/security.mdx +8 -0
  376. package/docs/content/locales/de-DE/audit-log.mdx +2 -1
  377. package/docs/content/locales/de-DE/automations.mdx +7 -7
  378. package/docs/content/locales/de-DE/getting-started.mdx +7 -1
  379. package/docs/content/locales/de-DE/internationalization.mdx +1 -1
  380. package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  381. package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  382. package/docs/content/locales/de-DE/security.mdx +8 -0
  383. package/docs/content/locales/es-ES/audit-log.mdx +2 -1
  384. package/docs/content/locales/es-ES/automations.mdx +7 -7
  385. package/docs/content/locales/es-ES/getting-started.mdx +7 -1
  386. package/docs/content/locales/es-ES/internationalization.mdx +1 -1
  387. package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  388. package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  389. package/docs/content/locales/es-ES/security.mdx +8 -0
  390. package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  391. package/docs/content/locales/fr-FR/automations.mdx +7 -7
  392. package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  393. package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  394. package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  395. package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  396. package/docs/content/locales/fr-FR/security.mdx +8 -0
  397. package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  398. package/docs/content/locales/hi-IN/automations.mdx +7 -7
  399. package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  400. package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  401. package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  402. package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  403. package/docs/content/locales/hi-IN/security.mdx +8 -0
  404. package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  405. package/docs/content/locales/ja-JP/automations.mdx +7 -7
  406. package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  407. package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  408. package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  409. package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  410. package/docs/content/locales/ja-JP/security.mdx +8 -0
  411. package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  412. package/docs/content/locales/ko-KR/automations.mdx +7 -7
  413. package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  414. package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  415. package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  416. package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  417. package/docs/content/locales/ko-KR/security.mdx +8 -0
  418. package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  419. package/docs/content/locales/pt-BR/automations.mdx +7 -7
  420. package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  421. package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  422. package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  423. package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  424. package/docs/content/locales/pt-BR/security.mdx +8 -0
  425. package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  426. package/docs/content/locales/zh-CN/automations.mdx +7 -7
  427. package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  428. package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  429. package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  430. package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  431. package/docs/content/locales/zh-CN/security.mdx +8 -0
  432. package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  433. package/docs/content/locales/zh-TW/automations.mdx +7 -7
  434. package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  435. package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  436. package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  437. package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  438. package/docs/content/locales/zh-TW/security.mdx +8 -0
  439. package/docs/content/package-lifecycle.mdx +51 -0
  440. package/docs/content/sandbox-adapters.mdx +16 -5
  441. package/package.json +10 -3
  442. package/src/templates/default/package.json +2 -2
  443. package/src/templates/headless/package.json +0 -1
  444. package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  445. package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  446. package/src/templates/workspace-core/package.json +0 -1
  447. package/src/templates/workspace-root/package.json +0 -1
  448. package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
  449. package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
  450. package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unscoped-queries.js","sourceRoot":"","sources":["../../src/guards/no-unscoped-queries.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * scanUnscopedQueries — ported from `scripts/guard-no-unscoped-queries.mjs`.\n *\n * Refuse to let any code query an \"ownable\" resource table (one that\n * spreads `...ownableColumns()`) without going through framework access\n * control: `accessFilter` / `resolveAccess` / `assertAccess`, or an\n * explicit `ownerEmail` / `userEmail` / `orgId` filter. The check is\n * per-STATEMENT, scoped to the enclosing block, so a sibling `if` branch\n * that correctly scopes its query does not defuse an unscoped sibling.\n *\n * Conditional guard — per report 005's V1 guard set table: the original\n * restricts scanning to `templates/[^/]+/(server|actions)/` and\n * `packages/[^/]+/src/`, and hardcodes a `FILE_ALLOWLIST` of this\n * framework's own sharing-primitive implementation files. For a generated\n * app (no `templates/`/`packages/` wrapper — source lives at `actions/`\n * and `server/` under the app root), this port scans `actions/` and\n * `server/` directly and drops the hardcoded allowlist in favor of an\n * optional `extraExemptPaths` param (default `[]`).\n *\n * Opt-out (within the enclosing block, or as a file header for whole-file\n * opt-outs):\n * // guard:allow-unscoped — short reason\n */\n\nimport { readFileSafe, relPosix, walk } from \"./scan-utils.js\";\nimport type { GuardFinding, GuardResult, GuardScanOptions } from \"./types.js\";\n\nexport interface UnscopedQueriesOptions extends GuardScanOptions {\n /** Exact repo-relative paths to exempt from the per-statement scan\n * (e.g. this app's own sharing-primitive implementation, if any).\n * Default `[]`. */\n extraExemptPaths?: string[];\n}\n\nconst ACCESS_CONTROL_HELPERS = [\n /\\baccessFilter\\s*\\(/,\n /\\bresolveAccess\\s*\\(/,\n /\\bassertAccess\\s*\\(/,\n /\\bgetShareableResource\\s*\\(/,\n /\\baccessFilterForShares\\s*\\(/,\n];\n\nconst EXPLICIT_OWNER_FILTERS = [\n /\\.\\s*ownerEmail\\b\\s*[,)]/,\n /\\.\\s*userEmail\\b\\s*[,)]/,\n /\\.\\s*orgId\\b\\s*[,)]/,\n /WHERE[\\s\\S]*?\\bowner_email\\b/i,\n /WHERE[\\s\\S]*?\\buser_email\\b/i,\n /WHERE[\\s\\S]*?\\borg_id\\b/i,\n];\n\nconst INSERT_OWNER_PATTERNS = [\n /\\bownerEmail\\s*:/,\n /\\bownerEmail\\s*[,}]/,\n /\\buserEmail\\s*[:,}]/,\n /\\borgId\\s*[:,}]/,\n];\n\nconst OPT_OUT_MARKER = /\\/\\/\\s*guard:allow-unscoped\\b/;\n\nconst BLOCK_OWNERSHIP_SIGNALS = [\n /\\bgetRequestUserEmail\\s*\\(/,\n /\\bgetRequestOrgId\\s*\\(/,\n /\\bgetCurrentUserEmail\\s*\\(/,\n /\\bgetCurrentOwnerEmail\\s*\\(/,\n /\\brunWithRequestContext\\s*\\(/,\n];\n\ninterface TableCall {\n exportName: string;\n sqlName: string;\n body: string;\n}\n\nfunction extractTableCalls(contents: string): TableCall[] {\n const out: TableCall[] = [];\n const headerRegex =\n /export\\s+const\\s+([a-zA-Z_$][\\w$]*)\\s*=\\s*(?:[a-zA-Z_$][\\w$]*Table|table)\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\\{/gm;\n let m: RegExpExecArray | null;\n while ((m = headerRegex.exec(contents)) !== null) {\n const exportName = m[1];\n const sqlName = m[2];\n const start = headerRegex.lastIndex - 1;\n let depth = 0;\n let inStr: string | null = null;\n let i = start;\n let bodyEnd = -1;\n for (; i < contents.length; i++) {\n const c = contents[i];\n const prev = contents[i - 1];\n if (inStr) {\n if (c === inStr && prev !== \"\\\\\") inStr = null;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n inStr = c;\n continue;\n }\n if (c === \"{\") depth++;\n else if (c === \"}\") {\n depth--;\n if (depth === 0) {\n bodyEnd = i;\n break;\n }\n }\n }\n if (bodyEnd === -1) continue;\n const body = contents.slice(start + 1, bodyEnd);\n out.push({ exportName, sqlName, body });\n }\n return out;\n}\n\nfunction extractOwnableExports(contents: string): Set<string> {\n const exports = new Set<string>();\n for (const { exportName, sqlName, body } of extractTableCalls(contents)) {\n if (/\\.\\.\\.ownableColumns\\s*\\(/.test(body)) {\n exports.add(exportName);\n exports.add(`__sql__${sqlName}`);\n }\n }\n return exports;\n}\n\nfunction collectOwnableTables(root: string): Map<string, Set<string>> {\n const byDir = new Map<string, Set<string>>();\n for (const file of walk(root)) {\n if (!file.endsWith(\"/db/schema.ts\")) continue;\n const contents = readFileSafe(file);\n if (contents === null || !/ownableColumns\\s*\\(/.test(contents)) continue;\n const ownables = extractOwnableExports(contents);\n if (ownables.size > 0) {\n byDir.set(relPosix(root, file), ownables);\n }\n }\n return byDir;\n}\n\ninterface Block {\n open: number;\n close: number;\n}\n\nfunction buildBlockTree(contents: string): Block[] {\n const blocks: Block[] = [];\n const stack: number[] = [];\n let i = 0;\n const n = contents.length;\n let inStr: string | null = null;\n const templateStack: string[] = [];\n\n while (i < n) {\n const c = contents[i];\n const next = contents[i + 1];\n\n if (inStr) {\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (inStr === \"`\") {\n if (c === \"`\") {\n inStr = null;\n i++;\n continue;\n }\n if (c === \"$\" && next === \"{\") {\n templateStack.push(\"`\");\n inStr = null;\n stack.push(-1 - templateStack.length);\n i += 2;\n continue;\n }\n } else if (c === inStr) {\n inStr = null;\n i++;\n continue;\n }\n i++;\n continue;\n }\n\n if (c === \"/\" && next === \"/\") {\n while (i < n && contents[i] !== \"\\n\") i++;\n continue;\n }\n if (c === \"/\" && next === \"*\") {\n i += 2;\n while (i < n - 1 && !(contents[i] === \"*\" && contents[i + 1] === \"/\"))\n i++;\n i += 2;\n continue;\n }\n\n if (c === '\"' || c === \"'\" || c === \"`\") {\n inStr = c;\n i++;\n continue;\n }\n\n if (c === \"{\") {\n stack.push(i);\n i++;\n continue;\n }\n if (c === \"}\") {\n const open = stack.pop();\n if (open !== undefined && open >= 0) {\n blocks.push({ open, close: i });\n } else if (open !== undefined && open < 0) {\n const kind = templateStack.pop();\n inStr = kind ?? null;\n }\n i++;\n continue;\n }\n i++;\n }\n blocks.sort((a, b) => a.open - b.open);\n return blocks;\n}\n\nfunction innermostBlock(\n blocks: Block[],\n offset: number,\n fileLen: number,\n): Block {\n let best: Block | null = null;\n for (const b of blocks) {\n if (b.open <= offset && b.close >= offset) {\n if (!best || b.open > best.open) best = b;\n }\n }\n return best || { open: 0, close: fileLen };\n}\n\nfunction computeLineOffsets(contents: string): number[] {\n const offsets = [0];\n for (let i = 0; i < contents.length; i++) {\n if (contents[i] === \"\\n\") offsets.push(i + 1);\n }\n return offsets;\n}\n\nfunction offsetToLine(offsets: number[], offset: number): number {\n let lo = 0;\n let hi = offsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (offsets[mid] <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n}\n\nfunction findChainEnd(contents: string, startIdx: number): number {\n let depth = 0;\n let inStr: string | null = null;\n let templateDepth = 0;\n const limit = Math.min(contents.length, startIdx + 10000);\n let sawOpen = false;\n for (let i = startIdx; i < limit; i++) {\n const c = contents[i];\n const prev = contents[i - 1];\n if (inStr) {\n if (inStr === \"`\") {\n if (c === \"`\") inStr = null;\n else if (c === \"$\" && contents[i + 1] === \"{\") {\n templateDepth++;\n i++;\n }\n } else if (c === inStr && prev !== \"\\\\\") {\n inStr = null;\n }\n continue;\n }\n if (templateDepth > 0 && c === \"}\") {\n templateDepth--;\n inStr = \"`\";\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n inStr = c;\n continue;\n }\n if (c === \"(\" || c === \"{\" || c === \"[\") {\n depth++;\n sawOpen = true;\n } else if (c === \")\" || c === \"}\" || c === \"]\") {\n depth--;\n } else if (c === \";\" && depth <= 0) {\n return i + 1;\n } else if (c === \"\\n\" && sawOpen && depth <= 0) {\n let j = i + 1;\n while (j < limit && /[ \\t]/.test(contents[j])) j++;\n if (j >= limit || (contents[j] !== \".\" && contents[j] !== \")\")) {\n return i + 1;\n }\n }\n }\n return limit;\n}\n\ninterface Statement {\n kind: \"drizzle\" | \"raw-sql\";\n op: string;\n name: string;\n line: number;\n queryStart: number;\n queryEnd: number;\n snippet: string;\n}\n\nfunction walkBackToChainHead(contents: string, idx: number): number {\n let i = idx;\n while (i > 0) {\n const c = contents[i];\n if (c === \";\" || c === \"{\" || c === \"}\") return i + 1;\n if (c === \"\\n\") {\n let j = i + 1;\n while (j < contents.length && /[ \\t]/.test(contents[j])) j++;\n if (contents[j] !== \".\" && contents[j] !== \")\") return i + 1;\n }\n i--;\n }\n return 0;\n}\n\nfunction findStatements(\n contents: string,\n ownableNames: Set<string>,\n ownableSqlNames: Set<string>,\n): Statement[] {\n const statements: Statement[] = [];\n const lineOffsets = computeLineOffsets(contents);\n\n const namesAlt = [...ownableNames]\n .map((n) => n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n .join(\"|\");\n\n if (namesAlt.length > 0) {\n const fromRe = new RegExp(\n `\\\\.\\\\s*from\\\\s*\\\\(\\\\s*(?:[a-zA-Z_$][\\\\w$]*\\\\s*\\\\.\\\\s*)?(${namesAlt})\\\\b`,\n \"g\",\n );\n let fromMatch: RegExpExecArray | null;\n while ((fromMatch = fromRe.exec(contents)) !== null) {\n const name = fromMatch[1];\n const queryStart = walkBackToChainHead(contents, fromMatch.index);\n const queryEnd = findChainEnd(contents, fromMatch.index);\n const snippet = contents.slice(queryStart, queryEnd);\n statements.push({\n kind: \"drizzle\",\n op: \"select\",\n name,\n line: offsetToLine(lineOffsets, fromMatch.index),\n queryStart,\n queryEnd,\n snippet,\n });\n }\n\n for (const op of [\"update\", \"delete\", \"insert\"]) {\n const re = new RegExp(\n `\\\\bdb\\\\s*\\\\.\\\\s*${op}\\\\s*\\\\(\\\\s*(?:[a-zA-Z_$][\\\\w$]*\\\\s*\\\\.\\\\s*)?(${namesAlt})\\\\b`,\n \"g\",\n );\n let m: RegExpExecArray | null;\n while ((m = re.exec(contents)) !== null) {\n const name = m[1];\n const queryStart = walkBackToChainHead(contents, m.index);\n const queryEnd = findChainEnd(contents, m.index);\n const snippet = contents.slice(queryStart, queryEnd);\n statements.push({\n kind: \"drizzle\",\n op,\n name,\n line: offsetToLine(lineOffsets, m.index),\n queryStart,\n queryEnd,\n snippet,\n });\n }\n }\n }\n\n for (const sqlName of ownableSqlNames) {\n const escaped = sqlName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const verbs: Array<[string, string]> = [\n [\"select\", `\\\\bFROM\\\\s+${escaped}\\\\b`],\n [\"update\", `\\\\bUPDATE\\\\s+${escaped}\\\\b`],\n [\"delete\", `\\\\bDELETE\\\\s+FROM\\\\s+${escaped}\\\\b`],\n [\"insert\", `\\\\bINSERT\\\\s+INTO\\\\s+${escaped}\\\\b`],\n ];\n for (const [op, body] of verbs) {\n const re = new RegExp(`[\"'\\`][^\"'\\`]*${body}[\\\\s\\\\S]*?[\"'\\`]`, \"gi\");\n let m: RegExpExecArray | null;\n while ((m = re.exec(contents)) !== null) {\n const matchStart = m.index;\n statements.push({\n kind: \"raw-sql\",\n op,\n name: sqlName,\n line: offsetToLine(lineOffsets, matchStart),\n queryStart: matchStart,\n queryEnd: matchStart + m[0].length,\n snippet: m[0],\n });\n }\n }\n }\n\n return statements;\n}\n\nfunction isControlFlowBlock(contents: string, block: Block): boolean {\n let i = block.open - 1;\n while (i > 0 && /[ \\t\\n\\r]/.test(contents[i])) i--;\n if (contents[i] === \")\") {\n let depth = 1;\n let j = i - 1;\n while (j > 0 && depth > 0) {\n if (contents[j] === \")\") depth++;\n else if (contents[j] === \"(\") depth--;\n if (depth === 0) break;\n j--;\n }\n j--;\n while (j > 0 && /[ \\t\\n\\r]/.test(contents[j])) j--;\n let end = j + 1;\n while (j > 0 && /[a-zA-Z_$]/.test(contents[j])) j--;\n const word = contents.slice(j + 1, end);\n return [\"if\", \"for\", \"while\", \"switch\", \"catch\"].includes(word);\n }\n let end = i + 1;\n while (i > 0 && /[a-zA-Z_$]/.test(contents[i])) i--;\n const word = contents.slice(i + 1, end);\n return [\"else\", \"try\", \"do\", \"finally\"].includes(word);\n}\n\nfunction directBlockText(\n contents: string,\n block: Block,\n blocks: Block[],\n queryOffset: number,\n): string {\n const candidates = blocks.filter(\n (b) =>\n b.open > block.open &&\n b.close < block.close &&\n !(b.open <= queryOffset && b.close >= queryOffset) &&\n isControlFlowBlock(contents, b),\n );\n const descendants = candidates\n .filter(\n (b) =>\n !candidates.some(\n (p) => p !== b && p.open < b.open && p.close > b.close,\n ),\n )\n .sort((a, b) => b.open - a.open);\n\n let result = contents.slice(block.open, block.close + 1);\n for (const child of descendants) {\n const start = child.open - block.open;\n const end = child.close - block.open;\n if (start < 0 || end >= result.length) continue;\n const inner = result.slice(start + 1, end);\n const blanked = inner.replace(/[^\\n]/g, \" \");\n result = result.slice(0, start + 1) + blanked + result.slice(end);\n }\n return result;\n}\n\nfunction blockHasAccessControl(blockText: string): boolean {\n if (ACCESS_CONTROL_HELPERS.some((re) => re.test(blockText))) return true;\n if (EXPLICIT_OWNER_FILTERS.some((re) => re.test(blockText))) return true;\n if (BLOCK_OWNERSHIP_SIGNALS.some((re) => re.test(blockText))) return true;\n return false;\n}\n\nfunction collectAccessControlBindings(contents: string): Set<string> {\n const names = new Set<string>();\n const bindRe =\n /\\b(?:const|let|var)\\s+([a-zA-Z_$][\\w$]*)\\s*(?::[^=]+)?\\s*=\\s*([\\s\\S]{0,400}?)(?:;|\\n\\s*(?:const|let|var|if|return|await|function|export|}|\\/\\/))/g;\n let m: RegExpExecArray | null;\n while ((m = bindRe.exec(contents)) !== null) {\n const name = m[1];\n const rhs = m[2];\n if (\n ACCESS_CONTROL_HELPERS.some((re) => re.test(rhs)) ||\n EXPLICIT_OWNER_FILTERS.some((re) => re.test(rhs))\n ) {\n names.add(name);\n }\n }\n const pushRe = /\\b([a-zA-Z_$][\\w$]*)\\.push\\s*\\(([^;]{0,400})\\)/g;\n while ((m = pushRe.exec(contents)) !== null) {\n const name = m[1];\n const arg = m[2];\n if (\n ACCESS_CONTROL_HELPERS.some((re) => re.test(arg)) ||\n EXPLICIT_OWNER_FILTERS.some((re) => re.test(arg))\n ) {\n names.add(name);\n }\n }\n return names;\n}\n\nfunction statementHasInlineAccessControl(stmt: Statement): boolean {\n const snippet = stmt.snippet;\n if (ACCESS_CONTROL_HELPERS.some((re) => re.test(snippet))) return true;\n if (\n stmt.kind === \"drizzle\" &&\n EXPLICIT_OWNER_FILTERS.some((re) => re.test(snippet))\n )\n return true;\n if (stmt.op === \"insert\") {\n if (INSERT_OWNER_PATTERNS.some((re) => re.test(snippet))) return true;\n }\n if (stmt.kind === \"raw-sql\") {\n if (\n /\\bowner_email\\b/i.test(snippet) ||\n /\\buser_email\\b/i.test(snippet) ||\n /\\borg_id\\b/i.test(snippet)\n ) {\n return true;\n }\n }\n return false;\n}\n\nfunction isOptedOutWithinBlock(blockText: string): boolean {\n return OPT_OUT_MARKER.test(blockText);\n}\n\nfunction isEligibleScanPath(rel: string): boolean {\n return /^actions\\//.test(rel) || /^server\\//.test(rel);\n}\n\nfunction scanFiles(\n root: string,\n ownablesByDir: Map<string, Set<string>>,\n extraExemptPaths: string[],\n): Array<{ file: string; hits: Statement[] }> {\n const allOwnableNames = new Set<string>();\n const allOwnableSqlNames = new Set<string>();\n for (const set of ownablesByDir.values()) {\n for (const name of set) {\n if (name.startsWith(\"__sql__\")) allOwnableSqlNames.add(name.slice(7));\n else allOwnableNames.add(name);\n }\n }\n\n const violations: Array<{ file: string; hits: Statement[] }> = [];\n\n for (const file of walk(root)) {\n if (!file.endsWith(\".ts\") && !file.endsWith(\".tsx\")) continue;\n if (file.endsWith(\".d.ts\")) continue;\n const rel = relPosix(root, file);\n if (extraExemptPaths.includes(rel)) continue;\n if (rel.endsWith(\"/db/schema.ts\")) continue;\n if (!isEligibleScanPath(rel)) continue;\n\n const contents = readFileSafe(file);\n if (contents === null) continue;\n\n if (\n !/\\bfrom\\s*\\(/.test(contents) &&\n !/\\bdb\\s*\\.\\s*(update|delete|insert)\\b/.test(contents) &&\n !/\\b(FROM|UPDATE|INSERT INTO|DELETE FROM)\\b/i.test(contents)\n ) {\n continue;\n }\n\n const head = contents.split(\"\\n\").slice(0, 30).join(\"\\n\");\n if (OPT_OUT_MARKER.test(head)) continue;\n\n const statements = findStatements(\n contents,\n allOwnableNames,\n allOwnableSqlNames,\n );\n if (statements.length === 0) continue;\n\n const fileHasAccessControl =\n ACCESS_CONTROL_HELPERS.some((re) => re.test(contents)) ||\n EXPLICIT_OWNER_FILTERS.some((re) => re.test(contents));\n if (!fileHasAccessControl) continue;\n\n const blocks = buildBlockTree(contents);\n const accessControlBindings = collectAccessControlBindings(contents);\n\n const fileViolations: Statement[] = [];\n for (const stmt of statements) {\n if (statementHasInlineAccessControl(stmt)) continue;\n\n let scoped = false;\n let cur = innermostBlock(blocks, stmt.queryStart, contents.length);\n let levels = 0;\n while (cur && levels < 8) {\n const directText = directBlockText(\n contents,\n cur,\n blocks,\n stmt.queryStart,\n );\n if (isOptedOutWithinBlock(directText)) {\n scoped = true;\n break;\n }\n if (blockHasAccessControl(directText)) {\n scoped = true;\n break;\n }\n if (\n accessControlBindings.size > 0 &&\n [...accessControlBindings].some((name) =>\n new RegExp(`\\\\b${name}\\\\b`).test(stmt.snippet),\n )\n ) {\n scoped = true;\n break;\n }\n const parents = blocks.filter(\n (b) => b.open < cur.open && b.close > cur.close,\n );\n const parent =\n parents.length > 0\n ? parents.reduce((a, b) => (a.open > b.open ? a : b))\n : null;\n if (!parent) break;\n cur = parent;\n levels++;\n }\n if (scoped) continue;\n\n fileViolations.push(stmt);\n }\n if (fileViolations.length > 0) {\n violations.push({ file: rel, hits: fileViolations });\n }\n }\n\n return violations;\n}\n\ninterface MentionViolation {\n file: string;\n line: number;\n table: string;\n}\n\nfunction scanMentionProviders(\n root: string,\n ownablesByDir: Map<string, Set<string>>,\n extraExemptPaths: string[],\n): MentionViolation[] {\n const allOwnableNames = new Set<string>();\n for (const set of ownablesByDir.values()) {\n for (const name of set) {\n if (!name.startsWith(\"__sql__\")) allOwnableNames.add(name);\n }\n }\n if (allOwnableNames.size === 0) return [];\n\n const namesAlt = [...allOwnableNames]\n .map((n) => n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n .join(\"|\");\n\n const fromOwnableRe = new RegExp(\n `\\\\.\\\\s*from\\\\s*\\\\(\\\\s*(?:[a-zA-Z_$][\\\\w$]*\\\\s*\\\\.\\\\s*)?(${namesAlt})\\\\b`,\n \"g\",\n );\n\n const CLOSURE_ACCESS_PATTERNS = [\n ...ACCESS_CONTROL_HELPERS,\n ...EXPLICIT_OWNER_FILTERS,\n ...BLOCK_OWNERSHIP_SIGNALS,\n ];\n\n const violations: MentionViolation[] = [];\n\n for (const file of walk(root)) {\n if (!file.endsWith(\".ts\") && !file.endsWith(\".tsx\")) continue;\n if (file.endsWith(\".d.ts\")) continue;\n const rel = relPosix(root, file);\n if (extraExemptPaths.includes(rel)) continue;\n if (!/^server\\//.test(rel)) continue;\n\n const contents = readFileSafe(file);\n if (contents === null) continue;\n if (\n !/\\bmentionProviders\\b/.test(contents) ||\n !/\\bfrom\\s*\\(/.test(contents)\n ) {\n continue;\n }\n\n const head = contents.split(\"\\n\").slice(0, 30).join(\"\\n\");\n if (OPT_OUT_MARKER.test(head)) continue;\n\n const lineOffsets = computeLineOffsets(contents);\n\n const mpHeaderRe = /\\bmentionProviders\\s*:/g;\n while (mpHeaderRe.exec(contents) !== null) {\n let i = mpHeaderRe.lastIndex;\n while (i < contents.length && contents[i] !== \"{\" && contents[i] !== \"\\n\")\n i++;\n if (i >= contents.length || contents[i] !== \"{\") continue;\n\n let depth = 0;\n let inStr: string | null = null;\n const mpStart = i;\n let mpEnd = -1;\n for (let j = i; j < contents.length; j++) {\n const c = contents[j];\n if (inStr) {\n if (c === \"\\\\\" && inStr !== \"`\") {\n j++;\n continue;\n }\n if (c === inStr) inStr = null;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n inStr = c;\n continue;\n }\n if (c === \"{\") depth++;\n else if (c === \"}\") {\n depth--;\n if (depth === 0) {\n mpEnd = j;\n break;\n }\n }\n }\n if (mpEnd === -1) continue;\n\n const mpBody = contents.slice(mpStart, mpEnd + 1);\n\n const searchRe = /\\bsearch\\s*:\\s*async\\s*\\([^)]*\\)\\s*=>\\s*\\{/g;\n let searchMatch: RegExpExecArray | null;\n while ((searchMatch = searchRe.exec(mpBody)) !== null) {\n const closureStart =\n mpStart + searchMatch.index + searchMatch[0].length - 1;\n let closureDepth = 0;\n let closureEnd = -1;\n let closureInStr: string | null = null;\n for (let j = closureStart; j < mpEnd; j++) {\n const c = contents[j];\n if (closureInStr) {\n if (c === \"\\\\\" && closureInStr !== \"`\") {\n j++;\n continue;\n }\n if (c === closureInStr) closureInStr = null;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n closureInStr = c;\n continue;\n }\n if (c === \"{\") closureDepth++;\n else if (c === \"}\") {\n closureDepth--;\n if (closureDepth === 0) {\n closureEnd = j;\n break;\n }\n }\n }\n if (closureEnd === -1) continue;\n\n const closureBody = contents.slice(closureStart, closureEnd + 1);\n if (OPT_OUT_MARKER.test(closureBody)) continue;\n\n fromOwnableRe.lastIndex = 0;\n let fromMatch: RegExpExecArray | null;\n while ((fromMatch = fromOwnableRe.exec(closureBody)) !== null) {\n const tableName = fromMatch[1];\n if (CLOSURE_ACCESS_PATTERNS.some((re) => re.test(closureBody)))\n continue;\n const absOffset = closureStart + fromMatch.index;\n const line = offsetToLine(lineOffsets, absOffset);\n violations.push({ file: rel, line, table: tableName });\n }\n }\n }\n }\n\n return violations;\n}\n\nexport function scanUnscopedQueries(\n options: UnscopedQueriesOptions,\n): GuardResult {\n const { root, extraExemptPaths = [] } = options;\n const findings: GuardFinding[] = [];\n\n const ownablesByDir = collectOwnableTables(root);\n if (ownablesByDir.size === 0) {\n return { name: \"no-unscoped-queries\", findings };\n }\n\n const violations = scanFiles(root, ownablesByDir, extraExemptPaths);\n for (const v of violations) {\n for (const hit of v.hits) {\n findings.push({\n file: v.file,\n line: hit.line,\n message: `unscoped ${hit.op} on \"${hit.name}\" (${hit.kind}) — add accessFilter/resolveAccess/assertAccess or an explicit ownerEmail/userEmail/orgId filter.`,\n });\n }\n }\n\n const mentionViolations = scanMentionProviders(\n root,\n ownablesByDir,\n extraExemptPaths,\n );\n for (const v of mentionViolations) {\n findings.push({\n file: v.file,\n line: v.line,\n message: `mentionProviders search closure queries \"${v.table}\" without access control — every user who types @ in chat would see rows owned by other users.`,\n });\n }\n\n return { name: \"no-unscoped-queries\", findings };\n}\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared file-walking / offset helpers used by every guard in this
3
+ * directory. Ported (behavior-for-behavior) from the identical helpers
4
+ * duplicated across `scripts/guard-*.mjs` — see each guard file for the
5
+ * specific `scripts/guard-<name>.mjs` it was derived from.
6
+ *
7
+ * The original scripts walk directories with `fs/promises` async
8
+ * generators. Guard functions here are synchronous pure functions (per the
9
+ * plan's `scan<Name>(options): GuardResult` contract), so this module uses
10
+ * `fs.readdirSync` instead — same skip-dir semantics, no behavior change.
11
+ */
12
+ /** Directories skipped by every guard's repo/app walk. */
13
+ export declare const DEFAULT_SKIP_DIRS: Set<string>;
14
+ /** Recursively walk `dir`, yielding absolute file paths. Skips any
15
+ * directory whose basename is in `skipDirs`. */
16
+ export declare function walk(dir: string, skipDirs?: Set<string>): Generator<string>;
17
+ export declare function lineColForOffset(contents: string, offset: number): {
18
+ line: number;
19
+ col: number;
20
+ };
21
+ export declare function isCommentLine(lineText: string): boolean;
22
+ export declare function relPosix(root: string, file: string): string;
23
+ export declare function readFileSafe(file: string): string | null;
24
+ /** True if `rel` matches an exact-path entry in `extraExemptPaths`
25
+ * (posix-relative, exact string match — the same shape the exempt-path
26
+ * sets used in the original monorepo guards). */
27
+ export declare function isExtraExempt(rel: string, extraExemptPaths: string[] | undefined): boolean;
28
+ //# sourceMappingURL=scan-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-utils.d.ts","sourceRoot":"","sources":["../../src/guards/scan-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,aAkB5B,CAAC;AAEH;gDACgD;AAChD,wBAAiB,IAAI,CACnB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,GAAG,CAAC,MAAM,CAAqB,GACxC,SAAS,CAAC,MAAM,CAAC,CAgBnB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAU/B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOvD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMxD;AAED;;iDAEiD;AACjD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,gBAAgB,EAAE,MAAM,EAAE,GAAG,SAAS,GACrC,OAAO,CAGT"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Shared file-walking / offset helpers used by every guard in this
3
+ * directory. Ported (behavior-for-behavior) from the identical helpers
4
+ * duplicated across `scripts/guard-*.mjs` — see each guard file for the
5
+ * specific `scripts/guard-<name>.mjs` it was derived from.
6
+ *
7
+ * The original scripts walk directories with `fs/promises` async
8
+ * generators. Guard functions here are synchronous pure functions (per the
9
+ * plan's `scan<Name>(options): GuardResult` contract), so this module uses
10
+ * `fs.readdirSync` instead — same skip-dir semantics, no behavior change.
11
+ */
12
+ import fs from "node:fs";
13
+ import path from "node:path";
14
+ /** Directories skipped by every guard's repo/app walk. */
15
+ export const DEFAULT_SKIP_DIRS = new Set([
16
+ "node_modules",
17
+ ".git",
18
+ "dist",
19
+ "build",
20
+ ".next",
21
+ ".nuxt",
22
+ ".output",
23
+ ".cache",
24
+ ".turbo",
25
+ ".netlify",
26
+ ".vercel",
27
+ ".wrangler",
28
+ ".react-router",
29
+ ".generated",
30
+ ".claude",
31
+ "out",
32
+ "coverage",
33
+ ]);
34
+ /** Recursively walk `dir`, yielding absolute file paths. Skips any
35
+ * directory whose basename is in `skipDirs`. */
36
+ export function* walk(dir, skipDirs = DEFAULT_SKIP_DIRS) {
37
+ let entries;
38
+ try {
39
+ entries = fs.readdirSync(dir, { withFileTypes: true });
40
+ }
41
+ catch {
42
+ return;
43
+ }
44
+ for (const entry of entries) {
45
+ const full = path.join(dir, entry.name);
46
+ if (entry.isDirectory()) {
47
+ if (skipDirs.has(entry.name))
48
+ continue;
49
+ yield* walk(full, skipDirs);
50
+ }
51
+ else if (entry.isFile()) {
52
+ yield full;
53
+ }
54
+ }
55
+ }
56
+ export function lineColForOffset(contents, offset) {
57
+ let line = 1;
58
+ let lineStart = 0;
59
+ for (let i = 0; i < offset; i++) {
60
+ if (contents.charCodeAt(i) === 10) {
61
+ line++;
62
+ lineStart = i + 1;
63
+ }
64
+ }
65
+ return { line, col: offset - lineStart + 1 };
66
+ }
67
+ export function isCommentLine(lineText) {
68
+ const trimmed = lineText.trimStart();
69
+ return (trimmed.startsWith("*") ||
70
+ trimmed.startsWith("//") ||
71
+ trimmed.startsWith("/*"));
72
+ }
73
+ export function relPosix(root, file) {
74
+ return path.relative(root, file).replaceAll("\\", "/");
75
+ }
76
+ export function readFileSafe(file) {
77
+ try {
78
+ return fs.readFileSync(file, "utf8");
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /** True if `rel` matches an exact-path entry in `extraExemptPaths`
85
+ * (posix-relative, exact string match — the same shape the exempt-path
86
+ * sets used in the original monorepo guards). */
87
+ export function isExtraExempt(rel, extraExemptPaths) {
88
+ if (!extraExemptPaths || extraExemptPaths.length === 0)
89
+ return false;
90
+ return extraExemptPaths.includes(rel);
91
+ }
92
+ //# sourceMappingURL=scan-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-utils.js","sourceRoot":"","sources":["../../src/guards/scan-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared file-walking / offset helpers used by every guard in this\n * directory. Ported (behavior-for-behavior) from the identical helpers\n * duplicated across `scripts/guard-*.mjs` — see each guard file for the\n * specific `scripts/guard-<name>.mjs` it was derived from.\n *\n * The original scripts walk directories with `fs/promises` async\n * generators. Guard functions here are synchronous pure functions (per the\n * plan's `scan<Name>(options): GuardResult` contract), so this module uses\n * `fs.readdirSync` instead — same skip-dir semantics, no behavior change.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/** Directories skipped by every guard's repo/app walk. */\nexport const DEFAULT_SKIP_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".nuxt\",\n \".output\",\n \".cache\",\n \".turbo\",\n \".netlify\",\n \".vercel\",\n \".wrangler\",\n \".react-router\",\n \".generated\",\n \".claude\",\n \"out\",\n \"coverage\",\n]);\n\n/** Recursively walk `dir`, yielding absolute file paths. Skips any\n * directory whose basename is in `skipDirs`. */\nexport function* walk(\n dir: string,\n skipDirs: Set<string> = DEFAULT_SKIP_DIRS,\n): Generator<string> {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (skipDirs.has(entry.name)) continue;\n yield* walk(full, skipDirs);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n}\n\nexport function lineColForOffset(\n contents: string,\n offset: number,\n): { line: number; col: number } {\n let line = 1;\n let lineStart = 0;\n for (let i = 0; i < offset; i++) {\n if (contents.charCodeAt(i) === 10) {\n line++;\n lineStart = i + 1;\n }\n }\n return { line, col: offset - lineStart + 1 };\n}\n\nexport function isCommentLine(lineText: string): boolean {\n const trimmed = lineText.trimStart();\n return (\n trimmed.startsWith(\"*\") ||\n trimmed.startsWith(\"//\") ||\n trimmed.startsWith(\"/*\")\n );\n}\n\nexport function relPosix(root: string, file: string): string {\n return path.relative(root, file).replaceAll(\"\\\\\", \"/\");\n}\n\nexport function readFileSafe(file: string): string | null {\n try {\n return fs.readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/** True if `rel` matches an exact-path entry in `extraExemptPaths`\n * (posix-relative, exact string match — the same shape the exempt-path\n * sets used in the original monorepo guards). */\nexport function isExtraExempt(\n rel: string,\n extraExemptPaths: string[] | undefined,\n): boolean {\n if (!extraExemptPaths || extraExemptPaths.length === 0) return false;\n return extraExemptPaths.includes(rel);\n}\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared types for `agent-native doctor`'s guard functions.
3
+ *
4
+ * Each guard is a pure function `scan<Name>(options): GuardResult` that
5
+ * scans a single app root and returns a flat list of findings. Guards do
6
+ * not print, exit, or read `agent-native.json` themselves — that
7
+ * orchestration lives in `../cli/doctor.ts` so the functions stay pure and
8
+ * independently testable/importable (see the `./guards` package export).
9
+ */
10
+ export interface GuardFinding {
11
+ /** Path to the offending file, relative to the scanned root. */
12
+ file: string;
13
+ /** 1-based line number of the offending line. */
14
+ line: number;
15
+ /** Human-readable explanation of the finding. */
16
+ message: string;
17
+ }
18
+ export interface GuardResult {
19
+ /** Stable guard identifier, e.g. "no-drizzle-push". Matches the name used
20
+ * by `--only` and `agent-native.json`'s `doctor.disabledGuards`. */
21
+ name: string;
22
+ findings: GuardFinding[];
23
+ }
24
+ export interface GuardScanOptions {
25
+ /** Absolute path to the app root to scan. */
26
+ root: string;
27
+ }
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guards/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B;wEACoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared types for `agent-native doctor`'s guard functions.
3
+ *
4
+ * Each guard is a pure function `scan<Name>(options): GuardResult` that
5
+ * scans a single app root and returns a flat list of findings. Guards do
6
+ * not print, exit, or read `agent-native.json` themselves — that
7
+ * orchestration lives in `../cli/doctor.ts` so the functions stay pure and
8
+ * independently testable/importable (see the `./guards` package export).
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/guards/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * Shared types for `agent-native doctor`'s guard functions.\n *\n * Each guard is a pure function `scan<Name>(options): GuardResult` that\n * scans a single app root and returns a flat list of findings. Guards do\n * not print, exit, or read `agent-native.json` themselves — that\n * orchestration lives in `../cli/doctor.ts` so the functions stay pure and\n * independently testable/importable (see the `./guards` package export).\n */\n\nexport interface GuardFinding {\n /** Path to the offending file, relative to the scanned root. */\n file: string;\n /** 1-based line number of the offending line. */\n line: number;\n /** Human-readable explanation of the finding. */\n message: string;\n}\n\nexport interface GuardResult {\n /** Stable guard identifier, e.g. \"no-drizzle-push\". Matches the name used\n * by `--only` and `agent-native.json`'s `doctor.disabledGuards`. */\n name: string;\n findings: GuardFinding[];\n}\n\nexport interface GuardScanOptions {\n /** Absolute path to the app root to scan. */\n root: string;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"accept-pending.d.ts","sourceRoot":"","sources":["../../src/org/accept-pending.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,mBAAmB,CAAC,CA8D9B"}
1
+ {"version":3,"file":"accept-pending.d.ts","sourceRoot":"","sources":["../../src/org/accept-pending.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,mBAAmB,CAAC,CAsE9B"}
@@ -50,8 +50,16 @@ export async function acceptPendingInvitationsForEmail(rawEmail) {
50
50
  });
51
51
  if (existing.rows.length === 0) {
52
52
  const role = inv.role === "admin" ? "admin" : "member";
53
+ // The SELECT above is a cheap pre-check, not a correctness guard —
54
+ // two concurrent acceptances (e.g. a retried signup hook) can both
55
+ // pass it before either INSERT commits. `ON CONFLICT (org_id,
56
+ // LOWER(email)) DO NOTHING` targets the unique expression index
57
+ // added in migrations.ts (org-members-unique-lower-email-idx) so the
58
+ // race's loser is a silent no-op instead of a thrown unique
59
+ // constraint violation or a duplicate row.
53
60
  await db.execute({
54
- sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,
61
+ sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)
62
+ ON CONFLICT (org_id, LOWER(email)) DO NOTHING`,
55
63
  args: [nanoid(), inv.orgId, email, role, Date.now()],
56
64
  });
57
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"accept-pending.js","sourceRoot":"","sources":["../../src/org/accept-pending.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec } from \"../db/client.js\";\nimport { putUserSetting } from \"../settings/user-settings.js\";\n\nconst nanoid = (): string =>\n globalThis.crypto?.randomUUID?.().replace(/-/g, \"\") ??\n Math.random().toString(36).slice(2) + Date.now().toString(36);\n\nexport interface AcceptPendingResult {\n accepted: Array<{ invitationId: string; orgId: string }>;\n activeOrgId: string | null;\n}\n\n/**\n * Accept every pending `org_invitations` row for this email:\n * - insert a matching `org_members` row (role 'member') when one doesn't exist\n * - flip the invitation's status to 'accepted'\n * - set the user's `active-org-id` to the most-recently-created invite\n *\n * Called from the Better Auth `user.create.after` hook so that a user who signs\n * up with an email they were just invited to lands in the org immediately,\n * rather than seeing a blank-slate app until they navigate to /team.\n *\n * Safe to call when the org tables don't exist (some templates don't use the\n * org module) — it swallows the \"no such table\" error and returns empty.\n */\nexport async function acceptPendingInvitationsForEmail(\n rawEmail: string,\n): Promise<AcceptPendingResult> {\n const email = rawEmail.trim().toLowerCase();\n if (!email) {\n return { accepted: [], activeOrgId: null };\n }\n\n const db = getDbExec();\n\n let rows: Array<{ id: string; orgId: string; role: string | null }> = [];\n try {\n const res = await db.execute({\n sql: `SELECT id, org_id AS \"orgId\", role FROM org_invitations\n WHERE LOWER(email) = ? AND status = 'pending'\n ORDER BY created_at DESC`,\n args: [email],\n });\n rows = res.rows.map((r: any) => ({\n id: String(r.id),\n orgId: String(r.orgId ?? r.org_id),\n role: r.role == null ? null : String(r.role),\n }));\n } catch {\n // Template doesn't use the org module / tables not migrated yet.\n return { accepted: [], activeOrgId: null };\n }\n\n if (rows.length === 0) {\n return { accepted: [], activeOrgId: null };\n }\n\n const accepted: AcceptPendingResult[\"accepted\"] = [];\n for (const inv of rows) {\n const existing = await db.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [inv.orgId, email],\n });\n if (existing.rows.length === 0) {\n const role = inv.role === \"admin\" ? \"admin\" : \"member\";\n await db.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,\n args: [nanoid(), inv.orgId, email, role, Date.now()],\n });\n }\n await db.execute({\n sql: `UPDATE org_invitations SET status = 'accepted' WHERE id = ?`,\n args: [inv.id],\n });\n accepted.push({ invitationId: inv.id, orgId: inv.orgId });\n }\n\n // Set active-org-id to the most recent invite so the user lands in a\n // populated workspace on first load.\n const activeOrgId = accepted[0]?.orgId ?? null;\n if (activeOrgId) {\n try {\n await putUserSetting(email, \"active-org-id\", { orgId: activeOrgId });\n } catch {\n // user_settings table might not exist in a minimal template — not fatal.\n }\n }\n\n return { accepted, activeOrgId };\n}\n"]}
1
+ {"version":3,"file":"accept-pending.js","sourceRoot":"","sources":["../../src/org/accept-pending.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec } from \"../db/client.js\";\nimport { putUserSetting } from \"../settings/user-settings.js\";\n\nconst nanoid = (): string =>\n globalThis.crypto?.randomUUID?.().replace(/-/g, \"\") ??\n Math.random().toString(36).slice(2) + Date.now().toString(36);\n\nexport interface AcceptPendingResult {\n accepted: Array<{ invitationId: string; orgId: string }>;\n activeOrgId: string | null;\n}\n\n/**\n * Accept every pending `org_invitations` row for this email:\n * - insert a matching `org_members` row (role 'member') when one doesn't exist\n * - flip the invitation's status to 'accepted'\n * - set the user's `active-org-id` to the most-recently-created invite\n *\n * Called from the Better Auth `user.create.after` hook so that a user who signs\n * up with an email they were just invited to lands in the org immediately,\n * rather than seeing a blank-slate app until they navigate to /team.\n *\n * Safe to call when the org tables don't exist (some templates don't use the\n * org module) — it swallows the \"no such table\" error and returns empty.\n */\nexport async function acceptPendingInvitationsForEmail(\n rawEmail: string,\n): Promise<AcceptPendingResult> {\n const email = rawEmail.trim().toLowerCase();\n if (!email) {\n return { accepted: [], activeOrgId: null };\n }\n\n const db = getDbExec();\n\n let rows: Array<{ id: string; orgId: string; role: string | null }> = [];\n try {\n const res = await db.execute({\n sql: `SELECT id, org_id AS \"orgId\", role FROM org_invitations\n WHERE LOWER(email) = ? AND status = 'pending'\n ORDER BY created_at DESC`,\n args: [email],\n });\n rows = res.rows.map((r: any) => ({\n id: String(r.id),\n orgId: String(r.orgId ?? r.org_id),\n role: r.role == null ? null : String(r.role),\n }));\n } catch {\n // Template doesn't use the org module / tables not migrated yet.\n return { accepted: [], activeOrgId: null };\n }\n\n if (rows.length === 0) {\n return { accepted: [], activeOrgId: null };\n }\n\n const accepted: AcceptPendingResult[\"accepted\"] = [];\n for (const inv of rows) {\n const existing = await db.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [inv.orgId, email],\n });\n if (existing.rows.length === 0) {\n const role = inv.role === \"admin\" ? \"admin\" : \"member\";\n // The SELECT above is a cheap pre-check, not a correctness guard —\n // two concurrent acceptances (e.g. a retried signup hook) can both\n // pass it before either INSERT commits. `ON CONFLICT (org_id,\n // LOWER(email)) DO NOTHING` targets the unique expression index\n // added in migrations.ts (org-members-unique-lower-email-idx) so the\n // race's loser is a silent no-op instead of a thrown unique\n // constraint violation or a duplicate row.\n await db.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT (org_id, LOWER(email)) DO NOTHING`,\n args: [nanoid(), inv.orgId, email, role, Date.now()],\n });\n }\n await db.execute({\n sql: `UPDATE org_invitations SET status = 'accepted' WHERE id = ?`,\n args: [inv.id],\n });\n accepted.push({ invitationId: inv.id, orgId: inv.orgId });\n }\n\n // Set active-org-id to the most recent invite so the user lands in a\n // populated workspace on first load.\n const activeOrgId = accepted[0]?.orgId ?? null;\n if (activeOrgId) {\n try {\n await putUserSetting(email, \"active-org-id\", { orgId: activeOrgId });\n } catch {\n // user_settings table might not exist in a minimal template — not fatal.\n }\n }\n\n return { accepted, activeOrgId };\n}\n"]}
@@ -3,8 +3,13 @@
3
3
  * range (1000+) so they don't collide with template-owned migrations sharing
4
4
  * the same `_migrations` table.
5
5
  */
6
- export declare const ORG_MIGRATIONS: {
6
+ export declare const ORG_MIGRATIONS: ({
7
7
  version: number;
8
8
  sql: string;
9
- }[];
9
+ name?: undefined;
10
+ } | {
11
+ version: number;
12
+ name: string;
13
+ sql: string;
14
+ })[];
10
15
  //# sourceMappingURL=migrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/org/migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;GA0D1B,CAAC"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/org/migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;IAmG1B,CAAC"}
@@ -61,5 +61,46 @@ export const ORG_MIGRATIONS = [
61
61
  version: 1008,
62
62
  sql: `CREATE INDEX IF NOT EXISTS organizations_lower_allowed_domain_idx ON organizations (LOWER(allowed_domain))`,
63
63
  },
64
+ {
65
+ // De-dup pass ahead of the unique index below. `org_members` has always
66
+ // had a `UNIQUE(org_id, email)` constraint (see v1002), but that's an
67
+ // exact-string match — callers that insert a session's raw-case email
68
+ // (e.g. handlers.ts's acceptInvitationHandler) can still create a
69
+ // second row for the same person under a different case, and every
70
+ // membership *read* in this module already matches case-insensitively
71
+ // via `LOWER(email)`. This keeps exactly one row per (org_id,
72
+ // LOWER(email)) — the row with the oldest `joined_at` (ties broken by
73
+ // `id` for determinism) — by deleting any row for which a strictly
74
+ // "older" row exists in the same group. Portable across SQLite and
75
+ // Postgres (plain correlated EXISTS subquery, no window functions).
76
+ // Must run before the unique index below or that CREATE would fail on
77
+ // any database that already has case-variant duplicates.
78
+ version: 1009,
79
+ name: "org-members-dedupe-lower-email",
80
+ sql: `DELETE FROM org_members
81
+ WHERE EXISTS (
82
+ SELECT 1 FROM org_members older
83
+ WHERE older.org_id = org_members.org_id
84
+ AND LOWER(older.email) = LOWER(org_members.email)
85
+ AND (
86
+ older.joined_at < org_members.joined_at
87
+ OR (older.joined_at = org_members.joined_at AND older.id < org_members.id)
88
+ )
89
+ )`,
90
+ },
91
+ {
92
+ // Closes the TOCTOU window in `acceptPendingInvitationsForEmail`
93
+ // (org/accept-pending.ts): a SELECT-then-INSERT check with no unique
94
+ // constraint standing behind the case-insensitive comparison every
95
+ // reader uses. Without this, two concurrent acceptances (e.g. a
96
+ // retried signup hook) can both pass the SELECT and both INSERT,
97
+ // producing duplicate membership rows. This expression index makes
98
+ // (org_id, LOWER(email)) unique at the database level so an
99
+ // `ON CONFLICT` insert (or a raw duplicate insert) is rejected
100
+ // instead of silently creating a second row.
101
+ version: 1010,
102
+ name: "org-members-unique-lower-email-idx",
103
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS org_members_org_lower_email_uidx ON org_members (org_id, LOWER(email))`,
104
+ },
64
105
  ];
65
106
  //# sourceMappingURL=migrations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/org/migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;MAKH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;;;MAOH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;;;MAOH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,wEAAwE;KAC9E;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,oEAAoE;KAC1E;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,gEAAgE;KACtE;IACD;QACE,kEAAkE;QAClE,mEAAmE;QACnE,oEAAoE;QACpE,8CAA8C;QAC9C,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,sFAAsF;KAC5F;IACD;QACE,gEAAgE;QAChE,yEAAyE;QACzE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,4GAA4G;KAClH;CACF,CAAC","sourcesContent":["/**\n * Migration definitions for the org module. Versions are namespaced into a high\n * range (1000+) so they don't collide with template-owned migrations sharing\n * the same `_migrations` table.\n */\nexport const ORG_MIGRATIONS = [\n {\n version: 1001,\n sql: `CREATE TABLE IF NOT EXISTS organizations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n created_by TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`,\n },\n {\n version: 1002,\n sql: `CREATE TABLE IF NOT EXISTS org_members (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n email TEXT NOT NULL,\n role TEXT NOT NULL,\n joined_at INTEGER NOT NULL,\n UNIQUE(org_id, email)\n )`,\n },\n {\n version: 1003,\n sql: `CREATE TABLE IF NOT EXISTS org_invitations (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n email TEXT NOT NULL,\n invited_by TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n status TEXT NOT NULL\n )`,\n },\n {\n version: 1004,\n sql: `ALTER TABLE organizations ADD COLUMN IF NOT EXISTS allowed_domain TEXT`,\n },\n {\n version: 1005,\n sql: `ALTER TABLE organizations ADD COLUMN IF NOT EXISTS a2a_secret TEXT`,\n },\n {\n version: 1006,\n sql: `ALTER TABLE org_invitations ADD COLUMN IF NOT EXISTS role TEXT`,\n },\n {\n // Every authenticated request calls `getOrgContext` which queries\n // `WHERE LOWER(m.email) = ?`. Without a supporting index this is a\n // full table scan on every request. A LOWER(email) expression index\n // lets the planner use an index seek instead.\n version: 1007,\n sql: `CREATE INDEX IF NOT EXISTS org_members_lower_email_idx ON org_members (LOWER(email))`,\n },\n {\n // Domain join and org resolution query `LOWER(allowed_domain)`.\n // Keep that opt-in lookup indexed before it appears on any request path.\n version: 1008,\n sql: `CREATE INDEX IF NOT EXISTS organizations_lower_allowed_domain_idx ON organizations (LOWER(allowed_domain))`,\n },\n];\n"]}
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/org/migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;MAKH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;;;MAOH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE;;;;;;;MAOH;KACH;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,wEAAwE;KAC9E;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,oEAAoE;KAC1E;IACD;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,gEAAgE;KACtE;IACD;QACE,kEAAkE;QAClE,mEAAmE;QACnE,oEAAoE;QACpE,8CAA8C;QAC9C,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,sFAAsF;KAC5F;IACD;QACE,gEAAgE;QAChE,yEAAyE;QACzE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,4GAA4G;KAClH;IACD;QACE,wEAAwE;QACxE,sEAAsE;QACtE,sEAAsE;QACtE,kEAAkE;QAClE,mEAAmE;QACnE,sEAAsE;QACtE,8DAA8D;QAC9D,sEAAsE;QACtE,mEAAmE;QACnE,mEAAmE;QACnE,oEAAoE;QACpE,sEAAsE;QACtE,yDAAyD;QACzD,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,gCAAgC;QACtC,GAAG,EAAE;;;;;;;;;QASD;KACL;IACD;QACE,iEAAiE;QACjE,qEAAqE;QACrE,mEAAmE;QACnE,gEAAgE;QAChE,iEAAiE;QACjE,mEAAmE;QACnE,4DAA4D;QAC5D,+DAA+D;QAC/D,6CAA6C;QAC7C,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,oCAAoC;QAC1C,GAAG,EAAE,0GAA0G;KAChH;CACF,CAAC","sourcesContent":["/**\n * Migration definitions for the org module. Versions are namespaced into a high\n * range (1000+) so they don't collide with template-owned migrations sharing\n * the same `_migrations` table.\n */\nexport const ORG_MIGRATIONS = [\n {\n version: 1001,\n sql: `CREATE TABLE IF NOT EXISTS organizations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n created_by TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`,\n },\n {\n version: 1002,\n sql: `CREATE TABLE IF NOT EXISTS org_members (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n email TEXT NOT NULL,\n role TEXT NOT NULL,\n joined_at INTEGER NOT NULL,\n UNIQUE(org_id, email)\n )`,\n },\n {\n version: 1003,\n sql: `CREATE TABLE IF NOT EXISTS org_invitations (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n email TEXT NOT NULL,\n invited_by TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n status TEXT NOT NULL\n )`,\n },\n {\n version: 1004,\n sql: `ALTER TABLE organizations ADD COLUMN IF NOT EXISTS allowed_domain TEXT`,\n },\n {\n version: 1005,\n sql: `ALTER TABLE organizations ADD COLUMN IF NOT EXISTS a2a_secret TEXT`,\n },\n {\n version: 1006,\n sql: `ALTER TABLE org_invitations ADD COLUMN IF NOT EXISTS role TEXT`,\n },\n {\n // Every authenticated request calls `getOrgContext` which queries\n // `WHERE LOWER(m.email) = ?`. Without a supporting index this is a\n // full table scan on every request. A LOWER(email) expression index\n // lets the planner use an index seek instead.\n version: 1007,\n sql: `CREATE INDEX IF NOT EXISTS org_members_lower_email_idx ON org_members (LOWER(email))`,\n },\n {\n // Domain join and org resolution query `LOWER(allowed_domain)`.\n // Keep that opt-in lookup indexed before it appears on any request path.\n version: 1008,\n sql: `CREATE INDEX IF NOT EXISTS organizations_lower_allowed_domain_idx ON organizations (LOWER(allowed_domain))`,\n },\n {\n // De-dup pass ahead of the unique index below. `org_members` has always\n // had a `UNIQUE(org_id, email)` constraint (see v1002), but that's an\n // exact-string match — callers that insert a session's raw-case email\n // (e.g. handlers.ts's acceptInvitationHandler) can still create a\n // second row for the same person under a different case, and every\n // membership *read* in this module already matches case-insensitively\n // via `LOWER(email)`. This keeps exactly one row per (org_id,\n // LOWER(email)) — the row with the oldest `joined_at` (ties broken by\n // `id` for determinism) — by deleting any row for which a strictly\n // \"older\" row exists in the same group. Portable across SQLite and\n // Postgres (plain correlated EXISTS subquery, no window functions).\n // Must run before the unique index below or that CREATE would fail on\n // any database that already has case-variant duplicates.\n version: 1009,\n name: \"org-members-dedupe-lower-email\",\n sql: `DELETE FROM org_members\n WHERE EXISTS (\n SELECT 1 FROM org_members older\n WHERE older.org_id = org_members.org_id\n AND LOWER(older.email) = LOWER(org_members.email)\n AND (\n older.joined_at < org_members.joined_at\n OR (older.joined_at = org_members.joined_at AND older.id < org_members.id)\n )\n )`,\n },\n {\n // Closes the TOCTOU window in `acceptPendingInvitationsForEmail`\n // (org/accept-pending.ts): a SELECT-then-INSERT check with no unique\n // constraint standing behind the case-insensitive comparison every\n // reader uses. Without this, two concurrent acceptances (e.g. a\n // retried signup hook) can both pass the SELECT and both INSERT,\n // producing duplicate membership rows. This expression index makes\n // (org_id, LOWER(email)) unique at the database level so an\n // `ON CONFLICT` insert (or a raw duplicate insert) is rejected\n // instead of silently creating a second row.\n version: 1010,\n name: \"org-members-unique-lower-email-idx\",\n sql: `CREATE UNIQUE INDEX IF NOT EXISTS org_members_org_lower_email_uidx ON org_members (org_id, LOWER(email))`,\n },\n];\n"]}
@@ -0,0 +1,2 @@
1
+ export * from "./manifest.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/package-lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./manifest.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/package-lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC","sourcesContent":["export * from \"./manifest.js\";\n"]}
@@ -0,0 +1,24 @@
1
+ export declare const AGENT_NATIVE_PACKAGE_MANIFEST_VERSION: 1;
2
+ export interface AgentNativePackageManifest {
3
+ manifestVersion: typeof AGENT_NATIVE_PACKAGE_MANIFEST_VERSION;
4
+ name: string;
5
+ actions: string[];
6
+ schemaEntryPoint: string;
7
+ docs: {
8
+ llms: string;
9
+ llmsFull: string;
10
+ skills: string[];
11
+ };
12
+ requiredSecrets: {
13
+ key: string;
14
+ label: string;
15
+ optional?: boolean;
16
+ }[];
17
+ peerProviders: string[];
18
+ eject?: {
19
+ sourceRoot: string;
20
+ targetDirectory: string;
21
+ };
22
+ }
23
+ export declare function assertAgentNativePackageManifest(value: unknown): asserts value is AgentNativePackageManifest;
24
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/package-lifecycle/manifest.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qCAAqC,EAAG,CAAU,CAAC;AAEhE,MAAM,WAAW,0BAA0B;IACzC,eAAe,EAAE,OAAO,qCAAqC,CAAC;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,eAAe,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACtE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,0BAA0B,CAyE7C"}
@@ -0,0 +1,81 @@
1
+ export const AGENT_NATIVE_PACKAGE_MANIFEST_VERSION = 1;
2
+ export function assertAgentNativePackageManifest(value) {
3
+ const manifest = value;
4
+ if (!manifest || typeof manifest !== "object") {
5
+ throw new Error("Package manifest must be an object");
6
+ }
7
+ if (manifest.manifestVersion !== AGENT_NATIVE_PACKAGE_MANIFEST_VERSION) {
8
+ throw new Error(`Unsupported package manifest version ${String(manifest.manifestVersion)}`);
9
+ }
10
+ if (!manifest.name || !manifest.schemaEntryPoint) {
11
+ throw new Error("Package manifest is missing name or schemaEntryPoint");
12
+ }
13
+ if (!/^(@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(manifest.name)) {
14
+ throw new Error(`Invalid package manifest name: ${manifest.name}`);
15
+ }
16
+ if (!manifest.schemaEntryPoint.startsWith(`${manifest.name}/`) ||
17
+ !isSafeSpecifier(manifest.schemaEntryPoint)) {
18
+ throw new Error("Package manifest schemaEntryPoint is unsafe");
19
+ }
20
+ if (!Array.isArray(manifest.actions) ||
21
+ !manifest.docs ||
22
+ !Array.isArray(manifest.docs.skills) ||
23
+ !Array.isArray(manifest.requiredSecrets) ||
24
+ !Array.isArray(manifest.peerProviders)) {
25
+ throw new Error("Package manifest contribution lists are invalid");
26
+ }
27
+ for (const action of manifest.actions) {
28
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(action)) {
29
+ throw new Error(`Invalid package action name: ${action}`);
30
+ }
31
+ }
32
+ for (const skill of manifest.docs.skills) {
33
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(skill)) {
34
+ throw new Error(`Invalid package skill name: ${skill}`);
35
+ }
36
+ }
37
+ for (const docPath of [manifest.docs.llms, manifest.docs.llmsFull]) {
38
+ if (!isSafeRelativePath(docPath)) {
39
+ throw new Error(`Unsafe package documentation path: ${docPath}`);
40
+ }
41
+ }
42
+ for (const secret of manifest.requiredSecrets) {
43
+ if (!secret ||
44
+ !/^[A-Z][A-Z0-9_]*$/.test(secret.key) ||
45
+ typeof secret.label !== "string" ||
46
+ secret.label.length === 0 ||
47
+ secret.label.length > 120 ||
48
+ (secret.optional !== undefined && typeof secret.optional !== "boolean")) {
49
+ throw new Error("Invalid package requiredSecrets entry");
50
+ }
51
+ }
52
+ for (const provider of manifest.peerProviders) {
53
+ if (typeof provider !== "string" ||
54
+ !/^[a-z0-9][a-z0-9-]*$/.test(provider)) {
55
+ throw new Error(`Invalid package peer provider: ${String(provider)}`);
56
+ }
57
+ }
58
+ if (manifest.eject &&
59
+ (!isSafeRelativePath(manifest.eject.sourceRoot) ||
60
+ !isSafeRelativePath(manifest.eject.targetDirectory))) {
61
+ throw new Error("Package eject paths must be safe relative paths");
62
+ }
63
+ }
64
+ function isSafeRelativePath(value) {
65
+ if (typeof value !== "string" || value.length === 0 || value.includes("\\")) {
66
+ return false;
67
+ }
68
+ if (value.includes("\0") ||
69
+ value.startsWith("/") ||
70
+ pathSegments(value).includes("..")) {
71
+ return false;
72
+ }
73
+ return !value.split("/").includes("");
74
+ }
75
+ function isSafeSpecifier(value) {
76
+ return (/^[A-Za-z0-9@._/-]+$/.test(value) && !pathSegments(value).includes(".."));
77
+ }
78
+ function pathSegments(value) {
79
+ return value.split("/");
80
+ }
81
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/package-lifecycle/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["export const AGENT_NATIVE_PACKAGE_MANIFEST_VERSION = 1 as const;\n\nexport interface AgentNativePackageManifest {\n manifestVersion: typeof AGENT_NATIVE_PACKAGE_MANIFEST_VERSION;\n name: string;\n actions: string[];\n schemaEntryPoint: string;\n docs: {\n llms: string;\n llmsFull: string;\n skills: string[];\n };\n requiredSecrets: { key: string; label: string; optional?: boolean }[];\n peerProviders: string[];\n eject?: {\n sourceRoot: string;\n targetDirectory: string;\n };\n}\n\nexport function assertAgentNativePackageManifest(\n value: unknown,\n): asserts value is AgentNativePackageManifest {\n const manifest = value as Partial<AgentNativePackageManifest> | null;\n if (!manifest || typeof manifest !== \"object\") {\n throw new Error(\"Package manifest must be an object\");\n }\n if (manifest.manifestVersion !== AGENT_NATIVE_PACKAGE_MANIFEST_VERSION) {\n throw new Error(\n `Unsupported package manifest version ${String(manifest.manifestVersion)}`,\n );\n }\n if (!manifest.name || !manifest.schemaEntryPoint) {\n throw new Error(\"Package manifest is missing name or schemaEntryPoint\");\n }\n if (!/^(@[a-z0-9._-]+\\/)?[a-z0-9._-]+$/.test(manifest.name)) {\n throw new Error(`Invalid package manifest name: ${manifest.name}`);\n }\n if (\n !manifest.schemaEntryPoint.startsWith(`${manifest.name}/`) ||\n !isSafeSpecifier(manifest.schemaEntryPoint)\n ) {\n throw new Error(\"Package manifest schemaEntryPoint is unsafe\");\n }\n if (\n !Array.isArray(manifest.actions) ||\n !manifest.docs ||\n !Array.isArray(manifest.docs.skills) ||\n !Array.isArray(manifest.requiredSecrets) ||\n !Array.isArray(manifest.peerProviders)\n ) {\n throw new Error(\"Package manifest contribution lists are invalid\");\n }\n for (const action of manifest.actions) {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(action)) {\n throw new Error(`Invalid package action name: ${action}`);\n }\n }\n for (const skill of manifest.docs.skills) {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(skill)) {\n throw new Error(`Invalid package skill name: ${skill}`);\n }\n }\n for (const docPath of [manifest.docs.llms, manifest.docs.llmsFull]) {\n if (!isSafeRelativePath(docPath)) {\n throw new Error(`Unsafe package documentation path: ${docPath}`);\n }\n }\n for (const secret of manifest.requiredSecrets) {\n if (\n !secret ||\n !/^[A-Z][A-Z0-9_]*$/.test(secret.key) ||\n typeof secret.label !== \"string\" ||\n secret.label.length === 0 ||\n secret.label.length > 120 ||\n (secret.optional !== undefined && typeof secret.optional !== \"boolean\")\n ) {\n throw new Error(\"Invalid package requiredSecrets entry\");\n }\n }\n for (const provider of manifest.peerProviders) {\n if (\n typeof provider !== \"string\" ||\n !/^[a-z0-9][a-z0-9-]*$/.test(provider)\n ) {\n throw new Error(`Invalid package peer provider: ${String(provider)}`);\n }\n }\n if (\n manifest.eject &&\n (!isSafeRelativePath(manifest.eject.sourceRoot) ||\n !isSafeRelativePath(manifest.eject.targetDirectory))\n ) {\n throw new Error(\"Package eject paths must be safe relative paths\");\n }\n}\n\nfunction isSafeRelativePath(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length === 0 || value.includes(\"\\\\\")) {\n return false;\n }\n if (\n value.includes(\"\\0\") ||\n value.startsWith(\"/\") ||\n pathSegments(value).includes(\"..\")\n ) {\n return false;\n }\n return !value.split(\"/\").includes(\"\");\n}\n\nfunction isSafeSpecifier(value: string): boolean {\n return (\n /^[A-Za-z0-9@._/-]+$/.test(value) && !pathSegments(value).includes(\"..\")\n );\n}\n\nfunction pathSegments(value: string): string[] {\n return value.split(\"/\");\n}\n"]}
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map