@agent-native/core 0.15.10 → 0.15.11
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.
- package/dist/client/dev-mode.d.ts +14 -0
- package/dist/client/dev-mode.d.ts.map +1 -0
- package/dist/client/dev-mode.js +14 -0
- package/dist/client/dev-mode.js.map +1 -0
- package/dist/client/extensions/EmbeddedTool.d.ts +20 -0
- package/dist/client/extensions/EmbeddedTool.d.ts.map +1 -0
- package/dist/client/extensions/EmbeddedTool.js +199 -0
- package/dist/client/extensions/EmbeddedTool.js.map +1 -0
- package/dist/client/extensions/ToolEditor.d.ts +5 -0
- package/dist/client/extensions/ToolEditor.d.ts.map +1 -0
- package/dist/client/extensions/ToolEditor.js +129 -0
- package/dist/client/extensions/ToolEditor.js.map +1 -0
- package/dist/client/extensions/ToolViewer.d.ts +5 -0
- package/dist/client/extensions/ToolViewer.d.ts.map +1 -0
- package/dist/client/extensions/ToolViewer.js +400 -0
- package/dist/client/extensions/ToolViewer.js.map +1 -0
- package/dist/client/extensions/ToolViewerPage.d.ts +2 -0
- package/dist/client/extensions/ToolViewerPage.d.ts.map +1 -0
- package/dist/client/extensions/ToolViewerPage.js +24 -0
- package/dist/client/extensions/ToolViewerPage.js.map +1 -0
- package/dist/client/extensions/ToolsListPage.d.ts +2 -0
- package/dist/client/extensions/ToolsListPage.d.ts.map +1 -0
- package/dist/client/extensions/ToolsListPage.js +67 -0
- package/dist/client/extensions/ToolsListPage.js.map +1 -0
- package/dist/client/extensions/ToolsSidebarSection.d.ts +2 -0
- package/dist/client/extensions/ToolsSidebarSection.d.ts.map +1 -0
- package/dist/client/extensions/ToolsSidebarSection.js +236 -0
- package/dist/client/extensions/ToolsSidebarSection.js.map +1 -0
- package/dist/client/extensions/tool-order.d.ts +7 -0
- package/dist/client/extensions/tool-order.d.ts.map +1 -0
- package/dist/client/extensions/tool-order.js +47 -0
- package/dist/client/extensions/tool-order.js.map +1 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +5 -28
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.spec.js +11 -6
- package/dist/client/settings/useBuilderStatus.spec.js.map +1 -1
- package/dist/client/tools/EmbeddedTool.d.ts +20 -0
- package/dist/client/tools/EmbeddedTool.d.ts.map +1 -0
- package/dist/client/tools/EmbeddedTool.js +199 -0
- package/dist/client/tools/EmbeddedTool.js.map +1 -0
- package/dist/client/tools/ExtensionSlot.d.ts +27 -0
- package/dist/client/tools/ExtensionSlot.d.ts.map +1 -0
- package/dist/client/tools/ExtensionSlot.js +96 -0
- package/dist/client/tools/ExtensionSlot.js.map +1 -0
- package/dist/client/tools/ToolEditor.d.ts +5 -0
- package/dist/client/tools/ToolEditor.d.ts.map +1 -0
- package/dist/client/tools/ToolEditor.js +129 -0
- package/dist/client/tools/ToolEditor.js.map +1 -0
- package/dist/client/tools/ToolViewer.d.ts +5 -0
- package/dist/client/tools/ToolViewer.d.ts.map +1 -0
- package/dist/client/tools/ToolViewer.js +400 -0
- package/dist/client/tools/ToolViewer.js.map +1 -0
- package/dist/client/tools/ToolViewerPage.d.ts +2 -0
- package/dist/client/tools/ToolViewerPage.d.ts.map +1 -0
- package/dist/client/tools/ToolViewerPage.js +24 -0
- package/dist/client/tools/ToolViewerPage.js.map +1 -0
- package/dist/client/tools/ToolsListPage.d.ts +2 -0
- package/dist/client/tools/ToolsListPage.d.ts.map +1 -0
- package/dist/client/tools/ToolsListPage.js +67 -0
- package/dist/client/tools/ToolsListPage.js.map +1 -0
- package/dist/client/tools/ToolsSidebarSection.d.ts +2 -0
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -0
- package/dist/client/tools/ToolsSidebarSection.js +236 -0
- package/dist/client/tools/ToolsSidebarSection.js.map +1 -0
- package/dist/client/tools/iframe-bridge.d.ts +38 -0
- package/dist/client/tools/iframe-bridge.d.ts.map +1 -0
- package/dist/client/tools/iframe-bridge.js +207 -0
- package/dist/client/tools/iframe-bridge.js.map +1 -0
- package/dist/client/tools/index.d.ts +8 -0
- package/dist/client/tools/index.d.ts.map +1 -0
- package/dist/client/tools/index.js +8 -0
- package/dist/client/tools/index.js.map +1 -0
- package/dist/client/tools/tool-order.d.ts +7 -0
- package/dist/client/tools/tool-order.d.ts.map +1 -0
- package/dist/client/tools/tool-order.js +47 -0
- package/dist/client/tools/tool-order.js.map +1 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +4 -2
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/local-migration.d.ts +41 -0
- package/dist/server/local-migration.d.ts.map +1 -0
- package/dist/server/local-migration.js +235 -0
- package/dist/server/local-migration.js.map +1 -0
- package/dist/tools/actions.d.ts +3 -0
- package/dist/tools/actions.d.ts.map +1 -0
- package/dist/tools/actions.js +272 -0
- package/dist/tools/actions.js.map +1 -0
- package/dist/tools/fetch-tool.d.ts +23 -0
- package/dist/tools/fetch-tool.d.ts.map +1 -0
- package/dist/tools/fetch-tool.js +178 -0
- package/dist/tools/fetch-tool.js.map +1 -0
- package/dist/tools/html-shell.d.ts +45 -0
- package/dist/tools/html-shell.d.ts.map +1 -0
- package/dist/tools/html-shell.js +514 -0
- package/dist/tools/html-shell.js.map +1 -0
- package/dist/tools/proxy-security.d.ts +12 -0
- package/dist/tools/proxy-security.d.ts.map +1 -0
- package/dist/tools/proxy-security.js +158 -0
- package/dist/tools/proxy-security.js.map +1 -0
- package/dist/tools/routes.d.ts +2 -0
- package/dist/tools/routes.d.ts.map +1 -0
- package/dist/tools/routes.js +627 -0
- package/dist/tools/routes.js.map +1 -0
- package/dist/tools/schema.d.ts +664 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/schema.js +146 -0
- package/dist/tools/schema.js.map +1 -0
- package/dist/tools/slots/routes.d.ts +15 -0
- package/dist/tools/slots/routes.d.ts.map +1 -0
- package/dist/tools/slots/routes.js +94 -0
- package/dist/tools/slots/routes.js.map +1 -0
- package/dist/tools/slots/schema.d.ts +303 -0
- package/dist/tools/slots/schema.d.ts.map +1 -0
- package/dist/tools/slots/schema.js +76 -0
- package/dist/tools/slots/schema.js.map +1 -0
- package/dist/tools/slots/store.d.ts +66 -0
- package/dist/tools/slots/store.d.ts.map +1 -0
- package/dist/tools/slots/store.js +227 -0
- package/dist/tools/slots/store.js.map +1 -0
- package/dist/tools/store.d.ts +40 -0
- package/dist/tools/store.d.ts.map +1 -0
- package/dist/tools/store.js +193 -0
- package/dist/tools/store.js.map +1 -0
- package/dist/tools/theme.d.ts +2 -0
- package/dist/tools/theme.d.ts.map +1 -0
- package/dist/tools/theme.js +67 -0
- package/dist/tools/theme.js.map +1 -0
- package/dist/tools/url-safety.d.ts +24 -0
- package/dist/tools/url-safety.d.ts.map +1 -0
- package/dist/tools/url-safety.js +224 -0
- package/dist/tools/url-safety.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-migration.js","sourceRoot":"","sources":["../../src/server/local-migration.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Migrate data owned by `local@localhost` to a real account.\n *\n * When a user starts an app in local mode and later signs in to create a real\n * account, this function moves all of their existing data over to the new\n * account so they don't lose anything.\n *\n * Scope of the migration:\n * - `application_state`: rows with `session_id = 'local'`\n * - `settings`: keys prefixed with `u:local@localhost:`\n * - `oauth_tokens`: rows with `owner = 'local@localhost'`\n * - Any template table that has an `owner_email` column: rows with\n * `owner_email = 'local@localhost'`\n *\n * The operation is a no-op if the target email is itself `local@localhost`,\n * empty, or if there is nothing to migrate.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\n\nconst LOCAL_EMAIL = \"local@localhost\";\nconst LOCAL_SESSION_ID = \"local\";\nconst OWNER_COLUMN = \"owner_email\";\n\nexport interface LocalMigrationResult {\n /** Whether anything was actually migrated. */\n migrated: boolean;\n /** Per-table row counts that were updated. Omits tables with zero updates. */\n tables: Record<string, number>;\n /** Target email the data now belongs to. */\n targetEmail: string;\n /**\n * Non-fatal per-step errors encountered during migration. One bad table\n * no longer fails the whole upgrade — we migrate everything we can and\n * report any steps that threw here so the UI can surface them.\n */\n errors?: Array<{ step: string; message: string }>;\n}\n\n/**\n * Error messages that indicate a missing/inaccessible table or column — the\n * migration treats these as \"feature not enabled\" and skips silently.\n */\nconst SCHEMA_SKIP_ERR =\n /no such table|no such column|does not exist|undefined table|undefined column|relation .* does not exist|column .* does not exist|permission denied|is not a table|cannot update view|cannot change column in a view/i;\n\nfunction shouldSkipSchemaError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err);\n return SCHEMA_SKIP_ERR.test(message);\n}\n\n/**\n * Discover every table (not view) in `public` that has an `owner_email`\n * column. Views and materialized views are excluded — they can't be updated\n * directly and would 500 the migration.\n */\nasync function discoverOwnerEmailTables(): Promise<string[]> {\n const client = getDbExec();\n if (isPostgres()) {\n // Join against information_schema.tables to filter out views and\n // materialized views (anything that isn't a plain BASE TABLE).\n const { rows } = await client.execute({\n sql: `SELECT c.table_name\n FROM information_schema.columns c\n JOIN information_schema.tables t\n ON t.table_schema = c.table_schema\n AND t.table_name = c.table_name\n WHERE c.table_schema = 'public'\n AND c.column_name = $1\n AND t.table_type = 'BASE TABLE'`,\n args: [OWNER_COLUMN],\n });\n return rows.map((r: any) => r.table_name ?? r[0]).filter(Boolean);\n }\n\n // SQLite path: iterate tables (type='table', not 'view') and inspect columns via PRAGMA\n const tablesRes = await client.execute(\n `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`,\n );\n const tables = tablesRes.rows\n .map((r: any) => (r.name ?? r[0]) as string)\n .filter(Boolean);\n\n const withOwner: string[] = [];\n for (const table of tables) {\n const escaped = table.replace(/\"/g, '\"\"');\n const colsRes = await client.execute(`PRAGMA table_info(\"${escaped}\")`);\n const hasOwner = colsRes.rows.some(\n (row: any) => (row.name ?? row[1]) === OWNER_COLUMN,\n );\n if (hasOwner) withOwner.push(table);\n }\n return withOwner;\n}\n\n/** Replace `?` placeholders with `$1`, `$2`, … for Postgres. */\nfunction sqlWithParams(sql: string): string {\n if (!isPostgres()) return sql;\n let i = 0;\n return sql.replace(/\\?/g, () => `$${++i}`);\n}\n\n/**\n * Rename `settings` keys so a user's config carries over. Keys are prefixed\n * with `u:<email>:` — moving from one email to another is a prefix swap.\n *\n * If a destination key already exists (unlikely but possible if the user had\n * previously signed in with the same email) we leave the destination alone\n * and drop the local row, so we never clobber real-account state.\n */\nasync function migrateSettings(targetEmail: string): Promise<number> {\n const client = getDbExec();\n const oldPrefix = `u:${LOCAL_EMAIL}:`;\n const newPrefix = `u:${targetEmail}:`;\n\n const { rows } = await client.execute({\n sql: sqlWithParams(`SELECT key FROM settings WHERE key LIKE ? ESCAPE '\\\\'`),\n args: [oldPrefix.replace(/([\\\\%_])/g, \"\\\\$1\") + \"%\"],\n });\n\n let updated = 0;\n for (const row of rows) {\n const oldKey = (row.key ?? (row as any)[0]) as string;\n if (!oldKey.startsWith(oldPrefix)) continue;\n const newKey = newPrefix + oldKey.slice(oldPrefix.length);\n\n // Skip if the destination already exists — don't overwrite real data.\n const existsRes = await client.execute({\n sql: sqlWithParams(`SELECT 1 FROM settings WHERE key = ?`),\n args: [newKey],\n });\n if (existsRes.rows.length > 0) {\n await client.execute({\n sql: sqlWithParams(`DELETE FROM settings WHERE key = ?`),\n args: [oldKey],\n });\n continue;\n }\n\n await client.execute({\n sql: sqlWithParams(`UPDATE settings SET key = ? WHERE key = ?`),\n args: [newKey, oldKey],\n });\n updated++;\n }\n return updated;\n}\n\n/**\n * Move application_state rows from `session_id='local'` to the target email.\n * Rows that already exist for the destination session are left alone.\n */\nasync function migrateApplicationState(targetEmail: string): Promise<number> {\n const client = getDbExec();\n // Only migrate keys that don't already exist under the destination session.\n const { rows } = await client.execute({\n sql: sqlWithParams(\n `SELECT key FROM application_state WHERE session_id = ?`,\n ),\n args: [LOCAL_SESSION_ID],\n });\n\n let updated = 0;\n for (const row of rows) {\n const key = (row.key ?? (row as any)[0]) as string;\n const existsRes = await client.execute({\n sql: sqlWithParams(\n `SELECT 1 FROM application_state WHERE session_id = ? AND key = ?`,\n ),\n args: [targetEmail, key],\n });\n if (existsRes.rows.length > 0) {\n await client.execute({\n sql: sqlWithParams(\n `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n ),\n args: [LOCAL_SESSION_ID, key],\n });\n continue;\n }\n await client.execute({\n sql: sqlWithParams(\n `UPDATE application_state SET session_id = ? WHERE session_id = ? AND key = ?`,\n ),\n args: [targetEmail, LOCAL_SESSION_ID, key],\n });\n updated++;\n }\n return updated;\n}\n\n/** Move oauth_tokens rows. `owner` is the user's email in core tables. */\nasync function migrateOauthTokens(targetEmail: string): Promise<number> {\n const client = getDbExec();\n const res = await client.execute({\n sql: sqlWithParams(`UPDATE oauth_tokens SET owner = ? WHERE owner = ?`),\n args: [targetEmail, LOCAL_EMAIL],\n });\n return res.rowsAffected ?? 0;\n}\n\n/** Move rows in a template table that uses the `owner_email` convention. */\nasync function migrateOwnerEmailTable(\n table: string,\n targetEmail: string,\n): Promise<number> {\n const client = getDbExec();\n const escaped = table.replace(/\"/g, '\"\"');\n const res = await client.execute({\n sql: sqlWithParams(\n `UPDATE \"${escaped}\" SET owner_email = ? WHERE owner_email = ?`,\n ),\n args: [targetEmail, LOCAL_EMAIL],\n });\n return res.rowsAffected ?? 0;\n}\n\n/**\n * Migrate every piece of local-mode data to the given real account email.\n * Safe to call multiple times — it only touches rows that are still attached\n * to `local@localhost`.\n */\nexport async function migrateLocalUserData(\n targetEmail: string,\n): Promise<LocalMigrationResult> {\n const email = targetEmail?.trim().toLowerCase();\n if (!email || email === LOCAL_EMAIL) {\n return { migrated: false, tables: {}, targetEmail: email || \"\" };\n }\n\n const tables: Record<string, number> = {};\n\n // Core tables — best-effort. A missing table just means the feature isn't\n // enabled in this app (e.g. an app that doesn't use oauth_tokens).\n const coreSteps: Array<[string, () => Promise<number>]> = [\n [\"settings\", () => migrateSettings(email)],\n [\"application_state\", () => migrateApplicationState(email)],\n [\"oauth_tokens\", () => migrateOauthTokens(email)],\n ];\n const errors: Array<{ step: string; message: string }> = [];\n for (const [name, fn] of coreSteps) {\n try {\n const count = await fn();\n if (count > 0) tables[name] = count;\n } catch (err: any) {\n // Missing table or column — skip silently. Other errors are logged\n // per-step so one bad table doesn't 500 the entire migration.\n if (!shouldSkipSchemaError(err)) {\n const message = err?.message ?? String(err);\n errors.push({ step: name, message });\n console.error(`[local-migration] ${name} failed:`, err);\n }\n }\n }\n\n // Template tables — discovered dynamically. If discovery itself fails,\n // fall back to an empty list so the migration doesn't 500.\n let templateTables: string[] = [];\n try {\n templateTables = await discoverOwnerEmailTables();\n } catch (err) {\n console.error(\"[local-migration] owner_email table discovery failed:\", err);\n templateTables = [];\n }\n for (const table of templateTables) {\n try {\n const count = await migrateOwnerEmailTable(table, email);\n if (count > 0) tables[table] = count;\n } catch (err: any) {\n if (!shouldSkipSchemaError(err)) {\n const message = err?.message ?? String(err);\n errors.push({ step: table, message });\n console.error(`[local-migration] ${table} failed:`, err);\n }\n }\n }\n\n const migrated = Object.values(tables).some((n) => n > 0);\n const result: LocalMigrationResult = { migrated, tables, targetEmail: email };\n if (errors.length > 0) result.errors = errors;\n return result;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/tools/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAYhE,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAiRrE"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createTool, getTool, updateTool, updateToolContent } from "./store.js";
|
|
2
|
+
import { addToolSlotTarget, installToolSlot, uninstallToolSlot, listToolsForSlot, listSlotsForTool, } from "./slots/store.js";
|
|
3
|
+
export function createToolActionEntries() {
|
|
4
|
+
return {
|
|
5
|
+
"create-tool": {
|
|
6
|
+
tool: {
|
|
7
|
+
description: "Create a sandboxed Alpine.js mini-app tool. Use this when the user asks to create, build, or make a tool/widget/dashboard/calculator. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), dbExec(), toolFetch(), and toolData.",
|
|
8
|
+
parameters: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
name: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: 'Short display name for the tool. Do not include "app" — e.g. name a todo app "Todos", a weather app "Weather".',
|
|
14
|
+
},
|
|
15
|
+
description: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "One-sentence summary of what the tool does.",
|
|
18
|
+
},
|
|
19
|
+
content: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Self-contained Alpine.js HTML body snippet. The iframe body has no padding, so add p-4 or p-6 to the outermost element. Use semantic Tailwind colors (bg-background, text-foreground, bg-primary, etc.) for native theming. Do not include a full app build, React code, or source files.",
|
|
22
|
+
},
|
|
23
|
+
icon: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Optional icon name or short label.",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["name", "content"],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
run: async (args) => {
|
|
32
|
+
const name = String(args?.name ?? "").trim();
|
|
33
|
+
const content = String(args?.content ?? "").trim();
|
|
34
|
+
if (!name)
|
|
35
|
+
return "Error: name is required.";
|
|
36
|
+
if (!content)
|
|
37
|
+
return "Error: content is required.";
|
|
38
|
+
const tool = await createTool({
|
|
39
|
+
name,
|
|
40
|
+
description: String(args?.description ?? "").trim(),
|
|
41
|
+
content,
|
|
42
|
+
icon: args?.icon ? String(args.icon) : undefined,
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
ok: true,
|
|
46
|
+
tool,
|
|
47
|
+
next: `Navigate to /tools/${tool.id} or use the navigate action with --view=tools --toolId=${tool.id}.`,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"update-tool": {
|
|
52
|
+
tool: {
|
|
53
|
+
description: "Update an existing sandboxed Alpine.js mini-app tool. Prefer patches for surgical edits; use full content replacement only when necessary.",
|
|
54
|
+
parameters: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
id: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Tool id to update.",
|
|
60
|
+
},
|
|
61
|
+
name: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "Optional new display name.",
|
|
64
|
+
},
|
|
65
|
+
description: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Optional new description.",
|
|
68
|
+
},
|
|
69
|
+
content: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Optional full replacement Alpine.js HTML body snippet.",
|
|
72
|
+
},
|
|
73
|
+
patches: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: 'Optional JSON array of { "find": "...", "replace": "..." } patches to apply to the current content.',
|
|
76
|
+
},
|
|
77
|
+
icon: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Optional icon name or short label.",
|
|
80
|
+
},
|
|
81
|
+
visibility: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Optional sharing visibility.",
|
|
84
|
+
enum: ["private", "org", "public"],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ["id"],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
run: async (args) => {
|
|
91
|
+
const id = String(args?.id ?? "").trim();
|
|
92
|
+
if (!id)
|
|
93
|
+
return "Error: id is required.";
|
|
94
|
+
let result = null;
|
|
95
|
+
if (args?.content !== undefined || args?.patches !== undefined) {
|
|
96
|
+
const patches = parsePatches(args.patches);
|
|
97
|
+
if (args?.patches !== undefined && !patches) {
|
|
98
|
+
return "Error: patches must be a JSON array of { find, replace } objects.";
|
|
99
|
+
}
|
|
100
|
+
result = await updateToolContent(id, {
|
|
101
|
+
content: args?.content !== undefined ? String(args.content) : undefined,
|
|
102
|
+
patches,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const meta = {};
|
|
106
|
+
if (args?.name !== undefined)
|
|
107
|
+
meta.name = String(args.name).trim();
|
|
108
|
+
if (args?.description !== undefined) {
|
|
109
|
+
meta.description = String(args.description).trim();
|
|
110
|
+
}
|
|
111
|
+
if (args?.icon !== undefined)
|
|
112
|
+
meta.icon = String(args.icon);
|
|
113
|
+
if (args?.visibility !== undefined) {
|
|
114
|
+
meta.visibility = String(args.visibility);
|
|
115
|
+
}
|
|
116
|
+
if (Object.keys(meta).length > 0) {
|
|
117
|
+
result = await updateTool(id, meta);
|
|
118
|
+
}
|
|
119
|
+
if (!result)
|
|
120
|
+
result = await getTool(id);
|
|
121
|
+
if (!result)
|
|
122
|
+
return `Error: tool not found: ${id}`;
|
|
123
|
+
return { ok: true, tool: result };
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
"add-tool-slot-target": {
|
|
127
|
+
tool: {
|
|
128
|
+
description: 'Declare that a tool can render in a UI extension-point slot of an app (e.g. "mail.contact-sidebar.bottom"). Apps drop ExtensionSlot components in their UI; this action registers a tool as installable into one of those slots. Slot IDs follow the convention <app>.<area>.<position>. Caller must have editor access to the tool.',
|
|
129
|
+
parameters: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
toolId: { type: "string", description: "Tool id." },
|
|
133
|
+
slotId: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: 'Slot identifier — e.g. "mail.contact-sidebar.bottom".',
|
|
136
|
+
},
|
|
137
|
+
config: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Optional JSON string with slot-specific config (defaults, hints, etc.).",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
required: ["toolId", "slotId"],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
run: async (args) => {
|
|
146
|
+
const toolId = String(args?.toolId ?? "").trim();
|
|
147
|
+
const slotId = String(args?.slotId ?? "").trim();
|
|
148
|
+
if (!toolId)
|
|
149
|
+
return "Error: toolId is required.";
|
|
150
|
+
if (!slotId)
|
|
151
|
+
return "Error: slotId is required.";
|
|
152
|
+
const row = await addToolSlotTarget(toolId, slotId, args?.config ? String(args.config) : undefined);
|
|
153
|
+
return { ok: true, slot: row };
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
"install-extension": {
|
|
157
|
+
tool: {
|
|
158
|
+
description: "Install a tool as a widget in an extension-point slot for the current user. The tool must already declare the slot via add-tool-slot-target. Per-user installation — only affects the calling user's view. Use after creating a tool that targets a slot, or when the user asks to add an existing widget to a slot.",
|
|
159
|
+
parameters: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
toolId: { type: "string", description: "Tool id to install." },
|
|
163
|
+
slotId: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: 'Slot identifier — e.g. "mail.contact-sidebar.bottom".',
|
|
166
|
+
},
|
|
167
|
+
position: {
|
|
168
|
+
type: "number",
|
|
169
|
+
description: "Optional integer position within the slot (lower = earlier). Defaults to end.",
|
|
170
|
+
},
|
|
171
|
+
config: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description: "Optional JSON string with per-install config (overrides, settings).",
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
required: ["toolId", "slotId"],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
run: async (args) => {
|
|
180
|
+
const toolId = String(args?.toolId ?? "").trim();
|
|
181
|
+
const slotId = String(args?.slotId ?? "").trim();
|
|
182
|
+
if (!toolId)
|
|
183
|
+
return "Error: toolId is required.";
|
|
184
|
+
if (!slotId)
|
|
185
|
+
return "Error: slotId is required.";
|
|
186
|
+
const position = args?.position !== undefined && args.position !== null
|
|
187
|
+
? Number(args.position)
|
|
188
|
+
: undefined;
|
|
189
|
+
const row = await installToolSlot(toolId, slotId, {
|
|
190
|
+
position: Number.isFinite(position) ? position : undefined,
|
|
191
|
+
config: args?.config ? String(args.config) : undefined,
|
|
192
|
+
});
|
|
193
|
+
return { ok: true, install: row };
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
"uninstall-extension": {
|
|
197
|
+
tool: {
|
|
198
|
+
description: "Remove a tool from an extension-point slot for the current user. Does not delete the tool itself.",
|
|
199
|
+
parameters: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
toolId: { type: "string", description: "Tool id." },
|
|
203
|
+
slotId: { type: "string", description: "Slot identifier." },
|
|
204
|
+
},
|
|
205
|
+
required: ["toolId", "slotId"],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
run: async (args) => {
|
|
209
|
+
const toolId = String(args?.toolId ?? "").trim();
|
|
210
|
+
const slotId = String(args?.slotId ?? "").trim();
|
|
211
|
+
if (!toolId)
|
|
212
|
+
return "Error: toolId is required.";
|
|
213
|
+
if (!slotId)
|
|
214
|
+
return "Error: slotId is required.";
|
|
215
|
+
await uninstallToolSlot(toolId, slotId);
|
|
216
|
+
return { ok: true };
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
"list-tools-for-slot": {
|
|
220
|
+
tool: {
|
|
221
|
+
description: "List tools the current user has access to that declare a given extension-point slot. Use to discover what's available to install into a slot the user mentioned.",
|
|
222
|
+
parameters: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
slotId: { type: "string", description: "Slot identifier." },
|
|
226
|
+
},
|
|
227
|
+
required: ["slotId"],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
run: async (args) => {
|
|
231
|
+
const slotId = String(args?.slotId ?? "").trim();
|
|
232
|
+
if (!slotId)
|
|
233
|
+
return "Error: slotId is required.";
|
|
234
|
+
return { tools: await listToolsForSlot(slotId) };
|
|
235
|
+
},
|
|
236
|
+
readOnly: true,
|
|
237
|
+
},
|
|
238
|
+
"list-tool-slots": {
|
|
239
|
+
tool: {
|
|
240
|
+
description: "List the extension-point slots a specific tool declares it can render in. Caller must have viewer access to the tool.",
|
|
241
|
+
parameters: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: {
|
|
244
|
+
toolId: { type: "string", description: "Tool id." },
|
|
245
|
+
},
|
|
246
|
+
required: ["toolId"],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
run: async (args) => {
|
|
250
|
+
const toolId = String(args?.toolId ?? "").trim();
|
|
251
|
+
if (!toolId)
|
|
252
|
+
return "Error: toolId is required.";
|
|
253
|
+
return { slots: await listSlotsForTool(toolId) };
|
|
254
|
+
},
|
|
255
|
+
readOnly: true,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function parsePatches(value) {
|
|
260
|
+
if (value === undefined)
|
|
261
|
+
return undefined;
|
|
262
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
263
|
+
if (!Array.isArray(parsed))
|
|
264
|
+
return undefined;
|
|
265
|
+
if (parsed.some((patch) => !patch ||
|
|
266
|
+
typeof patch.find !== "string" ||
|
|
267
|
+
typeof patch.replace !== "string")) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
return parsed;
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/tools/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ActionEntry } from \"../agent/production-agent.js\";\nimport { createTool, getTool, updateTool, updateToolContent } from \"./store.js\";\nimport {\n addToolSlotTarget,\n installToolSlot,\n uninstallToolSlot,\n listToolsForSlot,\n listSlotsForTool,\n} from \"./slots/store.js\";\n\ntype ToolPatch = { find: string; replace: string };\n\nexport function createToolActionEntries(): Record<string, ActionEntry> {\n return {\n \"create-tool\": {\n tool: {\n description:\n \"Create a sandboxed Alpine.js mini-app tool. Use this when the user asks to create, build, or make a tool/widget/dashboard/calculator. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), dbExec(), toolFetch(), and toolData.\",\n parameters: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description:\n 'Short display name for the tool. Do not include \"app\" — e.g. name a todo app \"Todos\", a weather app \"Weather\".',\n },\n description: {\n type: \"string\",\n description: \"One-sentence summary of what the tool does.\",\n },\n content: {\n type: \"string\",\n description:\n \"Self-contained Alpine.js HTML body snippet. The iframe body has no padding, so add p-4 or p-6 to the outermost element. Use semantic Tailwind colors (bg-background, text-foreground, bg-primary, etc.) for native theming. Do not include a full app build, React code, or source files.\",\n },\n icon: {\n type: \"string\",\n description: \"Optional icon name or short label.\",\n },\n },\n required: [\"name\", \"content\"],\n },\n },\n run: async (args) => {\n const name = String(args?.name ?? \"\").trim();\n const content = String(args?.content ?? \"\").trim();\n if (!name) return \"Error: name is required.\";\n if (!content) return \"Error: content is required.\";\n\n const tool = await createTool({\n name,\n description: String(args?.description ?? \"\").trim(),\n content,\n icon: args?.icon ? String(args.icon) : undefined,\n });\n\n return {\n ok: true,\n tool,\n next: `Navigate to /tools/${tool.id} or use the navigate action with --view=tools --toolId=${tool.id}.`,\n };\n },\n },\n\n \"update-tool\": {\n tool: {\n description:\n \"Update an existing sandboxed Alpine.js mini-app tool. Prefer patches for surgical edits; use full content replacement only when necessary.\",\n parameters: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n description: \"Tool id to update.\",\n },\n name: {\n type: \"string\",\n description: \"Optional new display name.\",\n },\n description: {\n type: \"string\",\n description: \"Optional new description.\",\n },\n content: {\n type: \"string\",\n description:\n \"Optional full replacement Alpine.js HTML body snippet.\",\n },\n patches: {\n type: \"string\",\n description:\n 'Optional JSON array of { \"find\": \"...\", \"replace\": \"...\" } patches to apply to the current content.',\n },\n icon: {\n type: \"string\",\n description: \"Optional icon name or short label.\",\n },\n visibility: {\n type: \"string\",\n description: \"Optional sharing visibility.\",\n enum: [\"private\", \"org\", \"public\"],\n },\n },\n required: [\"id\"],\n },\n },\n run: async (args) => {\n const id = String(args?.id ?? \"\").trim();\n if (!id) return \"Error: id is required.\";\n\n let result = null;\n if (args?.content !== undefined || args?.patches !== undefined) {\n const patches = parsePatches((args as any).patches);\n if (args?.patches !== undefined && !patches) {\n return \"Error: patches must be a JSON array of { find, replace } objects.\";\n }\n result = await updateToolContent(id, {\n content:\n args?.content !== undefined ? String(args.content) : undefined,\n patches,\n });\n }\n\n const meta: Record<string, string> = {};\n if (args?.name !== undefined) meta.name = String(args.name).trim();\n if (args?.description !== undefined) {\n meta.description = String(args.description).trim();\n }\n if (args?.icon !== undefined) meta.icon = String(args.icon);\n if (args?.visibility !== undefined) {\n meta.visibility = String(args.visibility);\n }\n if (Object.keys(meta).length > 0) {\n result = await updateTool(id, meta as any);\n }\n\n if (!result) result = await getTool(id);\n if (!result) return `Error: tool not found: ${id}`;\n return { ok: true, tool: result };\n },\n },\n\n \"add-tool-slot-target\": {\n tool: {\n description:\n 'Declare that a tool can render in a UI extension-point slot of an app (e.g. \"mail.contact-sidebar.bottom\"). Apps drop ExtensionSlot components in their UI; this action registers a tool as installable into one of those slots. Slot IDs follow the convention <app>.<area>.<position>. Caller must have editor access to the tool.',\n parameters: {\n type: \"object\",\n properties: {\n toolId: { type: \"string\", description: \"Tool id.\" },\n slotId: {\n type: \"string\",\n description:\n 'Slot identifier — e.g. \"mail.contact-sidebar.bottom\".',\n },\n config: {\n type: \"string\",\n description:\n \"Optional JSON string with slot-specific config (defaults, hints, etc.).\",\n },\n },\n required: [\"toolId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const toolId = String(args?.toolId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!toolId) return \"Error: toolId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n const row = await addToolSlotTarget(\n toolId,\n slotId,\n args?.config ? String(args.config) : undefined,\n );\n return { ok: true, slot: row };\n },\n },\n\n \"install-extension\": {\n tool: {\n description:\n \"Install a tool as a widget in an extension-point slot for the current user. The tool must already declare the slot via add-tool-slot-target. Per-user installation — only affects the calling user's view. Use after creating a tool that targets a slot, or when the user asks to add an existing widget to a slot.\",\n parameters: {\n type: \"object\",\n properties: {\n toolId: { type: \"string\", description: \"Tool id to install.\" },\n slotId: {\n type: \"string\",\n description:\n 'Slot identifier — e.g. \"mail.contact-sidebar.bottom\".',\n },\n position: {\n type: \"number\",\n description:\n \"Optional integer position within the slot (lower = earlier). Defaults to end.\",\n },\n config: {\n type: \"string\",\n description:\n \"Optional JSON string with per-install config (overrides, settings).\",\n },\n },\n required: [\"toolId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const toolId = String(args?.toolId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!toolId) return \"Error: toolId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n const position =\n args?.position !== undefined && args.position !== null\n ? Number(args.position)\n : undefined;\n const row = await installToolSlot(toolId, slotId, {\n position: Number.isFinite(position as number) ? position : undefined,\n config: args?.config ? String(args.config) : undefined,\n });\n return { ok: true, install: row };\n },\n },\n\n \"uninstall-extension\": {\n tool: {\n description:\n \"Remove a tool from an extension-point slot for the current user. Does not delete the tool itself.\",\n parameters: {\n type: \"object\",\n properties: {\n toolId: { type: \"string\", description: \"Tool id.\" },\n slotId: { type: \"string\", description: \"Slot identifier.\" },\n },\n required: [\"toolId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const toolId = String(args?.toolId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!toolId) return \"Error: toolId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n await uninstallToolSlot(toolId, slotId);\n return { ok: true };\n },\n },\n\n \"list-tools-for-slot\": {\n tool: {\n description:\n \"List tools the current user has access to that declare a given extension-point slot. Use to discover what's available to install into a slot the user mentioned.\",\n parameters: {\n type: \"object\",\n properties: {\n slotId: { type: \"string\", description: \"Slot identifier.\" },\n },\n required: [\"slotId\"],\n },\n },\n run: async (args) => {\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!slotId) return \"Error: slotId is required.\";\n return { tools: await listToolsForSlot(slotId) };\n },\n readOnly: true,\n },\n\n \"list-tool-slots\": {\n tool: {\n description:\n \"List the extension-point slots a specific tool declares it can render in. Caller must have viewer access to the tool.\",\n parameters: {\n type: \"object\",\n properties: {\n toolId: { type: \"string\", description: \"Tool id.\" },\n },\n required: [\"toolId\"],\n },\n },\n run: async (args) => {\n const toolId = String(args?.toolId ?? \"\").trim();\n if (!toolId) return \"Error: toolId is required.\";\n return { slots: await listSlotsForTool(toolId) };\n },\n readOnly: true,\n },\n };\n}\n\nfunction parsePatches(value: unknown): ToolPatch[] | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (!Array.isArray(parsed)) return undefined;\n if (\n parsed.some(\n (patch) =>\n !patch ||\n typeof patch.find !== \"string\" ||\n typeof patch.replace !== \"string\",\n )\n ) {\n return undefined;\n }\n return parsed;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch tool — outbound HTTP for automations and agent use.
|
|
3
|
+
*
|
|
4
|
+
* Supports ${keys.NAME} reference substitution in URL, headers, and body.
|
|
5
|
+
* Values are resolved server-side AFTER the model emits the tool call —
|
|
6
|
+
* the raw secret never enters the model's context.
|
|
7
|
+
*/
|
|
8
|
+
import type { ActionEntry } from "../agent/production-agent.js";
|
|
9
|
+
export interface FetchToolOptions {
|
|
10
|
+
/** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */
|
|
11
|
+
resolveKeys?: (text: string) => Promise<{
|
|
12
|
+
resolved: string;
|
|
13
|
+
usedKeys: string[];
|
|
14
|
+
secretValues?: string[];
|
|
15
|
+
}>;
|
|
16
|
+
/** Validate URL against per-key allowlists. */
|
|
17
|
+
validateUrl?: (url: string, usedKeys: string[]) => Promise<boolean>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create the fetch tool entry for the agent tool registry.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createFetchToolEntry(opts?: FetchToolOptions): Record<string, ActionEntry>;
|
|
23
|
+
//# sourceMappingURL=fetch-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-tool.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAchE,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC,CAAC;IACH,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAgM7B"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch tool — outbound HTTP for automations and agent use.
|
|
3
|
+
*
|
|
4
|
+
* Supports ${keys.NAME} reference substitution in URL, headers, and body.
|
|
5
|
+
* Values are resolved server-side AFTER the model emits the tool call —
|
|
6
|
+
* the raw secret never enters the model's context.
|
|
7
|
+
*/
|
|
8
|
+
import { collectSecretValues, MAX_TOOL_PROXY_RESPONSE_SIZE, normalizeToolProxyMethod, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
|
|
9
|
+
import { isBlockedToolUrlWithDns } from "./url-safety.js";
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
11
|
+
/**
|
|
12
|
+
* Create the fetch tool entry for the agent tool registry.
|
|
13
|
+
*/
|
|
14
|
+
export function createFetchToolEntry(opts = {}) {
|
|
15
|
+
return {
|
|
16
|
+
"web-request": {
|
|
17
|
+
tool: {
|
|
18
|
+
description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered action tools directly (e.g. \`log-meal\`, \`bigquery\`, \`hubspot-deals\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,
|
|
19
|
+
parameters: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
url: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: 'Full URL. May contain ${keys.NAME} references, e.g. "${keys.SLACK_WEBHOOK}".',
|
|
25
|
+
},
|
|
26
|
+
method: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "HTTP method. Default: GET.",
|
|
29
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"],
|
|
30
|
+
},
|
|
31
|
+
headers: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: 'JSON object of headers. May contain ${keys.NAME} references. Example: \'{"Authorization": "Bearer ${keys.API_TOKEN}"}\'.',
|
|
34
|
+
},
|
|
35
|
+
body: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.",
|
|
38
|
+
},
|
|
39
|
+
timeout_ms: {
|
|
40
|
+
type: "number",
|
|
41
|
+
description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ["url"],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
run: async (args) => {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
const rawUrl = args.url;
|
|
50
|
+
const method = normalizeToolProxyMethod(args.method || "GET");
|
|
51
|
+
if (!method) {
|
|
52
|
+
return "Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.";
|
|
53
|
+
}
|
|
54
|
+
const rawHeaders = args.headers || "{}";
|
|
55
|
+
const rawBody = args.body;
|
|
56
|
+
const timeoutMs = Math.min(Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS, 30_000);
|
|
57
|
+
// Resolve key references
|
|
58
|
+
let resolvedUrl = rawUrl;
|
|
59
|
+
let resolvedHeaders = rawHeaders;
|
|
60
|
+
let resolvedBody = rawBody;
|
|
61
|
+
const allUsedKeys = [];
|
|
62
|
+
const allSecretValues = [];
|
|
63
|
+
if (opts.resolveKeys) {
|
|
64
|
+
try {
|
|
65
|
+
const urlResult = await opts.resolveKeys(rawUrl);
|
|
66
|
+
resolvedUrl = urlResult.resolved;
|
|
67
|
+
allUsedKeys.push(...urlResult.usedKeys);
|
|
68
|
+
allSecretValues.push(...(urlResult.secretValues ?? []));
|
|
69
|
+
const headerResult = await opts.resolveKeys(rawHeaders);
|
|
70
|
+
resolvedHeaders = headerResult.resolved;
|
|
71
|
+
allUsedKeys.push(...headerResult.usedKeys);
|
|
72
|
+
allSecretValues.push(...(headerResult.secretValues ?? []));
|
|
73
|
+
if (rawBody) {
|
|
74
|
+
const bodyResult = await opts.resolveKeys(rawBody);
|
|
75
|
+
resolvedBody = bodyResult.resolved;
|
|
76
|
+
allUsedKeys.push(...bodyResult.usedKeys);
|
|
77
|
+
allSecretValues.push(...(bodyResult.secretValues ?? []));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return `Error resolving key references: ${err?.message ?? err}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const secretValues = collectSecretValues(allSecretValues);
|
|
85
|
+
// Block SSRF targets regardless of key usage
|
|
86
|
+
if (await isBlockedToolUrlWithDns(resolvedUrl)) {
|
|
87
|
+
return `Requests to private/internal addresses are not allowed: "${rawUrl}".`;
|
|
88
|
+
}
|
|
89
|
+
// Validate URL against per-key allowlists
|
|
90
|
+
if (opts.validateUrl && allUsedKeys.length > 0) {
|
|
91
|
+
try {
|
|
92
|
+
const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys);
|
|
93
|
+
if (!allowed) {
|
|
94
|
+
return `URL "${rawUrl}" is not in the allowlist for the referenced keys. Check your key settings.`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return `URL validation error: ${err?.message ?? err}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Parse headers
|
|
102
|
+
let headers;
|
|
103
|
+
try {
|
|
104
|
+
headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return `Invalid headers JSON: ${rawHeaders}`;
|
|
108
|
+
}
|
|
109
|
+
// Make the request
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
112
|
+
try {
|
|
113
|
+
const fetchOpts = {
|
|
114
|
+
method,
|
|
115
|
+
headers,
|
|
116
|
+
signal: controller.signal,
|
|
117
|
+
redirect: "manual",
|
|
118
|
+
};
|
|
119
|
+
if (resolvedBody && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
120
|
+
fetchOpts.body = resolvedBody;
|
|
121
|
+
if (!headers["content-type"] && !headers["Content-Type"]) {
|
|
122
|
+
headers["Content-Type"] = "application/json";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const response = await fetch(resolvedUrl, fetchOpts);
|
|
126
|
+
const elapsed = Date.now() - startTime;
|
|
127
|
+
if (response.status >= 300 && response.status < 400) {
|
|
128
|
+
const location = response.headers.get("location");
|
|
129
|
+
const redirectUrl = location
|
|
130
|
+
? new URL(location, resolvedUrl).href
|
|
131
|
+
: null;
|
|
132
|
+
if (redirectUrl && (await isBlockedToolUrlWithDns(redirectUrl))) {
|
|
133
|
+
return "Redirect to private/internal address blocked.";
|
|
134
|
+
}
|
|
135
|
+
if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {
|
|
136
|
+
const allowed = await opts.validateUrl(redirectUrl, allUsedKeys);
|
|
137
|
+
if (!allowed) {
|
|
138
|
+
return "Redirect URL is not in the allowlist for the referenced keys.";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return `HTTP ${response.status} ${response.statusText}\n\nRedirect: ${redirectUrl ? redactString(redirectUrl, secretValues) : "(none)"}`;
|
|
142
|
+
}
|
|
143
|
+
let body;
|
|
144
|
+
try {
|
|
145
|
+
const result = await readResponseTextWithLimit(response, MAX_TOOL_PROXY_RESPONSE_SIZE);
|
|
146
|
+
body = result.text;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
body = "(could not read response body)";
|
|
150
|
+
}
|
|
151
|
+
body = redactString(body, secretValues);
|
|
152
|
+
// Truncate very long responses for the agent
|
|
153
|
+
if (body.length > 8000) {
|
|
154
|
+
body = body.slice(0, 8000) + "\n... (truncated)";
|
|
155
|
+
}
|
|
156
|
+
// Audit log
|
|
157
|
+
console.log(`[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(",") || "none"})`);
|
|
158
|
+
return `HTTP ${response.status} ${response.statusText}\n\n${body}`;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
const elapsed = Date.now() - startTime;
|
|
162
|
+
if (err?.name === "AbortError") {
|
|
163
|
+
console.log(`[fetch-tool] ${method} ${rawUrl} → TIMEOUT (${elapsed}ms)`);
|
|
164
|
+
return `Request timed out after ${timeoutMs}ms.`;
|
|
165
|
+
}
|
|
166
|
+
const message = redactSecrets(err?.message ?? String(err), secretValues);
|
|
167
|
+
console.log(`[fetch-tool] ${method} ${rawUrl} → ERROR: ${message} (${elapsed}ms)`);
|
|
168
|
+
return `Request failed: ${message}`;
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
readOnly: true,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=fetch-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-tool.js","sourceRoot":"","sources":["../../src/tools/fetch-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fetch tool — outbound HTTP for automations and agent use.\n *\n * Supports ${keys.NAME} reference substitution in URL, headers, and body.\n * Values are resolved server-side AFTER the model emits the tool call —\n * the raw secret never enters the model's context.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n collectSecretValues,\n MAX_TOOL_PROXY_RESPONSE_SIZE,\n normalizeToolProxyMethod,\n readResponseTextWithLimit,\n redactSecrets,\n redactString,\n sanitizeOutboundHeaders,\n} from \"./proxy-security.js\";\nimport { isBlockedToolUrlWithDns } from \"./url-safety.js\";\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\nexport interface FetchToolOptions {\n /** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */\n resolveKeys?: (text: string) => Promise<{\n resolved: string;\n usedKeys: string[];\n secretValues?: string[];\n }>;\n /** Validate URL against per-key allowlists. */\n validateUrl?: (url: string, usedKeys: string[]) => Promise<boolean>;\n}\n\n/**\n * Create the fetch tool entry for the agent tool registry.\n */\nexport function createFetchToolEntry(\n opts: FetchToolOptions = {},\n): Record<string, ActionEntry> {\n return {\n \"web-request\": {\n tool: {\n description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \\${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \\${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered action tools directly (e.g. \\`log-meal\\`, \\`bigquery\\`, \\`hubspot-deals\\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,\n parameters: {\n type: \"object\" as const,\n properties: {\n url: {\n type: \"string\",\n description:\n 'Full URL. May contain ${keys.NAME} references, e.g. \"${keys.SLACK_WEBHOOK}\".',\n },\n method: {\n type: \"string\",\n description: \"HTTP method. Default: GET.\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"],\n },\n headers: {\n type: \"string\",\n description:\n 'JSON object of headers. May contain ${keys.NAME} references. Example: \\'{\"Authorization\": \"Bearer ${keys.API_TOKEN}\"}\\'.',\n },\n body: {\n type: \"string\",\n description:\n \"Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.\",\n },\n timeout_ms: {\n type: \"number\",\n description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`,\n },\n },\n required: [\"url\"],\n },\n },\n run: async (args: Record<string, string>) => {\n const startTime = Date.now();\n const rawUrl = args.url;\n const method = normalizeToolProxyMethod(args.method || \"GET\");\n if (!method) {\n return \"Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.\";\n }\n const rawHeaders = args.headers || \"{}\";\n const rawBody = args.body;\n const timeoutMs = Math.min(\n Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS,\n 30_000,\n );\n\n // Resolve key references\n let resolvedUrl = rawUrl;\n let resolvedHeaders = rawHeaders;\n let resolvedBody = rawBody;\n const allUsedKeys: string[] = [];\n const allSecretValues: string[] = [];\n\n if (opts.resolveKeys) {\n try {\n const urlResult = await opts.resolveKeys(rawUrl);\n resolvedUrl = urlResult.resolved;\n allUsedKeys.push(...urlResult.usedKeys);\n allSecretValues.push(...(urlResult.secretValues ?? []));\n\n const headerResult = await opts.resolveKeys(rawHeaders);\n resolvedHeaders = headerResult.resolved;\n allUsedKeys.push(...headerResult.usedKeys);\n allSecretValues.push(...(headerResult.secretValues ?? []));\n\n if (rawBody) {\n const bodyResult = await opts.resolveKeys(rawBody);\n resolvedBody = bodyResult.resolved;\n allUsedKeys.push(...bodyResult.usedKeys);\n allSecretValues.push(...(bodyResult.secretValues ?? []));\n }\n } catch (err: any) {\n return `Error resolving key references: ${err?.message ?? err}`;\n }\n }\n const secretValues = collectSecretValues(allSecretValues);\n\n // Block SSRF targets regardless of key usage\n if (await isBlockedToolUrlWithDns(resolvedUrl)) {\n return `Requests to private/internal addresses are not allowed: \"${rawUrl}\".`;\n }\n\n // Validate URL against per-key allowlists\n if (opts.validateUrl && allUsedKeys.length > 0) {\n try {\n const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys);\n if (!allowed) {\n return `URL \"${rawUrl}\" is not in the allowlist for the referenced keys. Check your key settings.`;\n }\n } catch (err: any) {\n return `URL validation error: ${err?.message ?? err}`;\n }\n }\n\n // Parse headers\n let headers: Record<string, string>;\n try {\n headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));\n } catch {\n return `Invalid headers JSON: ${rawHeaders}`;\n }\n\n // Make the request\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const fetchOpts: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n redirect: \"manual\",\n };\n if (resolvedBody && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n fetchOpts.body = resolvedBody;\n if (!headers[\"content-type\"] && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n }\n\n const response = await fetch(resolvedUrl, fetchOpts);\n const elapsed = Date.now() - startTime;\n\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n const redirectUrl = location\n ? new URL(location, resolvedUrl).href\n : null;\n if (redirectUrl && (await isBlockedToolUrlWithDns(redirectUrl))) {\n return \"Redirect to private/internal address blocked.\";\n }\n if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {\n const allowed = await opts.validateUrl(redirectUrl, allUsedKeys);\n if (!allowed) {\n return \"Redirect URL is not in the allowlist for the referenced keys.\";\n }\n }\n return `HTTP ${response.status} ${response.statusText}\\n\\nRedirect: ${\n redirectUrl ? redactString(redirectUrl, secretValues) : \"(none)\"\n }`;\n }\n\n let body: string;\n try {\n const result = await readResponseTextWithLimit(\n response,\n MAX_TOOL_PROXY_RESPONSE_SIZE,\n );\n body = result.text;\n } catch {\n body = \"(could not read response body)\";\n }\n body = redactString(body, secretValues);\n\n // Truncate very long responses for the agent\n if (body.length > 8000) {\n body = body.slice(0, 8000) + \"\\n... (truncated)\";\n }\n\n // Audit log\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(\",\") || \"none\"})`,\n );\n\n return `HTTP ${response.status} ${response.statusText}\\n\\n${body}`;\n } catch (err: any) {\n const elapsed = Date.now() - startTime;\n if (err?.name === \"AbortError\") {\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → TIMEOUT (${elapsed}ms)`,\n );\n return `Request timed out after ${timeoutMs}ms.`;\n }\n const message = redactSecrets(\n err?.message ?? String(err),\n secretValues,\n );\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ERROR: ${message} (${elapsed}ms)`,\n );\n return `Request failed: ${message}`;\n } finally {\n clearTimeout(timeout);\n }\n },\n readOnly: true,\n },\n };\n}\n"]}
|