@agent-native/core 0.7.83 → 0.8.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 (225) hide show
  1. package/dist/action.js +1 -1
  2. package/dist/action.js.map +1 -1
  3. package/dist/agent/production-agent.d.ts.map +1 -1
  4. package/dist/agent/production-agent.js +8 -8
  5. package/dist/agent/production-agent.js.map +1 -1
  6. package/dist/agent/run-manager.d.ts +2 -0
  7. package/dist/agent/run-manager.d.ts.map +1 -1
  8. package/dist/agent/run-manager.js +44 -18
  9. package/dist/agent/run-manager.js.map +1 -1
  10. package/dist/agent/types.d.ts +1 -1
  11. package/dist/agent/types.d.ts.map +1 -1
  12. package/dist/agent/types.js.map +1 -1
  13. package/dist/cli/create.d.ts.map +1 -1
  14. package/dist/cli/create.js +54 -11
  15. package/dist/cli/create.js.map +1 -1
  16. package/dist/cli/workspacify.d.ts.map +1 -1
  17. package/dist/cli/workspacify.js +12 -9
  18. package/dist/cli/workspacify.js.map +1 -1
  19. package/dist/client/AgentPanel.d.ts +1 -1
  20. package/dist/client/AgentPanel.d.ts.map +1 -1
  21. package/dist/client/AgentPanel.js +22 -1
  22. package/dist/client/AgentPanel.js.map +1 -1
  23. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  24. package/dist/client/agent-chat-adapter.js +254 -29
  25. package/dist/client/agent-chat-adapter.js.map +1 -1
  26. package/dist/client/agent-chat.d.ts +2 -0
  27. package/dist/client/agent-chat.d.ts.map +1 -1
  28. package/dist/client/agent-chat.js +11 -2
  29. package/dist/client/agent-chat.js.map +1 -1
  30. package/dist/client/composer/ComposerPlusMenu.js +1 -1
  31. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  32. package/dist/client/composer/PromptComposer.js +1 -1
  33. package/dist/client/composer/PromptComposer.js.map +1 -1
  34. package/dist/client/composer/TiptapComposer.js +8 -8
  35. package/dist/client/composer/TiptapComposer.js.map +1 -1
  36. package/dist/client/composer/types.d.ts +1 -1
  37. package/dist/client/composer/types.d.ts.map +1 -1
  38. package/dist/client/composer/types.js.map +1 -1
  39. package/dist/client/extensions/EmbeddedExtension.d.ts +20 -0
  40. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -0
  41. package/dist/client/{tools/EmbeddedTool.js → extensions/EmbeddedExtension.js} +41 -41
  42. package/dist/client/extensions/EmbeddedExtension.js.map +1 -0
  43. package/dist/client/extensions/ExtensionEditor.d.ts +5 -0
  44. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -0
  45. package/dist/client/extensions/ExtensionEditor.js +129 -0
  46. package/dist/client/extensions/ExtensionEditor.js.map +1 -0
  47. package/dist/client/{tools → extensions}/ExtensionSlot.d.ts +3 -3
  48. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -0
  49. package/dist/client/{tools → extensions}/ExtensionSlot.js +14 -14
  50. package/dist/client/extensions/ExtensionSlot.js.map +1 -0
  51. package/dist/client/extensions/ExtensionViewer.d.ts +5 -0
  52. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -0
  53. package/dist/client/{tools/ToolViewer.js → extensions/ExtensionViewer.js} +67 -65
  54. package/dist/client/extensions/ExtensionViewer.js.map +1 -0
  55. package/dist/client/extensions/ExtensionViewerPage.d.ts +2 -0
  56. package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -0
  57. package/dist/client/{tools/ToolViewerPage.js → extensions/ExtensionViewerPage.js} +8 -8
  58. package/dist/client/extensions/ExtensionViewerPage.js.map +1 -0
  59. package/dist/client/extensions/ExtensionsListPage.d.ts +2 -0
  60. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -0
  61. package/dist/client/extensions/ExtensionsListPage.js +67 -0
  62. package/dist/client/extensions/ExtensionsListPage.js.map +1 -0
  63. package/dist/client/extensions/ExtensionsSidebarSection.d.ts +2 -0
  64. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -0
  65. package/dist/client/{tools/ToolsSidebarSection.js → extensions/ExtensionsSidebarSection.js} +58 -58
  66. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -0
  67. package/dist/client/{tools/tool-order.d.ts → extensions/extension-order.d.ts} +2 -2
  68. package/dist/client/extensions/extension-order.d.ts.map +1 -0
  69. package/dist/client/{tools/tool-order.js → extensions/extension-order.js} +3 -3
  70. package/dist/client/extensions/extension-order.js.map +1 -0
  71. package/dist/client/{tools → extensions}/iframe-bridge.d.ts +11 -11
  72. package/dist/client/extensions/iframe-bridge.d.ts.map +1 -0
  73. package/dist/client/{tools → extensions}/iframe-bridge.js +24 -24
  74. package/dist/client/extensions/iframe-bridge.js.map +1 -0
  75. package/dist/client/extensions/index.d.ts +14 -0
  76. package/dist/client/extensions/index.d.ts.map +1 -0
  77. package/dist/client/extensions/index.js +19 -0
  78. package/dist/client/extensions/index.js.map +1 -0
  79. package/dist/client/sse-event-processor.d.ts +2 -1
  80. package/dist/client/sse-event-processor.d.ts.map +1 -1
  81. package/dist/client/sse-event-processor.js +87 -6
  82. package/dist/client/sse-event-processor.js.map +1 -1
  83. package/dist/extensions/actions.d.ts +3 -0
  84. package/dist/extensions/actions.d.ts.map +1 -0
  85. package/dist/{tools → extensions}/actions.js +54 -51
  86. package/dist/extensions/actions.js.map +1 -0
  87. package/dist/{tools → extensions}/fetch-tool.d.ts +4 -0
  88. package/dist/extensions/fetch-tool.d.ts.map +1 -0
  89. package/dist/{tools → extensions}/fetch-tool.js +12 -7
  90. package/dist/extensions/fetch-tool.js.map +1 -0
  91. package/dist/extensions/html-shell.d.ts +56 -0
  92. package/dist/extensions/html-shell.d.ts.map +1 -0
  93. package/dist/{tools → extensions}/html-shell.js +101 -83
  94. package/dist/extensions/html-shell.js.map +1 -0
  95. package/dist/{tools → extensions}/proxy-security.d.ts +2 -2
  96. package/dist/extensions/proxy-security.d.ts.map +1 -0
  97. package/dist/{tools → extensions}/proxy-security.js +3 -3
  98. package/dist/extensions/proxy-security.js.map +1 -0
  99. package/dist/extensions/routes.d.ts +2 -0
  100. package/dist/extensions/routes.d.ts.map +1 -0
  101. package/dist/{tools → extensions}/routes.js +73 -69
  102. package/dist/extensions/routes.js.map +1 -0
  103. package/dist/{tools → extensions}/schema.d.ts +44 -38
  104. package/dist/extensions/schema.d.ts.map +1 -0
  105. package/dist/{tools → extensions}/schema.js +41 -34
  106. package/dist/extensions/schema.js.map +1 -0
  107. package/dist/extensions/slots/routes.d.ts +15 -0
  108. package/dist/extensions/slots/routes.d.ts.map +1 -0
  109. package/dist/{tools → extensions}/slots/routes.js +26 -26
  110. package/dist/extensions/slots/routes.js.map +1 -0
  111. package/dist/{tools → extensions}/slots/schema.d.ts +24 -21
  112. package/dist/extensions/slots/schema.d.ts.map +1 -0
  113. package/dist/extensions/slots/schema.js +79 -0
  114. package/dist/extensions/slots/schema.js.map +1 -0
  115. package/dist/extensions/slots/store.d.ts +66 -0
  116. package/dist/extensions/slots/store.d.ts.map +1 -0
  117. package/dist/extensions/slots/store.js +238 -0
  118. package/dist/extensions/slots/store.js.map +1 -0
  119. package/dist/extensions/store.d.ts +40 -0
  120. package/dist/extensions/store.d.ts.map +1 -0
  121. package/dist/{tools → extensions}/store.js +59 -54
  122. package/dist/extensions/store.js.map +1 -0
  123. package/dist/extensions/theme.d.ts.map +1 -0
  124. package/dist/extensions/theme.js.map +1 -0
  125. package/dist/{tools → extensions}/url-safety.d.ts +5 -3
  126. package/dist/extensions/url-safety.d.ts.map +1 -0
  127. package/dist/{tools → extensions}/url-safety.js +11 -4
  128. package/dist/extensions/url-safety.js.map +1 -0
  129. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  130. package/dist/server/agent-chat-plugin.js +12 -10
  131. package/dist/server/agent-chat-plugin.js.map +1 -1
  132. package/dist/server/core-routes-plugin.d.ts +15 -0
  133. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  134. package/dist/server/core-routes-plugin.js +64 -10
  135. package/dist/server/core-routes-plugin.js.map +1 -1
  136. package/dist/server/csrf.d.ts +3 -2
  137. package/dist/server/csrf.d.ts.map +1 -1
  138. package/dist/server/csrf.js +3 -2
  139. package/dist/server/csrf.js.map +1 -1
  140. package/dist/shared/workspace-app-id.d.ts +1 -1
  141. package/dist/shared/workspace-app-id.d.ts.map +1 -1
  142. package/dist/shared/workspace-app-id.js +5 -1
  143. package/dist/shared/workspace-app-id.js.map +1 -1
  144. package/dist/templates/workspace-root/README.md +5 -4
  145. package/dist/usage/store.d.ts +1 -1
  146. package/dist/usage/store.d.ts.map +1 -1
  147. package/dist/usage/store.js +1 -1
  148. package/dist/usage/store.js.map +1 -1
  149. package/dist/vite/client.d.ts.map +1 -1
  150. package/dist/vite/client.js +10 -1
  151. package/dist/vite/client.js.map +1 -1
  152. package/docs/content/actions.md +10 -10
  153. package/docs/content/extensions.md +230 -0
  154. package/docs/content/key-concepts.md +2 -2
  155. package/docs/content/server.md +13 -13
  156. package/docs/content/sharing.md +2 -2
  157. package/docs/content/template-dispatch.md +5 -0
  158. package/docs/content/what-is-agent-native.md +1 -1
  159. package/package.json +22 -17
  160. package/src/templates/workspace-root/README.md +5 -4
  161. package/dist/client/tools/EmbeddedTool.d.ts +0 -20
  162. package/dist/client/tools/EmbeddedTool.d.ts.map +0 -1
  163. package/dist/client/tools/EmbeddedTool.js.map +0 -1
  164. package/dist/client/tools/ExtensionSlot.d.ts.map +0 -1
  165. package/dist/client/tools/ExtensionSlot.js.map +0 -1
  166. package/dist/client/tools/ToolEditor.d.ts +0 -5
  167. package/dist/client/tools/ToolEditor.d.ts.map +0 -1
  168. package/dist/client/tools/ToolEditor.js +0 -129
  169. package/dist/client/tools/ToolEditor.js.map +0 -1
  170. package/dist/client/tools/ToolViewer.d.ts +0 -5
  171. package/dist/client/tools/ToolViewer.d.ts.map +0 -1
  172. package/dist/client/tools/ToolViewer.js.map +0 -1
  173. package/dist/client/tools/ToolViewerPage.d.ts +0 -2
  174. package/dist/client/tools/ToolViewerPage.d.ts.map +0 -1
  175. package/dist/client/tools/ToolViewerPage.js.map +0 -1
  176. package/dist/client/tools/ToolsListPage.d.ts +0 -2
  177. package/dist/client/tools/ToolsListPage.d.ts.map +0 -1
  178. package/dist/client/tools/ToolsListPage.js +0 -67
  179. package/dist/client/tools/ToolsListPage.js.map +0 -1
  180. package/dist/client/tools/ToolsSidebarSection.d.ts +0 -2
  181. package/dist/client/tools/ToolsSidebarSection.d.ts.map +0 -1
  182. package/dist/client/tools/ToolsSidebarSection.js.map +0 -1
  183. package/dist/client/tools/iframe-bridge.d.ts.map +0 -1
  184. package/dist/client/tools/iframe-bridge.js.map +0 -1
  185. package/dist/client/tools/index.d.ts +0 -8
  186. package/dist/client/tools/index.d.ts.map +0 -1
  187. package/dist/client/tools/index.js +0 -8
  188. package/dist/client/tools/index.js.map +0 -1
  189. package/dist/client/tools/tool-order.d.ts.map +0 -1
  190. package/dist/client/tools/tool-order.js.map +0 -1
  191. package/dist/tools/actions.d.ts +0 -3
  192. package/dist/tools/actions.d.ts.map +0 -1
  193. package/dist/tools/actions.js.map +0 -1
  194. package/dist/tools/fetch-tool.d.ts.map +0 -1
  195. package/dist/tools/fetch-tool.js.map +0 -1
  196. package/dist/tools/html-shell.d.ts +0 -45
  197. package/dist/tools/html-shell.d.ts.map +0 -1
  198. package/dist/tools/html-shell.js.map +0 -1
  199. package/dist/tools/proxy-security.d.ts.map +0 -1
  200. package/dist/tools/proxy-security.js.map +0 -1
  201. package/dist/tools/routes.d.ts +0 -2
  202. package/dist/tools/routes.d.ts.map +0 -1
  203. package/dist/tools/routes.js.map +0 -1
  204. package/dist/tools/schema.d.ts.map +0 -1
  205. package/dist/tools/schema.js.map +0 -1
  206. package/dist/tools/slots/routes.d.ts +0 -15
  207. package/dist/tools/slots/routes.d.ts.map +0 -1
  208. package/dist/tools/slots/routes.js.map +0 -1
  209. package/dist/tools/slots/schema.d.ts.map +0 -1
  210. package/dist/tools/slots/schema.js +0 -76
  211. package/dist/tools/slots/schema.js.map +0 -1
  212. package/dist/tools/slots/store.d.ts +0 -66
  213. package/dist/tools/slots/store.d.ts.map +0 -1
  214. package/dist/tools/slots/store.js +0 -227
  215. package/dist/tools/slots/store.js.map +0 -1
  216. package/dist/tools/store.d.ts +0 -40
  217. package/dist/tools/store.d.ts.map +0 -1
  218. package/dist/tools/store.js.map +0 -1
  219. package/dist/tools/theme.d.ts.map +0 -1
  220. package/dist/tools/theme.js.map +0 -1
  221. package/dist/tools/url-safety.d.ts.map +0 -1
  222. package/dist/tools/url-safety.js.map +0 -1
  223. package/docs/content/tools.md +0 -205
  224. /package/dist/{tools → extensions}/theme.d.ts +0 -0
  225. /package/dist/{tools → extensions}/theme.js +0 -0
@@ -6,14 +6,14 @@ import { recordChange } from "../server/poll.js";
6
6
  import { runWithRequestContext, getRequestOrgId, } from "../server/request-context.js";
7
7
  import { getOrgContext } from "../org/context.js";
8
8
  import { getDbExec, isPostgres } from "../db/client.js";
9
- import { listTools, getTool, createTool, updateTool, updateToolContent, deleteTool, ensureToolsTables, } from "./store.js";
10
- import { buildToolHtml, TOOL_IFRAME_CSP } from "./html-shell.js";
9
+ import { listExtensions, getExtension, createExtension, updateExtension, updateExtensionContent, deleteExtension, ensureExtensionsTables, } from "./store.js";
10
+ import { buildExtensionHtml, EXTENSION_IFRAME_CSP } from "./html-shell.js";
11
11
  import { getThemeVars } from "./theme.js";
12
12
  import { resolveKeyReferences, validateUrlAllowlist, getKeyAllowlist, } from "../secrets/substitution.js";
13
- import { collectSecretValues, normalizeToolProxyMethod, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
14
- import { createSsrfSafeDispatcher, isBlockedToolUrlWithDns, } from "./url-safety.js";
13
+ import { collectSecretValues, normalizeExtensionProxyMethod, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
14
+ import { createSsrfSafeDispatcher, isBlockedExtensionUrlWithDns, } from "./url-safety.js";
15
15
  import { ForbiddenError, resolveAccess } from "../sharing/access.js";
16
- export function createToolsHandler() {
16
+ export function createExtensionsHandler() {
17
17
  return defineEventHandler(async (event) => {
18
18
  const method = getMethod(event);
19
19
  const pathname = (event.url?.pathname || "")
@@ -41,31 +41,31 @@ export function createToolsHandler() {
41
41
  });
42
42
  }
43
43
  async function dispatch(event, method, parts, userEmail) {
44
- // POST /sql/query — read-only SQL for tool iframes
44
+ // POST /sql/query — read-only SQL for extension iframes
45
45
  if (method === "POST" &&
46
46
  parts.length === 2 &&
47
47
  parts[0] === "sql" &&
48
48
  parts[1] === "query") {
49
49
  return handleSqlQuery(event);
50
50
  }
51
- // POST /sql/exec — write SQL for tool iframes
51
+ // POST /sql/exec — write SQL for extension iframes
52
52
  if (method === "POST" &&
53
53
  parts.length === 2 &&
54
54
  parts[0] === "sql" &&
55
55
  parts[1] === "exec") {
56
56
  return handleSqlExec(event);
57
57
  }
58
- // GET /data/:toolId/:collection — list items in a collection
58
+ // GET /data/:extensionId/:collection — list items in a collection
59
59
  if (method === "GET" && parts.length === 3 && parts[0] === "data") {
60
- return handleToolDataList(event, parts[1], parts[2], userEmail);
60
+ return handleExtensionDataList(event, parts[1], parts[2], userEmail);
61
61
  }
62
- // POST /data/:toolId/:collection — create/upsert an item
62
+ // POST /data/:extensionId/:collection — create/upsert an item
63
63
  if (method === "POST" && parts.length === 3 && parts[0] === "data") {
64
- return handleToolDataUpsert(event, parts[1], parts[2], userEmail);
64
+ return handleExtensionDataUpsert(event, parts[1], parts[2], userEmail);
65
65
  }
66
- // DELETE /data/:toolId/:collection/:itemId — delete an item
66
+ // DELETE /data/:extensionId/:collection/:itemId — delete an item
67
67
  if (method === "DELETE" && parts.length === 4 && parts[0] === "data") {
68
- return handleToolDataDelete(event, parts[1], parts[2], parts[3], userEmail);
68
+ return handleExtensionDataDelete(event, parts[1], parts[2], parts[3], userEmail);
69
69
  }
70
70
  // POST /proxy
71
71
  if (method === "POST" && parts.length === 1 && parts[0] === "proxy") {
@@ -73,7 +73,7 @@ async function dispatch(event, method, parts, userEmail) {
73
73
  }
74
74
  // GET / — list
75
75
  if (method === "GET" && parts.length === 0) {
76
- return listTools();
76
+ return listExtensions();
77
77
  }
78
78
  // POST / — create
79
79
  if (method === "POST" && parts.length === 0) {
@@ -82,18 +82,18 @@ async function dispatch(event, method, parts, userEmail) {
82
82
  setResponseStatus(event, 400);
83
83
  return { error: "name is required" };
84
84
  }
85
- const tool = await createTool(body);
85
+ const extension = await createExtension(body);
86
86
  recordChange({ source: "action", type: "change" });
87
87
  setResponseStatus(event, 201);
88
- return tool;
88
+ return extension;
89
89
  }
90
90
  // GET /:id/render
91
91
  if (method === "GET" && parts.length === 2 && parts[1] === "render") {
92
- const access = await resolveAccess("tool", parts[0]);
93
- const tool = access?.resource;
94
- if (!tool) {
92
+ const access = await resolveAccess("extension", parts[0]);
93
+ const extension = access?.resource;
94
+ if (!extension) {
95
95
  setResponseStatus(event, 404);
96
- return { error: "Tool not found" };
96
+ return { error: "Extension not found" };
97
97
  }
98
98
  const search = event.url?.search || "";
99
99
  const isDark = search.includes("dark=1") || search.includes("dark=true");
@@ -101,9 +101,9 @@ async function dispatch(event, method, parts, userEmail) {
101
101
  // Compute viewer-vs-author binding so the iframe can warn when the
102
102
  // viewer is NOT the author. The role is plumbed through to gate
103
103
  // dangerous bridge helpers in iframe-bridge.ts (audit H4).
104
- const isAuthor = tool.ownerEmail === userEmail;
105
- const html = buildToolHtml(tool.content, themeVars, isDark, parts[0], {
106
- authorEmail: tool.ownerEmail,
104
+ const isAuthor = extension.ownerEmail === userEmail;
105
+ const html = buildExtensionHtml(extension.content, themeVars, isDark, parts[0], {
106
+ authorEmail: extension.ownerEmail,
107
107
  viewerEmail: userEmail,
108
108
  isAuthor,
109
109
  role: access.role,
@@ -115,7 +115,7 @@ async function dispatch(event, method, parts, userEmail) {
115
115
  // - frame-ancestors in the CSP must be set as an HTTP header to be
116
116
  // enforced; meta-CSP can't set it per spec.
117
117
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
118
- setResponseHeader(event, "Content-Security-Policy", TOOL_IFRAME_CSP);
118
+ setResponseHeader(event, "Content-Security-Policy", EXTENSION_IFRAME_CSP);
119
119
  setResponseHeader(event, "X-Frame-Options", "SAMEORIGIN");
120
120
  setResponseHeader(event, "X-Content-Type-Options", "nosniff");
121
121
  setResponseHeader(event, "Referrer-Policy", "no-referrer");
@@ -123,12 +123,12 @@ async function dispatch(event, method, parts, userEmail) {
123
123
  }
124
124
  // GET /:id
125
125
  if (method === "GET" && parts.length === 1) {
126
- const tool = await getTool(parts[0]);
127
- if (!tool) {
126
+ const extension = await getExtension(parts[0]);
127
+ if (!extension) {
128
128
  setResponseStatus(event, 404);
129
- return { error: "Tool not found" };
129
+ return { error: "Extension not found" };
130
130
  }
131
- return tool;
131
+ return extension;
132
132
  }
133
133
  // PUT /:id
134
134
  if (method === "PUT" && parts.length === 1) {
@@ -140,30 +140,30 @@ async function dispatch(event, method, parts, userEmail) {
140
140
  body.visibility !== undefined;
141
141
  let result = null;
142
142
  if (hasContentUpdate) {
143
- result = await updateToolContent(parts[0], {
143
+ result = await updateExtensionContent(parts[0], {
144
144
  content: body.content,
145
145
  patches: body.patches,
146
146
  });
147
147
  }
148
148
  if (hasMetaUpdate) {
149
- result = await updateTool(parts[0], body);
149
+ result = await updateExtension(parts[0], body);
150
150
  }
151
151
  if (!hasContentUpdate && !hasMetaUpdate) {
152
- result = await getTool(parts[0]);
152
+ result = await getExtension(parts[0]);
153
153
  }
154
154
  if (!result) {
155
155
  setResponseStatus(event, 404);
156
- return { error: "Tool not found" };
156
+ return { error: "Extension not found" };
157
157
  }
158
158
  recordChange({ source: "action", type: "change" });
159
159
  return result;
160
160
  }
161
161
  // DELETE /:id
162
162
  if (method === "DELETE" && parts.length === 1) {
163
- const ok = await deleteTool(parts[0]);
163
+ const ok = await deleteExtension(parts[0]);
164
164
  if (!ok) {
165
165
  setResponseStatus(event, 404);
166
- return { error: "Tool not found" };
166
+ return { error: "Extension not found" };
167
167
  }
168
168
  recordChange({ source: "action", type: "change" });
169
169
  return { ok: true };
@@ -171,12 +171,12 @@ async function dispatch(event, method, parts, userEmail) {
171
171
  setResponseStatus(event, 404);
172
172
  return { error: "Not found" };
173
173
  }
174
- async function handleToolDataList(event, toolId, collection, userEmail) {
175
- await ensureToolsTables();
176
- const tool = await getTool(toolId);
177
- if (!tool) {
174
+ async function handleExtensionDataList(event, extensionId, collection, userEmail) {
175
+ await ensureExtensionsTables();
176
+ const extension = await getExtension(extensionId);
177
+ if (!extension) {
178
178
  setResponseStatus(event, 404);
179
- return { error: "Tool not found" };
179
+ return { error: "Extension not found" };
180
180
  }
181
181
  const client = getDbExec();
182
182
  const url = event.url;
@@ -197,7 +197,7 @@ async function handleToolDataList(event, toolId, collection, userEmail) {
197
197
  WHERE tool_id = ? AND collection = ? AND scope = 'org' AND org_id = ?
198
198
  ORDER BY created_at DESC
199
199
  LIMIT ?`,
200
- args: [toolId, collection, orgId, limit],
200
+ args: [extensionId, collection, orgId, limit],
201
201
  });
202
202
  return result.rows ?? [];
203
203
  }
@@ -209,7 +209,7 @@ async function handleToolDataList(event, toolId, collection, userEmail) {
209
209
  AND ((scope = 'user' AND owner_email = ?) OR (scope = 'org' AND org_id = ?))
210
210
  ORDER BY created_at DESC
211
211
  LIMIT ?`,
212
- args: [toolId, collection, userEmail, orgId ?? "", limit],
212
+ args: [extensionId, collection, userEmail, orgId ?? "", limit],
213
213
  });
214
214
  return result.rows ?? [];
215
215
  }
@@ -219,16 +219,16 @@ async function handleToolDataList(event, toolId, collection, userEmail) {
219
219
  WHERE tool_id = ? AND collection = ? AND scope = 'user' AND owner_email = ?
220
220
  ORDER BY updated_at DESC
221
221
  LIMIT ?`,
222
- args: [toolId, collection, userEmail, limit],
222
+ args: [extensionId, collection, userEmail, limit],
223
223
  });
224
224
  return result.rows ?? [];
225
225
  }
226
- async function handleToolDataUpsert(event, toolId, collection, userEmail) {
227
- await ensureToolsTables();
228
- const tool = await getTool(toolId);
229
- if (!tool) {
226
+ async function handleExtensionDataUpsert(event, extensionId, collection, userEmail) {
227
+ await ensureExtensionsTables();
228
+ const extension = await getExtension(extensionId);
229
+ if (!extension) {
230
230
  setResponseStatus(event, 404);
231
- return { error: "Tool not found" };
231
+ return { error: "Extension not found" };
232
232
  }
233
233
  const body = await readBody(event);
234
234
  if (body.data === undefined) {
@@ -258,7 +258,7 @@ async function handleToolDataUpsert(event, toolId, collection, userEmail) {
258
258
  ${conflictClause}`,
259
259
  args: [
260
260
  randomUUID(),
261
- toolId,
261
+ extensionId,
262
262
  collection,
263
263
  itemId,
264
264
  data,
@@ -272,7 +272,7 @@ async function handleToolDataUpsert(event, toolId, collection, userEmail) {
272
272
  });
273
273
  return {
274
274
  id: itemId,
275
- toolId,
275
+ extensionId,
276
276
  collection,
277
277
  data,
278
278
  ownerEmail: userEmail,
@@ -282,12 +282,12 @@ async function handleToolDataUpsert(event, toolId, collection, userEmail) {
282
282
  updatedAt: now,
283
283
  };
284
284
  }
285
- async function handleToolDataDelete(event, toolId, collection, itemId, userEmail) {
286
- await ensureToolsTables();
287
- const tool = await getTool(toolId);
288
- if (!tool) {
285
+ async function handleExtensionDataDelete(event, extensionId, collection, itemId, userEmail) {
286
+ await ensureExtensionsTables();
287
+ const extension = await getExtension(extensionId);
288
+ if (!extension) {
289
289
  setResponseStatus(event, 404);
290
- return { error: "Tool not found" };
290
+ return { error: "Extension not found" };
291
291
  }
292
292
  const url = event.url;
293
293
  const scope = url?.searchParams?.get("scope") || "user";
@@ -300,13 +300,13 @@ async function handleToolDataDelete(event, toolId, collection, itemId, userEmail
300
300
  }
301
301
  await client.execute({
302
302
  sql: `DELETE FROM tool_data WHERE COALESCE(item_id, id) = ? AND tool_id = ? AND collection = ? AND scope = 'org' AND org_id = ?`,
303
- args: [itemId, toolId, collection, orgId],
303
+ args: [itemId, extensionId, collection, orgId],
304
304
  });
305
305
  return { ok: true };
306
306
  }
307
307
  await client.execute({
308
308
  sql: `DELETE FROM tool_data WHERE COALESCE(item_id, id) = ? AND tool_id = ? AND collection = ? AND scope = 'user' AND owner_email = ?`,
309
- args: [itemId, toolId, collection, userEmail],
309
+ args: [itemId, extensionId, collection, userEmail],
310
310
  });
311
311
  return { ok: true };
312
312
  }
@@ -317,7 +317,7 @@ async function handleProxy(event, userEmail) {
317
317
  setResponseStatus(event, 400);
318
318
  return { error: "url is required" };
319
319
  }
320
- const method = normalizeToolProxyMethod(body.method || "GET");
320
+ const method = normalizeExtensionProxyMethod(body.method || "GET");
321
321
  if (!method) {
322
322
  setResponseStatus(event, 405);
323
323
  return {
@@ -352,7 +352,7 @@ async function handleProxy(event, userEmail) {
352
352
  return { error: `Key resolution failed: ${err?.message ?? err}` };
353
353
  }
354
354
  const secretValues = collectSecretValues(allSecretValues);
355
- if (await isBlockedToolUrlWithDns(resolvedUrl)) {
355
+ if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {
356
356
  setResponseStatus(event, 403);
357
357
  return { error: "Requests to private/internal addresses are not allowed" };
358
358
  }
@@ -377,7 +377,7 @@ async function handleProxy(event, userEmail) {
377
377
  // Best-effort connect-time SSRF guard. When undici is available (it ships
378
378
  // with Node 18+ but is not always exposed as an importable module), the
379
379
  // dispatcher re-checks the resolved IP at TCP-connect time, closing the
380
- // TOCTOU between the pre-flight `isBlockedToolUrlWithDns` lookup and the
380
+ // TOCTOU between the pre-flight `isBlockedExtensionUrlWithDns` lookup and the
381
381
  // actual fetch lookup. If undici is not importable, fall through to plain
382
382
  // fetch — the pre-flight remains the primary protection.
383
383
  const dispatcher = (await createSsrfSafeDispatcher()) ?? undefined;
@@ -413,7 +413,7 @@ async function handleProxy(event, userEmail) {
413
413
  if (response.status >= 300 && response.status < 400) {
414
414
  const location = response.headers.get("location");
415
415
  const redirectUrl = location ? new URL(location, resolvedUrl).href : null;
416
- if (redirectUrl && (await isBlockedToolUrlWithDns(redirectUrl))) {
416
+ if (redirectUrl && (await isBlockedExtensionUrlWithDns(redirectUrl))) {
417
417
  setResponseStatus(event, 403);
418
418
  return { error: "Redirect to private/internal address blocked" };
419
419
  }
@@ -517,11 +517,13 @@ async function handleSqlQuery(event) {
517
517
  const cleanSql = stripSqlComments(sql);
518
518
  if (!/^\s*(SELECT|WITH)\b/i.test(cleanSql)) {
519
519
  setResponseStatus(event, 403);
520
- return { error: "Only SELECT queries are allowed from tools" };
520
+ return { error: "Only SELECT queries are allowed from extensions" };
521
521
  }
522
522
  if (SENSITIVE_SQL_RE.test(cleanSql)) {
523
523
  setResponseStatus(event, 403);
524
- return { error: "Sensitive framework tables are not readable from tools" };
524
+ return {
525
+ error: "Sensitive framework tables are not readable from extensions",
526
+ };
525
527
  }
526
528
  try {
527
529
  const mod = await import("../scripts/db/query.js");
@@ -549,19 +551,19 @@ async function handleSqlQuery(event) {
549
551
  }
550
552
  }
551
553
  // TODO(security): replace this regex blocklist with a SQL parser + an explicit
552
- // allowlist of tables a tool may read/write (e.g. only `tool_data`, plus a
554
+ // allowlist of tables a extension may read/write (e.g. only `tool_data`, plus a
553
555
  // per-template list). The current blocklist is best-effort defense in depth
554
556
  // and is by design bypassable via SQL constructions that don't include the
555
557
  // blocklisted token literally (string concat, dynamic SQL, etc). The temp-
556
558
  // view scoping in scripts/db/scoping.ts is the actual ownership boundary.
557
559
  const DESTRUCTIVE_SQL_RE = /\b(CREATE\s+(?:(?:LOCAL|GLOBAL)\s+)?(?:TEMPORARY|TEMP)?\s*(TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|EXTENSION|ROLE|TABLESPACE|PUBLICATION|SUBSCRIPTION)|DROP\s+(TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|EXTENSION|ROLE)|TRUNCATE|DELETE\s+FROM\s+(?!tool_data\b)|ALTER\s+(TABLE|VIEW|SCHEMA|DATABASE|FUNCTION|ROLE|EXTENSION|PUBLICATION)\s+(?!tool_data\b)|ATTACH|DETACH|VACUUM|REINDEX|PRAGMA|GRANT|REVOKE|SET\s+ROLE|RESET\s+ROLE|COPY)\b/i;
558
- // Sensitive tables that tools must not touch directly. Includes Better Auth
560
+ // Sensitive tables that extensions must not touch directly. Includes Better Auth
559
561
  // identity tables, framework infrastructure (tracing, evals, automations,
560
562
  // integrations, notifications, scheduling, sharing/orgs), and Postgres
561
- // catalogs that would let a tool enumerate or read internals.
562
- const SENSITIVE_SQL_RE = /\b(app_secrets|user|users|session|sessions|account|accounts|verification|oauth_tokens|tools|tool_shares|tool_slots|tool_slot_installs|member|organization|invitation|jwks|agent_trace_spans|agent_trace_summaries|agent_feedback|agent_satisfaction_scores|agent_evals|agent_runs|agent_run_events|notifications|progress_runs|integration_configs|integration_pending_tasks|integration_thread_mappings|resources|org_members|org_invitations|bigquery_cache|dashboard_views|pg_catalog|information_schema|pg_class|pg_proc|pg_namespace|pg_user|pg_roles|pg_authid|pg_shadow)\b/i;
563
+ // catalogs that would let a extension enumerate or read internals.
564
+ const SENSITIVE_SQL_RE = /\b(app_secrets|user|users|session|sessions|account|accounts|verification|oauth_tokens|extensions|tool_shares|tool_slots|tool_slot_installs|member|organization|invitation|jwks|agent_trace_spans|agent_trace_summaries|agent_feedback|agent_satisfaction_scores|agent_evals|agent_runs|agent_run_events|notifications|progress_runs|integration_configs|integration_pending_tasks|integration_thread_mappings|resources|org_members|org_invitations|bigquery_cache|dashboard_views|pg_catalog|information_schema|pg_class|pg_proc|pg_namespace|pg_user|pg_roles|pg_authid|pg_shadow)\b/i;
563
565
  // Refuses positional INSERTs (no column list). `INSERT INTO recordings VALUES
564
- // (...)` would let a tool stuff arbitrary owner_email values into a row.
566
+ // (...)` would let a extension stuff arbitrary owner_email values into a row.
565
567
  // `INSERT INTO recordings (col1, col2) VALUES (...)` is required so the
566
568
  // downstream injectOwnership helper can append owner_email.
567
569
  const POSITIONAL_INSERT_RE = /\bINSERT\s+INTO\s+["'`]?\w+["'`]?\s+VALUES\b/i;
@@ -588,12 +590,14 @@ async function handleSqlExec(event) {
588
590
  if (DESTRUCTIVE_SQL_RE.test(cleanSql)) {
589
591
  setResponseStatus(event, 403);
590
592
  return {
591
- error: "Schema changes and destructive SQL are not allowed from tools",
593
+ error: "Schema changes and destructive SQL are not allowed from extensions",
592
594
  };
593
595
  }
594
596
  if (SENSITIVE_SQL_RE.test(cleanSql)) {
595
597
  setResponseStatus(event, 403);
596
- return { error: "Sensitive framework tables are not writable from tools" };
598
+ return {
599
+ error: "Sensitive framework tables are not writable from extensions",
600
+ };
597
601
  }
598
602
  if (POSITIONAL_INSERT_RE.test(cleanSql)) {
599
603
  setResponseStatus(event, 400);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/extensions/routes.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n setResponseHeader,\n type H3Event,\n} from \"h3\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { getSession } from \"../server/auth.js\";\nimport { recordChange } from \"../server/poll.js\";\nimport {\n runWithRequestContext,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport {\n listExtensions,\n getExtension,\n createExtension,\n updateExtension,\n updateExtensionContent,\n deleteExtension,\n ensureExtensionsTables,\n} from \"./store.js\";\nimport { buildExtensionHtml, EXTENSION_IFRAME_CSP } from \"./html-shell.js\";\nimport { getThemeVars } from \"./theme.js\";\nimport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n} from \"../secrets/substitution.js\";\nimport {\n collectSecretValues,\n normalizeExtensionProxyMethod,\n readResponseTextWithLimit,\n redactSecrets,\n redactString,\n sanitizeOutboundHeaders,\n} from \"./proxy-security.js\";\nimport {\n createSsrfSafeDispatcher,\n isBlockedExtensionUrlWithDns,\n} from \"./url-safety.js\";\nimport { ForbiddenError, resolveAccess } from \"../sharing/access.js\";\n\nexport function createExtensionsHandler() {\n return defineEventHandler(async (event: H3Event) => {\n const method = getMethod(event);\n const pathname = (event.url?.pathname || \"\")\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n const parts = pathname ? pathname.split(\"/\") : [];\n\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const orgCtx = await getOrgContext(event).catch(() => null);\n const userEmail = session.email;\n const orgId = orgCtx?.orgId ?? undefined;\n\n try {\n return await runWithRequestContext({ userEmail, orgId }, () =>\n dispatch(event, method, parts, userEmail),\n );\n } catch (err) {\n if (err instanceof ForbiddenError) {\n setResponseStatus(event, 403);\n return { error: err.message };\n }\n throw err;\n }\n });\n}\n\nasync function dispatch(\n event: H3Event,\n method: string,\n parts: string[],\n userEmail: string,\n): Promise<unknown> {\n // POST /sql/query — read-only SQL for extension iframes\n if (\n method === \"POST\" &&\n parts.length === 2 &&\n parts[0] === \"sql\" &&\n parts[1] === \"query\"\n ) {\n return handleSqlQuery(event);\n }\n\n // POST /sql/exec — write SQL for extension iframes\n if (\n method === \"POST\" &&\n parts.length === 2 &&\n parts[0] === \"sql\" &&\n parts[1] === \"exec\"\n ) {\n return handleSqlExec(event);\n }\n\n // GET /data/:extensionId/:collection — list items in a collection\n if (method === \"GET\" && parts.length === 3 && parts[0] === \"data\") {\n return handleExtensionDataList(event, parts[1], parts[2], userEmail);\n }\n\n // POST /data/:extensionId/:collection — create/upsert an item\n if (method === \"POST\" && parts.length === 3 && parts[0] === \"data\") {\n return handleExtensionDataUpsert(event, parts[1], parts[2], userEmail);\n }\n\n // DELETE /data/:extensionId/:collection/:itemId — delete an item\n if (method === \"DELETE\" && parts.length === 4 && parts[0] === \"data\") {\n return handleExtensionDataDelete(\n event,\n parts[1],\n parts[2],\n parts[3],\n userEmail,\n );\n }\n\n // POST /proxy\n if (method === \"POST\" && parts.length === 1 && parts[0] === \"proxy\") {\n return handleProxy(event, userEmail);\n }\n\n // GET / — list\n if (method === \"GET\" && parts.length === 0) {\n return listExtensions();\n }\n\n // POST / — create\n if (method === \"POST\" && parts.length === 0) {\n const body = await readBody(event);\n if (!body.name) {\n setResponseStatus(event, 400);\n return { error: \"name is required\" };\n }\n const extension = await createExtension(body);\n recordChange({ source: \"action\", type: \"change\" });\n setResponseStatus(event, 201);\n return extension;\n }\n\n // GET /:id/render\n if (method === \"GET\" && parts.length === 2 && parts[1] === \"render\") {\n const access = await resolveAccess(\"extension\", parts[0]);\n const extension = access?.resource;\n if (!extension) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n const search = event.url?.search || \"\";\n const isDark = search.includes(\"dark=1\") || search.includes(\"dark=true\");\n const themeVars = getThemeVars(isDark);\n // Compute viewer-vs-author binding so the iframe can warn when the\n // viewer is NOT the author. The role is plumbed through to gate\n // dangerous bridge helpers in iframe-bridge.ts (audit H4).\n const isAuthor = extension.ownerEmail === userEmail;\n\n const html = buildExtensionHtml(\n extension.content,\n themeVars,\n isDark,\n parts[0],\n {\n authorEmail: extension.ownerEmail,\n viewerEmail: userEmail,\n isAuthor,\n role: access.role,\n },\n );\n // Security headers per render. We set these explicitly here (rather than\n // rely on the global security-headers middleware) because:\n // - The global middleware sets X-Frame-Options: DENY which would break\n // the legitimate iframe usage of this route inside the app.\n // - frame-ancestors in the CSP must be set as an HTTP header to be\n // enforced; meta-CSP can't set it per spec.\n setResponseHeader(event, \"Content-Type\", \"text/html; charset=utf-8\");\n setResponseHeader(event, \"Content-Security-Policy\", EXTENSION_IFRAME_CSP);\n setResponseHeader(event, \"X-Frame-Options\", \"SAMEORIGIN\");\n setResponseHeader(event, \"X-Content-Type-Options\", \"nosniff\");\n setResponseHeader(event, \"Referrer-Policy\", \"no-referrer\");\n return html;\n }\n\n // GET /:id\n if (method === \"GET\" && parts.length === 1) {\n const extension = await getExtension(parts[0]);\n if (!extension) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n return extension;\n }\n\n // PUT /:id\n if (method === \"PUT\" && parts.length === 1) {\n const body = await readBody(event);\n const hasContentUpdate =\n body.content !== undefined || body.patches !== undefined;\n const hasMetaUpdate =\n body.name !== undefined ||\n body.description !== undefined ||\n body.icon !== undefined ||\n body.visibility !== undefined;\n\n let result = null;\n if (hasContentUpdate) {\n result = await updateExtensionContent(parts[0], {\n content: body.content,\n patches: body.patches,\n });\n }\n if (hasMetaUpdate) {\n result = await updateExtension(parts[0], body);\n }\n if (!hasContentUpdate && !hasMetaUpdate) {\n result = await getExtension(parts[0]);\n }\n if (!result) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n recordChange({ source: \"action\", type: \"change\" });\n return result;\n }\n\n // DELETE /:id\n if (method === \"DELETE\" && parts.length === 1) {\n const ok = await deleteExtension(parts[0]);\n if (!ok) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n recordChange({ source: \"action\", type: \"change\" });\n return { ok: true };\n }\n\n setResponseStatus(event, 404);\n return { error: \"Not found\" };\n}\n\nasync function handleExtensionDataList(\n event: H3Event,\n extensionId: string,\n collection: string,\n userEmail: string,\n): Promise<unknown> {\n await ensureExtensionsTables();\n const extension = await getExtension(extensionId);\n if (!extension) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n const client = getDbExec();\n const url = event.url;\n const limitParam = url?.searchParams?.get(\"limit\");\n const limit = limitParam\n ? Math.min(Math.max(1, Number(limitParam)), 1000)\n : 100;\n const scope = url?.searchParams?.get(\"scope\") || \"user\";\n const orgId = getRequestOrgId();\n\n if (scope === \"org\") {\n if (!orgId) {\n setResponseStatus(event, 400);\n return { error: \"Org context required for scope=org\" };\n }\n const result = await client.execute({\n sql: `SELECT COALESCE(item_id, id) AS id, tool_id, collection, data, owner_email, scope, org_id, created_at, updated_at\n FROM tool_data\n WHERE tool_id = ? AND collection = ? AND scope = 'org' AND org_id = ?\n ORDER BY created_at DESC\n LIMIT ?`,\n args: [extensionId, collection, orgId, limit],\n });\n return result.rows ?? [];\n }\n\n if (scope === \"all\") {\n const result = await client.execute({\n sql: `SELECT COALESCE(item_id, id) AS id, tool_id, collection, data, owner_email, scope, org_id, created_at, updated_at\n FROM tool_data\n WHERE tool_id = ? AND collection = ?\n AND ((scope = 'user' AND owner_email = ?) OR (scope = 'org' AND org_id = ?))\n ORDER BY created_at DESC\n LIMIT ?`,\n args: [extensionId, collection, userEmail, orgId ?? \"\", limit],\n });\n return result.rows ?? [];\n }\n\n const result = await client.execute({\n sql: `SELECT COALESCE(item_id, id) AS id, tool_id, collection, data, owner_email, scope, org_id, created_at, updated_at\n FROM tool_data\n WHERE tool_id = ? AND collection = ? AND scope = 'user' AND owner_email = ?\n ORDER BY updated_at DESC\n LIMIT ?`,\n args: [extensionId, collection, userEmail, limit],\n });\n return result.rows ?? [];\n}\n\nasync function handleExtensionDataUpsert(\n event: H3Event,\n extensionId: string,\n collection: string,\n userEmail: string,\n): Promise<unknown> {\n await ensureExtensionsTables();\n const extension = await getExtension(extensionId);\n if (!extension) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n const body = await readBody(event);\n if (body.data === undefined) {\n setResponseStatus(event, 400);\n return { error: \"data is required\" };\n }\n const itemId = String(body.id || randomUUID());\n const data =\n typeof body.data === \"string\" ? body.data : JSON.stringify(body.data);\n const now = new Date().toISOString();\n const scope = body.scope === \"org\" ? \"org\" : \"user\";\n const orgId = getRequestOrgId();\n\n if (scope === \"org\" && !orgId) {\n setResponseStatus(event, 400);\n return { error: \"Org context required for scope=org\" };\n }\n\n const scopeKey = scope === \"org\" ? `org:${orgId}` : userEmail;\n const client = getDbExec();\n const pg = isPostgres();\n const conflictClause = pg\n ? `ON CONFLICT (tool_id, collection, scope_key, item_id)\n DO UPDATE SET data = EXCLUDED.data, updated_at = EXCLUDED.updated_at`\n : `ON CONFLICT (tool_id, collection, scope_key, item_id)\n DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at`;\n\n await client.execute({\n sql: `INSERT INTO tool_data (id, tool_id, collection, item_id, data, owner_email, scope, org_id, scope_key, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ${conflictClause}`,\n args: [\n randomUUID(),\n extensionId,\n collection,\n itemId,\n data,\n userEmail,\n scope,\n scope === \"org\" ? orgId! : null,\n scopeKey,\n now,\n now,\n ],\n });\n return {\n id: itemId,\n extensionId,\n collection,\n data,\n ownerEmail: userEmail,\n scope,\n orgId: scope === \"org\" ? orgId : null,\n createdAt: now,\n updatedAt: now,\n };\n}\n\nasync function handleExtensionDataDelete(\n event: H3Event,\n extensionId: string,\n collection: string,\n itemId: string,\n userEmail: string,\n): Promise<unknown> {\n await ensureExtensionsTables();\n const extension = await getExtension(extensionId);\n if (!extension) {\n setResponseStatus(event, 404);\n return { error: \"Extension not found\" };\n }\n const url = event.url;\n const scope = url?.searchParams?.get(\"scope\") || \"user\";\n const orgId = getRequestOrgId();\n const client = getDbExec();\n\n if (scope === \"org\") {\n if (!orgId) {\n setResponseStatus(event, 400);\n return { error: \"Org context required for scope=org\" };\n }\n await client.execute({\n sql: `DELETE FROM tool_data WHERE COALESCE(item_id, id) = ? AND tool_id = ? AND collection = ? AND scope = 'org' AND org_id = ?`,\n args: [itemId, extensionId, collection, orgId],\n });\n return { ok: true };\n }\n\n await client.execute({\n sql: `DELETE FROM tool_data WHERE COALESCE(item_id, id) = ? AND tool_id = ? AND collection = ? AND scope = 'user' AND owner_email = ?`,\n args: [itemId, extensionId, collection, userEmail],\n });\n return { ok: true };\n}\n\nasync function handleProxy(\n event: H3Event,\n userEmail: string,\n): Promise<unknown> {\n const body = await readBody(event);\n const rawUrl = body.url;\n if (!rawUrl || typeof rawUrl !== \"string\") {\n setResponseStatus(event, 400);\n return { error: \"url is required\" };\n }\n\n const method = normalizeExtensionProxyMethod(body.method || \"GET\");\n if (!method) {\n setResponseStatus(event, 405);\n return {\n error:\n \"Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.\",\n };\n }\n const rawHeaders: Record<string, string> = body.headers || {};\n const rawBody = body.body;\n\n let resolvedUrl = rawUrl;\n let resolvedHeaders = JSON.stringify(rawHeaders);\n let resolvedBody = rawBody;\n const allUsedKeys: string[] = [];\n const allSecretValues: string[] = [];\n\n try {\n const urlResult = await resolveKeyReferences(rawUrl, \"user\", userEmail);\n resolvedUrl = urlResult.resolved;\n allUsedKeys.push(...urlResult.usedKeys);\n allSecretValues.push(...urlResult.secretValues);\n\n const headerResult = await resolveKeyReferences(\n resolvedHeaders,\n \"user\",\n userEmail,\n );\n resolvedHeaders = headerResult.resolved;\n allUsedKeys.push(...headerResult.usedKeys);\n allSecretValues.push(...headerResult.secretValues);\n\n if (rawBody) {\n const bodyResult = await resolveKeyReferences(\n typeof rawBody === \"string\" ? rawBody : JSON.stringify(rawBody),\n \"user\",\n userEmail,\n );\n resolvedBody = bodyResult.resolved;\n allUsedKeys.push(...bodyResult.usedKeys);\n allSecretValues.push(...bodyResult.secretValues);\n }\n } catch (err: any) {\n setResponseStatus(event, 400);\n return { error: `Key resolution failed: ${err?.message ?? err}` };\n }\n const secretValues = collectSecretValues(allSecretValues);\n\n if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {\n setResponseStatus(event, 403);\n return { error: \"Requests to private/internal addresses are not allowed\" };\n }\n\n for (const keyName of new Set(allUsedKeys)) {\n const allowlist = await getKeyAllowlist(keyName, \"user\", userEmail);\n if (!validateUrlAllowlist(resolvedUrl, allowlist)) {\n setResponseStatus(event, 403);\n return {\n error: `Key \"${keyName}\" is not allowed for this URL origin`,\n };\n }\n }\n\n let headers: Record<string, string>;\n try {\n headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));\n } catch {\n headers = sanitizeOutboundHeaders(rawHeaders);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15_000);\n\n // Best-effort connect-time SSRF guard. When undici is available (it ships\n // with Node 18+ but is not always exposed as an importable module), the\n // dispatcher re-checks the resolved IP at TCP-connect time, closing the\n // TOCTOU between the pre-flight `isBlockedExtensionUrlWithDns` lookup and the\n // actual fetch lookup. If undici is not importable, fall through to plain\n // fetch — the pre-flight remains the primary protection.\n const dispatcher = (await createSsrfSafeDispatcher()) ?? undefined;\n\n try {\n const fetchOpts: RequestInit & { dispatcher?: unknown } = {\n method,\n headers,\n signal: controller.signal,\n redirect: \"manual\",\n };\n if (dispatcher) fetchOpts.dispatcher = dispatcher;\n if (resolvedBody && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n const isStringBody = typeof resolvedBody === \"string\";\n fetchOpts.body = isStringBody\n ? resolvedBody\n : JSON.stringify(resolvedBody);\n // Only inject Content-Type when (a) the caller didn't set one and\n // (b) the body is actually JSON-shaped (object or stringified JSON).\n // Otherwise leave it unset so the runtime fetch picks an appropriate\n // default and we don't misrepresent text/plain bodies as JSON.\n const hasContentType = Object.keys(headers).some(\n (k) => k.toLowerCase() === \"content-type\",\n );\n if (!hasContentType) {\n const isJsonShaped =\n !isStringBody ||\n (typeof resolvedBody === \"string\" &&\n /^\\s*[{[]/.test(resolvedBody) &&\n isLikelyJson(resolvedBody));\n if (isJsonShaped) headers[\"Content-Type\"] = \"application/json\";\n }\n }\n\n const response = await fetch(resolvedUrl, fetchOpts);\n\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n const redirectUrl = location ? new URL(location, resolvedUrl).href : null;\n if (redirectUrl && (await isBlockedExtensionUrlWithDns(redirectUrl))) {\n setResponseStatus(event, 403);\n return { error: \"Redirect to private/internal address blocked\" };\n }\n if (redirectUrl) {\n for (const keyName of new Set(allUsedKeys)) {\n const allowlist = await getKeyAllowlist(keyName, \"user\", userEmail);\n if (!validateUrlAllowlist(redirectUrl, allowlist)) {\n setResponseStatus(event, 403);\n return {\n error: `Redirect URL is not allowed for key \"${keyName}\"`,\n };\n }\n }\n }\n return {\n status: response.status,\n body: {\n redirect: redirectUrl\n ? redactString(redirectUrl, secretValues)\n : location,\n },\n };\n }\n\n const { text } = await readResponseTextWithLimit(response);\n let responseBody: unknown;\n try {\n responseBody = JSON.parse(text);\n } catch {\n responseBody = text;\n }\n\n return {\n status: response.status,\n body: redactSecrets(responseBody, secretValues),\n };\n } catch (err: any) {\n if (err?.name === \"AbortError\") {\n setResponseStatus(event, 504);\n return { error: \"Upstream request timed out\" };\n }\n setResponseStatus(event, 502);\n return {\n error: `Proxy request failed: ${redactSecrets(\n err?.message ?? String(err),\n secretValues,\n )}`,\n };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Capture console output from a CLI script that uses console.log for results.\n * Same technique as wrapCliScript in agent-chat-plugin.ts.\n */\nlet captureCliOutputQueue: Promise<void> = Promise.resolve();\n\nasync function captureCliOutput(\n fn: (args: string[]) => Promise<void>,\n args: string[],\n): Promise<string> {\n const previousCapture = captureCliOutputQueue;\n let releaseCapture!: () => void;\n captureCliOutputQueue = new Promise<void>((resolve) => {\n releaseCapture = resolve;\n });\n await previousCapture;\n\n const logs: string[] = [];\n const origLog = console.log;\n const origError = console.error;\n const origStdoutWrite = process.stdout.write;\n console.log = (...a: unknown[]) => {\n logs.push(a.map(String).join(\" \"));\n };\n console.error = (...a: unknown[]) => {\n logs.push(a.map(String).join(\" \"));\n };\n process.stdout.write = ((chunk: any) => {\n if (typeof chunk === \"string\") logs.push(chunk);\n else if (Buffer.isBuffer(chunk)) logs.push(chunk.toString());\n return true;\n }) as any;\n try {\n await fn(args);\n } catch (err: any) {\n logs.push(`Error: ${err?.message ?? String(err)}`);\n } finally {\n console.log = origLog;\n console.error = origError;\n process.stdout.write = origStdoutWrite;\n releaseCapture();\n }\n return logs.join(\"\\n\") || \"(no output)\";\n}\n\nasync function handleSqlQuery(event: H3Event): Promise<unknown> {\n const body = await readBody(event);\n const sql = body.sql;\n if (!sql || typeof sql !== \"string\") {\n setResponseStatus(event, 400);\n return { error: \"sql is required\" };\n }\n\n const cleanSql = stripSqlComments(sql);\n if (!/^\\s*(SELECT|WITH)\\b/i.test(cleanSql)) {\n setResponseStatus(event, 403);\n return { error: \"Only SELECT queries are allowed from extensions\" };\n }\n if (SENSITIVE_SQL_RE.test(cleanSql)) {\n setResponseStatus(event, 403);\n return {\n error: \"Sensitive framework tables are not readable from extensions\",\n };\n }\n\n try {\n const mod = await import(\"../scripts/db/query.js\");\n const args = [\"--sql\", sql, \"--format\", \"json\"];\n if (body.limit) args.push(\"--limit\", String(body.limit));\n if (body.args !== undefined) {\n if (!Array.isArray(body.args)) {\n setResponseStatus(event, 400);\n return { error: \"args must be an array\" };\n }\n args.push(\"--args\", JSON.stringify(body.args));\n }\n const output = await captureCliOutput(mod.default, args);\n try {\n return JSON.parse(output);\n } catch {\n return { output };\n }\n } catch (err: any) {\n setResponseStatus(event, 500);\n return { error: err?.message ?? \"Query failed\" };\n }\n}\n\n// TODO(security): replace this regex blocklist with a SQL parser + an explicit\n// allowlist of tables a extension may read/write (e.g. only `tool_data`, plus a\n// per-template list). The current blocklist is best-effort defense in depth\n// and is by design bypassable via SQL constructions that don't include the\n// blocklisted token literally (string concat, dynamic SQL, etc). The temp-\n// view scoping in scripts/db/scoping.ts is the actual ownership boundary.\nconst DESTRUCTIVE_SQL_RE =\n /\\b(CREATE\\s+(?:(?:LOCAL|GLOBAL)\\s+)?(?:TEMPORARY|TEMP)?\\s*(TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|EXTENSION|ROLE|TABLESPACE|PUBLICATION|SUBSCRIPTION)|DROP\\s+(TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|EXTENSION|ROLE)|TRUNCATE|DELETE\\s+FROM\\s+(?!tool_data\\b)|ALTER\\s+(TABLE|VIEW|SCHEMA|DATABASE|FUNCTION|ROLE|EXTENSION|PUBLICATION)\\s+(?!tool_data\\b)|ATTACH|DETACH|VACUUM|REINDEX|PRAGMA|GRANT|REVOKE|SET\\s+ROLE|RESET\\s+ROLE|COPY)\\b/i;\n\n// Sensitive tables that extensions must not touch directly. Includes Better Auth\n// identity tables, framework infrastructure (tracing, evals, automations,\n// integrations, notifications, scheduling, sharing/orgs), and Postgres\n// catalogs that would let a extension enumerate or read internals.\nconst SENSITIVE_SQL_RE =\n /\\b(app_secrets|user|users|session|sessions|account|accounts|verification|oauth_tokens|extensions|tool_shares|tool_slots|tool_slot_installs|member|organization|invitation|jwks|agent_trace_spans|agent_trace_summaries|agent_feedback|agent_satisfaction_scores|agent_evals|agent_runs|agent_run_events|notifications|progress_runs|integration_configs|integration_pending_tasks|integration_thread_mappings|resources|org_members|org_invitations|bigquery_cache|dashboard_views|pg_catalog|information_schema|pg_class|pg_proc|pg_namespace|pg_user|pg_roles|pg_authid|pg_shadow)\\b/i;\n\n// Refuses positional INSERTs (no column list). `INSERT INTO recordings VALUES\n// (...)` would let a extension stuff arbitrary owner_email values into a row.\n// `INSERT INTO recordings (col1, col2) VALUES (...)` is required so the\n// downstream injectOwnership helper can append owner_email.\nconst POSITIONAL_INSERT_RE = /\\bINSERT\\s+INTO\\s+[\"'`]?\\w+[\"'`]?\\s+VALUES\\b/i;\n\nfunction stripSqlComments(sql: string): string {\n return sql.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \").replace(/--[^\\n]*/g, \" \");\n}\n\nfunction isLikelyJson(text: string): boolean {\n try {\n const parsed = JSON.parse(text);\n return parsed !== null && typeof parsed === \"object\";\n } catch {\n return false;\n }\n}\n\nasync function handleSqlExec(event: H3Event): Promise<unknown> {\n const body = await readBody(event);\n const sql = body.sql;\n if (!sql || typeof sql !== \"string\") {\n setResponseStatus(event, 400);\n return { error: \"sql is required\" };\n }\n\n const cleanSql = stripSqlComments(sql);\n if (DESTRUCTIVE_SQL_RE.test(cleanSql)) {\n setResponseStatus(event, 403);\n return {\n error:\n \"Schema changes and destructive SQL are not allowed from extensions\",\n };\n }\n if (SENSITIVE_SQL_RE.test(cleanSql)) {\n setResponseStatus(event, 403);\n return {\n error: \"Sensitive framework tables are not writable from extensions\",\n };\n }\n if (POSITIONAL_INSERT_RE.test(cleanSql)) {\n setResponseStatus(event, 400);\n return {\n error:\n \"INSERT must specify an explicit column list (e.g. INSERT INTO t (col1, col2) VALUES (?, ?)) so ownership can be injected.\",\n };\n }\n\n try {\n const mod = await import(\"../scripts/db/exec.js\");\n const args = [\"--sql\", sql, \"--format\", \"json\"];\n if (body.args !== undefined) {\n if (!Array.isArray(body.args)) {\n setResponseStatus(event, 400);\n return { error: \"args must be an array\" };\n }\n args.push(\"--args\", JSON.stringify(body.args));\n }\n const output = await captureCliOutput(mod.default, args);\n try {\n return JSON.parse(output);\n } catch {\n return { output };\n }\n } catch (err: any) {\n setResponseStatus(event, 500);\n return { error: err?.message ?? \"Exec failed\" };\n }\n}\n"]}