@agent-native/core 0.19.0 → 0.19.1
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/a2a/caller-auth.d.ts +1 -0
- package/dist/a2a/caller-auth.d.ts.map +1 -1
- package/dist/a2a/caller-auth.js +1 -1
- package/dist/a2a/caller-auth.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +34 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +47 -256
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +3 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +11 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +4 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/dynamic-suggestions.d.ts +43 -0
- package/dist/client/dynamic-suggestions.d.ts.map +1 -0
- package/dist/client/dynamic-suggestions.js +344 -0
- package/dist/client/dynamic-suggestions.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +2 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/coding-tools/index.d.ts +31 -0
- package/dist/coding-tools/index.d.ts.map +1 -0
- package/dist/coding-tools/index.js +411 -0
- package/dist/coding-tools/index.js.map +1 -0
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +85 -26
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +148 -42
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/org-directory.d.ts +83 -0
- package/dist/mcp/org-directory.d.ts.map +1 -0
- package/dist/mcp/org-directory.js +201 -0
- package/dist/mcp/org-directory.js.map +1 -0
- package/dist/mcp/server.d.ts +38 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +208 -77
- package/dist/mcp/server.js.map +1 -1
- package/dist/scripts/dev/index.d.ts +6 -4
- package/dist/scripts/dev/index.d.ts.map +1 -1
- package/dist/scripts/dev/index.js +28 -13
- package/dist/scripts/dev/index.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +6 -6
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +32 -32
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.js +2 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/agents-bundle.d.ts +3 -3
- package/dist/server/agents-bundle.js +5 -5
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +17 -2
- package/dist/server/sentry.js.map +1 -1
- package/docs/content/client.md +15 -0
- package/docs/content/code-agents-ui.md +11 -1
- package/docs/content/drop-in-agent.md +3 -1
- package/docs/content/frames.md +1 -1
- package/docs/content/migration-workbench.md +5 -0
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
* `mountMCP` — the Node bits are dynamically imported inside `run()`.
|
|
30
30
|
*/
|
|
31
31
|
import { buildDeepLink } from "../server/deep-link.js";
|
|
32
|
+
import { fetchOrgApps } from "./org-directory.js";
|
|
32
33
|
/**
|
|
33
34
|
* Build an `ActionTool`. `parameters` is wrapped in the
|
|
34
35
|
* `{ type:"object", properties, required }` shape `createMCPServerForRequest`
|
|
@@ -87,25 +88,52 @@ async function resolveTargetAppOrigin(config, targetAppId) {
|
|
|
87
88
|
// ---------------------------------------------------------------------------
|
|
88
89
|
// list_apps
|
|
89
90
|
// ---------------------------------------------------------------------------
|
|
90
|
-
function listAppsTool() {
|
|
91
|
+
function listAppsTool(config) {
|
|
91
92
|
return {
|
|
92
|
-
tool: tool("List the workspace apps and their
|
|
93
|
-
"
|
|
94
|
-
"
|
|
93
|
+
tool: tool("List the workspace apps and their URLs. Use this to discover which " +
|
|
94
|
+
"apps exist before opening or asking one. In a single-app project " +
|
|
95
|
+
"this returns just that app. When an org directory is configured " +
|
|
96
|
+
"this also includes the org's deployed sibling apps."),
|
|
95
97
|
readOnly: true,
|
|
96
98
|
parallelSafe: true,
|
|
97
99
|
run: async () => {
|
|
98
100
|
const { resolveWorkspace } = await import("./workspace-resolve.js");
|
|
99
101
|
const ws = await resolveWorkspace();
|
|
102
|
+
const apps = ws.apps.map((a) => ({
|
|
103
|
+
id: a.id,
|
|
104
|
+
url: a.url,
|
|
105
|
+
port: a.port,
|
|
106
|
+
running: a.running,
|
|
107
|
+
source: "workspace",
|
|
108
|
+
}));
|
|
109
|
+
const seenIds = new Set(apps.map((a) => a.id.toLowerCase()));
|
|
110
|
+
const seenOrigins = new Set(apps.map((a) => a.url.replace(/\/+$/, "")));
|
|
111
|
+
// Merge the org directory's deployed sibling apps. Inactive (no env)
|
|
112
|
+
// or any failure ⇒ fetchOrgApps() returns [] and this is a no-op, so
|
|
113
|
+
// the existing local/workspace behavior is preserved exactly.
|
|
114
|
+
const orgApps = await fetchOrgApps({
|
|
115
|
+
selfId: currentAppId(config),
|
|
116
|
+
}).catch(() => []);
|
|
117
|
+
for (const oa of orgApps) {
|
|
118
|
+
const idKey = oa.id.toLowerCase();
|
|
119
|
+
const originKey = oa.url.replace(/\/+$/, "");
|
|
120
|
+
// Dedupe by id OR origin — a workspace app already listed wins.
|
|
121
|
+
if (seenIds.has(idKey) || seenOrigins.has(originKey))
|
|
122
|
+
continue;
|
|
123
|
+
seenIds.add(idKey);
|
|
124
|
+
seenOrigins.add(originKey);
|
|
125
|
+
apps.push({
|
|
126
|
+
id: oa.id,
|
|
127
|
+
url: oa.url,
|
|
128
|
+
port: undefined,
|
|
129
|
+
running: true,
|
|
130
|
+
source: "org-directory",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
100
133
|
return {
|
|
101
134
|
workspace: ws.isWorkspace,
|
|
102
135
|
gatewayUrl: ws.gatewayUrl,
|
|
103
|
-
apps
|
|
104
|
-
id: a.id,
|
|
105
|
-
url: a.url,
|
|
106
|
-
port: a.port,
|
|
107
|
-
running: a.running,
|
|
108
|
-
})),
|
|
136
|
+
apps,
|
|
109
137
|
};
|
|
110
138
|
},
|
|
111
139
|
};
|
|
@@ -175,6 +203,33 @@ function openAppTool(config) {
|
|
|
175
203
|
},
|
|
176
204
|
};
|
|
177
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Route an `ask_app` message to a *different* app's agent over A2A. Shared by
|
|
208
|
+
* the workspace-resolved path and the org-directory-resolved path so the A2A
|
|
209
|
+
* call logic is not duplicated. `origin` is the target app's A2A base
|
|
210
|
+
* (workspace dev origin or the directory's `a2aUrl`); `id` is reported back.
|
|
211
|
+
*
|
|
212
|
+
* Throws on failure so the caller can be honest — it never falls back to this
|
|
213
|
+
* app's agent and pretends it was the target.
|
|
214
|
+
*/
|
|
215
|
+
async function routeAskOverA2A(origin, id, message) {
|
|
216
|
+
const { callAgent } = await import("../a2a/client.js");
|
|
217
|
+
const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
|
|
218
|
+
// The MCP handler runs inside `runWithRequestContext`, so this is the
|
|
219
|
+
// verified caller identity and org scope. Reuse the same auth resolver as
|
|
220
|
+
// org-directory discovery so the directory lookup and actual A2A call are
|
|
221
|
+
// scoped the same way.
|
|
222
|
+
const auth = await resolveA2ACallerAuth();
|
|
223
|
+
const response = await callAgent(origin, message, {
|
|
224
|
+
apiKey: auth.apiKey,
|
|
225
|
+
userEmail: auth.userEmail,
|
|
226
|
+
orgDomain: auth.orgDomain,
|
|
227
|
+
orgSecret: auth.orgSecret,
|
|
228
|
+
// Bound the wait — cross-app A2A polls async by default.
|
|
229
|
+
timeoutMs: 5 * 60_000,
|
|
230
|
+
});
|
|
231
|
+
return { app: id, routedVia: "a2a", response };
|
|
232
|
+
}
|
|
178
233
|
// ---------------------------------------------------------------------------
|
|
179
234
|
// ask_app
|
|
180
235
|
// ---------------------------------------------------------------------------
|
|
@@ -208,21 +263,7 @@ function askAppTool(config) {
|
|
|
208
263
|
const targetApp = await resolveTargetAppOrigin(config, requestedApp);
|
|
209
264
|
if (targetApp) {
|
|
210
265
|
try {
|
|
211
|
-
|
|
212
|
-
const { getRequestUserEmail } = await import("../server/request-context.js");
|
|
213
|
-
// The MCP handler runs inside `runWithRequestContext`, so this is
|
|
214
|
-
// the verified caller's email — it lets `callAgent` mint a signed
|
|
215
|
-
// A2A JWT so the target app honours per-user scope.
|
|
216
|
-
const response = await callAgent(targetApp.origin, message, {
|
|
217
|
-
userEmail: getRequestUserEmail(),
|
|
218
|
-
// Bound the wait — cross-app A2A polls async by default.
|
|
219
|
-
timeoutMs: 5 * 60_000,
|
|
220
|
-
});
|
|
221
|
-
return {
|
|
222
|
-
app: targetApp.id,
|
|
223
|
-
routedVia: "a2a",
|
|
224
|
-
response,
|
|
225
|
-
};
|
|
266
|
+
return await routeAskOverA2A(targetApp.origin, targetApp.id, message);
|
|
226
267
|
}
|
|
227
268
|
catch (err) {
|
|
228
269
|
// Be honest: routing was attempted and failed — do NOT fall back to
|
|
@@ -231,6 +272,24 @@ function askAppTool(config) {
|
|
|
231
272
|
`${err?.message ?? err}`);
|
|
232
273
|
}
|
|
233
274
|
}
|
|
275
|
+
// Not a known local/workspace app — try the org directory. When a
|
|
276
|
+
// directory is configured and the requested app is one of the org's
|
|
277
|
+
// deployed sibling apps, route to it over A2A (same path as above,
|
|
278
|
+
// against its `a2aUrl`). Inactive directory / any failure ⇒ orgApps is
|
|
279
|
+
// [] and this is skipped, preserving the exact local-only behavior.
|
|
280
|
+
if (requestedApp && requestedApp.toLowerCase() !== selfId) {
|
|
281
|
+
const orgApps = await fetchOrgApps({ selfId }).catch(() => []);
|
|
282
|
+
const dirMatch = orgApps.find((a) => a.id === requestedApp.toLowerCase());
|
|
283
|
+
if (dirMatch) {
|
|
284
|
+
try {
|
|
285
|
+
return await routeAskOverA2A(dirMatch.a2aUrl, dirMatch.id, message);
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
throw new Error(`Failed to route ask_app to "${dirMatch.id}" via A2A ` +
|
|
289
|
+
`(org directory): ${err?.message ?? err}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
234
293
|
// Same app (or no workspace / unknown target): answer locally with this
|
|
235
294
|
// app's own ask-agent handler — the same entry point the HTTP MCP mount
|
|
236
295
|
// + A2A use, so there is no second agent runner.
|
|
@@ -391,7 +450,7 @@ function createWorkspaceAppTool() {
|
|
|
391
450
|
*/
|
|
392
451
|
export function getBuiltinCrossAppTools(config) {
|
|
393
452
|
return {
|
|
394
|
-
list_apps: listAppsTool(),
|
|
453
|
+
list_apps: listAppsTool(config),
|
|
395
454
|
open_app: openAppTool(config),
|
|
396
455
|
ask_app: askAppTool(config),
|
|
397
456
|
create_workspace_app: createWorkspaceAppTool(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-tools.js","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic cross-app MCP tools — a stable verb set every external agent gets\n * regardless of which template it is talking to.\n *\n * These are merged into the MCP action registry by\n * `createMCPServerForRequest` (see `build-server.ts`). **Precedence: template\n * actions win.** If a template defines an action named `list_apps` /\n * `open_app` / `ask_app` / `create_workspace_app` / `list_templates`, the\n * template's `ActionEntry` overwrites the builtin of the same name. This is\n * the same template-over-framework precedence `autoDiscoverActions` uses.\n *\n * | Tool | Side effects | Returns |\n * | --------------------- | ------------ | ---------------------------------------- |\n * | `list_apps` | none | `{ apps: [{ id, url, running }] }` |\n * | `open_app` | none | `{ url }` (+ deep-link `link`) |\n * | `ask_app` | agent loop | `{ app, routedVia, response }` |\n * | `create_workspace_app`| scaffolds | `{ name, url, port, deepLink }` (+ link) |\n *\n * `open_app` / `create_workspace_app` return an **absolute** URL on the\n * *target* app's origin when it differs from this app (so a workspace link\n * lands in the right app), and a relative path for the same app / standalone.\n * `ask_app` routes to a *different* workspace app over A2A when possible and\n * reports `routedVia: \"a2a\"`; otherwise it answers locally\n * (`routedVia: \"local\"`) and never falsely claims cross-app delegation.\n * | `list_templates` | none | `{ templates: [...] }` (allow-list only) |\n *\n * Node-only at call time (workspace resolution + scaffolding use `fs`), but\n * the module has no top-level Node imports so it bundles fine alongside\n * `mountMCP` — the Node bits are dynamically imported inside `run()`.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { buildDeepLink } from \"../server/deep-link.js\";\nimport type { MCPConfig } from \"./build-server.js\";\n\nimport type { ActionTool } from \"../agent/types.js\";\n\n/** Flat map of param name → JSON-schema property. */\ntype Params = Record<\n string,\n { type: string; description?: string; enum?: string[] }\n>;\n\n/**\n * Build an `ActionTool`. `parameters` is wrapped in the\n * `{ type:\"object\", properties, required }` shape `createMCPServerForRequest`\n * forwards verbatim as the MCP tool `inputSchema`.\n */\nfunction tool(\n description: string,\n parameters?: Params,\n required?: string[],\n): ActionTool {\n if (!parameters) return { description };\n return {\n description,\n parameters: {\n type: \"object\",\n properties: parameters,\n ...(required && required.length ? { required } : {}),\n },\n };\n}\n\n/**\n * The canonical app id this MCP server is mounted for. `MCPConfig.appId` is\n * authoritative; fall back to lowercasing `name` (which is the capitalized\n * app id at every call site) for back-compat with configs that predate the\n * `appId` field.\n */\nfunction currentAppId(config: MCPConfig): string {\n return (config.appId || config.name || \"app\").toLowerCase();\n}\n\n/**\n * Resolve the absolute origin of a *target* workspace app (e.g.\n * `http://127.0.0.1:8101`) so cross-app deep links / A2A calls point at the\n * right app instead of the current request's origin. Reuses the same\n * workspace resolution `list_apps` / the stdio proxy use.\n *\n * Returns `null` when:\n * - the target is the current app (caller should keep relative behavior),\n * - there is no workspace info (standalone / single app), or\n * - the target app is unknown.\n */\nasync function resolveTargetAppOrigin(\n config: MCPConfig,\n targetAppId: string,\n): Promise<{ origin: string; id: string } | null> {\n const target = targetAppId.trim().toLowerCase();\n if (!target || target === currentAppId(config)) return null;\n try {\n const { resolveWorkspace } = await import(\"./workspace-resolve.js\");\n const ws = await resolveWorkspace();\n if (!ws.isWorkspace) return null;\n const match = ws.apps.find((a) => a.id.toLowerCase() === target);\n if (!match) return null;\n return { origin: match.url, id: match.id };\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// list_apps\n// ---------------------------------------------------------------------------\n\nfunction listAppsTool(): ActionEntry {\n return {\n tool: tool(\n \"List the workspace apps and their local dev URLs/ports. Use this to \" +\n \"discover which apps exist before opening or asking one. In a single-\" +\n \"app project this returns just that app.\",\n ),\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const { resolveWorkspace } = await import(\"./workspace-resolve.js\");\n const ws = await resolveWorkspace();\n return {\n workspace: ws.isWorkspace,\n gatewayUrl: ws.gatewayUrl,\n apps: ws.apps.map((a) => ({\n id: a.id,\n url: a.url,\n port: a.port,\n running: a.running,\n })),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// open_app\n// ---------------------------------------------------------------------------\n\nfunction openAppTool(config: MCPConfig): ActionEntry {\n return {\n tool: tool(\n \"Build a deep link that opens an app at a specific view/record. No side \" +\n \"effects — returns a URL the user can click to land in the running UI. \" +\n 'After calling, surface the returned \"Open in … →\" link to the user.',\n {\n app: { type: \"string\", description: \"App id, e.g. 'mail'\" },\n view: {\n type: \"string\",\n description: \"Target view, e.g. 'inbox' (maps to navigate command)\",\n },\n params: {\n type: \"object\",\n description:\n \"Optional record-focus / filter params, e.g. { threadId: 'abc' }\",\n },\n },\n [\"app\", \"view\"],\n ),\n readOnly: true,\n parallelSafe: true,\n run: async (args: Record<string, any>) => {\n const app = String(args.app ?? \"\").trim();\n const view = String(args.view ?? \"\").trim();\n if (!app || !view) {\n throw new Error(\"open_app requires both 'app' and 'view'.\");\n }\n let params: Record<string, string | number | boolean> | undefined;\n const raw = args.params;\n if (raw && typeof raw === \"object\") {\n params = raw as Record<string, string | number | boolean>;\n } else if (typeof raw === \"string\" && raw.trim()) {\n try {\n params = JSON.parse(raw);\n } catch {\n params = undefined;\n }\n }\n const relUrl = buildDeepLink({ app, view, params });\n\n // Cross-app target in a workspace: resolve the TARGET app's origin and\n // return an absolute URL. Otherwise the MCP layer would prefix the\n // relative path with the CURRENT request origin, landing the user in\n // the wrong app (e.g. open_app({app:\"calendar\"}) served from Mail).\n // Same-app / standalone keeps the relative path (current behavior).\n const targetApp = await resolveTargetAppOrigin(config, app);\n const url = targetApp\n ? `${targetApp.origin.replace(/\\/+$/, \"\")}${relUrl}`\n : relUrl;\n\n return { app, view, url };\n },\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { url?: string; app?: string; view?: string };\n if (!r.url) return null;\n return {\n url: r.url,\n label: `Open ${r.app ?? \"app\"}`,\n view: r.view,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// ask_app\n// ---------------------------------------------------------------------------\n\nfunction askAppTool(config: MCPConfig): ActionEntry {\n return {\n tool: tool(\n \"Send a natural-language message to an app's AI agent and get its \" +\n \"response. Use for complex, multi-step tasks needing the agent's \" +\n \"reasoning and full app context. In a single-app project the 'app' \" +\n \"param is optional (defaults to this app). When 'app' names a \" +\n \"different workspace app it is routed there over A2A; the result's \" +\n \"'routedVia' field reports whether it ran cross-app or locally.\",\n {\n app: {\n type: \"string\",\n description: \"App id to route to (optional in a single-app project)\",\n },\n message: {\n type: \"string\",\n description: \"The message to send to the app's agent\",\n },\n },\n [\"message\"],\n ),\n run: async (args: Record<string, any>) => {\n const message = String(args.message ?? \"\").trim();\n if (!message) throw new Error(\"ask_app requires a 'message'.\");\n const requestedApp = String(args.app ?? \"\").trim();\n const selfId = currentAppId(config);\n\n // Cross-app: the caller named a *different* workspace app. Route the\n // message to THAT app's agent over A2A (its `/_agent-native/a2a`\n // endpoint runs the real agent loop with JWT identity) rather than\n // silently answering from this app's agent and claiming delegation.\n const targetApp = await resolveTargetAppOrigin(config, requestedApp);\n if (targetApp) {\n try {\n const { callAgent } = await import(\"../a2a/client.js\");\n const { getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n // The MCP handler runs inside `runWithRequestContext`, so this is\n // the verified caller's email — it lets `callAgent` mint a signed\n // A2A JWT so the target app honours per-user scope.\n const response = await callAgent(targetApp.origin, message, {\n userEmail: getRequestUserEmail(),\n // Bound the wait — cross-app A2A polls async by default.\n timeoutMs: 5 * 60_000,\n });\n return {\n app: targetApp.id,\n routedVia: \"a2a\",\n response,\n };\n } catch (err: any) {\n // Be honest: routing was attempted and failed — do NOT fall back to\n // this app's agent and pretend it was the target.\n throw new Error(\n `Failed to route ask_app to \"${targetApp.id}\" via A2A: ` +\n `${err?.message ?? err}`,\n );\n }\n }\n\n // Same app (or no workspace / unknown target): answer locally with this\n // app's own ask-agent handler — the same entry point the HTTP MCP mount\n // + A2A use, so there is no second agent runner.\n if (!config.askAgent) {\n throw new Error(\n \"This app does not expose an agent (no ask-agent handler).\",\n );\n }\n\n // If the caller named an app we couldn't route to (unknown id, or no\n // workspace), say so honestly instead of claiming we reached it.\n const unresolved =\n !!requestedApp && requestedApp.toLowerCase() !== selfId;\n const response = await config.askAgent(message);\n return {\n app: selfId,\n routedVia: \"local\",\n ...(unresolved\n ? {\n note:\n `Requested app \"${requestedApp}\" is not a reachable workspace ` +\n `app; answered with this app (\"${selfId}\") instead.`,\n }\n : {}),\n response,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// list_templates\n// ---------------------------------------------------------------------------\n\nfunction listTemplatesTool(): ActionEntry {\n return {\n tool: tool(\n \"List the first-party templates that can be scaffolded into a workspace \" +\n \"(allow-listed templates only).\",\n ),\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const { visibleTemplates } = await import(\"../cli/templates-meta.js\");\n return {\n templates: visibleTemplates().map((t) => ({\n name: t.name,\n label: t.label,\n hint: t.hint,\n })),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// create_workspace_app\n// ---------------------------------------------------------------------------\n\nfunction createWorkspaceAppTool(): ActionEntry {\n return {\n tool: tool(\n \"Scaffold a new app into the current workspace from an allow-listed \" +\n \"template, then return a deep link to open it. Idempotent: if an app \" +\n \"with that name already exists it is reused. After calling, surface \" +\n 'the returned \"Open … →\" link to the user.',\n {\n name: {\n type: \"string\",\n description: \"New app id (directory under apps/), e.g. 'mymail'\",\n },\n template: {\n type: \"string\",\n description:\n \"Template to scaffold from — must be allow-listed (see list_templates)\",\n },\n },\n [\"name\", \"template\"],\n ),\n run: async (args: Record<string, any>) => {\n const name = String(args.name ?? \"\").trim();\n const template = String(args.template ?? \"\").trim();\n if (!name || !template) {\n throw new Error(\n \"create_workspace_app requires both 'name' and 'template'.\",\n );\n }\n\n // Enforce the strict public template allow-list. The authoritative,\n // dependency-free source inside @agent-native/core is cli/templates-meta\n // (kept in sync with packages/shared-app-config/templates.ts; CI guard).\n const { visibleTemplates } = await import(\"../cli/templates-meta.js\");\n const allowed = new Set(visibleTemplates().map((t) => t.name));\n if (!allowed.has(template)) {\n throw new Error(\n `Template \"${template}\" is not allow-listed. Allowed: ${[...allowed]\n .sort()\n .join(\", \")}`,\n );\n }\n\n const { findWorkspaceRoot, resolveWorkspace } =\n await import(\"./workspace-resolve.js\");\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n const root = findWorkspaceRoot(process.cwd());\n if (!root) {\n throw new Error(\n \"Not inside a workspace. create_workspace_app only works in a \" +\n \"multi-app workspace (run from the workspace root).\",\n );\n }\n\n const appDir = path.join(root, \"apps\", name);\n const alreadyExisted = fs.existsSync(appDir);\n\n if (!alreadyExisted) {\n // Reuse the CLI scaffolder directly (no second `agent-native`\n // subprocess). `addAppToWorkspace(name, { template })` takes the\n // non-interactive single-template path when name + one template are\n // given. Run it from the workspace root so detectWorkspace resolves.\n const prevCwd = process.cwd();\n try {\n process.chdir(root);\n const { addAppToWorkspace } = await import(\"../cli/create.js\");\n await addAppToWorkspace(name, { template, noInstall: true });\n } finally {\n try {\n process.chdir(prevCwd);\n } catch {\n // best-effort cwd restore\n }\n }\n }\n\n // The workspace gateway auto-detects new apps/* dirs (fs.watch +\n // 2s sync) and lazily boots the dev server on first request, so we\n // don't spawn vite ourselves — opening the deep link warms it. Resolve\n // the port the gateway will use so we can report it.\n const ws = await resolveWorkspace(root);\n const appInfo = ws.apps.find((a) => a.id === name);\n const port = appInfo?.port;\n // The scaffolded app is always a *different* app from the host MCP\n // server, so anchor the deep link to the new app's own origin. A\n // relative path would otherwise be prefixed with the current request\n // origin and land on the wrong app. Fall back to the relative path\n // only if the gateway hasn't reported the new app's URL yet.\n const relDeepLink = buildDeepLink({ app: name, view: \"home\" });\n const deepLink = appInfo?.url\n ? `${appInfo.url.replace(/\\/+$/, \"\")}${relDeepLink}`\n : relDeepLink;\n\n return {\n name,\n template,\n created: !alreadyExisted,\n reused: alreadyExisted,\n port,\n url: appInfo?.url,\n gatewayUrl: ws.gatewayUrl,\n deepLink,\n };\n },\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { deepLink?: string; name?: string };\n if (!r.deepLink) return null;\n return {\n url: r.deepLink,\n label: `Open ${r.name ?? \"app\"}`,\n view: \"home\",\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/**\n * Build the generic cross-app builtin tool registry. Called by\n * `createMCPServerForRequest`; the result is merged UNDER the config's\n * actions so template actions of the same name win.\n */\nexport function getBuiltinCrossAppTools(\n config: MCPConfig,\n): Record<string, ActionEntry> {\n return {\n list_apps: listAppsTool(),\n open_app: openAppTool(config),\n ask_app: askAppTool(config),\n create_workspace_app: createWorkspaceAppTool(),\n list_templates: listTemplatesTool(),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"builtin-tools.js","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic cross-app MCP tools — a stable verb set every external agent gets\n * regardless of which template it is talking to.\n *\n * These are merged into the MCP action registry by\n * `createMCPServerForRequest` (see `build-server.ts`). **Precedence: template\n * actions win.** If a template defines an action named `list_apps` /\n * `open_app` / `ask_app` / `create_workspace_app` / `list_templates`, the\n * template's `ActionEntry` overwrites the builtin of the same name. This is\n * the same template-over-framework precedence `autoDiscoverActions` uses.\n *\n * | Tool | Side effects | Returns |\n * | --------------------- | ------------ | ---------------------------------------- |\n * | `list_apps` | none | `{ apps: [{ id, url, running }] }` |\n * | `open_app` | none | `{ url }` (+ deep-link `link`) |\n * | `ask_app` | agent loop | `{ app, routedVia, response }` |\n * | `create_workspace_app`| scaffolds | `{ name, url, port, deepLink }` (+ link) |\n *\n * `open_app` / `create_workspace_app` return an **absolute** URL on the\n * *target* app's origin when it differs from this app (so a workspace link\n * lands in the right app), and a relative path for the same app / standalone.\n * `ask_app` routes to a *different* workspace app over A2A when possible and\n * reports `routedVia: \"a2a\"`; otherwise it answers locally\n * (`routedVia: \"local\"`) and never falsely claims cross-app delegation.\n * | `list_templates` | none | `{ templates: [...] }` (allow-list only) |\n *\n * Node-only at call time (workspace resolution + scaffolding use `fs`), but\n * the module has no top-level Node imports so it bundles fine alongside\n * `mountMCP` — the Node bits are dynamically imported inside `run()`.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { buildDeepLink } from \"../server/deep-link.js\";\nimport type { MCPConfig } from \"./build-server.js\";\nimport { fetchOrgApps, type OrgApp } from \"./org-directory.js\";\n\nimport type { ActionTool } from \"../agent/types.js\";\n\n/** Flat map of param name → JSON-schema property. */\ntype Params = Record<\n string,\n { type: string; description?: string; enum?: string[] }\n>;\n\n/**\n * Build an `ActionTool`. `parameters` is wrapped in the\n * `{ type:\"object\", properties, required }` shape `createMCPServerForRequest`\n * forwards verbatim as the MCP tool `inputSchema`.\n */\nfunction tool(\n description: string,\n parameters?: Params,\n required?: string[],\n): ActionTool {\n if (!parameters) return { description };\n return {\n description,\n parameters: {\n type: \"object\",\n properties: parameters,\n ...(required && required.length ? { required } : {}),\n },\n };\n}\n\n/**\n * The canonical app id this MCP server is mounted for. `MCPConfig.appId` is\n * authoritative; fall back to lowercasing `name` (which is the capitalized\n * app id at every call site) for back-compat with configs that predate the\n * `appId` field.\n */\nfunction currentAppId(config: MCPConfig): string {\n return (config.appId || config.name || \"app\").toLowerCase();\n}\n\n/**\n * Resolve the absolute origin of a *target* workspace app (e.g.\n * `http://127.0.0.1:8101`) so cross-app deep links / A2A calls point at the\n * right app instead of the current request's origin. Reuses the same\n * workspace resolution `list_apps` / the stdio proxy use.\n *\n * Returns `null` when:\n * - the target is the current app (caller should keep relative behavior),\n * - there is no workspace info (standalone / single app), or\n * - the target app is unknown.\n */\nasync function resolveTargetAppOrigin(\n config: MCPConfig,\n targetAppId: string,\n): Promise<{ origin: string; id: string } | null> {\n const target = targetAppId.trim().toLowerCase();\n if (!target || target === currentAppId(config)) return null;\n try {\n const { resolveWorkspace } = await import(\"./workspace-resolve.js\");\n const ws = await resolveWorkspace();\n if (!ws.isWorkspace) return null;\n const match = ws.apps.find((a) => a.id.toLowerCase() === target);\n if (!match) return null;\n return { origin: match.url, id: match.id };\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// list_apps\n// ---------------------------------------------------------------------------\n\nfunction listAppsTool(config: MCPConfig): ActionEntry {\n return {\n tool: tool(\n \"List the workspace apps and their URLs. Use this to discover which \" +\n \"apps exist before opening or asking one. In a single-app project \" +\n \"this returns just that app. When an org directory is configured \" +\n \"this also includes the org's deployed sibling apps.\",\n ),\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const { resolveWorkspace } = await import(\"./workspace-resolve.js\");\n const ws = await resolveWorkspace();\n\n interface AppEntry {\n id: string;\n url: string;\n port: number | undefined;\n running: boolean;\n source: \"workspace\" | \"org-directory\";\n }\n\n const apps: AppEntry[] = ws.apps.map((a) => ({\n id: a.id,\n url: a.url,\n port: a.port as number | undefined,\n running: a.running,\n source: \"workspace\",\n }));\n const seenIds = new Set(apps.map((a) => a.id.toLowerCase()));\n const seenOrigins = new Set(apps.map((a) => a.url.replace(/\\/+$/, \"\")));\n\n // Merge the org directory's deployed sibling apps. Inactive (no env)\n // or any failure ⇒ fetchOrgApps() returns [] and this is a no-op, so\n // the existing local/workspace behavior is preserved exactly.\n const orgApps = await fetchOrgApps({\n selfId: currentAppId(config),\n }).catch(() => [] as OrgApp[]);\n for (const oa of orgApps) {\n const idKey = oa.id.toLowerCase();\n const originKey = oa.url.replace(/\\/+$/, \"\");\n // Dedupe by id OR origin — a workspace app already listed wins.\n if (seenIds.has(idKey) || seenOrigins.has(originKey)) continue;\n seenIds.add(idKey);\n seenOrigins.add(originKey);\n apps.push({\n id: oa.id,\n url: oa.url,\n port: undefined,\n running: true,\n source: \"org-directory\",\n });\n }\n\n return {\n workspace: ws.isWorkspace,\n gatewayUrl: ws.gatewayUrl,\n apps,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// open_app\n// ---------------------------------------------------------------------------\n\nfunction openAppTool(config: MCPConfig): ActionEntry {\n return {\n tool: tool(\n \"Build a deep link that opens an app at a specific view/record. No side \" +\n \"effects — returns a URL the user can click to land in the running UI. \" +\n 'After calling, surface the returned \"Open in … →\" link to the user.',\n {\n app: { type: \"string\", description: \"App id, e.g. 'mail'\" },\n view: {\n type: \"string\",\n description: \"Target view, e.g. 'inbox' (maps to navigate command)\",\n },\n params: {\n type: \"object\",\n description:\n \"Optional record-focus / filter params, e.g. { threadId: 'abc' }\",\n },\n },\n [\"app\", \"view\"],\n ),\n readOnly: true,\n parallelSafe: true,\n run: async (args: Record<string, any>) => {\n const app = String(args.app ?? \"\").trim();\n const view = String(args.view ?? \"\").trim();\n if (!app || !view) {\n throw new Error(\"open_app requires both 'app' and 'view'.\");\n }\n let params: Record<string, string | number | boolean> | undefined;\n const raw = args.params;\n if (raw && typeof raw === \"object\") {\n params = raw as Record<string, string | number | boolean>;\n } else if (typeof raw === \"string\" && raw.trim()) {\n try {\n params = JSON.parse(raw);\n } catch {\n params = undefined;\n }\n }\n const relUrl = buildDeepLink({ app, view, params });\n\n // Cross-app target in a workspace: resolve the TARGET app's origin and\n // return an absolute URL. Otherwise the MCP layer would prefix the\n // relative path with the CURRENT request origin, landing the user in\n // the wrong app (e.g. open_app({app:\"calendar\"}) served from Mail).\n // Same-app / standalone keeps the relative path (current behavior).\n const targetApp = await resolveTargetAppOrigin(config, app);\n const url = targetApp\n ? `${targetApp.origin.replace(/\\/+$/, \"\")}${relUrl}`\n : relUrl;\n\n return { app, view, url };\n },\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { url?: string; app?: string; view?: string };\n if (!r.url) return null;\n return {\n url: r.url,\n label: `Open ${r.app ?? \"app\"}`,\n view: r.view,\n };\n },\n };\n}\n\n/**\n * Route an `ask_app` message to a *different* app's agent over A2A. Shared by\n * the workspace-resolved path and the org-directory-resolved path so the A2A\n * call logic is not duplicated. `origin` is the target app's A2A base\n * (workspace dev origin or the directory's `a2aUrl`); `id` is reported back.\n *\n * Throws on failure so the caller can be honest — it never falls back to this\n * app's agent and pretends it was the target.\n */\nasync function routeAskOverA2A(\n origin: string,\n id: string,\n message: string,\n): Promise<{ app: string; routedVia: \"a2a\"; response: string }> {\n const { callAgent } = await import(\"../a2a/client.js\");\n const { resolveA2ACallerAuth } = await import(\"../a2a/caller-auth.js\");\n // The MCP handler runs inside `runWithRequestContext`, so this is the\n // verified caller identity and org scope. Reuse the same auth resolver as\n // org-directory discovery so the directory lookup and actual A2A call are\n // scoped the same way.\n const auth = await resolveA2ACallerAuth();\n const response = await callAgent(origin, message, {\n apiKey: auth.apiKey,\n userEmail: auth.userEmail,\n orgDomain: auth.orgDomain,\n orgSecret: auth.orgSecret,\n // Bound the wait — cross-app A2A polls async by default.\n timeoutMs: 5 * 60_000,\n });\n return { app: id, routedVia: \"a2a\", response };\n}\n\n// ---------------------------------------------------------------------------\n// ask_app\n// ---------------------------------------------------------------------------\n\nfunction askAppTool(config: MCPConfig): ActionEntry {\n return {\n tool: tool(\n \"Send a natural-language message to an app's AI agent and get its \" +\n \"response. Use for complex, multi-step tasks needing the agent's \" +\n \"reasoning and full app context. In a single-app project the 'app' \" +\n \"param is optional (defaults to this app). When 'app' names a \" +\n \"different workspace app it is routed there over A2A; the result's \" +\n \"'routedVia' field reports whether it ran cross-app or locally.\",\n {\n app: {\n type: \"string\",\n description: \"App id to route to (optional in a single-app project)\",\n },\n message: {\n type: \"string\",\n description: \"The message to send to the app's agent\",\n },\n },\n [\"message\"],\n ),\n run: async (args: Record<string, any>) => {\n const message = String(args.message ?? \"\").trim();\n if (!message) throw new Error(\"ask_app requires a 'message'.\");\n const requestedApp = String(args.app ?? \"\").trim();\n const selfId = currentAppId(config);\n\n // Cross-app: the caller named a *different* workspace app. Route the\n // message to THAT app's agent over A2A (its `/_agent-native/a2a`\n // endpoint runs the real agent loop with JWT identity) rather than\n // silently answering from this app's agent and claiming delegation.\n const targetApp = await resolveTargetAppOrigin(config, requestedApp);\n if (targetApp) {\n try {\n return await routeAskOverA2A(targetApp.origin, targetApp.id, message);\n } catch (err: any) {\n // Be honest: routing was attempted and failed — do NOT fall back to\n // this app's agent and pretend it was the target.\n throw new Error(\n `Failed to route ask_app to \"${targetApp.id}\" via A2A: ` +\n `${err?.message ?? err}`,\n );\n }\n }\n\n // Not a known local/workspace app — try the org directory. When a\n // directory is configured and the requested app is one of the org's\n // deployed sibling apps, route to it over A2A (same path as above,\n // against its `a2aUrl`). Inactive directory / any failure ⇒ orgApps is\n // [] and this is skipped, preserving the exact local-only behavior.\n if (requestedApp && requestedApp.toLowerCase() !== selfId) {\n const orgApps = await fetchOrgApps({ selfId }).catch(\n () => [] as OrgApp[],\n );\n const dirMatch = orgApps.find(\n (a) => a.id === requestedApp.toLowerCase(),\n );\n if (dirMatch) {\n try {\n return await routeAskOverA2A(dirMatch.a2aUrl, dirMatch.id, message);\n } catch (err: any) {\n throw new Error(\n `Failed to route ask_app to \"${dirMatch.id}\" via A2A ` +\n `(org directory): ${err?.message ?? err}`,\n );\n }\n }\n }\n\n // Same app (or no workspace / unknown target): answer locally with this\n // app's own ask-agent handler — the same entry point the HTTP MCP mount\n // + A2A use, so there is no second agent runner.\n if (!config.askAgent) {\n throw new Error(\n \"This app does not expose an agent (no ask-agent handler).\",\n );\n }\n\n // If the caller named an app we couldn't route to (unknown id, or no\n // workspace), say so honestly instead of claiming we reached it.\n const unresolved =\n !!requestedApp && requestedApp.toLowerCase() !== selfId;\n const response = await config.askAgent(message);\n return {\n app: selfId,\n routedVia: \"local\",\n ...(unresolved\n ? {\n note:\n `Requested app \"${requestedApp}\" is not a reachable workspace ` +\n `app; answered with this app (\"${selfId}\") instead.`,\n }\n : {}),\n response,\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// list_templates\n// ---------------------------------------------------------------------------\n\nfunction listTemplatesTool(): ActionEntry {\n return {\n tool: tool(\n \"List the first-party templates that can be scaffolded into a workspace \" +\n \"(allow-listed templates only).\",\n ),\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const { visibleTemplates } = await import(\"../cli/templates-meta.js\");\n return {\n templates: visibleTemplates().map((t) => ({\n name: t.name,\n label: t.label,\n hint: t.hint,\n })),\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// create_workspace_app\n// ---------------------------------------------------------------------------\n\nfunction createWorkspaceAppTool(): ActionEntry {\n return {\n tool: tool(\n \"Scaffold a new app into the current workspace from an allow-listed \" +\n \"template, then return a deep link to open it. Idempotent: if an app \" +\n \"with that name already exists it is reused. After calling, surface \" +\n 'the returned \"Open … →\" link to the user.',\n {\n name: {\n type: \"string\",\n description: \"New app id (directory under apps/), e.g. 'mymail'\",\n },\n template: {\n type: \"string\",\n description:\n \"Template to scaffold from — must be allow-listed (see list_templates)\",\n },\n },\n [\"name\", \"template\"],\n ),\n run: async (args: Record<string, any>) => {\n const name = String(args.name ?? \"\").trim();\n const template = String(args.template ?? \"\").trim();\n if (!name || !template) {\n throw new Error(\n \"create_workspace_app requires both 'name' and 'template'.\",\n );\n }\n\n // Enforce the strict public template allow-list. The authoritative,\n // dependency-free source inside @agent-native/core is cli/templates-meta\n // (kept in sync with packages/shared-app-config/templates.ts; CI guard).\n const { visibleTemplates } = await import(\"../cli/templates-meta.js\");\n const allowed = new Set(visibleTemplates().map((t) => t.name));\n if (!allowed.has(template)) {\n throw new Error(\n `Template \"${template}\" is not allow-listed. Allowed: ${[...allowed]\n .sort()\n .join(\", \")}`,\n );\n }\n\n const { findWorkspaceRoot, resolveWorkspace } =\n await import(\"./workspace-resolve.js\");\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n const root = findWorkspaceRoot(process.cwd());\n if (!root) {\n throw new Error(\n \"Not inside a workspace. create_workspace_app only works in a \" +\n \"multi-app workspace (run from the workspace root).\",\n );\n }\n\n const appDir = path.join(root, \"apps\", name);\n const alreadyExisted = fs.existsSync(appDir);\n\n if (!alreadyExisted) {\n // Reuse the CLI scaffolder directly (no second `agent-native`\n // subprocess). `addAppToWorkspace(name, { template })` takes the\n // non-interactive single-template path when name + one template are\n // given. Run it from the workspace root so detectWorkspace resolves.\n const prevCwd = process.cwd();\n try {\n process.chdir(root);\n const { addAppToWorkspace } = await import(\"../cli/create.js\");\n await addAppToWorkspace(name, { template, noInstall: true });\n } finally {\n try {\n process.chdir(prevCwd);\n } catch {\n // best-effort cwd restore\n }\n }\n }\n\n // The workspace gateway auto-detects new apps/* dirs (fs.watch +\n // 2s sync) and lazily boots the dev server on first request, so we\n // don't spawn vite ourselves — opening the deep link warms it. Resolve\n // the port the gateway will use so we can report it.\n const ws = await resolveWorkspace(root);\n const appInfo = ws.apps.find((a) => a.id === name);\n const port = appInfo?.port;\n // The scaffolded app is always a *different* app from the host MCP\n // server, so anchor the deep link to the new app's own origin. A\n // relative path would otherwise be prefixed with the current request\n // origin and land on the wrong app. Fall back to the relative path\n // only if the gateway hasn't reported the new app's URL yet.\n const relDeepLink = buildDeepLink({ app: name, view: \"home\" });\n const deepLink = appInfo?.url\n ? `${appInfo.url.replace(/\\/+$/, \"\")}${relDeepLink}`\n : relDeepLink;\n\n return {\n name,\n template,\n created: !alreadyExisted,\n reused: alreadyExisted,\n port,\n url: appInfo?.url,\n gatewayUrl: ws.gatewayUrl,\n deepLink,\n };\n },\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { deepLink?: string; name?: string };\n if (!r.deepLink) return null;\n return {\n url: r.deepLink,\n label: `Open ${r.name ?? \"app\"}`,\n view: \"home\",\n };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/**\n * Build the generic cross-app builtin tool registry. Called by\n * `createMCPServerForRequest`; the result is merged UNDER the config's\n * actions so template actions of the same name win.\n */\nexport function getBuiltinCrossAppTools(\n config: MCPConfig,\n): Record<string, ActionEntry> {\n return {\n list_apps: listAppsTool(config),\n open_app: openAppTool(config),\n ask_app: askAppTool(config),\n create_workspace_app: createWorkspaceAppTool(),\n list_templates: listTemplatesTool(),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-route.d.ts","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AA+BlC,MAAM,WAAW,sBAAsB;IACrC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"connect-route.d.ts","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AA+BlC,MAAM,WAAW,sBAAsB;IACrC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAufD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,QAAQ,CAAC,CAoPnB"}
|
|
@@ -176,71 +176,145 @@ function renderConnectPage(params) {
|
|
|
176
176
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
177
177
|
:root {
|
|
178
178
|
color-scheme: dark;
|
|
179
|
-
--bg: #
|
|
180
|
-
--
|
|
181
|
-
--
|
|
179
|
+
--bg: #08080a; --panel: #131316; --panel-2: #0d0d10;
|
|
180
|
+
--border: rgba(255,255,255,0.08); --border-strong: rgba(255,255,255,0.14);
|
|
181
|
+
--text: #fafafa; --muted: #a1a1aa; --subtle: #71717a;
|
|
182
|
+
--accent: #fafafa; --accent-fg: #09090b;
|
|
183
|
+
--ring: rgba(250,250,250,0.55);
|
|
182
184
|
--error: #fca5a5; --error-bg: rgba(127,29,29,0.18);
|
|
183
185
|
--ok: #86efac; --ok-bg: rgba(20,83,45,0.2);
|
|
184
186
|
}
|
|
187
|
+
html, body { -webkit-font-smoothing: antialiased; }
|
|
185
188
|
body {
|
|
186
189
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
187
|
-
background:
|
|
190
|
+
background:
|
|
191
|
+
radial-gradient(900px 540px at 50% -14%, rgba(255,255,255,0.05), transparent 60%),
|
|
192
|
+
linear-gradient(180deg, #121215 0%, var(--bg) 56%);
|
|
188
193
|
color: var(--text); display: flex; align-items: center;
|
|
189
|
-
justify-content: center; min-height: 100vh; padding: 1rem;
|
|
194
|
+
justify-content: center; min-height: 100vh; padding: 1.5rem 1rem;
|
|
190
195
|
}
|
|
191
196
|
.card {
|
|
192
|
-
width: 100%; max-width:
|
|
197
|
+
width: 100%; max-width: 420px;
|
|
193
198
|
background: var(--panel); border: 1px solid var(--border);
|
|
194
|
-
border-radius:
|
|
199
|
+
border-radius: 18px; box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,
|
|
200
|
+
0 30px 90px rgba(0,0,0,0.5);
|
|
201
|
+
padding: 2.25rem 2rem 1.75rem;
|
|
195
202
|
}
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
|
|
203
|
+
/* App-to-app glyph */
|
|
204
|
+
.glyph {
|
|
205
|
+
display: flex; align-items: center; justify-content: center;
|
|
206
|
+
gap: 0; margin: 0 auto 1.5rem; width: fit-content;
|
|
207
|
+
}
|
|
208
|
+
.glyph .tile {
|
|
209
|
+
width: 52px; height: 52px; border-radius: 14px;
|
|
210
|
+
display: flex; align-items: center; justify-content: center;
|
|
211
|
+
background: var(--panel-2); border: 1px solid var(--border-strong);
|
|
212
|
+
color: var(--text); flex-shrink: 0;
|
|
213
|
+
}
|
|
214
|
+
.glyph .tile svg { display: block; }
|
|
215
|
+
.glyph .conn {
|
|
216
|
+
width: 38px; height: 2px; flex-shrink: 0;
|
|
217
|
+
background-image: radial-gradient(circle, var(--subtle) 1px, transparent 1.4px);
|
|
218
|
+
background-size: 7px 2px; background-repeat: repeat-x;
|
|
219
|
+
background-position: center;
|
|
220
|
+
}
|
|
221
|
+
.eyebrow {
|
|
222
|
+
text-align: center; font-size: 0.72rem; font-weight: 600;
|
|
223
|
+
letter-spacing: 0.08em; text-transform: uppercase;
|
|
224
|
+
color: var(--subtle); margin-bottom: 0.5rem;
|
|
225
|
+
}
|
|
226
|
+
h1 {
|
|
227
|
+
text-align: center; font-size: 1.4rem; font-weight: 680;
|
|
228
|
+
line-height: 1.25; margin-bottom: 0.55rem;
|
|
229
|
+
letter-spacing: -0.01em;
|
|
230
|
+
}
|
|
231
|
+
.sub {
|
|
232
|
+
text-align: center; color: var(--muted); font-size: 0.9rem;
|
|
233
|
+
line-height: 1.5; margin: 0 auto 0.85rem; max-width: 34ch;
|
|
234
|
+
}
|
|
235
|
+
.identity {
|
|
236
|
+
text-align: center; color: var(--subtle); font-size: 0.78rem;
|
|
237
|
+
margin-bottom: 1.5rem;
|
|
238
|
+
}
|
|
239
|
+
.identity strong { color: var(--muted); font-weight: 600; }
|
|
199
240
|
.code-callout {
|
|
200
|
-
border: 1px solid var(--border); border-radius:
|
|
201
|
-
margin-bottom: 1.25rem;
|
|
241
|
+
border: 1px solid var(--border-strong); border-radius: 12px;
|
|
242
|
+
padding: 0.85rem 1rem; margin-bottom: 1.25rem;
|
|
243
|
+
background: var(--panel-2); text-align: center;
|
|
202
244
|
}
|
|
203
|
-
.code-callout .label { font-size: 0.
|
|
204
|
-
text-transform: uppercase; letter-spacing: 0.
|
|
205
|
-
.code-callout .value { font-size: 1.
|
|
245
|
+
.code-callout .label { font-size: 0.68rem; color: var(--subtle);
|
|
246
|
+
text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.4rem; }
|
|
247
|
+
.code-callout .value { font-size: 1.6rem; font-weight: 700;
|
|
206
248
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
207
|
-
letter-spacing: 0.
|
|
249
|
+
letter-spacing: 0.14em; color: var(--text); }
|
|
208
250
|
button {
|
|
209
251
|
cursor: pointer; font: inherit; font-weight: 600; border: none;
|
|
210
|
-
border-radius:
|
|
252
|
+
border-radius: 10px; padding: 0.8rem 1.1rem;
|
|
211
253
|
}
|
|
212
|
-
|
|
213
|
-
.primary
|
|
254
|
+
button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
|
|
255
|
+
.primary {
|
|
256
|
+
background: var(--accent); color: var(--accent-fg); width: 100%;
|
|
257
|
+
font-size: 0.95rem;
|
|
258
|
+
}
|
|
259
|
+
.primary:hover:not(:disabled) { background: #e4e4e7; }
|
|
260
|
+
.primary:disabled { opacity: 0.55; cursor: default; }
|
|
214
261
|
.ghost {
|
|
215
262
|
background: transparent; color: var(--muted);
|
|
216
|
-
border: 1px solid var(--border); padding: 0.35rem 0.7rem;
|
|
217
|
-
font-size: 0.78rem; font-weight: 500;
|
|
263
|
+
border: 1px solid var(--border-strong); padding: 0.35rem 0.7rem;
|
|
264
|
+
font-size: 0.78rem; font-weight: 500; border-radius: 8px;
|
|
218
265
|
}
|
|
266
|
+
.ghost:hover:not(:disabled) { color: var(--text); border-color: var(--subtle); }
|
|
219
267
|
pre {
|
|
220
|
-
background:
|
|
268
|
+
background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px;
|
|
221
269
|
padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;
|
|
222
270
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
223
271
|
color: #d4d4d8; margin: 0.5rem 0 1rem;
|
|
224
272
|
}
|
|
225
|
-
|
|
226
|
-
.
|
|
273
|
+
/* Advanced disclosure */
|
|
274
|
+
.advanced { margin: 0 0 1rem; }
|
|
275
|
+
.advanced > summary {
|
|
276
|
+
list-style: none; cursor: pointer; user-select: none;
|
|
277
|
+
display: flex; align-items: center; justify-content: center; gap: 0.35rem;
|
|
278
|
+
color: var(--subtle); font-size: 0.8rem; font-weight: 500;
|
|
279
|
+
padding: 0.5rem 0; text-align: center;
|
|
280
|
+
}
|
|
281
|
+
.advanced > summary::-webkit-details-marker { display: none; }
|
|
282
|
+
.advanced > summary:hover { color: var(--muted); }
|
|
283
|
+
.advanced > summary:focus-visible { outline: 2px solid var(--ring);
|
|
284
|
+
outline-offset: 2px; border-radius: 6px; }
|
|
285
|
+
.advanced > summary .chev {
|
|
286
|
+
width: 14px; height: 14px; transition: transform 0.15s ease;
|
|
287
|
+
}
|
|
288
|
+
.advanced[open] > summary .chev { transform: rotate(180deg); }
|
|
289
|
+
.advanced-body {
|
|
290
|
+
padding: 0.85rem 0.1rem 0.25rem;
|
|
291
|
+
}
|
|
292
|
+
.field { margin-bottom: 0.9rem; }
|
|
293
|
+
.field:last-child { margin-bottom: 0; }
|
|
294
|
+
.field label { display: block; font-size: 0.78rem; color: var(--muted);
|
|
227
295
|
margin-bottom: 0.35rem; }
|
|
228
296
|
.field input {
|
|
229
|
-
width: 100%; padding: 0.
|
|
230
|
-
background:
|
|
297
|
+
width: 100%; padding: 0.6rem 0.7rem; font: inherit; color: var(--text);
|
|
298
|
+
background: var(--panel-2); border: 1px solid var(--border-strong);
|
|
299
|
+
border-radius: 8px;
|
|
300
|
+
}
|
|
301
|
+
.field input:focus-visible {
|
|
302
|
+
outline: none; border-color: var(--ring);
|
|
303
|
+
box-shadow: 0 0 0 3px rgba(250,250,250,0.12);
|
|
231
304
|
}
|
|
232
305
|
.inline { display: flex; gap: 0.5rem; }
|
|
233
306
|
.inline input { flex: 1; }
|
|
234
|
-
.tokens { margin-top: 1.
|
|
307
|
+
.tokens { margin-top: 1.5rem; border-top: 1px solid var(--border);
|
|
235
308
|
padding-top: 1.25rem; }
|
|
236
|
-
.tokens h2 { font-size: 0.
|
|
309
|
+
.tokens h2 { font-size: 0.78rem; font-weight: 600; color: var(--muted);
|
|
310
|
+
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.6rem; }
|
|
237
311
|
.tok { display: flex; align-items: center; justify-content: space-between;
|
|
238
|
-
gap: 0.75rem; padding: 0.
|
|
312
|
+
gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border);
|
|
239
313
|
font-size: 0.83rem; }
|
|
240
314
|
.tok:last-child { border-bottom: none; }
|
|
241
|
-
.tok .meta { color: var(--subtle); font-size: 0.74rem; }
|
|
315
|
+
.tok .meta { color: var(--subtle); font-size: 0.74rem; margin-top: 0.1rem; }
|
|
242
316
|
.tok.revoked { opacity: 0.45; }
|
|
243
|
-
.msg { font-size: 0.83rem; padding: 0.6rem 0.8rem; border-radius:
|
|
317
|
+
.msg { font-size: 0.83rem; padding: 0.6rem 0.8rem; border-radius: 8px;
|
|
244
318
|
margin-bottom: 1rem; display: none; }
|
|
245
319
|
.msg.err { display: block; color: var(--error); background: var(--error-bg); }
|
|
246
320
|
.msg.ok { display: block; color: var(--ok); background: var(--ok-bg); }
|
|
@@ -249,9 +323,31 @@ function renderConnectPage(params) {
|
|
|
249
323
|
</head>
|
|
250
324
|
<body>
|
|
251
325
|
<div class="card">
|
|
252
|
-
|
|
253
|
-
<div class="
|
|
254
|
-
|
|
326
|
+
<!-- "Connect an external agent" — kept as an accessible label / consent eyebrow -->
|
|
327
|
+
<div class="glyph" role="img" aria-label="Connect an external agent to ${safeApp}">
|
|
328
|
+
<span class="tile" aria-hidden="true">
|
|
329
|
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none">
|
|
330
|
+
<rect x="3" y="3" width="8" height="8" rx="2.2" fill="currentColor"/>
|
|
331
|
+
<rect x="13" y="3" width="8" height="8" rx="2.2" fill="currentColor" opacity="0.55"/>
|
|
332
|
+
<rect x="3" y="13" width="8" height="8" rx="2.2" fill="currentColor" opacity="0.55"/>
|
|
333
|
+
<rect x="13" y="13" width="8" height="8" rx="2.2" fill="currentColor"/>
|
|
334
|
+
</svg>
|
|
335
|
+
</span>
|
|
336
|
+
<span class="conn" aria-hidden="true"></span>
|
|
337
|
+
<span class="tile" aria-hidden="true">
|
|
338
|
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none"
|
|
339
|
+
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
340
|
+
stroke-linejoin="round">
|
|
341
|
+
<path d="M9 8 L5 12 L9 16"/>
|
|
342
|
+
<path d="M15 8 L19 12 L15 16"/>
|
|
343
|
+
</svg>
|
|
344
|
+
</span>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div class="eyebrow">Connect an external agent</div>
|
|
348
|
+
<h1>Authorize ${safeApp}?</h1>
|
|
349
|
+
<p class="sub">Mint a personal token so a coding agent (Claude Code, Codex, Cowork) can act as you on ${safeApp}.</p>
|
|
350
|
+
<p class="identity">Signed in as <strong>${safeEmail}</strong> · ${safeOrigin}</p>
|
|
255
351
|
|
|
256
352
|
<div id="codeCallout" class="code-callout ${safeUserCode ? "" : "hidden"}">
|
|
257
353
|
<div class="label">Authorizing device code</div>
|
|
@@ -261,15 +357,25 @@ function renderConnectPage(params) {
|
|
|
261
357
|
<div id="msg" class="msg"></div>
|
|
262
358
|
|
|
263
359
|
<div id="mintForm">
|
|
264
|
-
<div class="field">
|
|
265
|
-
<label for="label">Label (optional)</label>
|
|
266
|
-
<input id="label" type="text" placeholder="e.g. Claude Code on my laptop" maxlength="120" />
|
|
267
|
-
</div>
|
|
268
|
-
<div class="field">
|
|
269
|
-
<label for="ttl">Expires in (days, 1–365)</label>
|
|
270
|
-
<input id="ttl" type="number" min="1" max="365" value="${DEFAULT_TOKEN_TTL_DAYS}" />
|
|
271
|
-
</div>
|
|
272
360
|
<button id="authorizeBtn" class="primary">${safeUserCode ? "Authorize device" : "Create connection token"}</button>
|
|
361
|
+
<details class="advanced">
|
|
362
|
+
<summary>
|
|
363
|
+
Advanced options
|
|
364
|
+
<svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
365
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
366
|
+
aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
|
|
367
|
+
</summary>
|
|
368
|
+
<div class="advanced-body">
|
|
369
|
+
<div class="field">
|
|
370
|
+
<label for="label">Label (optional)</label>
|
|
371
|
+
<input id="label" type="text" placeholder="e.g. Claude Code on my laptop" maxlength="120" />
|
|
372
|
+
</div>
|
|
373
|
+
<div class="field">
|
|
374
|
+
<label for="ttl">Expires in (days, 1–365)</label>
|
|
375
|
+
<input id="ttl" type="number" min="1" max="365" value="${DEFAULT_TOKEN_TTL_DAYS}" />
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
</details>
|
|
273
379
|
</div>
|
|
274
380
|
|
|
275
381
|
<div id="result" class="hidden">
|