@agent-native/core 0.26.8 → 0.27.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 (218) hide show
  1. package/dist/agent/run-ownership.d.ts +12 -0
  2. package/dist/agent/run-ownership.d.ts.map +1 -0
  3. package/dist/agent/run-ownership.js +39 -0
  4. package/dist/agent/run-ownership.js.map +1 -0
  5. package/dist/cli/index.js +2 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/skills.d.ts.map +1 -1
  8. package/dist/cli/skills.js +108 -3
  9. package/dist/cli/skills.js.map +1 -1
  10. package/dist/client/db-admin/DataGrid.d.ts +42 -0
  11. package/dist/client/db-admin/DataGrid.d.ts.map +1 -0
  12. package/dist/client/db-admin/DataGrid.js +204 -0
  13. package/dist/client/db-admin/DataGrid.js.map +1 -0
  14. package/dist/client/db-admin/DbAdminPage.d.ts +2 -0
  15. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -0
  16. package/dist/client/db-admin/DbAdminPage.js +72 -0
  17. package/dist/client/db-admin/DbAdminPage.js.map +1 -0
  18. package/dist/client/db-admin/DevDatabaseLink.d.ts +19 -0
  19. package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -0
  20. package/dist/client/db-admin/DevDatabaseLink.js +25 -0
  21. package/dist/client/db-admin/DevDatabaseLink.js.map +1 -0
  22. package/dist/client/db-admin/EditableCell.d.ts +26 -0
  23. package/dist/client/db-admin/EditableCell.d.ts.map +1 -0
  24. package/dist/client/db-admin/EditableCell.js +150 -0
  25. package/dist/client/db-admin/EditableCell.js.map +1 -0
  26. package/dist/client/db-admin/FilterBar.d.ts +8 -0
  27. package/dist/client/db-admin/FilterBar.d.ts.map +1 -0
  28. package/dist/client/db-admin/FilterBar.js +68 -0
  29. package/dist/client/db-admin/FilterBar.js.map +1 -0
  30. package/dist/client/db-admin/ResultsGrid.d.ts +6 -0
  31. package/dist/client/db-admin/ResultsGrid.d.ts.map +1 -0
  32. package/dist/client/db-admin/ResultsGrid.js +41 -0
  33. package/dist/client/db-admin/ResultsGrid.js.map +1 -0
  34. package/dist/client/db-admin/RowSidePanel.d.ts +18 -0
  35. package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -0
  36. package/dist/client/db-admin/RowSidePanel.js +104 -0
  37. package/dist/client/db-admin/RowSidePanel.js.map +1 -0
  38. package/dist/client/db-admin/SqlEditor.d.ts +8 -0
  39. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -0
  40. package/dist/client/db-admin/SqlEditor.js +350 -0
  41. package/dist/client/db-admin/SqlEditor.js.map +1 -0
  42. package/dist/client/db-admin/TableBrowser.d.ts +10 -0
  43. package/dist/client/db-admin/TableBrowser.d.ts.map +1 -0
  44. package/dist/client/db-admin/TableBrowser.js +61 -0
  45. package/dist/client/db-admin/TableBrowser.js.map +1 -0
  46. package/dist/client/db-admin/TableEditor.d.ts +9 -0
  47. package/dist/client/db-admin/TableEditor.d.ts.map +1 -0
  48. package/dist/client/db-admin/TableEditor.js +254 -0
  49. package/dist/client/db-admin/TableEditor.js.map +1 -0
  50. package/dist/client/db-admin/cell-format.d.ts +55 -0
  51. package/dist/client/db-admin/cell-format.d.ts.map +1 -0
  52. package/dist/client/db-admin/cell-format.js +223 -0
  53. package/dist/client/db-admin/cell-format.js.map +1 -0
  54. package/dist/client/db-admin/changeset.d.ts +74 -0
  55. package/dist/client/db-admin/changeset.d.ts.map +1 -0
  56. package/dist/client/db-admin/changeset.js +169 -0
  57. package/dist/client/db-admin/changeset.js.map +1 -0
  58. package/dist/client/db-admin/export-utils.d.ts +15 -0
  59. package/dist/client/db-admin/export-utils.d.ts.map +1 -0
  60. package/dist/client/db-admin/export-utils.js +62 -0
  61. package/dist/client/db-admin/export-utils.js.map +1 -0
  62. package/dist/client/db-admin/index.d.ts +7 -0
  63. package/dist/client/db-admin/index.d.ts.map +1 -0
  64. package/dist/client/db-admin/index.js +8 -0
  65. package/dist/client/db-admin/index.js.map +1 -0
  66. package/dist/client/db-admin/sql-storage.d.ts +35 -0
  67. package/dist/client/db-admin/sql-storage.d.ts.map +1 -0
  68. package/dist/client/db-admin/sql-storage.js +117 -0
  69. package/dist/client/db-admin/sql-storage.js.map +1 -0
  70. package/dist/client/db-admin/storage.d.ts +24 -0
  71. package/dist/client/db-admin/storage.d.ts.map +1 -0
  72. package/dist/client/db-admin/storage.js +50 -0
  73. package/dist/client/db-admin/storage.js.map +1 -0
  74. package/dist/client/db-admin/useAgentSync.d.ts +22 -0
  75. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -0
  76. package/dist/client/db-admin/useAgentSync.js +120 -0
  77. package/dist/client/db-admin/useAgentSync.js.map +1 -0
  78. package/dist/client/db-admin/useDbAdmin.d.ts +20 -0
  79. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -0
  80. package/dist/client/db-admin/useDbAdmin.js +154 -0
  81. package/dist/client/db-admin/useDbAdmin.js.map +1 -0
  82. package/dist/client/index.d.ts +1 -0
  83. package/dist/client/index.d.ts.map +1 -1
  84. package/dist/client/index.js +1 -0
  85. package/dist/client/index.js.map +1 -1
  86. package/dist/credentials/index.d.ts.map +1 -1
  87. package/dist/credentials/index.js +25 -5
  88. package/dist/credentials/index.js.map +1 -1
  89. package/dist/db-admin/agent-tools.d.ts +15 -0
  90. package/dist/db-admin/agent-tools.d.ts.map +1 -0
  91. package/dist/db-admin/agent-tools.js +147 -0
  92. package/dist/db-admin/agent-tools.js.map +1 -0
  93. package/dist/db-admin/operations.d.ts +17 -0
  94. package/dist/db-admin/operations.d.ts.map +1 -0
  95. package/dist/db-admin/operations.js +541 -0
  96. package/dist/db-admin/operations.js.map +1 -0
  97. package/dist/db-admin/routes.d.ts +5 -0
  98. package/dist/db-admin/routes.d.ts.map +1 -0
  99. package/dist/db-admin/routes.js +134 -0
  100. package/dist/db-admin/routes.js.map +1 -0
  101. package/dist/db-admin/types.d.ts +85 -0
  102. package/dist/db-admin/types.d.ts.map +1 -0
  103. package/dist/db-admin/types.js +9 -0
  104. package/dist/db-admin/types.js.map +1 -0
  105. package/dist/extensions/url-safety.d.ts +20 -0
  106. package/dist/extensions/url-safety.d.ts.map +1 -1
  107. package/dist/extensions/url-safety.js +43 -0
  108. package/dist/extensions/url-safety.js.map +1 -1
  109. package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
  110. package/dist/file-upload/actions/upload-image.js +6 -1
  111. package/dist/file-upload/actions/upload-image.js.map +1 -1
  112. package/dist/integrations/adapters/email.d.ts.map +1 -1
  113. package/dist/integrations/adapters/email.js +112 -0
  114. package/dist/integrations/adapters/email.js.map +1 -1
  115. package/dist/integrations/types.d.ts +11 -0
  116. package/dist/integrations/types.d.ts.map +1 -1
  117. package/dist/integrations/types.js.map +1 -1
  118. package/dist/scripts/db/exec.d.ts.map +1 -1
  119. package/dist/scripts/db/exec.js +2 -1
  120. package/dist/scripts/db/exec.js.map +1 -1
  121. package/dist/scripts/db/index.d.ts.map +1 -1
  122. package/dist/scripts/db/index.js +1 -0
  123. package/dist/scripts/db/index.js.map +1 -1
  124. package/dist/scripts/db/migrate-encrypt-credentials.d.ts +28 -0
  125. package/dist/scripts/db/migrate-encrypt-credentials.d.ts.map +1 -0
  126. package/dist/scripts/db/migrate-encrypt-credentials.js +190 -0
  127. package/dist/scripts/db/migrate-encrypt-credentials.js.map +1 -0
  128. package/dist/scripts/db/query.d.ts.map +1 -1
  129. package/dist/scripts/db/query.js +2 -1
  130. package/dist/scripts/db/query.js.map +1 -1
  131. package/dist/scripts/db/safety.d.ts +1 -0
  132. package/dist/scripts/db/safety.d.ts.map +1 -1
  133. package/dist/scripts/db/safety.js +32 -0
  134. package/dist/scripts/db/safety.js.map +1 -1
  135. package/dist/scripts/db/scoping.d.ts.map +1 -1
  136. package/dist/scripts/db/scoping.js +11 -1
  137. package/dist/scripts/db/scoping.js.map +1 -1
  138. package/dist/secrets/crypto.d.ts +28 -0
  139. package/dist/secrets/crypto.d.ts.map +1 -0
  140. package/dist/secrets/crypto.js +81 -0
  141. package/dist/secrets/crypto.js.map +1 -0
  142. package/dist/secrets/storage.d.ts.map +1 -1
  143. package/dist/secrets/storage.js +3 -61
  144. package/dist/secrets/storage.js.map +1 -1
  145. package/dist/server/action-discovery.d.ts.map +1 -1
  146. package/dist/server/action-discovery.js +5 -2
  147. package/dist/server/action-discovery.js.map +1 -1
  148. package/dist/server/action-routes.d.ts.map +1 -1
  149. package/dist/server/action-routes.js +24 -7
  150. package/dist/server/action-routes.js.map +1 -1
  151. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  152. package/dist/server/agent-chat-plugin.js +39 -0
  153. package/dist/server/agent-chat-plugin.js.map +1 -1
  154. package/dist/server/auth.d.ts +1 -1
  155. package/dist/server/auth.d.ts.map +1 -1
  156. package/dist/server/auth.js.map +1 -1
  157. package/dist/server/better-auth-instance.js +3 -3
  158. package/dist/server/better-auth-instance.js.map +1 -1
  159. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  160. package/dist/server/core-routes-plugin.js +5 -0
  161. package/dist/server/core-routes-plugin.js.map +1 -1
  162. package/dist/server/csrf.d.ts.map +1 -1
  163. package/dist/server/csrf.js +9 -1
  164. package/dist/server/csrf.js.map +1 -1
  165. package/dist/server/design-token-utils.d.ts +8 -1
  166. package/dist/server/design-token-utils.d.ts.map +1 -1
  167. package/dist/server/design-token-utils.js +12 -4
  168. package/dist/server/design-token-utils.js.map +1 -1
  169. package/dist/templates/default/AGENTS.md +4 -4
  170. package/dist/templates/default/app/routes/database.tsx +13 -0
  171. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
  172. package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
  173. package/dist/vite/client.d.ts.map +1 -1
  174. package/dist/vite/client.js +4 -0
  175. package/dist/vite/client.js.map +1 -1
  176. package/docs/content/a2a-protocol.md +2 -2
  177. package/docs/content/actions.md +2 -54
  178. package/docs/content/agent-mentions.md +1 -1
  179. package/docs/content/agent-teams.md +1 -1
  180. package/docs/content/authentication.md +2 -2
  181. package/docs/content/cli-adapters.md +33 -17
  182. package/docs/content/client.md +11 -20
  183. package/docs/content/code-agents-ui.md +19 -6
  184. package/docs/content/context-awareness.md +36 -20
  185. package/docs/content/database.md +3 -3
  186. package/docs/content/deployment.md +8 -8
  187. package/docs/content/dispatch.md +1 -1
  188. package/docs/content/external-agents.md +5 -1
  189. package/docs/content/faq.md +1 -0
  190. package/docs/content/frames.md +110 -30
  191. package/docs/content/getting-started.md +15 -14
  192. package/docs/content/mcp-clients.md +1 -1
  193. package/docs/content/mcp-protocol.md +11 -88
  194. package/docs/content/messaging.md +1 -1
  195. package/docs/content/migration-workbench.md +13 -87
  196. package/docs/content/multi-app-workspace.md +2 -38
  197. package/docs/content/multi-tenancy.md +3 -26
  198. package/docs/content/onboarding.md +10 -3
  199. package/docs/content/recurring-jobs.md +2 -2
  200. package/docs/content/security.md +33 -1
  201. package/docs/content/server.md +1 -1
  202. package/docs/content/skills-guide.md +7 -4
  203. package/docs/content/template-assets.md +9 -9
  204. package/docs/content/template-brain.md +114 -388
  205. package/docs/content/template-clips.md +42 -2
  206. package/docs/content/template-content.md +1 -1
  207. package/docs/content/template-design.md +38 -0
  208. package/docs/content/template-dispatch.md +3 -3
  209. package/docs/content/template-forms.md +6 -6
  210. package/docs/content/template-starter.md +2 -2
  211. package/docs/content/using-your-agent.md +56 -0
  212. package/docs/content/workspace-management.md +6 -6
  213. package/docs/content/workspace.md +19 -0
  214. package/package.json +10 -3
  215. package/src/templates/default/AGENTS.md +4 -4
  216. package/src/templates/default/app/routes/database.tsx +13 -0
  217. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
  218. package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/scripts/db/exec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core script: db-exec\n *\n * Execute write SQL statements (INSERT, UPDATE, DELETE, REPLACE)\n * against a SQLite or Postgres database.\n *\n * In production mode, temporary views scope UPDATE/DELETE to the current\n * user's data (AGENT_USER_EMAIL / AGENT_ORG_ID). For INSERT, the\n * `owner_email` and `org_id` columns are auto-injected if the target\n * table uses the ownership convention.\n *\n * Usage:\n * pnpm action db-exec --sql \"UPDATE forms SET status=? WHERE id=?\" [--args '[\"published\",\"abc\"]'] [--db path]\n * pnpm action db-exec --statements '[{\"sql\":\"INSERT INTO notes (id,title) VALUES (?,?)\",\"args\":[\"n1\",\"One\"]},{\"sql\":\"UPDATE counters SET value=value+1 WHERE key=?\",\"args\":[\"notes\"]}]'\n */\n\nimport path from \"path\";\nimport { getDatabaseUrl } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport {\n buildScopingPostgres,\n buildScopingSqlite,\n type ScopingContext,\n} from \"./scoping.js\";\nimport {\n assertNoRawDbAccessControlWrite,\n assertNoSensitiveFrameworkTables,\n} from \"./safety.js\";\nimport { createSqliteScriptClient } from \"./sqlite-client.js\";\n\nfunction isPostgresUrl(url: string): boolean {\n return url.startsWith(\"postgres://\") || url.startsWith(\"postgresql://\");\n}\n\ninterface DbExecStatement {\n sql: string;\n args: unknown[];\n}\n\ninterface DbExecResult {\n index: number;\n sql: string;\n changes?: number;\n lastInsertRowid?: bigint | number;\n rows?: Record<string, unknown>[];\n}\n\nfunction parseSqlArgs(raw: string | undefined, label = \"--args\"): unknown[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n // Fall through to the shared error below.\n }\n fail(`${label} must be a JSON array`);\n}\n\nfunction parseStatements(parsed: Record<string, string>): DbExecStatement[] {\n if (parsed.statements) {\n if (parsed.sql) {\n fail(\"Pass either --sql or --statements, not both.\");\n }\n let raw: unknown;\n try {\n raw = JSON.parse(parsed.statements);\n } catch {\n fail(\n '--statements must be a JSON array of {\"sql\": string, \"args\"?: unknown[]} objects',\n );\n }\n if (!Array.isArray(raw) || raw.length === 0) {\n fail(\"--statements must be a non-empty JSON array\");\n }\n return raw.map((entry, index) => {\n if (\n !entry ||\n typeof entry !== \"object\" ||\n typeof (entry as any).sql !== \"string\" ||\n !(entry as any).sql.trim()\n ) {\n fail(`Statement ${index + 1} must include a non-empty sql string`);\n }\n const args = (entry as any).args;\n if (args != null && !Array.isArray(args)) {\n fail(`Statement ${index + 1} args must be a JSON array`);\n }\n return { sql: (entry as any).sql, args: args ?? [] };\n });\n }\n\n if (!parsed.sql) {\n fail(\n '--sql is required unless --statements is provided. Example: --sql \"UPDATE forms SET status=? WHERE id=?\" --args \\'[\"published\",\"abc\"]\\'',\n );\n }\n return [{ sql: parsed.sql, args: parseSqlArgs(parsed.args) }];\n}\n\nfunction stripLeadingSqlComments(sql: string): string {\n return sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim();\n}\n\nfunction hasAdditionalStatement(sql: string): boolean {\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n if (state === \"block-comment\") {\n if (ch === \"*\" && next === \"/\") {\n i++;\n state = \"normal\";\n }\n continue;\n }\n if (state === \"single\") {\n if (ch === \"'\" && next === \"'\") {\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n if (state === \"double\") {\n if (ch === '\"' && next === '\"') {\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n state = \"double\";\n continue;\n }\n if (ch === \";\") {\n return sql.slice(i + 1).trim().length > 0;\n }\n }\n return false;\n}\n\nfunction normalizeUserSql(sql: string, index: number): string {\n const stripped = stripLeadingSqlComments(sql);\n if (!stripped) {\n fail(`Statement ${index} is empty`);\n }\n if (hasAdditionalStatement(stripped)) {\n fail(\n `Statement ${index} contains multiple SQL statements. Use --statements for batches so each write can be validated and run transactionally.`,\n );\n }\n return stripped.replace(/;\\s*$/, \"\");\n}\n\nfunction validateWriteSql(sql: string, index: number): string {\n const normalized = normalizeUserSql(sql, index);\n const upper = normalized.toUpperCase();\n const allowed = [\"INSERT\", \"UPDATE\", \"DELETE\", \"REPLACE\"];\n const blocked = [\"SELECT\", \"WITH\", \"EXPLAIN\", \"PRAGMA\"];\n\n if (blocked.some((kw) => upper.startsWith(kw))) {\n fail(\n `Statement ${index}: use db-query for SELECT/read statements. db-exec is for writes only.`,\n );\n }\n if (upper.startsWith(\"CREATE\") || upper.startsWith(\"ALTER\")) {\n fail(\n `Statement ${index}: schema changes are not allowed through db-exec. Additive schema changes must go through reviewed migrations/startup code, not ad-hoc agent SQL.`,\n );\n }\n if (!allowed.some((kw) => upper.startsWith(kw))) {\n fail(\n `Statement ${index}: only ${allowed.join(\", \")} statements are allowed. ` +\n `Dangerous operations like DROP, ATTACH, VACUUM, DETACH, CREATE, and ALTER are blocked.`,\n );\n }\n assertNoSensitiveFrameworkTables(normalized, \"write\");\n assertNoRawDbAccessControlWrite(normalized);\n return normalized;\n}\n\nfunction convertQuestionMarksToPostgresParams(sql: string): string {\n let index = 0;\n let out = \"\";\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n out += ch;\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n\n if (state === \"block-comment\") {\n out += ch;\n if (ch === \"*\" && next === \"/\") {\n out += next;\n i++;\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"single\") {\n out += ch;\n if (ch === \"'\" && next === \"'\") {\n out += next;\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"double\") {\n out += ch;\n if (ch === '\"' && next === '\"') {\n out += next;\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n out += ch + next;\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n out += ch + next;\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n out += ch;\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n out += ch;\n state = \"double\";\n continue;\n }\n if (ch === \"?\") {\n index++;\n out += `$${index}`;\n continue;\n }\n out += ch;\n }\n\n return out;\n}\n\nfunction normalizePostgresSql(sql: string, args: unknown[]): string {\n if (args.length === 0 || /\\$\\d+\\b/.test(sql)) return sql;\n return convertQuestionMarksToPostgresParams(sql);\n}\n\n/**\n * For INSERT statements targeting a table with owner_email / org_id columns,\n * auto-inject the current user's email and org ID if not already present.\n *\n * Handles the explicit column list form:\n * INSERT INTO table (col1, col2) VALUES (val1, val2)\n */\nfunction injectOwnership(sql: string, scoping: ScopingContext): string {\n if (!scoping.active) return sql;\n\n const upper = sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim()\n .toUpperCase();\n if (!upper.startsWith(\"INSERT\")) return sql;\n\n // Extract table name: INSERT INTO <table> ...\n const match = sql.match(/INSERT\\s+INTO\\s+[\"']?(\\w+)[\"']?/i);\n if (!match) return sql;\n\n const tableName = match[1];\n\n // Determine which columns to inject\n const injections: { col: string; value: string }[] = [];\n\n if (\n scoping.userEmail &&\n scoping.ownerEmailTables.has(tableName) &&\n !/owner_email/i.test(sql)\n ) {\n injections.push({\n col: \"owner_email\",\n value: `'${scoping.userEmail.replace(/'/g, \"''\")}'`,\n });\n }\n\n if (\n scoping.orgId &&\n scoping.orgIdTables.has(tableName) &&\n !/org_id/i.test(sql)\n ) {\n injections.push({\n col: \"org_id\",\n value: `'${scoping.orgId.replace(/'/g, \"''\")}'`,\n });\n }\n\n if (injections.length === 0) return sql;\n\n // Try to inject into explicit column list: INSERT INTO t (cols) VALUES (vals)\n const colListMatch = sql.match(\n /(INSERT\\s+INTO\\s+[\"']?\\w+[\"']?\\s*)\\(([^)]+)\\)(\\s*VALUES\\s*)\\(([^)]+)\\)/i,\n );\n if (colListMatch) {\n const [, prefix, cols, valueKeyword, vals] = colListMatch;\n const extraCols = injections.map((i) => i.col).join(\", \");\n const extraVals = injections.map((i) => i.value).join(\", \");\n return `${prefix}(${cols}, ${extraCols})${valueKeyword}(${vals}, ${extraVals})`;\n }\n\n return sql;\n}\n\nfunction escapeSqlString(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction sqliteScopePredicate(\n tableName: string,\n scoping: ScopingContext,\n): string | null {\n if (tableName === \"tool_data\" && scoping.userEmail) {\n const userClause = `(scope = 'user' AND owner_email = '${escapeSqlString(scoping.userEmail)}')`;\n const orgClause = scoping.orgId\n ? ` OR (scope = 'org' AND org_id = '${escapeSqlString(scoping.orgId)}')`\n : \"\";\n return `(${userClause}${orgClause})`;\n }\n\n const clauses: string[] = [];\n const hasOwner = scoping.ownerEmailTables.has(tableName);\n const hasOrg = scoping.orgIdTables.has(tableName);\n if (scoping.userEmail && hasOwner) {\n const ownerClause = `owner_email = '${escapeSqlString(scoping.userEmail)}'`;\n if (scoping.orgId && hasOrg) {\n clauses.push(\n `${ownerClause} AND (org_id = '${escapeSqlString(scoping.orgId)}' OR org_id IS NULL)`,\n );\n } else {\n clauses.push(ownerClause);\n }\n } else if (scoping.orgId && hasOrg) {\n clauses.push(`org_id = '${escapeSqlString(scoping.orgId)}'`);\n }\n if (clauses.length > 0) return clauses.join(\" AND \");\n return scoping.tablePredicates.get(tableName) ?? null;\n}\n\nfunction splitReturning(sql: string): { body: string; returning: string } {\n const match = /\\bRETURNING\\b/i.exec(sql);\n if (!match) return { body: sql, returning: \"\" };\n return {\n body: sql.slice(0, match.index).trimEnd(),\n returning: sql.slice(match.index),\n };\n}\n\nfunction addSqliteScopeToWhere(sql: string, predicate: string): string {\n const { body, returning } = splitReturning(sql);\n const whereMatch = /\\bWHERE\\b/i.exec(body);\n const scoped = whereMatch\n ? `${body.slice(0, whereMatch.index)}WHERE ${predicate} AND (${body.slice(whereMatch.index + whereMatch[0].length).trim()})`\n : `${body} WHERE ${predicate}`;\n return returning ? `${scoped} ${returning}` : scoped;\n}\n\nfunction qualifySqliteWrite(sql: string, scoping: ScopingContext): string {\n if (!scoping.active) return sql;\n\n const updateMatch = sql.match(/^\\s*UPDATE\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i);\n if (updateMatch) {\n const tableName = updateMatch[1] ?? updateMatch[2] ?? updateMatch[3];\n const predicate = sqliteScopePredicate(tableName, scoping);\n if (!predicate) return sql;\n const qualified = sql.replace(\n /^\\s*UPDATE\\s+(?:\"[^\"]+\"|'[^']+'|\\w+)/i,\n `UPDATE main.\"${tableName.replace(/\"/g, '\"\"')}\"`,\n );\n return addSqliteScopeToWhere(qualified, predicate);\n }\n\n const deleteMatch = sql.match(\n /^\\s*DELETE\\s+FROM\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i,\n );\n if (deleteMatch) {\n const tableName = deleteMatch[1] ?? deleteMatch[2] ?? deleteMatch[3];\n const predicate = sqliteScopePredicate(tableName, scoping);\n if (!predicate) return sql;\n const qualified = sql.replace(\n /^\\s*DELETE\\s+FROM\\s+(?:\"[^\"]+\"|'[^']+'|\\w+)/i,\n `DELETE FROM main.\"${tableName.replace(/\"/g, '\"\"')}\"`,\n );\n return addSqliteScopeToWhere(qualified, predicate);\n }\n\n return sql.replace(\n /^\\s*(INSERT\\s+INTO|REPLACE\\s+INTO)\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i,\n (match, keyword, quotedDouble, quotedSingle, bare) => {\n const tableName = quotedDouble ?? quotedSingle ?? bare;\n if (\n !scoping.ownerEmailTables.has(tableName) &&\n !(scoping.orgId && scoping.orgIdTables.has(tableName))\n ) {\n if (scoping.tablePredicates.has(tableName)) {\n throw new Error(\n `INSERT/REPLACE into \"${tableName}\" is not allowed through raw DB tools because the table does not have owner_email/org_id columns for automatic write scoping. Use a template action, or add scoped ownership columns and an additive migration.`,\n );\n }\n return match;\n }\n return `${keyword} main.\"${tableName.replace(/\"/g, '\"\"')}\"`;\n },\n );\n}\n\nfunction printResult(\n sql: string,\n result: {\n count?: number;\n rowsAffected?: number;\n lastInsertRowid?: bigint | number;\n rows?: Record<string, unknown>[];\n },\n hasReturning: boolean,\n format?: string,\n) {\n if (hasReturning && result.rows && result.rows.length > 0) {\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n { sql, rows: result.rows, count: result.rows.length },\n null,\n 2,\n ),\n );\n return;\n }\n console.log(`Executed: ${sql}`);\n console.log(`Returned ${result.rows.length} row(s):`);\n console.log(JSON.stringify(result.rows, null, 2));\n } else {\n const changes = result.count ?? result.rowsAffected ?? 0;\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n {\n sql,\n changes,\n ...(result.lastInsertRowid && changes > 0\n ? { lastInsertRowid: Number(result.lastInsertRowid) }\n : {}),\n },\n null,\n 2,\n ),\n );\n return;\n }\n console.log(`Executed: ${sql}`);\n console.log(`Changes: ${changes}`);\n if (result.lastInsertRowid && changes > 0) {\n console.log(`Last Insert Row ID: ${result.lastInsertRowid}`);\n }\n if (changes === 0) {\n console.log(zeroChangesHint(sql));\n }\n }\n}\n\n/**\n * Hint emitted when an UPDATE/DELETE/REPLACE matches zero rows. Matches the\n * wording used by db-patch's \"no rows matched\" error so the agent gets the\n * same scoping nudge from both tools — without this hint, the agent reports\n * \"Changes: 0\" as success and the user sees no UI update because the row\n * either didn't exist or wasn't visible to the current user under per-user\n * scoping.\n */\nfunction zeroChangesHint(sql: string): string {\n const upper = sql.toUpperCase(); // leading whitespace already stripped by normalizeUserSql\n if (upper.startsWith(\"INSERT\")) {\n // INSERT changes=0 means INSERT OR IGNORE skipped a duplicate — different\n // failure mode, not a scoping issue.\n return \"Hint: 0 rows inserted. The row likely violated a UNIQUE / PRIMARY KEY constraint and was skipped (INSERT OR IGNORE).\";\n }\n return (\n \"Hint: 0 rows changed. The WHERE clause matched no rows — either the row \" +\n \"doesn't exist, or it exists but is owned by a different user (per-user \" +\n \"and per-org scoping is automatic for db-exec).\"\n );\n}\n\nfunction printBatchResult(results: DbExecResult[], format?: string): void {\n if (results.length === 1) {\n const result = results[0];\n printResult(\n result.sql,\n {\n count: result.changes,\n rowsAffected: result.changes,\n lastInsertRowid: result.lastInsertRowid,\n rows: result.rows,\n },\n Boolean(result.rows?.length),\n format,\n );\n return;\n }\n\n const totalChanges = results.reduce(\n (sum, result) => sum + Number(result.changes ?? 0),\n 0,\n );\n\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n {\n statements: results.map((result) => ({\n index: result.index,\n sql: result.sql,\n changes: result.changes ?? 0,\n ...(result.lastInsertRowid && Number(result.changes ?? 0) > 0\n ? { lastInsertRowid: Number(result.lastInsertRowid) }\n : {}),\n ...(result.rows?.length\n ? { rows: result.rows, count: result.rows.length }\n : {}),\n })),\n changes: totalChanges,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(`Executed ${results.length} statements in one transaction.`);\n for (const result of results) {\n if (result.rows?.length) {\n console.log(`[${result.index}] Returned ${result.rows.length} row(s):`);\n console.log(JSON.stringify(result.rows, null, 2));\n } else {\n const changes = Number(result.changes ?? 0);\n console.log(`[${result.index}] Changes: ${changes}`);\n if (changes === 0) {\n console.log(`[${result.index}] ${zeroChangesHint(result.sql)}`);\n }\n }\n }\n console.log(`Total changes: ${totalChanges}`);\n}\n\nfunction sqliteRowsToObjects(\n rows: any[],\n columns: string[],\n): Record<string, unknown>[] {\n return rows.map((row) => {\n if (!Array.isArray(row) && row && typeof row === \"object\") {\n return { ...row };\n }\n const obj: Record<string, unknown> = {};\n for (let i = 0; i < columns.length; i++) {\n obj[columns[i]] = row[i];\n }\n return obj;\n });\n}\n\nexport default async function dbExec(args: string[]): Promise<void> {\n const parsed = parseArgs(args);\n\n if (parsed.help === \"true\") {\n console.log(`Usage: pnpm action db-exec --sql \"<statement>\" [options]\n pnpm action db-exec --statements '[{\"sql\":\"UPDATE ...\",\"args\":[...]}]' [options]\n\nOptions:\n --sql <stmt> Single INSERT / UPDATE / DELETE / REPLACE statement\n --args <json> JSON array of positional SQL bind parameters for --sql\n --statements <json> JSON array of {sql, args?}; runs in one transaction\n --db <path> Path to SQLite database (default: data/app.db)\n --format json Output as JSON\n --help Show this help message`);\n return;\n }\n\n const statements = parseStatements(parsed).map((statement, index) => ({\n sql: validateWriteSql(statement.sql, index + 1),\n args: statement.args,\n }));\n\n // Resolve database URL: --db flag → DATABASE_URL env → default file path\n let url: string;\n if (parsed.db) {\n url = \"file:\" + path.resolve(parsed.db);\n } else if (getDatabaseUrl()) {\n url = getDatabaseUrl();\n } else {\n url = \"file:\" + path.resolve(process.cwd(), \"data\", \"app.db\");\n }\n\n // Postgres path\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const pgSql = pg(url);\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingPostgres(pgSql);\n\n const results: DbExecResult[] = [];\n await pgSql.begin(async (tx: any) => {\n try {\n // For UPDATE/DELETE: temp views scope to current user's rows.\n // Creating and dropping them inside the same transaction keeps\n // pooled Postgres backends from retaining session-local views.\n for (const stmt of scoping.setup) {\n await tx.unsafe(stmt);\n }\n\n for (let i = 0; i < statements.length; i++) {\n const statement = statements[i];\n const hasReturning = /\\bRETURNING\\b/i.test(statement.sql);\n const finalSql = normalizePostgresSql(\n injectOwnership(statement.sql, scoping),\n statement.args,\n );\n try {\n const result =\n statement.args.length > 0\n ? await tx.unsafe(finalSql, statement.args as any[])\n : await tx.unsafe(finalSql);\n const rows: Record<string, unknown>[] =\n hasReturning && result.length > 0 ? Array.from(result) : [];\n results.push({\n index: i + 1,\n sql: finalSql,\n changes: result.count ?? 0,\n rows,\n });\n } catch (err: any) {\n throw new Error(\n `Statement ${i + 1} failed: ${err?.message ?? String(err)}`,\n );\n }\n }\n } finally {\n for (const stmt of scoping.teardown) {\n await tx.unsafe(stmt).catch(() => {});\n }\n }\n });\n\n printBatchResult(results, parsed.format);\n } finally {\n await pgSql.end();\n }\n return;\n }\n\n // libsql / SQLite path\n const client = await createSqliteScriptClient(url);\n\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingSqlite(client);\n for (const stmt of scoping.setup) {\n await client.execute(stmt);\n }\n\n const results: DbExecResult[] = [];\n const shouldTransact = statements.length > 1;\n if (shouldTransact) await client.execute(\"BEGIN\");\n try {\n for (let i = 0; i < statements.length; i++) {\n const statement = statements[i];\n const hasReturning = /\\bRETURNING\\b/i.test(statement.sql);\n const finalSql = qualifySqliteWrite(\n injectOwnership(statement.sql, scoping),\n scoping,\n );\n try {\n const result =\n statement.args.length > 0\n ? await client.execute({\n sql: finalSql,\n args: statement.args as any[],\n })\n : await client.execute(finalSql);\n\n const rows: Record<string, unknown>[] =\n hasReturning && result.rows.length > 0\n ? sqliteRowsToObjects(result.rows, result.columns)\n : [];\n results.push({\n index: i + 1,\n sql: finalSql,\n changes: result.rowsAffected,\n lastInsertRowid: result.lastInsertRowid,\n rows,\n });\n } catch (err: any) {\n throw new Error(\n `Statement ${i + 1} failed: ${err?.message ?? String(err)}`,\n );\n }\n }\n if (shouldTransact) await client.execute(\"COMMIT\");\n } catch (err) {\n if (shouldTransact) {\n await client.execute(\"ROLLBACK\").catch(() => {});\n }\n throw err;\n }\n\n printBatchResult(results, parsed.format);\n\n for (const stmt of scoping.teardown) {\n await client.execute(stmt).catch(() => {});\n }\n } finally {\n client.close();\n }\n}\n"]}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/scripts/db/exec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core script: db-exec\n *\n * Execute write SQL statements (INSERT, UPDATE, DELETE, REPLACE)\n * against a SQLite or Postgres database.\n *\n * In production mode, temporary views scope UPDATE/DELETE to the current\n * user's data (AGENT_USER_EMAIL / AGENT_ORG_ID). For INSERT, the\n * `owner_email` and `org_id` columns are auto-injected if the target\n * table uses the ownership convention.\n *\n * Usage:\n * pnpm action db-exec --sql \"UPDATE forms SET status=? WHERE id=?\" [--args '[\"published\",\"abc\"]'] [--db path]\n * pnpm action db-exec --statements '[{\"sql\":\"INSERT INTO notes (id,title) VALUES (?,?)\",\"args\":[\"n1\",\"One\"]},{\"sql\":\"UPDATE counters SET value=value+1 WHERE key=?\",\"args\":[\"notes\"]}]'\n */\n\nimport path from \"path\";\nimport { getDatabaseUrl } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport {\n buildScopingPostgres,\n buildScopingSqlite,\n type ScopingContext,\n} from \"./scoping.js\";\nimport {\n assertNoRawDbAccessControlWrite,\n assertNoSchemaQualifiedTables,\n assertNoSensitiveFrameworkTables,\n} from \"./safety.js\";\nimport { createSqliteScriptClient } from \"./sqlite-client.js\";\n\nfunction isPostgresUrl(url: string): boolean {\n return url.startsWith(\"postgres://\") || url.startsWith(\"postgresql://\");\n}\n\ninterface DbExecStatement {\n sql: string;\n args: unknown[];\n}\n\ninterface DbExecResult {\n index: number;\n sql: string;\n changes?: number;\n lastInsertRowid?: bigint | number;\n rows?: Record<string, unknown>[];\n}\n\nfunction parseSqlArgs(raw: string | undefined, label = \"--args\"): unknown[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n // Fall through to the shared error below.\n }\n fail(`${label} must be a JSON array`);\n}\n\nfunction parseStatements(parsed: Record<string, string>): DbExecStatement[] {\n if (parsed.statements) {\n if (parsed.sql) {\n fail(\"Pass either --sql or --statements, not both.\");\n }\n let raw: unknown;\n try {\n raw = JSON.parse(parsed.statements);\n } catch {\n fail(\n '--statements must be a JSON array of {\"sql\": string, \"args\"?: unknown[]} objects',\n );\n }\n if (!Array.isArray(raw) || raw.length === 0) {\n fail(\"--statements must be a non-empty JSON array\");\n }\n return raw.map((entry, index) => {\n if (\n !entry ||\n typeof entry !== \"object\" ||\n typeof (entry as any).sql !== \"string\" ||\n !(entry as any).sql.trim()\n ) {\n fail(`Statement ${index + 1} must include a non-empty sql string`);\n }\n const args = (entry as any).args;\n if (args != null && !Array.isArray(args)) {\n fail(`Statement ${index + 1} args must be a JSON array`);\n }\n return { sql: (entry as any).sql, args: args ?? [] };\n });\n }\n\n if (!parsed.sql) {\n fail(\n '--sql is required unless --statements is provided. Example: --sql \"UPDATE forms SET status=? WHERE id=?\" --args \\'[\"published\",\"abc\"]\\'',\n );\n }\n return [{ sql: parsed.sql, args: parseSqlArgs(parsed.args) }];\n}\n\nfunction stripLeadingSqlComments(sql: string): string {\n return sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim();\n}\n\nfunction hasAdditionalStatement(sql: string): boolean {\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n if (state === \"block-comment\") {\n if (ch === \"*\" && next === \"/\") {\n i++;\n state = \"normal\";\n }\n continue;\n }\n if (state === \"single\") {\n if (ch === \"'\" && next === \"'\") {\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n if (state === \"double\") {\n if (ch === '\"' && next === '\"') {\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n state = \"double\";\n continue;\n }\n if (ch === \";\") {\n return sql.slice(i + 1).trim().length > 0;\n }\n }\n return false;\n}\n\nfunction normalizeUserSql(sql: string, index: number): string {\n const stripped = stripLeadingSqlComments(sql);\n if (!stripped) {\n fail(`Statement ${index} is empty`);\n }\n if (hasAdditionalStatement(stripped)) {\n fail(\n `Statement ${index} contains multiple SQL statements. Use --statements for batches so each write can be validated and run transactionally.`,\n );\n }\n return stripped.replace(/;\\s*$/, \"\");\n}\n\nfunction validateWriteSql(sql: string, index: number): string {\n const normalized = normalizeUserSql(sql, index);\n const upper = normalized.toUpperCase();\n const allowed = [\"INSERT\", \"UPDATE\", \"DELETE\", \"REPLACE\"];\n const blocked = [\"SELECT\", \"WITH\", \"EXPLAIN\", \"PRAGMA\"];\n\n if (blocked.some((kw) => upper.startsWith(kw))) {\n fail(\n `Statement ${index}: use db-query for SELECT/read statements. db-exec is for writes only.`,\n );\n }\n if (upper.startsWith(\"CREATE\") || upper.startsWith(\"ALTER\")) {\n fail(\n `Statement ${index}: schema changes are not allowed through db-exec. Additive schema changes must go through reviewed migrations/startup code, not ad-hoc agent SQL.`,\n );\n }\n if (!allowed.some((kw) => upper.startsWith(kw))) {\n fail(\n `Statement ${index}: only ${allowed.join(\", \")} statements are allowed. ` +\n `Dangerous operations like DROP, ATTACH, VACUUM, DETACH, CREATE, and ALTER are blocked.`,\n );\n }\n assertNoSensitiveFrameworkTables(normalized, \"write\");\n assertNoRawDbAccessControlWrite(normalized);\n assertNoSchemaQualifiedTables(normalized, \"write\");\n return normalized;\n}\n\nfunction convertQuestionMarksToPostgresParams(sql: string): string {\n let index = 0;\n let out = \"\";\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n out += ch;\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n\n if (state === \"block-comment\") {\n out += ch;\n if (ch === \"*\" && next === \"/\") {\n out += next;\n i++;\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"single\") {\n out += ch;\n if (ch === \"'\" && next === \"'\") {\n out += next;\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"double\") {\n out += ch;\n if (ch === '\"' && next === '\"') {\n out += next;\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n out += ch + next;\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n out += ch + next;\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n out += ch;\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n out += ch;\n state = \"double\";\n continue;\n }\n if (ch === \"?\") {\n index++;\n out += `$${index}`;\n continue;\n }\n out += ch;\n }\n\n return out;\n}\n\nfunction normalizePostgresSql(sql: string, args: unknown[]): string {\n if (args.length === 0 || /\\$\\d+\\b/.test(sql)) return sql;\n return convertQuestionMarksToPostgresParams(sql);\n}\n\n/**\n * For INSERT statements targeting a table with owner_email / org_id columns,\n * auto-inject the current user's email and org ID if not already present.\n *\n * Handles the explicit column list form:\n * INSERT INTO table (col1, col2) VALUES (val1, val2)\n */\nfunction injectOwnership(sql: string, scoping: ScopingContext): string {\n if (!scoping.active) return sql;\n\n const upper = sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim()\n .toUpperCase();\n if (!upper.startsWith(\"INSERT\")) return sql;\n\n // Extract table name: INSERT INTO <table> ...\n const match = sql.match(/INSERT\\s+INTO\\s+[\"']?(\\w+)[\"']?/i);\n if (!match) return sql;\n\n const tableName = match[1];\n\n // Determine which columns to inject\n const injections: { col: string; value: string }[] = [];\n\n if (\n scoping.userEmail &&\n scoping.ownerEmailTables.has(tableName) &&\n !/owner_email/i.test(sql)\n ) {\n injections.push({\n col: \"owner_email\",\n value: `'${scoping.userEmail.replace(/'/g, \"''\")}'`,\n });\n }\n\n if (\n scoping.orgId &&\n scoping.orgIdTables.has(tableName) &&\n !/org_id/i.test(sql)\n ) {\n injections.push({\n col: \"org_id\",\n value: `'${scoping.orgId.replace(/'/g, \"''\")}'`,\n });\n }\n\n if (injections.length === 0) return sql;\n\n // Try to inject into explicit column list: INSERT INTO t (cols) VALUES (vals)\n const colListMatch = sql.match(\n /(INSERT\\s+INTO\\s+[\"']?\\w+[\"']?\\s*)\\(([^)]+)\\)(\\s*VALUES\\s*)\\(([^)]+)\\)/i,\n );\n if (colListMatch) {\n const [, prefix, cols, valueKeyword, vals] = colListMatch;\n const extraCols = injections.map((i) => i.col).join(\", \");\n const extraVals = injections.map((i) => i.value).join(\", \");\n return `${prefix}(${cols}, ${extraCols})${valueKeyword}(${vals}, ${extraVals})`;\n }\n\n return sql;\n}\n\nfunction escapeSqlString(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction sqliteScopePredicate(\n tableName: string,\n scoping: ScopingContext,\n): string | null {\n if (tableName === \"tool_data\" && scoping.userEmail) {\n const userClause = `(scope = 'user' AND owner_email = '${escapeSqlString(scoping.userEmail)}')`;\n const orgClause = scoping.orgId\n ? ` OR (scope = 'org' AND org_id = '${escapeSqlString(scoping.orgId)}')`\n : \"\";\n return `(${userClause}${orgClause})`;\n }\n\n const clauses: string[] = [];\n const hasOwner = scoping.ownerEmailTables.has(tableName);\n const hasOrg = scoping.orgIdTables.has(tableName);\n if (scoping.userEmail && hasOwner) {\n const ownerClause = `owner_email = '${escapeSqlString(scoping.userEmail)}'`;\n if (scoping.orgId && hasOrg) {\n clauses.push(\n `${ownerClause} AND (org_id = '${escapeSqlString(scoping.orgId)}' OR org_id IS NULL)`,\n );\n } else {\n clauses.push(ownerClause);\n }\n } else if (scoping.orgId && hasOrg) {\n clauses.push(`org_id = '${escapeSqlString(scoping.orgId)}'`);\n }\n if (clauses.length > 0) return clauses.join(\" AND \");\n return scoping.tablePredicates.get(tableName) ?? null;\n}\n\nfunction splitReturning(sql: string): { body: string; returning: string } {\n const match = /\\bRETURNING\\b/i.exec(sql);\n if (!match) return { body: sql, returning: \"\" };\n return {\n body: sql.slice(0, match.index).trimEnd(),\n returning: sql.slice(match.index),\n };\n}\n\nfunction addSqliteScopeToWhere(sql: string, predicate: string): string {\n const { body, returning } = splitReturning(sql);\n const whereMatch = /\\bWHERE\\b/i.exec(body);\n const scoped = whereMatch\n ? `${body.slice(0, whereMatch.index)}WHERE ${predicate} AND (${body.slice(whereMatch.index + whereMatch[0].length).trim()})`\n : `${body} WHERE ${predicate}`;\n return returning ? `${scoped} ${returning}` : scoped;\n}\n\nfunction qualifySqliteWrite(sql: string, scoping: ScopingContext): string {\n if (!scoping.active) return sql;\n\n const updateMatch = sql.match(/^\\s*UPDATE\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i);\n if (updateMatch) {\n const tableName = updateMatch[1] ?? updateMatch[2] ?? updateMatch[3];\n const predicate = sqliteScopePredicate(tableName, scoping);\n if (!predicate) return sql;\n const qualified = sql.replace(\n /^\\s*UPDATE\\s+(?:\"[^\"]+\"|'[^']+'|\\w+)/i,\n `UPDATE main.\"${tableName.replace(/\"/g, '\"\"')}\"`,\n );\n return addSqliteScopeToWhere(qualified, predicate);\n }\n\n const deleteMatch = sql.match(\n /^\\s*DELETE\\s+FROM\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i,\n );\n if (deleteMatch) {\n const tableName = deleteMatch[1] ?? deleteMatch[2] ?? deleteMatch[3];\n const predicate = sqliteScopePredicate(tableName, scoping);\n if (!predicate) return sql;\n const qualified = sql.replace(\n /^\\s*DELETE\\s+FROM\\s+(?:\"[^\"]+\"|'[^']+'|\\w+)/i,\n `DELETE FROM main.\"${tableName.replace(/\"/g, '\"\"')}\"`,\n );\n return addSqliteScopeToWhere(qualified, predicate);\n }\n\n return sql.replace(\n /^\\s*(INSERT\\s+INTO|REPLACE\\s+INTO)\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\w+))/i,\n (match, keyword, quotedDouble, quotedSingle, bare) => {\n const tableName = quotedDouble ?? quotedSingle ?? bare;\n if (\n !scoping.ownerEmailTables.has(tableName) &&\n !(scoping.orgId && scoping.orgIdTables.has(tableName))\n ) {\n if (scoping.tablePredicates.has(tableName)) {\n throw new Error(\n `INSERT/REPLACE into \"${tableName}\" is not allowed through raw DB tools because the table does not have owner_email/org_id columns for automatic write scoping. Use a template action, or add scoped ownership columns and an additive migration.`,\n );\n }\n return match;\n }\n return `${keyword} main.\"${tableName.replace(/\"/g, '\"\"')}\"`;\n },\n );\n}\n\nfunction printResult(\n sql: string,\n result: {\n count?: number;\n rowsAffected?: number;\n lastInsertRowid?: bigint | number;\n rows?: Record<string, unknown>[];\n },\n hasReturning: boolean,\n format?: string,\n) {\n if (hasReturning && result.rows && result.rows.length > 0) {\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n { sql, rows: result.rows, count: result.rows.length },\n null,\n 2,\n ),\n );\n return;\n }\n console.log(`Executed: ${sql}`);\n console.log(`Returned ${result.rows.length} row(s):`);\n console.log(JSON.stringify(result.rows, null, 2));\n } else {\n const changes = result.count ?? result.rowsAffected ?? 0;\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n {\n sql,\n changes,\n ...(result.lastInsertRowid && changes > 0\n ? { lastInsertRowid: Number(result.lastInsertRowid) }\n : {}),\n },\n null,\n 2,\n ),\n );\n return;\n }\n console.log(`Executed: ${sql}`);\n console.log(`Changes: ${changes}`);\n if (result.lastInsertRowid && changes > 0) {\n console.log(`Last Insert Row ID: ${result.lastInsertRowid}`);\n }\n if (changes === 0) {\n console.log(zeroChangesHint(sql));\n }\n }\n}\n\n/**\n * Hint emitted when an UPDATE/DELETE/REPLACE matches zero rows. Matches the\n * wording used by db-patch's \"no rows matched\" error so the agent gets the\n * same scoping nudge from both tools — without this hint, the agent reports\n * \"Changes: 0\" as success and the user sees no UI update because the row\n * either didn't exist or wasn't visible to the current user under per-user\n * scoping.\n */\nfunction zeroChangesHint(sql: string): string {\n const upper = sql.toUpperCase(); // leading whitespace already stripped by normalizeUserSql\n if (upper.startsWith(\"INSERT\")) {\n // INSERT changes=0 means INSERT OR IGNORE skipped a duplicate — different\n // failure mode, not a scoping issue.\n return \"Hint: 0 rows inserted. The row likely violated a UNIQUE / PRIMARY KEY constraint and was skipped (INSERT OR IGNORE).\";\n }\n return (\n \"Hint: 0 rows changed. The WHERE clause matched no rows — either the row \" +\n \"doesn't exist, or it exists but is owned by a different user (per-user \" +\n \"and per-org scoping is automatic for db-exec).\"\n );\n}\n\nfunction printBatchResult(results: DbExecResult[], format?: string): void {\n if (results.length === 1) {\n const result = results[0];\n printResult(\n result.sql,\n {\n count: result.changes,\n rowsAffected: result.changes,\n lastInsertRowid: result.lastInsertRowid,\n rows: result.rows,\n },\n Boolean(result.rows?.length),\n format,\n );\n return;\n }\n\n const totalChanges = results.reduce(\n (sum, result) => sum + Number(result.changes ?? 0),\n 0,\n );\n\n if (format === \"json\") {\n console.log(\n JSON.stringify(\n {\n statements: results.map((result) => ({\n index: result.index,\n sql: result.sql,\n changes: result.changes ?? 0,\n ...(result.lastInsertRowid && Number(result.changes ?? 0) > 0\n ? { lastInsertRowid: Number(result.lastInsertRowid) }\n : {}),\n ...(result.rows?.length\n ? { rows: result.rows, count: result.rows.length }\n : {}),\n })),\n changes: totalChanges,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(`Executed ${results.length} statements in one transaction.`);\n for (const result of results) {\n if (result.rows?.length) {\n console.log(`[${result.index}] Returned ${result.rows.length} row(s):`);\n console.log(JSON.stringify(result.rows, null, 2));\n } else {\n const changes = Number(result.changes ?? 0);\n console.log(`[${result.index}] Changes: ${changes}`);\n if (changes === 0) {\n console.log(`[${result.index}] ${zeroChangesHint(result.sql)}`);\n }\n }\n }\n console.log(`Total changes: ${totalChanges}`);\n}\n\nfunction sqliteRowsToObjects(\n rows: any[],\n columns: string[],\n): Record<string, unknown>[] {\n return rows.map((row) => {\n if (!Array.isArray(row) && row && typeof row === \"object\") {\n return { ...row };\n }\n const obj: Record<string, unknown> = {};\n for (let i = 0; i < columns.length; i++) {\n obj[columns[i]] = row[i];\n }\n return obj;\n });\n}\n\nexport default async function dbExec(args: string[]): Promise<void> {\n const parsed = parseArgs(args);\n\n if (parsed.help === \"true\") {\n console.log(`Usage: pnpm action db-exec --sql \"<statement>\" [options]\n pnpm action db-exec --statements '[{\"sql\":\"UPDATE ...\",\"args\":[...]}]' [options]\n\nOptions:\n --sql <stmt> Single INSERT / UPDATE / DELETE / REPLACE statement\n --args <json> JSON array of positional SQL bind parameters for --sql\n --statements <json> JSON array of {sql, args?}; runs in one transaction\n --db <path> Path to SQLite database (default: data/app.db)\n --format json Output as JSON\n --help Show this help message`);\n return;\n }\n\n const statements = parseStatements(parsed).map((statement, index) => ({\n sql: validateWriteSql(statement.sql, index + 1),\n args: statement.args,\n }));\n\n // Resolve database URL: --db flag → DATABASE_URL env → default file path\n let url: string;\n if (parsed.db) {\n url = \"file:\" + path.resolve(parsed.db);\n } else if (getDatabaseUrl()) {\n url = getDatabaseUrl();\n } else {\n url = \"file:\" + path.resolve(process.cwd(), \"data\", \"app.db\");\n }\n\n // Postgres path\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const pgSql = pg(url);\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingPostgres(pgSql);\n\n const results: DbExecResult[] = [];\n await pgSql.begin(async (tx: any) => {\n try {\n // For UPDATE/DELETE: temp views scope to current user's rows.\n // Creating and dropping them inside the same transaction keeps\n // pooled Postgres backends from retaining session-local views.\n for (const stmt of scoping.setup) {\n await tx.unsafe(stmt);\n }\n\n for (let i = 0; i < statements.length; i++) {\n const statement = statements[i];\n const hasReturning = /\\bRETURNING\\b/i.test(statement.sql);\n const finalSql = normalizePostgresSql(\n injectOwnership(statement.sql, scoping),\n statement.args,\n );\n try {\n const result =\n statement.args.length > 0\n ? await tx.unsafe(finalSql, statement.args as any[])\n : await tx.unsafe(finalSql);\n const rows: Record<string, unknown>[] =\n hasReturning && result.length > 0 ? Array.from(result) : [];\n results.push({\n index: i + 1,\n sql: finalSql,\n changes: result.count ?? 0,\n rows,\n });\n } catch (err: any) {\n throw new Error(\n `Statement ${i + 1} failed: ${err?.message ?? String(err)}`,\n );\n }\n }\n } finally {\n for (const stmt of scoping.teardown) {\n await tx.unsafe(stmt).catch(() => {});\n }\n }\n });\n\n printBatchResult(results, parsed.format);\n } finally {\n await pgSql.end();\n }\n return;\n }\n\n // libsql / SQLite path\n const client = await createSqliteScriptClient(url);\n\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingSqlite(client);\n for (const stmt of scoping.setup) {\n await client.execute(stmt);\n }\n\n const results: DbExecResult[] = [];\n const shouldTransact = statements.length > 1;\n if (shouldTransact) await client.execute(\"BEGIN\");\n try {\n for (let i = 0; i < statements.length; i++) {\n const statement = statements[i];\n const hasReturning = /\\bRETURNING\\b/i.test(statement.sql);\n const finalSql = qualifySqliteWrite(\n injectOwnership(statement.sql, scoping),\n scoping,\n );\n try {\n const result =\n statement.args.length > 0\n ? await client.execute({\n sql: finalSql,\n args: statement.args as any[],\n })\n : await client.execute(finalSql);\n\n const rows: Record<string, unknown>[] =\n hasReturning && result.rows.length > 0\n ? sqliteRowsToObjects(result.rows, result.columns)\n : [];\n results.push({\n index: i + 1,\n sql: finalSql,\n changes: result.rowsAffected,\n lastInsertRowid: result.lastInsertRowid,\n rows,\n });\n } catch (err: any) {\n throw new Error(\n `Statement ${i + 1} failed: ${err?.message ?? String(err)}`,\n );\n }\n }\n if (shouldTransact) await client.execute(\"COMMIT\");\n } catch (err) {\n if (shouldTransact) {\n await client.execute(\"ROLLBACK\").catch(() => {});\n }\n throw err;\n }\n\n printBatchResult(results, parsed.format);\n\n for (const stmt of scoping.teardown) {\n await client.execute(stmt).catch(() => {});\n }\n } finally {\n client.close();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAczE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAgBzE,CAAC"}
@@ -6,6 +6,7 @@ export const coreDbScripts = {
6
6
  "db-check-scoping": (args) => import("./check-scoping.js").then((m) => m.default(args)),
7
7
  "db-wipe-leaked-builder-keys": (args) => import("./wipe-leaked-builder-keys.js").then((m) => m.default(args)),
8
8
  "db-migrate-user-api-keys": (args) => import("./migrate-user-api-keys.js").then((m) => m.default(args)),
9
+ "db-migrate-encrypt-credentials": (args) => import("./migrate-encrypt-credentials.js").then((m) => m.default(args)),
9
10
  "db-reset-dev-owner": (args) => import("./reset-dev-owner.js").then((m) => m.default(args)),
10
11
  };
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scripts/db/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GACxB;IACE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,IAAI,EAAE,EAAE,CAC3B,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,6BAA6B,EAAE,CAAC,IAAI,EAAE,EAAE,CACtC,MAAM,CAAC,+BAA+B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,MAAM,CAAC,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAC7B,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D,CAAC","sourcesContent":["export const coreDbScripts: Record<string, (args: string[]) => Promise<void>> =\n {\n \"db-schema\": (args) => import(\"./schema.js\").then((m) => m.default(args)),\n \"db-query\": (args) => import(\"./query.js\").then((m) => m.default(args)),\n \"db-exec\": (args) => import(\"./exec.js\").then((m) => m.default(args)),\n \"db-patch\": (args) => import(\"./patch.js\").then((m) => m.default(args)),\n \"db-check-scoping\": (args) =>\n import(\"./check-scoping.js\").then((m) => m.default(args)),\n \"db-wipe-leaked-builder-keys\": (args) =>\n import(\"./wipe-leaked-builder-keys.js\").then((m) => m.default(args)),\n \"db-migrate-user-api-keys\": (args) =>\n import(\"./migrate-user-api-keys.js\").then((m) => m.default(args)),\n \"db-reset-dev-owner\": (args) =>\n import(\"./reset-dev-owner.js\").then((m) => m.default(args)),\n };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scripts/db/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GACxB;IACE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,IAAI,EAAE,EAAE,CAC3B,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,6BAA6B,EAAE,CAAC,IAAI,EAAE,EAAE,CACtC,MAAM,CAAC,+BAA+B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,MAAM,CAAC,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,gCAAgC,EAAE,CAAC,IAAI,EAAE,EAAE,CACzC,MAAM,CAAC,kCAAkC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAC7B,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D,CAAC","sourcesContent":["export const coreDbScripts: Record<string, (args: string[]) => Promise<void>> =\n {\n \"db-schema\": (args) => import(\"./schema.js\").then((m) => m.default(args)),\n \"db-query\": (args) => import(\"./query.js\").then((m) => m.default(args)),\n \"db-exec\": (args) => import(\"./exec.js\").then((m) => m.default(args)),\n \"db-patch\": (args) => import(\"./patch.js\").then((m) => m.default(args)),\n \"db-check-scoping\": (args) =>\n import(\"./check-scoping.js\").then((m) => m.default(args)),\n \"db-wipe-leaked-builder-keys\": (args) =>\n import(\"./wipe-leaked-builder-keys.js\").then((m) => m.default(args)),\n \"db-migrate-user-api-keys\": (args) =>\n import(\"./migrate-user-api-keys.js\").then((m) => m.default(args)),\n \"db-migrate-encrypt-credentials\": (args) =>\n import(\"./migrate-encrypt-credentials.js\").then((m) => m.default(args)),\n \"db-reset-dev-owner\": (args) =>\n import(\"./reset-dev-owner.js\").then((m) => m.default(args)),\n };\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Core script: db-migrate-encrypt-credentials
3
+ *
4
+ * One-shot, in-place migration: encrypt any plaintext per-user / per-org
5
+ * credential rows in the `settings` table (`u:<email>:credential:<KEY>` and
6
+ * `o:<orgId>:credential:<KEY>`) with the same AES-256-GCM scheme the secrets
7
+ * vault uses.
8
+ *
9
+ * Background. `resolveCredential` / `saveCredential` historically stored
10
+ * third-party API keys as plaintext JSON in `settings`. Writes are now
11
+ * encrypted at rest, and reads transparently fall back to plaintext, so this
12
+ * migration is OPTIONAL — it re-encrypts existing rows so a leaked DB
13
+ * backup / pg_dump / read replica no longer exposes plaintext keys.
14
+ *
15
+ * Non-destructive: it only rewrites the `value` of credential rows in place
16
+ * (no row is dropped). Idempotent: already-encrypted rows are skipped.
17
+ *
18
+ * IMPORTANT: run with the SAME SECRETS_ENCRYPTION_KEY / BETTER_AUTH_SECRET the
19
+ * app uses, or the app won't be able to decrypt the result. The script refuses
20
+ * to run without an explicit key.
21
+ *
22
+ * Usage:
23
+ * DATABASE_URL=postgres://... SECRETS_ENCRYPTION_KEY=... pnpm action db-migrate-encrypt-credentials
24
+ * pnpm action db-migrate-encrypt-credentials --db ./data/app.db
25
+ * pnpm action db-migrate-encrypt-credentials --dry-run
26
+ */
27
+ export default function dbMigrateEncryptCredentials(args: string[]): Promise<void>;
28
+ //# sourceMappingURL=migrate-encrypt-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-encrypt-credentials.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/migrate-encrypt-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AA0CH,wBAA8B,2BAA2B,CACvD,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CA+Ef"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Core script: db-migrate-encrypt-credentials
3
+ *
4
+ * One-shot, in-place migration: encrypt any plaintext per-user / per-org
5
+ * credential rows in the `settings` table (`u:<email>:credential:<KEY>` and
6
+ * `o:<orgId>:credential:<KEY>`) with the same AES-256-GCM scheme the secrets
7
+ * vault uses.
8
+ *
9
+ * Background. `resolveCredential` / `saveCredential` historically stored
10
+ * third-party API keys as plaintext JSON in `settings`. Writes are now
11
+ * encrypted at rest, and reads transparently fall back to plaintext, so this
12
+ * migration is OPTIONAL — it re-encrypts existing rows so a leaked DB
13
+ * backup / pg_dump / read replica no longer exposes plaintext keys.
14
+ *
15
+ * Non-destructive: it only rewrites the `value` of credential rows in place
16
+ * (no row is dropped). Idempotent: already-encrypted rows are skipped.
17
+ *
18
+ * IMPORTANT: run with the SAME SECRETS_ENCRYPTION_KEY / BETTER_AUTH_SECRET the
19
+ * app uses, or the app won't be able to decrypt the result. The script refuses
20
+ * to run without an explicit key.
21
+ *
22
+ * Usage:
23
+ * DATABASE_URL=postgres://... SECRETS_ENCRYPTION_KEY=... pnpm action db-migrate-encrypt-credentials
24
+ * pnpm action db-migrate-encrypt-credentials --db ./data/app.db
25
+ * pnpm action db-migrate-encrypt-credentials --dry-run
26
+ */
27
+ import path from "path";
28
+ import { createClient } from "@libsql/client";
29
+ import { getDatabaseUrl, getDatabaseAuthToken } from "../../db/client.js";
30
+ import { parseArgs } from "../utils.js";
31
+ import { encryptSecretValue, isEncryptedSecretValue, } from "../../secrets/crypto.js";
32
+ function isPostgresUrl(url) {
33
+ return url.startsWith("postgres://") || url.startsWith("postgresql://");
34
+ }
35
+ const CREDENTIAL_LIKE = "%:credential:%";
36
+ /** Extract the stored credential string from a settings JSON value. */
37
+ function extractValue(raw) {
38
+ if (!raw)
39
+ return null;
40
+ try {
41
+ const parsed = JSON.parse(raw);
42
+ if (parsed && typeof parsed.value === "string" && parsed.value.length > 0) {
43
+ return parsed.value;
44
+ }
45
+ return null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ function mask(value) {
52
+ if (!value)
53
+ return "(empty)";
54
+ if (value.length <= 8)
55
+ return "***";
56
+ return `${value.slice(0, 3)}…${value.slice(-3)} (len=${value.length})`;
57
+ }
58
+ export default async function dbMigrateEncryptCredentials(args) {
59
+ const parsed = parseArgs(args);
60
+ if (parsed.help === "true") {
61
+ console.log(`Usage: pnpm action db-migrate-encrypt-credentials [options]
62
+
63
+ Encrypts plaintext credential rows (u:<email>:credential:* and
64
+ o:<orgId>:credential:*) in the settings table, in place. Idempotent and
65
+ non-destructive (skips already-encrypted rows; never deletes a row).
66
+
67
+ Run with the same SECRETS_ENCRYPTION_KEY / BETTER_AUTH_SECRET the app uses.
68
+
69
+ Options:
70
+ --db <path> Path to SQLite database (default: data/app.db)
71
+ --dry-run Print what would be encrypted without writing
72
+ --help Show this help message`);
73
+ return;
74
+ }
75
+ if (!process.env.SECRETS_ENCRYPTION_KEY && !process.env.BETTER_AUTH_SECRET) {
76
+ console.error("[migrate-encrypt-credentials] Refusing to run without SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET set. " +
77
+ "Encrypting with the machine-local fallback would produce values the app cannot decrypt.");
78
+ throw new Error("Missing encryption key");
79
+ }
80
+ const dryRun = parsed["dry-run"] === "true";
81
+ let url;
82
+ if (parsed.db) {
83
+ url = "file:" + path.resolve(parsed.db);
84
+ }
85
+ else if (getDatabaseUrl()) {
86
+ url = getDatabaseUrl();
87
+ }
88
+ else {
89
+ url = "file:" + path.resolve(process.cwd(), "data", "app.db");
90
+ }
91
+ const dbLabel = url.startsWith("file:")
92
+ ? url.slice("file:".length)
93
+ : new URL(url).host || url;
94
+ console.log(`[migrate-encrypt-credentials] target: ${dbLabel}${dryRun ? " (dry-run)" : ""}`);
95
+ const rows = await fetchPlaintextCredentialRows(url);
96
+ if (rows.length === 0) {
97
+ console.log("[migrate-encrypt-credentials] nothing to encrypt.");
98
+ return;
99
+ }
100
+ console.log(`[migrate-encrypt-credentials] found ${rows.length} plaintext credential row(s):`);
101
+ for (const row of rows) {
102
+ console.log(` - ${row.settingsKey} ${mask(row.plaintext)}`);
103
+ }
104
+ if (dryRun)
105
+ return;
106
+ let encrypted = 0;
107
+ let failed = 0;
108
+ for (const row of rows) {
109
+ try {
110
+ const newValue = JSON.stringify({
111
+ value: encryptSecretValue(row.plaintext),
112
+ });
113
+ await updateRow(url, row.settingsKey, newValue);
114
+ encrypted++;
115
+ }
116
+ catch (err) {
117
+ console.error(` ! failed to encrypt ${row.settingsKey}:`, err instanceof Error ? err.message : err);
118
+ failed++;
119
+ }
120
+ }
121
+ console.log(`[migrate-encrypt-credentials] done. encrypted=${encrypted} failed=${failed}`);
122
+ }
123
+ async function fetchPlaintextCredentialRows(url) {
124
+ const out = [];
125
+ const collect = (key, value) => {
126
+ if (!key.includes(":credential:"))
127
+ return;
128
+ const plaintext = extractValue(value);
129
+ if (plaintext === null)
130
+ return;
131
+ // Already encrypted → leave it (idempotent).
132
+ if (isEncryptedSecretValue(plaintext))
133
+ return;
134
+ out.push({ settingsKey: key, plaintext });
135
+ };
136
+ if (isPostgresUrl(url)) {
137
+ const { default: pg } = await import("postgres");
138
+ const sql = pg(url);
139
+ try {
140
+ const rows = (await sql.unsafe(`SELECT key, value FROM settings WHERE key LIKE $1`, [CREDENTIAL_LIKE]));
141
+ for (const row of rows)
142
+ collect(row.key, row.value);
143
+ }
144
+ finally {
145
+ await sql.end();
146
+ }
147
+ return out;
148
+ }
149
+ const client = createClient({ url, authToken: getDatabaseAuthToken() });
150
+ try {
151
+ const result = await client.execute({
152
+ sql: `SELECT key, value FROM settings WHERE key LIKE ?`,
153
+ args: [CREDENTIAL_LIKE],
154
+ });
155
+ for (const row of result.rows) {
156
+ collect(row.key, row.value);
157
+ }
158
+ }
159
+ finally {
160
+ client.close();
161
+ }
162
+ return out;
163
+ }
164
+ async function updateRow(url, key, value) {
165
+ if (isPostgresUrl(url)) {
166
+ const { default: pg } = await import("postgres");
167
+ const sql = pg(url);
168
+ try {
169
+ await sql.unsafe(`UPDATE settings SET value = $1 WHERE key = $2`, [
170
+ value,
171
+ key,
172
+ ]);
173
+ }
174
+ finally {
175
+ await sql.end();
176
+ }
177
+ return;
178
+ }
179
+ const client = createClient({ url, authToken: getDatabaseAuthToken() });
180
+ try {
181
+ await client.execute({
182
+ sql: `UPDATE settings SET value = ? WHERE key = ?`,
183
+ args: [value, key],
184
+ });
185
+ }
186
+ finally {
187
+ client.close();
188
+ }
189
+ }
190
+ //# sourceMappingURL=migrate-encrypt-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-encrypt-credentials.js","sourceRoot":"","sources":["../../../src/scripts/db/migrate-encrypt-credentials.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core script: db-migrate-encrypt-credentials\n *\n * One-shot, in-place migration: encrypt any plaintext per-user / per-org\n * credential rows in the `settings` table (`u:<email>:credential:<KEY>` and\n * `o:<orgId>:credential:<KEY>`) with the same AES-256-GCM scheme the secrets\n * vault uses.\n *\n * Background. `resolveCredential` / `saveCredential` historically stored\n * third-party API keys as plaintext JSON in `settings`. Writes are now\n * encrypted at rest, and reads transparently fall back to plaintext, so this\n * migration is OPTIONAL — it re-encrypts existing rows so a leaked DB\n * backup / pg_dump / read replica no longer exposes plaintext keys.\n *\n * Non-destructive: it only rewrites the `value` of credential rows in place\n * (no row is dropped). Idempotent: already-encrypted rows are skipped.\n *\n * IMPORTANT: run with the SAME SECRETS_ENCRYPTION_KEY / BETTER_AUTH_SECRET the\n * app uses, or the app won't be able to decrypt the result. The script refuses\n * to run without an explicit key.\n *\n * Usage:\n * DATABASE_URL=postgres://... SECRETS_ENCRYPTION_KEY=... pnpm action db-migrate-encrypt-credentials\n * pnpm action db-migrate-encrypt-credentials --db ./data/app.db\n * pnpm action db-migrate-encrypt-credentials --dry-run\n */\n\nimport path from \"path\";\nimport { createClient } from \"@libsql/client\";\nimport { getDatabaseUrl, getDatabaseAuthToken } from \"../../db/client.js\";\nimport { parseArgs } from \"../utils.js\";\nimport {\n encryptSecretValue,\n isEncryptedSecretValue,\n} from \"../../secrets/crypto.js\";\n\ninterface CredentialRow {\n settingsKey: string;\n plaintext: string;\n}\n\nfunction isPostgresUrl(url: string): boolean {\n return url.startsWith(\"postgres://\") || url.startsWith(\"postgresql://\");\n}\n\nconst CREDENTIAL_LIKE = \"%:credential:%\";\n\n/** Extract the stored credential string from a settings JSON value. */\nfunction extractValue(raw: string): string | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.value === \"string\" && parsed.value.length > 0) {\n return parsed.value;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nfunction mask(value: string): string {\n if (!value) return \"(empty)\";\n if (value.length <= 8) return \"***\";\n return `${value.slice(0, 3)}…${value.slice(-3)} (len=${value.length})`;\n}\n\nexport default async function dbMigrateEncryptCredentials(\n args: string[],\n): Promise<void> {\n const parsed = parseArgs(args);\n\n if (parsed.help === \"true\") {\n console.log(`Usage: pnpm action db-migrate-encrypt-credentials [options]\n\nEncrypts plaintext credential rows (u:<email>:credential:* and\no:<orgId>:credential:*) in the settings table, in place. Idempotent and\nnon-destructive (skips already-encrypted rows; never deletes a row).\n\nRun with the same SECRETS_ENCRYPTION_KEY / BETTER_AUTH_SECRET the app uses.\n\nOptions:\n --db <path> Path to SQLite database (default: data/app.db)\n --dry-run Print what would be encrypted without writing\n --help Show this help message`);\n return;\n }\n\n if (!process.env.SECRETS_ENCRYPTION_KEY && !process.env.BETTER_AUTH_SECRET) {\n console.error(\n \"[migrate-encrypt-credentials] Refusing to run without SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET set. \" +\n \"Encrypting with the machine-local fallback would produce values the app cannot decrypt.\",\n );\n throw new Error(\"Missing encryption key\");\n }\n\n const dryRun = parsed[\"dry-run\"] === \"true\";\n\n let url: string;\n if (parsed.db) {\n url = \"file:\" + path.resolve(parsed.db);\n } else if (getDatabaseUrl()) {\n url = getDatabaseUrl();\n } else {\n url = \"file:\" + path.resolve(process.cwd(), \"data\", \"app.db\");\n }\n\n const dbLabel = url.startsWith(\"file:\")\n ? url.slice(\"file:\".length)\n : new URL(url).host || url;\n console.log(\n `[migrate-encrypt-credentials] target: ${dbLabel}${dryRun ? \" (dry-run)\" : \"\"}`,\n );\n\n const rows = await fetchPlaintextCredentialRows(url);\n if (rows.length === 0) {\n console.log(\"[migrate-encrypt-credentials] nothing to encrypt.\");\n return;\n }\n\n console.log(\n `[migrate-encrypt-credentials] found ${rows.length} plaintext credential row(s):`,\n );\n for (const row of rows) {\n console.log(` - ${row.settingsKey} ${mask(row.plaintext)}`);\n }\n if (dryRun) return;\n\n let encrypted = 0;\n let failed = 0;\n for (const row of rows) {\n try {\n const newValue = JSON.stringify({\n value: encryptSecretValue(row.plaintext),\n });\n await updateRow(url, row.settingsKey, newValue);\n encrypted++;\n } catch (err) {\n console.error(\n ` ! failed to encrypt ${row.settingsKey}:`,\n err instanceof Error ? err.message : err,\n );\n failed++;\n }\n }\n console.log(\n `[migrate-encrypt-credentials] done. encrypted=${encrypted} failed=${failed}`,\n );\n}\n\nasync function fetchPlaintextCredentialRows(\n url: string,\n): Promise<CredentialRow[]> {\n const out: CredentialRow[] = [];\n const collect = (key: string, value: string) => {\n if (!key.includes(\":credential:\")) return;\n const plaintext = extractValue(value);\n if (plaintext === null) return;\n // Already encrypted → leave it (idempotent).\n if (isEncryptedSecretValue(plaintext)) return;\n out.push({ settingsKey: key, plaintext });\n };\n\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const sql = pg(url);\n try {\n const rows = (await sql.unsafe(\n `SELECT key, value FROM settings WHERE key LIKE $1`,\n [CREDENTIAL_LIKE],\n )) as unknown as Array<{ key: string; value: string }>;\n for (const row of rows) collect(row.key, row.value);\n } finally {\n await sql.end();\n }\n return out;\n }\n\n const client = createClient({ url, authToken: getDatabaseAuthToken() });\n try {\n const result = await client.execute({\n sql: `SELECT key, value FROM settings WHERE key LIKE ?`,\n args: [CREDENTIAL_LIKE],\n });\n for (const row of result.rows) {\n collect(row.key as string, row.value as string);\n }\n } finally {\n client.close();\n }\n return out;\n}\n\nasync function updateRow(\n url: string,\n key: string,\n value: string,\n): Promise<void> {\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const sql = pg(url);\n try {\n await sql.unsafe(`UPDATE settings SET value = $1 WHERE key = $2`, [\n value,\n key,\n ]);\n } finally {\n await sql.end();\n }\n return;\n }\n const client = createClient({ url, authToken: getDatabaseAuthToken() });\n try {\n await client.execute({\n sql: `UPDATE settings SET value = ? WHERE key = ?`,\n args: [value, key],\n });\n } finally {\n client.close();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAyJH,wBAA8B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsInE"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4JH,wBAA8B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuInE"}
@@ -14,7 +14,7 @@
14
14
  import path from "path";
15
15
  import { getDatabaseUrl } from "../../db/client.js";
16
16
  import { parseArgs, fail } from "../utils.js";
17
- import { assertNoSensitiveFrameworkTables } from "./safety.js";
17
+ import { assertNoSchemaQualifiedTables, assertNoSensitiveFrameworkTables, } from "./safety.js";
18
18
  import { buildScopingPostgres, buildScopingSqlite } from "./scoping.js";
19
19
  import { createSqliteScriptClient } from "./sqlite-client.js";
20
20
  function isPostgresUrl(url) {
@@ -177,6 +177,7 @@ Options:
177
177
  fail("Only SELECT, WITH, EXPLAIN, and PRAGMA queries are allowed. Use db-exec for writes.");
178
178
  }
179
179
  assertNoSensitiveFrameworkTables(stripped, "read");
180
+ assertNoSchemaQualifiedTables(stripped, "read");
180
181
  // Resolve database URL: --db flag → DATABASE_URL env → default file path
181
182
  let url;
182
183
  if (parsed.db) {
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/scripts/db/query.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core script: db-query\n *\n * Run a read-only SQL query against a SQLite or Postgres database.\n *\n * In production mode, temporary views are created to scope data to the\n * current user (AGENT_USER_EMAIL). Tables with an `owner_email` column\n * and core tables (settings, application_state, etc.) are automatically\n * filtered so queries only return the current user's data.\n *\n * Usage:\n * pnpm action db-query --sql \"SELECT * FROM forms WHERE id = ?\" [--args '[\"abc\"]'] [--db path] [--format json] [--limit N]\n */\n\nimport path from \"path\";\nimport { getDatabaseUrl } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport { assertNoSensitiveFrameworkTables } from \"./safety.js\";\nimport { buildScopingPostgres, buildScopingSqlite } from \"./scoping.js\";\nimport { createSqliteScriptClient } from \"./sqlite-client.js\";\n\nfunction isPostgresUrl(url: string): boolean {\n return url.startsWith(\"postgres://\") || url.startsWith(\"postgresql://\");\n}\n\nfunction parseSqlArgs(raw: string | undefined): unknown[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n // Fall through to the shared error below.\n }\n fail(\"--args must be a JSON array\");\n}\n\nfunction convertQuestionMarksToPostgresParams(sql: string): string {\n let index = 0;\n let out = \"\";\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n out += ch;\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n\n if (state === \"block-comment\") {\n out += ch;\n if (ch === \"*\" && next === \"/\") {\n out += next;\n i++;\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"single\") {\n out += ch;\n if (ch === \"'\" && next === \"'\") {\n out += next;\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"double\") {\n out += ch;\n if (ch === '\"' && next === '\"') {\n out += next;\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n out += ch + next;\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n out += ch + next;\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n out += ch;\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n out += ch;\n state = \"double\";\n continue;\n }\n if (ch === \"?\") {\n index++;\n out += `$${index}`;\n continue;\n }\n out += ch;\n }\n\n return out;\n}\n\nfunction normalizePostgresSql(sql: string, args: unknown[]): string {\n if (args.length === 0 || /\\$\\d+\\b/.test(sql)) return sql;\n return convertQuestionMarksToPostgresParams(sql);\n}\n\nfunction printTable(\n rows: Record<string, unknown>[],\n finalSql: string,\n format?: string,\n) {\n if (format === \"json\") {\n console.log(\n JSON.stringify({ query: finalSql, rows, count: rows.length }, null, 2),\n );\n return;\n }\n\n console.log(`Query: ${finalSql}`);\n console.log(`Rows: ${rows.length}\\n`);\n\n if (rows.length === 0) {\n console.log(\"(no results)\");\n return;\n }\n\n const keys = Object.keys(rows[0]);\n const widths = keys.map((k) => {\n const maxVal = Math.max(...rows.map((r) => String(r[k] ?? \"NULL\").length));\n return Math.max(k.length, Math.min(maxVal, 60));\n });\n\n const header = keys.map((k, i) => k.padEnd(widths[i])).join(\" | \");\n console.log(header);\n console.log(widths.map((w) => \"-\".repeat(w)).join(\"-+-\"));\n\n for (const row of rows) {\n const line = keys\n .map((k, i) => {\n const val = String(row[k] ?? \"NULL\");\n return val.length > 60\n ? val.slice(0, 57) + \"...\"\n : val.padEnd(widths[i]);\n })\n .join(\" | \");\n console.log(line);\n }\n}\n\nexport default async function dbQuery(args: string[]): Promise<void> {\n const parsed = parseArgs(args);\n\n if (parsed.help === \"true\") {\n console.log(`Usage: pnpm action db-query --sql \"<query>\" [options]\n\nOptions:\n --sql <query> SQL SELECT query to run (required)\n --args <json> JSON array of positional SQL bind parameters\n --db <path> Path to SQLite database (default: data/app.db)\n --format json Output as JSON instead of a table\n --limit N Append LIMIT N if not already present\n --help Show this help message`);\n return;\n }\n\n const sql = parsed.sql;\n if (!sql) {\n fail('--sql is required. Example: --sql \"SELECT * FROM forms\"');\n }\n const sqlArgs = parseSqlArgs(parsed.args);\n\n // Safety: only allow read-only statements.\n // Strip leading SQL comments before checking the prefix.\n const stripped = sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim();\n const upper = stripped.toUpperCase();\n if (\n !upper.startsWith(\"SELECT\") &&\n !upper.startsWith(\"WITH\") &&\n !upper.startsWith(\"EXPLAIN\") &&\n !upper.startsWith(\"PRAGMA\")\n ) {\n fail(\n \"Only SELECT, WITH, EXPLAIN, and PRAGMA queries are allowed. Use db-exec for writes.\",\n );\n }\n assertNoSensitiveFrameworkTables(stripped, \"read\");\n\n // Resolve database URL: --db flag → DATABASE_URL env → default file path\n let url: string;\n if (parsed.db) {\n url = \"file:\" + path.resolve(parsed.db);\n } else if (getDatabaseUrl()) {\n url = getDatabaseUrl();\n } else {\n url = \"file:\" + path.resolve(process.cwd(), \"data\", \"app.db\");\n }\n\n let finalSql = sql;\n if (\n parsed.limit &&\n (upper.startsWith(\"SELECT\") || upper.startsWith(\"WITH\")) &&\n !/\\bLIMIT\\b/i.test(stripped)\n ) {\n const limitVal = parseInt(parsed.limit, 10);\n if (isNaN(limitVal) || limitVal < 1)\n fail(\"--limit must be a positive integer\");\n finalSql = `${sql} LIMIT ${limitVal}`;\n }\n\n // Postgres path\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const pgSql = pg(url);\n try {\n const pgSqlText = normalizePostgresSql(finalSql, sqlArgs);\n let rows: Record<string, unknown>[] = [];\n await pgSql.begin(async (tx: any) => {\n // Temp views are session state. Keep setup/query/teardown on one\n // transaction-bound backend so pooled Postgres never retains them.\n const scoping = await buildScopingPostgres(tx);\n try {\n for (const stmt of scoping.setup) {\n await tx.unsafe(stmt);\n }\n\n const result =\n sqlArgs.length > 0\n ? await tx.unsafe(pgSqlText, sqlArgs as any[])\n : await tx.unsafe(pgSqlText);\n rows = Array.from(result);\n } finally {\n for (const stmt of scoping.teardown) {\n await tx.unsafe(stmt).catch(() => {});\n }\n }\n });\n const keys = rows.length > 0 ? Object.keys(rows[0]) : [];\n\n printTable(\n rows.length > 0 ? rows : keys.length > 0 ? rows : [],\n pgSqlText,\n parsed.format,\n );\n } finally {\n await pgSql.end();\n }\n return;\n }\n\n // libsql / SQLite path\n const client = await createSqliteScriptClient(url);\n\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingSqlite(client);\n for (const stmt of scoping.setup) {\n await client.execute(stmt);\n }\n\n const result =\n sqlArgs.length > 0\n ? await client.execute({ sql: finalSql, args: sqlArgs as any[] })\n : await client.execute(finalSql);\n const rows: Record<string, unknown>[] = result.rows.map((row) => {\n const obj: Record<string, unknown> = {};\n for (let i = 0; i < result.columns.length; i++) {\n obj[result.columns[i]] = row[i];\n }\n return obj;\n });\n\n printTable(rows, finalSql, parsed.format);\n\n // Tear down temp views\n for (const stmt of scoping.teardown) {\n await client.execute(stmt).catch(() => {});\n }\n } finally {\n client.close();\n }\n}\n"]}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/scripts/db/query.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core script: db-query\n *\n * Run a read-only SQL query against a SQLite or Postgres database.\n *\n * In production mode, temporary views are created to scope data to the\n * current user (AGENT_USER_EMAIL). Tables with an `owner_email` column\n * and core tables (settings, application_state, etc.) are automatically\n * filtered so queries only return the current user's data.\n *\n * Usage:\n * pnpm action db-query --sql \"SELECT * FROM forms WHERE id = ?\" [--args '[\"abc\"]'] [--db path] [--format json] [--limit N]\n */\n\nimport path from \"path\";\nimport { getDatabaseUrl } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport {\n assertNoSchemaQualifiedTables,\n assertNoSensitiveFrameworkTables,\n} from \"./safety.js\";\nimport { buildScopingPostgres, buildScopingSqlite } from \"./scoping.js\";\nimport { createSqliteScriptClient } from \"./sqlite-client.js\";\n\nfunction isPostgresUrl(url: string): boolean {\n return url.startsWith(\"postgres://\") || url.startsWith(\"postgresql://\");\n}\n\nfunction parseSqlArgs(raw: string | undefined): unknown[] {\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n // Fall through to the shared error below.\n }\n fail(\"--args must be a JSON array\");\n}\n\nfunction convertQuestionMarksToPostgresParams(sql: string): string {\n let index = 0;\n let out = \"\";\n let state: \"normal\" | \"single\" | \"double\" | \"line-comment\" | \"block-comment\" =\n \"normal\";\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i];\n const next = sql[i + 1];\n\n if (state === \"line-comment\") {\n out += ch;\n if (ch === \"\\n\") state = \"normal\";\n continue;\n }\n\n if (state === \"block-comment\") {\n out += ch;\n if (ch === \"*\" && next === \"/\") {\n out += next;\n i++;\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"single\") {\n out += ch;\n if (ch === \"'\" && next === \"'\") {\n out += next;\n i++;\n } else if (ch === \"'\") {\n state = \"normal\";\n }\n continue;\n }\n\n if (state === \"double\") {\n out += ch;\n if (ch === '\"' && next === '\"') {\n out += next;\n i++;\n } else if (ch === '\"') {\n state = \"normal\";\n }\n continue;\n }\n\n if (ch === \"-\" && next === \"-\") {\n out += ch + next;\n i++;\n state = \"line-comment\";\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n out += ch + next;\n i++;\n state = \"block-comment\";\n continue;\n }\n if (ch === \"'\") {\n out += ch;\n state = \"single\";\n continue;\n }\n if (ch === '\"') {\n out += ch;\n state = \"double\";\n continue;\n }\n if (ch === \"?\") {\n index++;\n out += `$${index}`;\n continue;\n }\n out += ch;\n }\n\n return out;\n}\n\nfunction normalizePostgresSql(sql: string, args: unknown[]): string {\n if (args.length === 0 || /\\$\\d+\\b/.test(sql)) return sql;\n return convertQuestionMarksToPostgresParams(sql);\n}\n\nfunction printTable(\n rows: Record<string, unknown>[],\n finalSql: string,\n format?: string,\n) {\n if (format === \"json\") {\n console.log(\n JSON.stringify({ query: finalSql, rows, count: rows.length }, null, 2),\n );\n return;\n }\n\n console.log(`Query: ${finalSql}`);\n console.log(`Rows: ${rows.length}\\n`);\n\n if (rows.length === 0) {\n console.log(\"(no results)\");\n return;\n }\n\n const keys = Object.keys(rows[0]);\n const widths = keys.map((k) => {\n const maxVal = Math.max(...rows.map((r) => String(r[k] ?? \"NULL\").length));\n return Math.max(k.length, Math.min(maxVal, 60));\n });\n\n const header = keys.map((k, i) => k.padEnd(widths[i])).join(\" | \");\n console.log(header);\n console.log(widths.map((w) => \"-\".repeat(w)).join(\"-+-\"));\n\n for (const row of rows) {\n const line = keys\n .map((k, i) => {\n const val = String(row[k] ?? \"NULL\");\n return val.length > 60\n ? val.slice(0, 57) + \"...\"\n : val.padEnd(widths[i]);\n })\n .join(\" | \");\n console.log(line);\n }\n}\n\nexport default async function dbQuery(args: string[]): Promise<void> {\n const parsed = parseArgs(args);\n\n if (parsed.help === \"true\") {\n console.log(`Usage: pnpm action db-query --sql \"<query>\" [options]\n\nOptions:\n --sql <query> SQL SELECT query to run (required)\n --args <json> JSON array of positional SQL bind parameters\n --db <path> Path to SQLite database (default: data/app.db)\n --format json Output as JSON instead of a table\n --limit N Append LIMIT N if not already present\n --help Show this help message`);\n return;\n }\n\n const sql = parsed.sql;\n if (!sql) {\n fail('--sql is required. Example: --sql \"SELECT * FROM forms\"');\n }\n const sqlArgs = parseSqlArgs(parsed.args);\n\n // Safety: only allow read-only statements.\n // Strip leading SQL comments before checking the prefix.\n const stripped = sql\n .replace(/^\\s*--[^\\n]*\\n/gm, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n .trim();\n const upper = stripped.toUpperCase();\n if (\n !upper.startsWith(\"SELECT\") &&\n !upper.startsWith(\"WITH\") &&\n !upper.startsWith(\"EXPLAIN\") &&\n !upper.startsWith(\"PRAGMA\")\n ) {\n fail(\n \"Only SELECT, WITH, EXPLAIN, and PRAGMA queries are allowed. Use db-exec for writes.\",\n );\n }\n assertNoSensitiveFrameworkTables(stripped, \"read\");\n assertNoSchemaQualifiedTables(stripped, \"read\");\n\n // Resolve database URL: --db flag → DATABASE_URL env → default file path\n let url: string;\n if (parsed.db) {\n url = \"file:\" + path.resolve(parsed.db);\n } else if (getDatabaseUrl()) {\n url = getDatabaseUrl();\n } else {\n url = \"file:\" + path.resolve(process.cwd(), \"data\", \"app.db\");\n }\n\n let finalSql = sql;\n if (\n parsed.limit &&\n (upper.startsWith(\"SELECT\") || upper.startsWith(\"WITH\")) &&\n !/\\bLIMIT\\b/i.test(stripped)\n ) {\n const limitVal = parseInt(parsed.limit, 10);\n if (isNaN(limitVal) || limitVal < 1)\n fail(\"--limit must be a positive integer\");\n finalSql = `${sql} LIMIT ${limitVal}`;\n }\n\n // Postgres path\n if (isPostgresUrl(url)) {\n const { default: pg } = await import(\"postgres\");\n const pgSql = pg(url);\n try {\n const pgSqlText = normalizePostgresSql(finalSql, sqlArgs);\n let rows: Record<string, unknown>[] = [];\n await pgSql.begin(async (tx: any) => {\n // Temp views are session state. Keep setup/query/teardown on one\n // transaction-bound backend so pooled Postgres never retains them.\n const scoping = await buildScopingPostgres(tx);\n try {\n for (const stmt of scoping.setup) {\n await tx.unsafe(stmt);\n }\n\n const result =\n sqlArgs.length > 0\n ? await tx.unsafe(pgSqlText, sqlArgs as any[])\n : await tx.unsafe(pgSqlText);\n rows = Array.from(result);\n } finally {\n for (const stmt of scoping.teardown) {\n await tx.unsafe(stmt).catch(() => {});\n }\n }\n });\n const keys = rows.length > 0 ? Object.keys(rows[0]) : [];\n\n printTable(\n rows.length > 0 ? rows : keys.length > 0 ? rows : [],\n pgSqlText,\n parsed.format,\n );\n } finally {\n await pgSql.end();\n }\n return;\n }\n\n // libsql / SQLite path\n const client = await createSqliteScriptClient(url);\n\n try {\n // Set up user-scoped temp views in production\n const scoping = await buildScopingSqlite(client);\n for (const stmt of scoping.setup) {\n await client.execute(stmt);\n }\n\n const result =\n sqlArgs.length > 0\n ? await client.execute({ sql: finalSql, args: sqlArgs as any[] })\n : await client.execute(finalSql);\n const rows: Record<string, unknown>[] = result.rows.map((row) => {\n const obj: Record<string, unknown> = {};\n for (let i = 0; i < result.columns.length; i++) {\n obj[result.columns[i]] = row[i];\n }\n return obj;\n });\n\n printTable(rows, finalSql, parsed.format);\n\n // Tear down temp views\n for (const stmt of scoping.teardown) {\n await client.execute(stmt).catch(() => {});\n }\n } finally {\n client.close();\n }\n}\n"]}