@agent-native/core 0.7.21 → 0.7.23

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 (163) hide show
  1. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  2. package/dist/agent/engine/ai-sdk-engine.js +43 -1
  3. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  4. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  5. package/dist/agent/engine/anthropic-engine.js +8 -0
  6. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  7. package/dist/agent/engine/builder-engine.d.ts +1 -1
  8. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  9. package/dist/agent/engine/builder-engine.js +9 -4
  10. package/dist/agent/engine/builder-engine.js.map +1 -1
  11. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  12. package/dist/agent/engine/translate-ai-sdk.js +31 -1
  13. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  14. package/dist/agent/engine/translate-anthropic.d.ts.map +1 -1
  15. package/dist/agent/engine/translate-anthropic.js +16 -0
  16. package/dist/agent/engine/translate-anthropic.js.map +1 -1
  17. package/dist/agent/engine/types.d.ts +16 -1
  18. package/dist/agent/engine/types.d.ts.map +1 -1
  19. package/dist/agent/engine/types.js.map +1 -1
  20. package/dist/agent/production-agent.d.ts +4 -0
  21. package/dist/agent/production-agent.d.ts.map +1 -1
  22. package/dist/agent/production-agent.js +96 -4
  23. package/dist/agent/production-agent.js.map +1 -1
  24. package/dist/agent/types.d.ts +3 -0
  25. package/dist/agent/types.d.ts.map +1 -1
  26. package/dist/agent/types.js.map +1 -1
  27. package/dist/cli/create.d.ts +2 -1
  28. package/dist/cli/create.d.ts.map +1 -1
  29. package/dist/cli/create.js +21 -14
  30. package/dist/cli/create.js.map +1 -1
  31. package/dist/client/AgentPanel.d.ts.map +1 -1
  32. package/dist/client/AgentPanel.js +5 -5
  33. package/dist/client/AgentPanel.js.map +1 -1
  34. package/dist/client/AssistantChat.d.ts +5 -0
  35. package/dist/client/AssistantChat.d.ts.map +1 -1
  36. package/dist/client/AssistantChat.js +54 -2
  37. package/dist/client/AssistantChat.js.map +1 -1
  38. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  39. package/dist/client/MultiTabAssistantChat.js +33 -2
  40. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  41. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  42. package/dist/client/NewWorkspaceAppFlow.js +10 -1
  43. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  44. package/dist/client/agent-chat-adapter.d.ts +4 -0
  45. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  46. package/dist/client/agent-chat-adapter.js +5 -1
  47. package/dist/client/agent-chat-adapter.js.map +1 -1
  48. package/dist/client/composer/TiptapComposer.d.ts +6 -1
  49. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  50. package/dist/client/composer/TiptapComposer.js +25 -17
  51. package/dist/client/composer/TiptapComposer.js.map +1 -1
  52. package/dist/client/composer/useVoiceDictation.d.ts +6 -5
  53. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  54. package/dist/client/composer/useVoiceDictation.js +54 -21
  55. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  56. package/dist/client/org/OrgSwitcher.d.ts +3 -1
  57. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  58. package/dist/client/org/OrgSwitcher.js +12 -7
  59. package/dist/client/org/OrgSwitcher.js.map +1 -1
  60. package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
  61. package/dist/client/settings/AutomationsSection.js +2 -2
  62. package/dist/client/settings/AutomationsSection.js.map +1 -1
  63. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  64. package/dist/client/settings/VoiceTranscriptionSection.js +46 -15
  65. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  66. package/dist/client/tools/ToolViewer.d.ts.map +1 -1
  67. package/dist/client/tools/ToolViewer.js +2 -2
  68. package/dist/client/tools/ToolViewer.js.map +1 -1
  69. package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
  70. package/dist/client/tools/ToolsListPage.js +4 -4
  71. package/dist/client/tools/ToolsListPage.js.map +1 -1
  72. package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
  73. package/dist/client/tools/ToolsSidebarSection.js +2 -2
  74. package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
  75. package/dist/client/transcription/use-live-transcription.d.ts +1 -0
  76. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  77. package/dist/client/transcription/use-live-transcription.js +41 -0
  78. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  79. package/dist/integrations/adapters/email.js +81 -5
  80. package/dist/integrations/adapters/email.js.map +1 -1
  81. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  82. package/dist/integrations/adapters/slack.js +4 -1
  83. package/dist/integrations/adapters/slack.js.map +1 -1
  84. package/dist/integrations/plugin.d.ts.map +1 -1
  85. package/dist/integrations/plugin.js +2 -1
  86. package/dist/integrations/plugin.js.map +1 -1
  87. package/dist/integrations/types.d.ts +2 -0
  88. package/dist/integrations/types.d.ts.map +1 -1
  89. package/dist/integrations/types.js.map +1 -1
  90. package/dist/integrations/webhook-handler.js +12 -2
  91. package/dist/integrations/webhook-handler.js.map +1 -1
  92. package/dist/oauth-tokens/store.d.ts.map +1 -1
  93. package/dist/oauth-tokens/store.js +34 -16
  94. package/dist/oauth-tokens/store.js.map +1 -1
  95. package/dist/scripts/db/exec.d.ts.map +1 -1
  96. package/dist/scripts/db/exec.js +32 -23
  97. package/dist/scripts/db/exec.js.map +1 -1
  98. package/dist/scripts/db/patch.d.ts.map +1 -1
  99. package/dist/scripts/db/patch.js +48 -35
  100. package/dist/scripts/db/patch.js.map +1 -1
  101. package/dist/scripts/db/query.d.ts.map +1 -1
  102. package/dist/scripts/db/query.js +22 -13
  103. package/dist/scripts/db/query.js.map +1 -1
  104. package/dist/scripts/db/safety.d.ts +2 -0
  105. package/dist/scripts/db/safety.d.ts.map +1 -0
  106. package/dist/scripts/db/safety.js +67 -0
  107. package/dist/scripts/db/safety.js.map +1 -0
  108. package/dist/scripts/db/scoping.js +4 -4
  109. package/dist/scripts/db/scoping.js.map +1 -1
  110. package/dist/server/email-template.d.ts +5 -0
  111. package/dist/server/email-template.d.ts.map +1 -1
  112. package/dist/server/email-template.js +7 -4
  113. package/dist/server/email-template.js.map +1 -1
  114. package/dist/server/google-auth-plugin.d.ts.map +1 -1
  115. package/dist/server/google-auth-plugin.js +1 -8
  116. package/dist/server/google-auth-plugin.js.map +1 -1
  117. package/dist/server/index.d.ts +3 -2
  118. package/dist/server/index.d.ts.map +1 -1
  119. package/dist/server/index.js +3 -2
  120. package/dist/server/index.js.map +1 -1
  121. package/dist/server/onboarding-html.d.ts.map +1 -1
  122. package/dist/server/onboarding-html.js +3 -10
  123. package/dist/server/onboarding-html.js.map +1 -1
  124. package/dist/server/transcribe-voice.d.ts +9 -9
  125. package/dist/server/transcribe-voice.d.ts.map +1 -1
  126. package/dist/server/transcribe-voice.js +405 -51
  127. package/dist/server/transcribe-voice.js.map +1 -1
  128. package/dist/server/voice-providers-status.d.ts.map +1 -1
  129. package/dist/server/voice-providers-status.js +13 -1
  130. package/dist/server/voice-providers-status.js.map +1 -1
  131. package/dist/settings/store.d.ts.map +1 -1
  132. package/dist/settings/store.js +14 -6
  133. package/dist/settings/store.js.map +1 -1
  134. package/dist/shared/reasoning-effort.d.ts +8 -0
  135. package/dist/shared/reasoning-effort.d.ts.map +1 -0
  136. package/dist/shared/reasoning-effort.js +94 -0
  137. package/dist/shared/reasoning-effort.js.map +1 -0
  138. package/dist/templates/default/public/favicon.svg +1 -13
  139. package/dist/templates/default/public/icon-180.svg +1 -13
  140. package/dist/templates/default/public/icon-192.svg +1 -13
  141. package/dist/templates/default/public/icon-512.svg +1 -13
  142. package/dist/templates/workspace-root/.github/workflows/ci.yml +32 -0
  143. package/dist/templates/workspace-root/.prettierignore +19 -0
  144. package/dist/templates/workspace-root/_gitignore +5 -0
  145. package/dist/templates/workspace-root/package.json +13 -2
  146. package/dist/templates/workspace-root/pnpm-workspace.yaml +1 -0
  147. package/dist/templates/workspace-root/scripts/workspace-dev.ts +2 -0
  148. package/dist/transcription/builder-transcription.d.ts +2 -0
  149. package/dist/transcription/builder-transcription.d.ts.map +1 -1
  150. package/dist/transcription/builder-transcription.js +4 -0
  151. package/dist/transcription/builder-transcription.js.map +1 -1
  152. package/docs/content/voice-input.md +14 -13
  153. package/package.json +1 -1
  154. package/src/templates/default/public/favicon.svg +1 -13
  155. package/src/templates/default/public/icon-180.svg +1 -13
  156. package/src/templates/default/public/icon-192.svg +1 -13
  157. package/src/templates/default/public/icon-512.svg +1 -13
  158. package/src/templates/workspace-root/.github/workflows/ci.yml +32 -0
  159. package/src/templates/workspace-root/.prettierignore +19 -0
  160. package/src/templates/workspace-root/_gitignore +5 -0
  161. package/src/templates/workspace-root/package.json +13 -2
  162. package/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
  163. package/src/templates/workspace-root/scripts/workspace-dev.ts +2 -0
@@ -1,11 +1,15 @@
1
1
  import { getDbExec, isPostgres, intType } from "../db/client.js";
2
2
  let _initPromise;
3
+ function oauthTokensTable() {
4
+ return isPostgres() ? "public.oauth_tokens" : "oauth_tokens";
5
+ }
3
6
  async function ensureTable() {
4
7
  if (!_initPromise) {
5
8
  _initPromise = (async () => {
6
9
  const client = getDbExec();
10
+ const table = oauthTokensTable();
7
11
  await client.execute(`
8
- CREATE TABLE IF NOT EXISTS oauth_tokens (
12
+ CREATE TABLE IF NOT EXISTS ${table} (
9
13
  provider TEXT NOT NULL,
10
14
  account_id TEXT NOT NULL,
11
15
  owner TEXT,
@@ -16,20 +20,20 @@ async function ensureTable() {
16
20
  `);
17
21
  // Migration: add owner column to existing tables
18
22
  try {
19
- await client.execute(`ALTER TABLE oauth_tokens ADD COLUMN owner TEXT`);
23
+ await client.execute(`ALTER TABLE ${table} ADD COLUMN owner TEXT`);
20
24
  }
21
25
  catch {
22
26
  // Column already exists
23
27
  }
24
28
  // Migration: add display_name column
25
29
  try {
26
- await client.execute(`ALTER TABLE oauth_tokens ADD COLUMN display_name TEXT`);
30
+ await client.execute(`ALTER TABLE ${table} ADD COLUMN display_name TEXT`);
27
31
  }
28
32
  catch {
29
33
  // Column already exists
30
34
  }
31
35
  // Backfill: set owner = account_id for existing rows without an owner
32
- await client.execute(`UPDATE oauth_tokens SET owner = account_id WHERE owner IS NULL`);
36
+ await client.execute(`UPDATE ${table} SET owner = account_id WHERE owner IS NULL`);
33
37
  })();
34
38
  }
35
39
  return _initPromise;
@@ -37,8 +41,9 @@ async function ensureTable() {
37
41
  export async function getOAuthTokens(provider, accountId) {
38
42
  await ensureTable();
39
43
  const client = getDbExec();
44
+ const table = oauthTokensTable();
40
45
  const { rows } = await client.execute({
41
- sql: `SELECT tokens FROM oauth_tokens WHERE provider = ? AND account_id = ?`,
46
+ sql: `SELECT tokens FROM ${table} WHERE provider = ? AND account_id = ?`,
42
47
  args: [provider, accountId],
43
48
  });
44
49
  if (rows.length === 0)
@@ -87,6 +92,7 @@ export class OAuthAccountOwnedByOtherUserError extends Error {
87
92
  export async function saveOAuthTokens(provider, accountId, tokens, owner) {
88
93
  await ensureTable();
89
94
  const client = getDbExec();
95
+ const table = oauthTokensTable();
90
96
  // Never store "local@localhost" as owner — it creates shared-ownership bugs
91
97
  const sanitizedOwner = owner === "local@localhost" ? accountId : owner;
92
98
  // Read the current row before deciding what to write. We use this to
@@ -96,13 +102,15 @@ export async function saveOAuthTokens(provider, accountId, tokens, owner) {
96
102
  let resolvedOwner = sanitizedOwner ?? accountId;
97
103
  let existingDisplayName = null;
98
104
  let existingOwner = null;
105
+ let existingTokens = null;
99
106
  const { rows: existing } = await client.execute({
100
- sql: `SELECT owner, display_name FROM oauth_tokens WHERE provider = ? AND account_id = ?`,
107
+ sql: `SELECT owner, display_name, tokens FROM ${table} WHERE provider = ? AND account_id = ?`,
101
108
  args: [provider, accountId],
102
109
  });
103
110
  if (existing.length > 0) {
104
111
  existingOwner = existing[0].owner ?? null;
105
112
  existingDisplayName = existing[0].display_name ?? null;
113
+ existingTokens = JSON.parse(existing[0].tokens ?? "{}");
106
114
  }
107
115
  if (!owner) {
108
116
  // Token-refresh path: keep the existing owner/displayName unchanged.
@@ -129,16 +137,21 @@ export async function saveOAuthTokens(provider, accountId, tokens, owner) {
129
137
  attemptedOwner: sanitizedOwner,
130
138
  });
131
139
  }
140
+ const cleanedIncomingTokens = Object.fromEntries(Object.entries(tokens).filter(([, value]) => value !== undefined));
141
+ const tokensToStore = {
142
+ ...(existingTokens ?? {}),
143
+ ...cleanedIncomingTokens,
144
+ };
132
145
  await client.execute({
133
146
  sql: isPostgres()
134
- ? `INSERT INTO oauth_tokens (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, oauth_tokens.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at`
135
- : `INSERT OR REPLACE INTO oauth_tokens (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,
147
+ ? `INSERT INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, ${table}.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at`
148
+ : `INSERT OR REPLACE INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,
136
149
  args: [
137
150
  provider,
138
151
  accountId,
139
152
  resolvedOwner,
140
153
  existingDisplayName,
141
- JSON.stringify(tokens),
154
+ JSON.stringify(tokensToStore),
142
155
  Date.now(),
143
156
  ],
144
157
  });
@@ -146,15 +159,16 @@ export async function saveOAuthTokens(provider, accountId, tokens, owner) {
146
159
  export async function deleteOAuthTokens(provider, accountId) {
147
160
  await ensureTable();
148
161
  const client = getDbExec();
162
+ const table = oauthTokensTable();
149
163
  if (accountId) {
150
164
  const result = await client.execute({
151
- sql: `DELETE FROM oauth_tokens WHERE provider = ? AND account_id = ?`,
165
+ sql: `DELETE FROM ${table} WHERE provider = ? AND account_id = ?`,
152
166
  args: [provider, accountId],
153
167
  });
154
168
  return result.rowsAffected;
155
169
  }
156
170
  const result = await client.execute({
157
- sql: `DELETE FROM oauth_tokens WHERE provider = ?`,
171
+ sql: `DELETE FROM ${table} WHERE provider = ?`,
158
172
  args: [provider],
159
173
  });
160
174
  return result.rowsAffected;
@@ -162,8 +176,9 @@ export async function deleteOAuthTokens(provider, accountId) {
162
176
  export async function listOAuthAccounts(provider) {
163
177
  await ensureTable();
164
178
  const client = getDbExec();
179
+ const table = oauthTokensTable();
165
180
  const { rows } = await client.execute({
166
- sql: `SELECT account_id, owner, tokens FROM oauth_tokens WHERE provider = ?`,
181
+ sql: `SELECT account_id, owner, tokens FROM ${table} WHERE provider = ?`,
167
182
  args: [provider],
168
183
  });
169
184
  return rows.map((row) => ({
@@ -179,8 +194,9 @@ export async function listOAuthAccounts(provider) {
179
194
  export async function listOAuthAccountsByOwner(provider, owner) {
180
195
  await ensureTable();
181
196
  const client = getDbExec();
197
+ const table = oauthTokensTable();
182
198
  const { rows } = await client.execute({
183
- sql: `SELECT account_id, display_name, tokens FROM oauth_tokens WHERE provider = ? AND owner = ?`,
199
+ sql: `SELECT account_id, display_name, tokens FROM ${table} WHERE provider = ? AND owner = ?`,
184
200
  args: [provider, owner],
185
201
  });
186
202
  return rows.map((row) => ({
@@ -195,8 +211,9 @@ export async function listOAuthAccountsByOwner(provider, owner) {
195
211
  export async function setOAuthDisplayName(provider, accountId, displayName) {
196
212
  await ensureTable();
197
213
  const client = getDbExec();
214
+ const table = oauthTokensTable();
198
215
  await client.execute({
199
- sql: `UPDATE oauth_tokens SET display_name = ? WHERE provider = ? AND account_id = ?`,
216
+ sql: `UPDATE ${table} SET display_name = ? WHERE provider = ? AND account_id = ?`,
200
217
  args: [displayName, provider, accountId],
201
218
  });
202
219
  }
@@ -215,15 +232,16 @@ export async function setOAuthDisplayName(provider, accountId, displayName) {
215
232
  export async function hasOAuthTokens(provider, owner) {
216
233
  await ensureTable();
217
234
  const client = getDbExec();
235
+ const table = oauthTokensTable();
218
236
  if (owner === "local@localhost") {
219
237
  const { rows } = await client.execute({
220
- sql: `SELECT 1 FROM oauth_tokens WHERE provider = ? LIMIT 1`,
238
+ sql: `SELECT 1 FROM ${table} WHERE provider = ? LIMIT 1`,
221
239
  args: [provider],
222
240
  });
223
241
  return rows.length > 0;
224
242
  }
225
243
  const { rows } = await client.execute({
226
- sql: `SELECT 1 FROM oauth_tokens WHERE provider = ? AND owner = ? LIMIT 1`,
244
+ sql: `SELECT 1 FROM ${table} WHERE provider = ? AND owner = ? LIMIT 1`,
227
245
  args: [provider, owner],
228
246
  });
229
247
  return rows.length > 0;
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/oauth-tokens/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec, isPostgres, intType, type DbExec } from \"../db/client.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS oauth_tokens (\n provider TEXT NOT NULL,\n account_id TEXT NOT NULL,\n owner TEXT,\n tokens TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (provider, account_id)\n )\n `);\n // Migration: add owner column to existing tables\n try {\n await client.execute(`ALTER TABLE oauth_tokens ADD COLUMN owner TEXT`);\n } catch {\n // Column already exists\n }\n // Migration: add display_name column\n try {\n await client.execute(\n `ALTER TABLE oauth_tokens ADD COLUMN display_name TEXT`,\n );\n } catch {\n // Column already exists\n }\n // Backfill: set owner = account_id for existing rows without an owner\n await client.execute(\n `UPDATE oauth_tokens SET owner = account_id WHERE owner IS NULL`,\n );\n })();\n }\n return _initPromise;\n}\n\nexport async function getOAuthTokens(\n provider: string,\n accountId: string,\n): Promise<Record<string, unknown> | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT tokens FROM oauth_tokens WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].tokens as string);\n}\n\n/**\n * Thrown when an OAuth save would re-bind an `(provider, account_id)` row\n * to a different owner than already holds it. Callers should catch this and\n * surface a clean \"this account is already linked to another user\" message\n * to the requester rather than letting it propagate as a 500.\n *\n * Carries `statusCode = 409` so route handlers using h3's `createError` can\n * pass it straight through.\n */\nexport class OAuthAccountOwnedByOtherUserError extends Error {\n readonly statusCode = 409;\n readonly provider: string;\n readonly accountId: string;\n readonly existingOwner: string;\n readonly attemptedOwner: string;\n constructor(opts: {\n provider: string;\n accountId: string;\n existingOwner: string;\n attemptedOwner: string;\n }) {\n super(\n `OAuth account ${opts.provider}:${opts.accountId} is already linked to another user — refusing to overwrite the owner.`,\n );\n this.name = \"OAuthAccountOwnedByOtherUserError\";\n this.provider = opts.provider;\n this.accountId = opts.accountId;\n this.existingOwner = opts.existingOwner;\n this.attemptedOwner = opts.attemptedOwner;\n }\n}\n\n/**\n * Save OAuth tokens. The `owner` parameter specifies which user owns this\n * account — defaults to `accountId` (the account itself is the owner).\n * For multi-account support, pass the logged-in user's email as owner.\n *\n * If the account already exists and is owned by a different user, throws\n * `OAuthAccountOwnedByOtherUserError` (statusCode 409) to prevent silently\n * stealing another user's linked account.\n *\n * Read + write happen as a single linearised batch (Postgres) or paired\n * statements (SQLite). On both backends the per-row PK serialises concurrent\n * writes for the same `(provider, account_id)` so the owner check cannot be\n * raced by an attacker calling saveOAuthTokens twice in flight — the second\n * caller sees the first caller's owner row and raises 409.\n */\nexport async function saveOAuthTokens(\n provider: string,\n accountId: string,\n tokens: Record<string, unknown>,\n owner?: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n\n // Never store \"local@localhost\" as owner — it creates shared-ownership bugs\n const sanitizedOwner = owner === \"local@localhost\" ? accountId : owner;\n\n // Read the current row before deciding what to write. We use this to\n // (a) preserve owner / display_name when this is a token refresh (no\n // owner argument), and (b) reject the write when the caller is trying\n // to overwrite a row owned by someone else.\n let resolvedOwner = sanitizedOwner ?? accountId;\n let existingDisplayName: string | null = null;\n let existingOwner: string | null = null;\n const { rows: existing } = await client.execute({\n sql: `SELECT owner, display_name FROM oauth_tokens WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n if (existing.length > 0) {\n existingOwner = (existing[0].owner as string) ?? null;\n existingDisplayName = (existing[0].display_name as string) ?? null;\n }\n\n if (!owner) {\n // Token-refresh path: keep the existing owner/displayName unchanged.\n if (existingOwner) resolvedOwner = existingOwner;\n } else if (existingOwner === \"local@localhost\" && sanitizedOwner) {\n // Legacy dev rows from older OAuth flows were sometimes stored under the\n // synthetic local user. A successful OAuth callback proves control of the\n // Google account, so let the reconnect repair that owner in place.\n resolvedOwner = sanitizedOwner;\n } else if (\n existingOwner &&\n sanitizedOwner &&\n existingOwner !== sanitizedOwner\n ) {\n // Refuse to silently re-bind an account from one user to another.\n // This is the case the docstring promised but the previous\n // implementation didn't enforce — `ON CONFLICT DO UPDATE SET\n // owner=EXCLUDED.owner` would have overwritten the prior owner.\n throw new OAuthAccountOwnedByOtherUserError({\n provider,\n accountId,\n existingOwner,\n attemptedOwner: sanitizedOwner,\n });\n }\n\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO oauth_tokens (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, oauth_tokens.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO oauth_tokens (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,\n args: [\n provider,\n accountId,\n resolvedOwner,\n existingDisplayName,\n JSON.stringify(tokens),\n Date.now(),\n ],\n });\n}\n\nexport async function deleteOAuthTokens(\n provider: string,\n accountId?: string,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n if (accountId) {\n const result = await client.execute({\n sql: `DELETE FROM oauth_tokens WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n return result.rowsAffected;\n }\n const result = await client.execute({\n sql: `DELETE FROM oauth_tokens WHERE provider = ?`,\n args: [provider],\n });\n return result.rowsAffected;\n}\n\nexport async function listOAuthAccounts(provider: string): Promise<\n Array<{\n accountId: string;\n owner: string | null;\n tokens: Record<string, unknown>;\n }>\n> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT account_id, owner, tokens FROM oauth_tokens WHERE provider = ?`,\n args: [provider],\n });\n return rows.map((row) => ({\n accountId: row.account_id as string,\n owner: (row.owner as string) ?? null,\n tokens: JSON.parse(row.tokens as string),\n }));\n}\n\n/**\n * List all OAuth accounts owned by a specific user.\n * In multi-account mode, a user may have connected multiple Google accounts.\n */\nexport async function listOAuthAccountsByOwner(\n provider: string,\n owner: string,\n): Promise<\n Array<{\n accountId: string;\n displayName: string | null;\n tokens: Record<string, unknown>;\n }>\n> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT account_id, display_name, tokens FROM oauth_tokens WHERE provider = ? AND owner = ?`,\n args: [provider, owner],\n });\n return rows.map((row) => ({\n accountId: row.account_id as string,\n displayName: (row.display_name as string) ?? null,\n tokens: JSON.parse(row.tokens as string),\n }));\n}\n\n/**\n * Set the display name for an OAuth account (e.g. Google profile name).\n */\nexport async function setOAuthDisplayName(\n provider: string,\n accountId: string,\n displayName: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `UPDATE oauth_tokens SET display_name = ? WHERE provider = ? AND account_id = ?`,\n args: [displayName, provider, accountId],\n });\n}\n\n/**\n * Check whether a specific user has tokens for a provider.\n *\n * `owner` is REQUIRED. The previous unscoped form leaked information\n * across users — the onboarding banner would mark the OAuth secret as\n * \"set\" for user B as soon as ANY user in the deployment connected the\n * provider, and user B would never see the prompt to connect.\n *\n * For the local-dev / single-tenant case, callers pass the dev sentinel\n * (`DEV_MODE_USER_EMAIL`) and we degrade to \"any row exists\" — preserving\n * the original behaviour for that single-user surface.\n */\nexport async function hasOAuthTokens(\n provider: string,\n owner: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n if (owner === \"local@localhost\") {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM oauth_tokens WHERE provider = ? LIMIT 1`,\n args: [provider],\n });\n return rows.length > 0;\n }\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM oauth_tokens WHERE provider = ? AND owner = ? LIMIT 1`,\n args: [provider, owner],\n });\n return rows.length > 0;\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/oauth-tokens/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec, isPostgres, intType, type DbExec } from \"../db/client.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nfunction oauthTokensTable(): string {\n return isPostgres() ? \"public.oauth_tokens\" : \"oauth_tokens\";\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const table = oauthTokensTable();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS ${table} (\n provider TEXT NOT NULL,\n account_id TEXT NOT NULL,\n owner TEXT,\n tokens TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (provider, account_id)\n )\n `);\n // Migration: add owner column to existing tables\n try {\n await client.execute(`ALTER TABLE ${table} ADD COLUMN owner TEXT`);\n } catch {\n // Column already exists\n }\n // Migration: add display_name column\n try {\n await client.execute(\n `ALTER TABLE ${table} ADD COLUMN display_name TEXT`,\n );\n } catch {\n // Column already exists\n }\n // Backfill: set owner = account_id for existing rows without an owner\n await client.execute(\n `UPDATE ${table} SET owner = account_id WHERE owner IS NULL`,\n );\n })();\n }\n return _initPromise;\n}\n\nexport async function getOAuthTokens(\n provider: string,\n accountId: string,\n): Promise<Record<string, unknown> | null> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n const { rows } = await client.execute({\n sql: `SELECT tokens FROM ${table} WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].tokens as string);\n}\n\n/**\n * Thrown when an OAuth save would re-bind an `(provider, account_id)` row\n * to a different owner than already holds it. Callers should catch this and\n * surface a clean \"this account is already linked to another user\" message\n * to the requester rather than letting it propagate as a 500.\n *\n * Carries `statusCode = 409` so route handlers using h3's `createError` can\n * pass it straight through.\n */\nexport class OAuthAccountOwnedByOtherUserError extends Error {\n readonly statusCode = 409;\n readonly provider: string;\n readonly accountId: string;\n readonly existingOwner: string;\n readonly attemptedOwner: string;\n constructor(opts: {\n provider: string;\n accountId: string;\n existingOwner: string;\n attemptedOwner: string;\n }) {\n super(\n `OAuth account ${opts.provider}:${opts.accountId} is already linked to another user — refusing to overwrite the owner.`,\n );\n this.name = \"OAuthAccountOwnedByOtherUserError\";\n this.provider = opts.provider;\n this.accountId = opts.accountId;\n this.existingOwner = opts.existingOwner;\n this.attemptedOwner = opts.attemptedOwner;\n }\n}\n\n/**\n * Save OAuth tokens. The `owner` parameter specifies which user owns this\n * account — defaults to `accountId` (the account itself is the owner).\n * For multi-account support, pass the logged-in user's email as owner.\n *\n * If the account already exists and is owned by a different user, throws\n * `OAuthAccountOwnedByOtherUserError` (statusCode 409) to prevent silently\n * stealing another user's linked account.\n *\n * Read + write happen as a single linearised batch (Postgres) or paired\n * statements (SQLite). On both backends the per-row PK serialises concurrent\n * writes for the same `(provider, account_id)` so the owner check cannot be\n * raced by an attacker calling saveOAuthTokens twice in flight — the second\n * caller sees the first caller's owner row and raises 409.\n */\nexport async function saveOAuthTokens(\n provider: string,\n accountId: string,\n tokens: Record<string, unknown>,\n owner?: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n\n // Never store \"local@localhost\" as owner — it creates shared-ownership bugs\n const sanitizedOwner = owner === \"local@localhost\" ? accountId : owner;\n\n // Read the current row before deciding what to write. We use this to\n // (a) preserve owner / display_name when this is a token refresh (no\n // owner argument), and (b) reject the write when the caller is trying\n // to overwrite a row owned by someone else.\n let resolvedOwner = sanitizedOwner ?? accountId;\n let existingDisplayName: string | null = null;\n let existingOwner: string | null = null;\n let existingTokens: Record<string, unknown> | null = null;\n const { rows: existing } = await client.execute({\n sql: `SELECT owner, display_name, tokens FROM ${table} WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n if (existing.length > 0) {\n existingOwner = (existing[0].owner as string) ?? null;\n existingDisplayName = (existing[0].display_name as string) ?? null;\n existingTokens = JSON.parse((existing[0].tokens as string) ?? \"{}\");\n }\n\n if (!owner) {\n // Token-refresh path: keep the existing owner/displayName unchanged.\n if (existingOwner) resolvedOwner = existingOwner;\n } else if (existingOwner === \"local@localhost\" && sanitizedOwner) {\n // Legacy dev rows from older OAuth flows were sometimes stored under the\n // synthetic local user. A successful OAuth callback proves control of the\n // Google account, so let the reconnect repair that owner in place.\n resolvedOwner = sanitizedOwner;\n } else if (\n existingOwner &&\n sanitizedOwner &&\n existingOwner !== sanitizedOwner\n ) {\n // Refuse to silently re-bind an account from one user to another.\n // This is the case the docstring promised but the previous\n // implementation didn't enforce — `ON CONFLICT DO UPDATE SET\n // owner=EXCLUDED.owner` would have overwritten the prior owner.\n throw new OAuthAccountOwnedByOtherUserError({\n provider,\n accountId,\n existingOwner,\n attemptedOwner: sanitizedOwner,\n });\n }\n\n const cleanedIncomingTokens = Object.fromEntries(\n Object.entries(tokens).filter(([, value]) => value !== undefined),\n );\n const tokensToStore = {\n ...(existingTokens ?? {}),\n ...cleanedIncomingTokens,\n };\n\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, ${table}.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,\n args: [\n provider,\n accountId,\n resolvedOwner,\n existingDisplayName,\n JSON.stringify(tokensToStore),\n Date.now(),\n ],\n });\n}\n\nexport async function deleteOAuthTokens(\n provider: string,\n accountId?: string,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n if (accountId) {\n const result = await client.execute({\n sql: `DELETE FROM ${table} WHERE provider = ? AND account_id = ?`,\n args: [provider, accountId],\n });\n return result.rowsAffected;\n }\n const result = await client.execute({\n sql: `DELETE FROM ${table} WHERE provider = ?`,\n args: [provider],\n });\n return result.rowsAffected;\n}\n\nexport async function listOAuthAccounts(provider: string): Promise<\n Array<{\n accountId: string;\n owner: string | null;\n tokens: Record<string, unknown>;\n }>\n> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n const { rows } = await client.execute({\n sql: `SELECT account_id, owner, tokens FROM ${table} WHERE provider = ?`,\n args: [provider],\n });\n return rows.map((row) => ({\n accountId: row.account_id as string,\n owner: (row.owner as string) ?? null,\n tokens: JSON.parse(row.tokens as string),\n }));\n}\n\n/**\n * List all OAuth accounts owned by a specific user.\n * In multi-account mode, a user may have connected multiple Google accounts.\n */\nexport async function listOAuthAccountsByOwner(\n provider: string,\n owner: string,\n): Promise<\n Array<{\n accountId: string;\n displayName: string | null;\n tokens: Record<string, unknown>;\n }>\n> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n const { rows } = await client.execute({\n sql: `SELECT account_id, display_name, tokens FROM ${table} WHERE provider = ? AND owner = ?`,\n args: [provider, owner],\n });\n return rows.map((row) => ({\n accountId: row.account_id as string,\n displayName: (row.display_name as string) ?? null,\n tokens: JSON.parse(row.tokens as string),\n }));\n}\n\n/**\n * Set the display name for an OAuth account (e.g. Google profile name).\n */\nexport async function setOAuthDisplayName(\n provider: string,\n accountId: string,\n displayName: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n await client.execute({\n sql: `UPDATE ${table} SET display_name = ? WHERE provider = ? AND account_id = ?`,\n args: [displayName, provider, accountId],\n });\n}\n\n/**\n * Check whether a specific user has tokens for a provider.\n *\n * `owner` is REQUIRED. The previous unscoped form leaked information\n * across users — the onboarding banner would mark the OAuth secret as\n * \"set\" for user B as soon as ANY user in the deployment connected the\n * provider, and user B would never see the prompt to connect.\n *\n * For the local-dev / single-tenant case, callers pass the dev sentinel\n * (`DEV_MODE_USER_EMAIL`) and we degrade to \"any row exists\" — preserving\n * the original behaviour for that single-user surface.\n */\nexport async function hasOAuthTokens(\n provider: string,\n owner: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const table = oauthTokensTable();\n if (owner === \"local@localhost\") {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM ${table} WHERE provider = ? LIMIT 1`,\n args: [provider],\n });\n return rows.length > 0;\n }\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM ${table} WHERE provider = ? AND owner = ? LIMIT 1`,\n args: [provider, owner],\n });\n return rows.length > 0;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAsiBH,wBAA8B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsJlE"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwiBH,wBAA8B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4JlE"}
@@ -18,6 +18,7 @@ import { createClient } from "@libsql/client";
18
18
  import { getDatabaseUrl, getDatabaseAuthToken } from "../../db/client.js";
19
19
  import { parseArgs, fail } from "../utils.js";
20
20
  import { buildScopingPostgres, buildScopingSqlite, } from "./scoping.js";
21
+ import { assertNoSensitiveFrameworkTables } from "./safety.js";
21
22
  function isPostgresUrl(url) {
22
23
  return url.startsWith("postgres://") || url.startsWith("postgresql://");
23
24
  }
@@ -158,6 +159,7 @@ function validateWriteSql(sql, index) {
158
159
  fail(`Statement ${index}: only ${allowed.join(", ")} statements are allowed. ` +
159
160
  `Dangerous operations like DROP, ATTACH, VACUUM, DETACH, CREATE, and ALTER are blocked.`);
160
161
  }
162
+ assertNoSensitiveFrameworkTables(normalized, "write");
161
163
  return normalized;
162
164
  }
163
165
  function convertQuestionMarksToPostgresParams(sql) {
@@ -480,30 +482,37 @@ Options:
480
482
  const scoping = await buildScopingPostgres(pgSql);
481
483
  const results = [];
482
484
  await pgSql.begin(async (tx) => {
483
- // For UPDATE/DELETE: temp views scope to current user's rows. Creating
484
- // them inside the transaction keeps multi-statement batches on one
485
- // connection and avoids cross-call temp-view leakage.
486
- for (const stmt of scoping.setup) {
487
- await tx.unsafe(stmt);
488
- }
489
- for (let i = 0; i < statements.length; i++) {
490
- const statement = statements[i];
491
- const hasReturning = /\bRETURNING\b/i.test(statement.sql);
492
- const finalSql = normalizePostgresSql(injectOwnership(statement.sql, scoping), statement.args);
493
- try {
494
- const result = statement.args.length > 0
495
- ? await tx.unsafe(finalSql, statement.args)
496
- : await tx.unsafe(finalSql);
497
- const rows = hasReturning && result.length > 0 ? Array.from(result) : [];
498
- results.push({
499
- index: i + 1,
500
- sql: finalSql,
501
- changes: result.count ?? 0,
502
- rows,
503
- });
485
+ try {
486
+ // For UPDATE/DELETE: temp views scope to current user's rows.
487
+ // Creating and dropping them inside the same transaction keeps
488
+ // pooled Postgres backends from retaining session-local views.
489
+ for (const stmt of scoping.setup) {
490
+ await tx.unsafe(stmt);
504
491
  }
505
- catch (err) {
506
- throw new Error(`Statement ${i + 1} failed: ${err?.message ?? String(err)}`);
492
+ for (let i = 0; i < statements.length; i++) {
493
+ const statement = statements[i];
494
+ const hasReturning = /\bRETURNING\b/i.test(statement.sql);
495
+ const finalSql = normalizePostgresSql(injectOwnership(statement.sql, scoping), statement.args);
496
+ try {
497
+ const result = statement.args.length > 0
498
+ ? await tx.unsafe(finalSql, statement.args)
499
+ : await tx.unsafe(finalSql);
500
+ const rows = hasReturning && result.length > 0 ? Array.from(result) : [];
501
+ results.push({
502
+ index: i + 1,
503
+ sql: finalSql,
504
+ changes: result.count ?? 0,
505
+ rows,
506
+ });
507
+ }
508
+ catch (err) {
509
+ throw new Error(`Statement ${i + 1} failed: ${err?.message ?? String(err)}`);
510
+ }
511
+ }
512
+ }
513
+ finally {
514
+ for (const stmt of scoping.teardown) {
515
+ await tx.unsafe(stmt).catch(() => { });
507
516
  }
508
517
  }
509
518
  });
@@ -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 { createClient } from \"@libsql/client\";\nimport { getDatabaseUrl, getDatabaseAuthToken } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport {\n buildScopingPostgres,\n buildScopingSqlite,\n type ScopingContext,\n} from \"./scoping.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 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 if (scoping.userEmail && scoping.ownerEmailTables.has(tableName)) {\n clauses.push(`owner_email = '${escapeSqlString(scoping.userEmail)}'`);\n }\n if (scoping.orgId && scoping.orgIdTables.has(tableName)) {\n clauses.push(`org_id = '${escapeSqlString(scoping.orgId)}'`);\n }\n return clauses.length > 0 ? clauses.join(\" AND \") : 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.orgIdTables.has(tableName)\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 }\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 console.log(`[${result.index}] Changes: ${result.changes ?? 0}`);\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 // For UPDATE/DELETE: temp views scope to current user's rows. Creating\n // them inside the transaction keeps multi-statement batches on one\n // connection and avoids cross-call temp-view leakage.\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 });\n\n printBatchResult(results, parsed.format);\n } finally {\n await pgSql.end();\n }\n return;\n }\n\n // libsql / SQLite path\n const client = createClient({\n url,\n authToken: getDatabaseAuthToken(),\n });\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 { createClient } from \"@libsql/client\";\nimport { getDatabaseUrl, getDatabaseAuthToken } from \"../../db/client.js\";\nimport { parseArgs, fail } from \"../utils.js\";\nimport {\n buildScopingPostgres,\n buildScopingSqlite,\n type ScopingContext,\n} from \"./scoping.js\";\nimport { assertNoSensitiveFrameworkTables } from \"./safety.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 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 if (scoping.userEmail && scoping.ownerEmailTables.has(tableName)) {\n clauses.push(`owner_email = '${escapeSqlString(scoping.userEmail)}'`);\n }\n if (scoping.orgId && scoping.orgIdTables.has(tableName)) {\n clauses.push(`org_id = '${escapeSqlString(scoping.orgId)}'`);\n }\n return clauses.length > 0 ? clauses.join(\" AND \") : 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.orgIdTables.has(tableName)\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 }\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 console.log(`[${result.index}] Changes: ${result.changes ?? 0}`);\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 = createClient({\n url,\n authToken: getDatabaseAuthToken(),\n });\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":"patch.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAinBH,wBAA8B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2GnE"}
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../../src/scripts/db/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAsoBH,wBAA8B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GnE"}