skybridge 0.0.0-dev.dc1bd07 → 0.0.0-dev.dc576c4
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/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.js +1 -16
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.js +16 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/file-ref.d.ts +20 -0
- package/dist/server/file-ref.js +19 -0
- package/dist/server/file-ref.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/server.d.ts +152 -0
- package/dist/server/server.js +142 -57
- package/dist/server/server.js.map +1 -1
- package/dist/test/view.test.js +45 -0
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/adaptor.d.ts +48 -0
- package/dist/web/bridges/adaptor.js +308 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +205 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +3 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +9 -0
- package/dist/web/bridges/get-adaptor.js +27 -5
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +48 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/host-context-stores.d.ts +27 -0
- package/dist/web/bridges/host-context-stores.js +73 -0
- package/dist/web/bridges/host-context-stores.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.js +1 -3
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +21 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/types.d.ts +62 -0
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.js +4 -3
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +26 -0
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +7 -5
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +33 -0
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +8 -5
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -0
- package/dist/web/generate-helpers.js +2 -0
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +36 -23
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js +55 -20
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.test.js +11 -3
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +10 -2
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +59 -26
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +19 -5
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-request-close.d.ts +14 -0
- package/dist/web/hooks/use-request-close.js +13 -0
- package/dist/web/hooks/use-request-close.js.map +1 -1
- package/dist/web/hooks/use-request-close.test.js +34 -40
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +16 -1
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +10 -0
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +17 -0
- package/dist/web/hooks/use-request-size.js +16 -0
- package/dist/web/hooks/use-request-size.js.map +1 -1
- package/dist/web/hooks/use-request-size.test.js +35 -53
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +28 -8
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +33 -0
- package/dist/web/hooks/use-tool-info.js +26 -0
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +37 -27
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +81 -35
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +6 -2
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/dist/web/mount-view.d.ts +19 -0
- package/dist/web/mount-view.js +19 -0
- package/dist/web/mount-view.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +28 -0
- package/dist/web/plugin/plugin.js +26 -0
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -27
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -112
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -51
- package/dist/web/bridges/mcp-app/adaptor.js +0 -279
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveViewsDir(root: string): Promise<string | undefined>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export async function resolveViewsDir(root) {
|
|
2
|
+
const { loadConfigFromFile } = await import("vite");
|
|
3
|
+
const loaded = await loadConfigFromFile({ command: "build", mode: "production" }, undefined, root);
|
|
4
|
+
const isPluginCandidate = (value) => typeof value === "object" && value !== null;
|
|
5
|
+
const plugins = [];
|
|
6
|
+
const walk = (value) => {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
value.forEach(walk);
|
|
9
|
+
}
|
|
10
|
+
else if (isPluginCandidate(value)) {
|
|
11
|
+
plugins.push(value);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
walk(loaded?.config.plugins ?? []);
|
|
15
|
+
return plugins.find((p) => p.name === "skybridge")?.api?.viewsDir;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=resolve-views-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-views-dir.js","sourceRoot":"","sources":["../../src/cli/resolve-views-dir.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY;IAEZ,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EACxC,SAAS,EACT,IAAI,CACL,CAAC;IAEF,MAAM,iBAAiB,GAAG,CACxB,KAAc,EAC2C,EAAE,CAC3D,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;IAE9C,MAAM,OAAO,GAA0D,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE;QAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACnC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC;AACpE,CAAC","sourcesContent":["export async function resolveViewsDir(\n root: string,\n): Promise<string | undefined> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile(\n { command: \"build\", mode: \"production\" },\n undefined,\n root,\n );\n\n const isPluginCandidate = (\n value: unknown,\n ): value is { name?: string; api?: { viewsDir?: string } } =>\n typeof value === \"object\" && value !== null;\n\n const plugins: Array<{ name?: string; api?: { viewsDir?: string } }> = [];\n const walk = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(walk);\n } else if (isPluginCandidate(value)) {\n plugins.push(value);\n }\n };\n walk(loaded?.config.plugins ?? []);\n return plugins.find((p) => p.name === \"skybridge\")?.api?.viewsDir;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { isAbsolute, relative } from "node:path";
|
|
2
|
+
import spawn from "cross-spawn";
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
// TypeScript nests from general to specific — the deepest line is the root cause.
|
|
5
5
|
function extractBestMessage(message, continuationLines) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-typescript-check.js","sourceRoot":"","sources":["../../src/cli/use-typescript-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"use-typescript-check.js","sourceRoot":"","sources":["../../src/cli/use-typescript-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative } from \"node:path\";\nimport spawn from \"cross-spawn\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype TsError = {\n file: string;\n line: number;\n col: number;\n code: string;\n message: string;\n};\n\n// TypeScript nests from general to specific — the deepest line is the root cause.\nfunction extractBestMessage(\n message: string,\n continuationLines: Array<string>,\n): string {\n if (!continuationLines.length) {\n return message;\n }\n let maxIndent = 0;\n for (const line of continuationLines) {\n const indent = line.length - line.trimStart().length;\n if (indent > maxIndent) {\n maxIndent = indent;\n }\n }\n const deepest = continuationLines\n .filter((l) => l.length - l.trimStart().length === maxIndent)\n .map((l) => l.trim())\n .filter(Boolean)[0];\n return deepest ?? message;\n}\n\nexport function useTypeScriptCheck(): Array<TsError> {\n const tsProcessRef = useRef<ReturnType<typeof spawn> | null>(null);\n const [tsErrors, setTsErrors] = useState<Array<TsError>>([]);\n\n useEffect(() => {\n const tsProcess = spawn(\n \"npx\",\n [\"tsc\", \"--noEmit\", \"--watch\", \"--pretty\", \"false\"],\n {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: true,\n },\n );\n\n tsProcessRef.current = tsProcess;\n\n let outputBuffer = \"\";\n let currentErrors: Array<TsError> = [];\n let pendingError: TsError | null = null;\n let continuationLines: Array<string> = [];\n\n const flushPending = () => {\n if (!pendingError) {\n return;\n }\n pendingError.message = extractBestMessage(\n pendingError.message,\n continuationLines,\n );\n currentErrors.push(pendingError);\n pendingError = null;\n continuationLines = [];\n };\n\n const processOutput = (data: Buffer) => {\n outputBuffer += data.toString();\n const lines = outputBuffer.split(\"\\n\");\n outputBuffer = lines.pop() || \"\";\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n const errorMatch = trimmed.match(\n /^(.+?)\\((\\d+),(\\d+)\\):\\s+error\\s+(TS\\d+)?\\s*:?\\s*(.+)$/,\n );\n if (errorMatch) {\n flushPending();\n const [, file, lineStr, colStr, code, message] = errorMatch;\n if (file && lineStr && colStr && message) {\n let cleanFile = file.trim();\n if (isAbsolute(cleanFile)) {\n cleanFile = relative(process.cwd(), cleanFile);\n }\n pendingError = {\n file: cleanFile,\n line: Number.parseInt(lineStr, 10),\n col: Number.parseInt(colStr, 10),\n code: code ?? \"\",\n message: message.trim(),\n };\n }\n continue;\n }\n\n if (pendingError && line.startsWith(\" \")) {\n continuationLines.push(line);\n continue;\n }\n\n if (trimmed.includes(\"Found\") && trimmed.includes(\"error\")) {\n flushPending();\n setTsErrors(trimmed.match(/Found 0 error/) ? [] : [...currentErrors]);\n currentErrors = [];\n }\n }\n };\n\n if (tsProcess.stdout) {\n tsProcess.stdout.on(\"data\", processOutput);\n }\n if (tsProcess.stderr) {\n tsProcess.stderr.on(\"data\", processOutput);\n }\n\n return () => {\n if (tsProcessRef.current) {\n tsProcessRef.current.kill();\n }\n };\n }, []);\n\n return tsErrors;\n}\n"]}
|
package/dist/commands/build.js
CHANGED
|
@@ -5,24 +5,9 @@ import { Command } from "@oclif/core";
|
|
|
5
5
|
import { Box, render, Text } from "ink";
|
|
6
6
|
import { useEffect } from "react";
|
|
7
7
|
import { Header } from "../cli/header.js";
|
|
8
|
+
import { resolveViewsDir } from "../cli/resolve-views-dir.js";
|
|
8
9
|
import { useExecuteSteps } from "../cli/use-execute-steps.js";
|
|
9
10
|
import { scanAndWriteViewsDts } from "../web/plugin/scan-views.js";
|
|
10
|
-
async function resolveViewsDir(root) {
|
|
11
|
-
const { loadConfigFromFile } = await import("vite");
|
|
12
|
-
const loaded = await loadConfigFromFile({ command: "build", mode: "production" }, undefined, root);
|
|
13
|
-
const isPluginCandidate = (value) => typeof value === "object" && value !== null;
|
|
14
|
-
const plugins = [];
|
|
15
|
-
const walk = (value) => {
|
|
16
|
-
if (Array.isArray(value)) {
|
|
17
|
-
value.forEach(walk);
|
|
18
|
-
}
|
|
19
|
-
else if (isPluginCandidate(value)) {
|
|
20
|
-
plugins.push(value);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
walk(loaded?.config.plugins ?? []);
|
|
24
|
-
return plugins.find((p) => p.name === "skybridge")?.api?.viewsDir;
|
|
25
|
-
}
|
|
26
11
|
export const commandSteps = [
|
|
27
12
|
{
|
|
28
13
|
label: "Scanning views",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { Command } from \"@oclif/core\";\nimport { Box, render, Text } from \"ink\";\nimport { useEffect } from \"react\";\nimport { Header } from \"../cli/header.js\";\nimport { type CommandStep, useExecuteSteps } from \"../cli/use-execute-steps.js\";\nimport { scanAndWriteViewsDts } from \"../web/plugin/scan-views.js\";\n\nasync function resolveViewsDir(root: string): Promise<string | undefined> {\n const { loadConfigFromFile } = await import(\"vite\");\n const loaded = await loadConfigFromFile(\n { command: \"build\", mode: \"production\" },\n undefined,\n root,\n );\n\n const isPluginCandidate = (\n value: unknown,\n ): value is { name?: string; api?: { viewsDir?: string } } =>\n typeof value === \"object\" && value !== null;\n\n const plugins: Array<{ name?: string; api?: { viewsDir?: string } }> = [];\n const walk = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(walk);\n } else if (isPluginCandidate(value)) {\n plugins.push(value);\n }\n };\n walk(loaded?.config.plugins ?? []);\n return plugins.find((p) => p.name === \"skybridge\")?.api?.viewsDir;\n}\n\nexport const commandSteps: CommandStep[] = [\n {\n label: \"Scanning views\",\n run: async () => {\n const root = process.cwd();\n const viewsDir = await resolveViewsDir(root);\n scanAndWriteViewsDts(root, viewsDir);\n },\n },\n {\n label: \"Compiling server\",\n run: () => rmSync(\"dist\", { recursive: true, force: true }),\n command: \"tsc -b --force\",\n },\n {\n label: \"Building views\",\n command: \"vite build\",\n },\n {\n label: \"Emitting manifest module\",\n // Inline the Vite manifest as a JS module so the server can `import` it\n // instead of `readFileSync(process.cwd() + ...)` at runtime — required for\n // workerd, where neither cwd nor the assets directory is readable.\n // The path mirrors `skybridge start`'s entry convention (dist/server.js)\n // so the import in the user's entry resolves to a sibling file.\n run: () => {\n const root = process.cwd();\n const manifest = readFileSync(\n path.join(root, \"dist\", \"assets\", \".vite\", \"manifest.json\"),\n \"utf-8\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"vite-manifest.js\"),\n `export default ${manifest};\\n`,\n );\n },\n },\n\n {\n label: \"Emitting Cloudflare redirects\",\n // Cloudflare's `assets.directory` maps URL → file literally — no\n // mount-strip like `app.use(\"/assets\", express.static(...))`. Rewrite\n // `/assets/assets/*` to `/assets/*` before lookup; status 200 =\n // server-side rewrite, not HTTP redirect.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_redirects\"),\n \"/assets/assets/* /assets/:splat 200\\n\",\n );\n },\n },\n {\n label: \"Emitting Cloudflare headers\",\n // Cloudflare's static asset handler bypasses the worker entirely, so\n // `app.use(\"/assets\", cors())` never fires for asset requests. Attach\n // CORS at the edge so cross-origin view iframes can load JS/CSS.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_headers\"),\n \"/assets/*\\n Access-Control-Allow-Origin: *\\n\",\n );\n },\n },\n];\n\nexport default class Build extends Command {\n static override description = \"Build the views and MCP server\";\n static override examples = [\"skybridge build\"];\n static override flags = {};\n\n public async run(): Promise<void> {\n const App = () => {\n const { currentStep, status, error, execute } =\n useExecuteSteps(commandSteps);\n\n useEffect(() => {\n execute();\n }, [execute]);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Header version={this.config.version}>\n <Text color=\"green\"> → building for production…</Text>\n </Header>\n\n {commandSteps.map((step, index) => {\n const isCurrent = index === currentStep && status === \"running\";\n const isCompleted = index < currentStep || status === \"success\";\n const isError = status === \"error\" && index === currentStep;\n\n return (\n <Box key={step.label} marginBottom={0}>\n <Text color={isError ? \"red\" : isCompleted ? \"green\" : \"grey\"}>\n {isError ? \"✗\" : isCompleted ? \"✓\" : isCurrent ? \"⟳\" : \"○\"}{\" \"}\n {step.label}\n </Text>\n </Box>\n );\n })}\n\n {status === \"success\" && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n ✓ Build completed successfully!\n </Text>\n </Box>\n )}\n\n {status === \"error\" && error && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"red\" bold>\n ✗ Build failed\n </Text>\n <Box marginTop={1} flexDirection=\"column\">\n {error.split(\"\\n\").map((line) => (\n <Text key={line} color=\"red\">\n {line}\n </Text>\n ))}\n </Box>\n </Box>\n )}\n </Box>\n );\n };\n\n render(<App />, {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { Command } from \"@oclif/core\";\nimport { Box, render, Text } from \"ink\";\nimport { useEffect } from \"react\";\nimport { Header } from \"../cli/header.js\";\nimport { resolveViewsDir } from \"../cli/resolve-views-dir.js\";\nimport { type CommandStep, useExecuteSteps } from \"../cli/use-execute-steps.js\";\nimport { scanAndWriteViewsDts } from \"../web/plugin/scan-views.js\";\n\nexport const commandSteps: CommandStep[] = [\n {\n label: \"Scanning views\",\n run: async () => {\n const root = process.cwd();\n const viewsDir = await resolveViewsDir(root);\n scanAndWriteViewsDts(root, viewsDir);\n },\n },\n {\n label: \"Compiling server\",\n run: () => rmSync(\"dist\", { recursive: true, force: true }),\n command: \"tsc -b --force\",\n },\n {\n label: \"Building views\",\n command: \"vite build\",\n },\n {\n label: \"Emitting manifest module\",\n // Inline the Vite manifest as a JS module so the server can `import` it\n // instead of `readFileSync(process.cwd() + ...)` at runtime — required for\n // workerd, where neither cwd nor the assets directory is readable.\n // The path mirrors `skybridge start`'s entry convention (dist/server.js)\n // so the import in the user's entry resolves to a sibling file.\n run: () => {\n const root = process.cwd();\n const manifest = readFileSync(\n path.join(root, \"dist\", \"assets\", \".vite\", \"manifest.json\"),\n \"utf-8\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"vite-manifest.js\"),\n `export default ${manifest};\\n`,\n );\n },\n },\n\n {\n label: \"Emitting Cloudflare redirects\",\n // Cloudflare's `assets.directory` maps URL → file literally — no\n // mount-strip like `app.use(\"/assets\", express.static(...))`. Rewrite\n // `/assets/assets/*` to `/assets/*` before lookup; status 200 =\n // server-side rewrite, not HTTP redirect.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_redirects\"),\n \"/assets/assets/* /assets/:splat 200\\n\",\n );\n },\n },\n {\n label: \"Emitting Cloudflare headers\",\n // Cloudflare's static asset handler bypasses the worker entirely, so\n // `app.use(\"/assets\", cors())` never fires for asset requests. Attach\n // CORS at the edge so cross-origin view iframes can load JS/CSS.\n run: () => {\n const root = process.cwd();\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"_headers\"),\n \"/assets/*\\n Access-Control-Allow-Origin: *\\n\",\n );\n },\n },\n];\n\nexport default class Build extends Command {\n static override description = \"Build the views and MCP server\";\n static override examples = [\"skybridge build\"];\n static override flags = {};\n\n public async run(): Promise<void> {\n const App = () => {\n const { currentStep, status, error, execute } =\n useExecuteSteps(commandSteps);\n\n useEffect(() => {\n execute();\n }, [execute]);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Header version={this.config.version}>\n <Text color=\"green\"> → building for production…</Text>\n </Header>\n\n {commandSteps.map((step, index) => {\n const isCurrent = index === currentStep && status === \"running\";\n const isCompleted = index < currentStep || status === \"success\";\n const isError = status === \"error\" && index === currentStep;\n\n return (\n <Box key={step.label} marginBottom={0}>\n <Text color={isError ? \"red\" : isCompleted ? \"green\" : \"grey\"}>\n {isError ? \"✗\" : isCompleted ? \"✓\" : isCurrent ? \"⟳\" : \"○\"}{\" \"}\n {step.label}\n </Text>\n </Box>\n );\n })}\n\n {status === \"success\" && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n ✓ Build completed successfully!\n </Text>\n </Box>\n )}\n\n {status === \"error\" && error && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"red\" bold>\n ✗ Build failed\n </Text>\n <Box marginTop={1} flexDirection=\"column\">\n {error.split(\"\\n\").map((line) => (\n <Text key={line} color=\"red\">\n {line}\n </Text>\n ))}\n </Box>\n </Box>\n )}\n </Box>\n );\n };\n\n render(<App />, {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n }\n}\n"]}
|
package/dist/commands/dev.js
CHANGED
|
@@ -3,12 +3,14 @@ import { Command, Flags } from "@oclif/core";
|
|
|
3
3
|
import { Box, render, Text } from "ink";
|
|
4
4
|
import { resolvePort } from "../cli/detect-port.js";
|
|
5
5
|
import { Header } from "../cli/header.js";
|
|
6
|
+
import { resolveViewsDir } from "../cli/resolve-views-dir.js";
|
|
6
7
|
import { startTunnelControlServer } from "../cli/tunnel-control-server.js";
|
|
7
8
|
import { useMessages } from "../cli/use-messages.js";
|
|
8
9
|
import { useNodemon } from "../cli/use-nodemon.js";
|
|
9
10
|
import { useOpenBrowser } from "../cli/use-open-browser.js";
|
|
10
11
|
import { useTunnel } from "../cli/use-tunnel.js";
|
|
11
12
|
import { useTypeScriptCheck } from "../cli/use-typescript-check.js";
|
|
13
|
+
import { scanAndWriteViewsDts } from "../web/plugin/scan-views.js";
|
|
12
14
|
export default class Dev extends Command {
|
|
13
15
|
static description = "Start development server";
|
|
14
16
|
static examples = ["skybridge"];
|
|
@@ -35,6 +37,20 @@ export default class Dev extends Command {
|
|
|
35
37
|
};
|
|
36
38
|
async run() {
|
|
37
39
|
const { flags } = await this.parse(Dev);
|
|
40
|
+
// Generate .skybridge/views.d.ts before render() spawns `tsc --noEmit
|
|
41
|
+
// --watch`. Vite's plugin config hook writes the same file when nodemon
|
|
42
|
+
// boots the server, but tsc starts in parallel — if .skybridge/ doesn't
|
|
43
|
+
// exist at tsc startup, its watcher never picks up the late-created file
|
|
44
|
+
// and the dev UI reports phantom TS errors forever.
|
|
45
|
+
const root = process.cwd();
|
|
46
|
+
try {
|
|
47
|
+
scanAndWriteViewsDts(root, await resolveViewsDir(root));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Best-effort: if the scan fails (e.g. broken vite config, duplicate
|
|
51
|
+
// view names) tsc may show phantom errors, but the dev server should
|
|
52
|
+
// still start so the developer can fix the underlying issue.
|
|
53
|
+
}
|
|
38
54
|
const { port, fallback, envWarning } = await resolvePort(flags.port);
|
|
39
55
|
if (envWarning) {
|
|
40
56
|
this.warn(envWarning);
|
package/dist/commands/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Command, Flags } from \"@oclif/core\";\nimport { Box, render, Text } from \"ink\";\nimport { resolvePort } from \"../cli/detect-port.js\";\nimport { Header } from \"../cli/header.js\";\nimport { startTunnelControlServer } from \"../cli/tunnel-control-server.js\";\nimport { useMessages } from \"../cli/use-messages.js\";\nimport { useNodemon } from \"../cli/use-nodemon.js\";\nimport { useOpenBrowser } from \"../cli/use-open-browser.js\";\nimport { useTunnel } from \"../cli/use-tunnel.js\";\nimport { useTypeScriptCheck } from \"../cli/use-typescript-check.js\";\n\nexport default class Dev extends Command {\n static override description = \"Start development server\";\n static override examples = [\"skybridge\"];\n static override flags = {\n port: Flags.integer({\n char: \"p\",\n description: \"Port to run the server on\",\n min: 1,\n }),\n tunnel: Flags.boolean({\n description: \"Open an Alpic tunnel for remote testing\",\n default: false,\n }),\n open: Flags.boolean({\n description: \"Open DevTools in the browser when the server is ready\",\n default: process.env.SKYBRIDGE_OPEN !== \"false\",\n allowNo: true,\n }),\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Show tunnel logs\",\n default: false,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(Dev);\n\n const { port, fallback, envWarning } = await resolvePort(flags.port);\n if (envWarning) {\n this.warn(envWarning);\n }\n\n const {\n port: controlPort,\n manager: tunnelManager,\n close: closeTunnelControl,\n } = await startTunnelControlServer(() => port);\n\n const env = {\n ...process.env,\n __PORT: String(port),\n __TUNNEL_CONTROL_PORT: String(controlPort),\n };\n\n const App = () => {\n const tsErrors = useTypeScriptCheck();\n const [messages, pushMessage] = useMessages();\n useNodemon(env, pushMessage);\n useOpenBrowser(port, flags.open);\n const tunnelState = useTunnel(\n port,\n pushMessage,\n flags.verbose,\n flags.tunnel,\n );\n\n return (\n <Box flexDirection=\"column\" padding={1} marginLeft={1}>\n <Header version={this.config.version} />\n\n <Box>\n <Text>🏠{\" \"}</Text>\n {fallback ? (\n <Text color=\"yellow\">3000 in use, running on </Text>\n ) : (\n <Text>Running on </Text>\n )}\n <Text color=\"green\">{`http://localhost:${port}/mcp`}</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"white\" bold>\n Test locally with DevTools:{\" \"}\n </Text>\n <Text color=\"green\">{`http://localhost:${port}/`}</Text>\n </Box>\n\n {tunnelState.status === \"idle\" && (\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text>Get a public URL and LLM Playground access with </Text>\n <Text color=\"cyan\" bold>\n --tunnel\n </Text>\n <Text>.</Text>\n </Box>\n )}\n {tunnelState.status === \"starting\" && (\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text color=\"yellow\">{tunnelState.message}</Text>\n </Box>\n )}\n {tunnelState.status === \"connected\" && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text>Exposed on </Text>\n <Text color=\"green\">{`${tunnelState.url}/mcp`}</Text>\n </Box>\n <Box>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"white\" bold>\n Test with an LLM on Playground:{\" \"}\n </Text>\n <Text color=\"green\">{`${tunnelState.url}/try`}</Text>\n </Box>\n </Box>\n )}\n {tunnelState.status === \"error\" && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text color=\"red\">\n Cannot open tunnel: {tunnelState.message}\n </Text>\n </Box>\n <Box>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"red\">{`Try manually: npx alpic tunnel --port ${port}`}</Text>\n </Box>\n </Box>\n )}\n\n <Box>\n <Text>🛟{\" \"}</Text>\n <Text>Need help? Reach us on </Text>\n <Text color=\"white\" underline>\n https://discord.alpic.ai\n </Text>\n </Box>\n\n {tsErrors.length > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"red\" bold>\n ⚠️ TypeScript errors found:\n </Text>\n {tsErrors.map((error) => (\n <Box\n key={`${error.file}:${error.line}:${error.col}`}\n marginLeft={2}\n flexDirection=\"column\"\n >\n <Box>\n <Text color=\"white\">{error.file}</Text>\n <Text color=\"grey\">\n ({error.line},{error.col}):{\" \"}\n </Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"red\">{error.message}</Text>\n </Box>\n </Box>\n ))}\n </Box>\n )}\n {messages.length > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"white\" bold>\n Logs:\n </Text>\n {messages.map((message) => (\n <Box key={message.id} marginLeft={2}>\n {message.type === \"restart\" ? (\n <>\n <Text color=\"green\">✓{\" \"}</Text>\n <Text color=\"white\">{message.text}</Text>\n </>\n ) : message.type === \"error\" ? (\n <Text color=\"red\">{message.text}</Text>\n ) : (\n <Text>{message.text}</Text>\n )}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n );\n };\n\n // Note: `exitOnCtrlC: false` because we own SIGINT below to guarantee\n // alpic gets killed before we exit. If anything ever calls `useInput` or\n // puts stdin into raw mode, also wire an explicit `\\x03` keypress to the\n // shutdown function — Ink will otherwise swallow Ctrl-C without ever\n // delivering SIGINT.\n const ink = render(<App />, { exitOnCtrlC: false, patchConsole: true });\n\n // Synchronous-first shutdown: kill the alpic subprocess up front so we\n // can't leave it orphaned even if another SIGINT listener (e.g. nodemon's)\n // exits the process before our async cleanup completes.\n const shutdown = (code: number) => () => {\n tunnelManager.stop();\n void closeTunnelControl()\n .catch((err) => {\n console.error(\"Failed to close tunnel control server\", err);\n })\n .finally(() => {\n ink.unmount();\n process.exit(code);\n });\n };\n process.once(\"SIGINT\", shutdown(130));\n process.once(\"SIGTERM\", shutdown(143));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,CAAC,OAAO,OAAO,GAAI,SAAQ,OAAO;IACtC,MAAM,CAAU,WAAW,GAAG,0BAA0B,CAAC;IACzD,MAAM,CAAU,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,CAAU,KAAK,GAAG;QACtB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;YAClB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,2BAA2B;YACxC,GAAG,EAAE,CAAC;SACP,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;YACpB,WAAW,EAAE,yCAAyC;YACtD,OAAO,EAAE,KAAK;SACf,CAAC;QACF,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;YAClB,WAAW,EAAE,uDAAuD;YACpE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,OAAO;YAC/C,OAAO,EAAE,IAAI;SACd,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;YACrB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,kBAAkB;YAC/B,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAExC,sEAAsE;QACtE,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,oDAAoD;QACpD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,qEAAqE;YACrE,6DAA6D;QAC/D,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;QAED,MAAM,EACJ,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,kBAAkB,GAC1B,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAE/C,MAAM,GAAG,GAAG;YACV,GAAG,OAAO,CAAC,GAAG;YACd,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC;YACpB,qBAAqB,EAAE,MAAM,CAAC,WAAW,CAAC;SAC3C,CAAC;QAEF,MAAM,GAAG,GAAG,GAAG,EAAE;YACf,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;YACtC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,WAAW,EAAE,CAAC;YAC9C,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC7B,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,WAAW,GAAG,SAAS,CAC3B,IAAI,EACJ,WAAW,EACX,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,MAAM,CACb,CAAC;YAEF,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,aACnD,KAAC,MAAM,IAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,GAAI,EAExC,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACpB,QAAQ,CAAC,CAAC,CAAC,CACV,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,yCAAgC,CACrD,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,8BAAmB,CACzB,EACD,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,oBAAoB,IAAI,MAAM,GAAQ,IACvD,EACN,MAAC,GAAG,IAAC,YAAY,EAAE,CAAC,aAClB,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,uBAAG,IAAI,IAAQ,EACpC,MAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,kDACM,GAAG,IAC1B,EACP,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,oBAAoB,IAAI,GAAG,GAAQ,IACpD,EAEL,WAAW,CAAC,MAAM,KAAK,MAAM,IAAI,CAChC,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACrB,KAAC,IAAI,mEAAwD,EAC7D,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,+BAEhB,EACP,KAAC,IAAI,oBAAS,IACV,CACP,EACA,WAAW,CAAC,MAAM,KAAK,UAAU,IAAI,CACpC,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACrB,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,YAAE,WAAW,CAAC,OAAO,GAAQ,IAC7C,CACP,EACA,WAAW,CAAC,MAAM,KAAK,WAAW,IAAI,CACrC,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC,aACzC,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACrB,KAAC,IAAI,8BAAmB,EACxB,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,GAAG,WAAW,CAAC,GAAG,MAAM,GAAQ,IACjD,EACN,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,uBAAG,IAAI,IAAQ,EACpC,MAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,sDACU,GAAG,IAC9B,EACP,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,GAAG,WAAW,CAAC,GAAG,MAAM,GAAQ,IACjD,IACF,CACP,EACA,WAAW,CAAC,MAAM,KAAK,OAAO,IAAI,CACjC,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC,aACzC,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACrB,MAAC,IAAI,IAAC,KAAK,EAAC,KAAK,qCACM,WAAW,CAAC,OAAO,IACnC,IACH,EACN,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,uBAAG,IAAI,IAAQ,EACpC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,yCAAyC,IAAI,EAAE,GAAQ,IACtE,IACF,CACP,EAED,MAAC,GAAG,eACF,MAAC,IAAI,+BAAI,IAAI,IAAQ,EACrB,KAAC,IAAI,0CAA+B,EACpC,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,SAAS,+CAEtB,IACH,EAEL,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,4DAEf,EACN,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACvB,MAAC,GAAG,IAEF,UAAU,EAAE,CAAC,EACb,aAAa,EAAC,QAAQ,aAEtB,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,KAAK,CAAC,IAAI,GAAQ,EACvC,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBACd,KAAK,CAAC,IAAI,OAAG,KAAK,CAAC,GAAG,QAAI,GAAG,IAC1B,IACH,EACN,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,KAAK,CAAC,OAAO,GAAQ,GACpC,KAZD,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,CAa3C,CACP,CAAC,IACE,CACP,EACA,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,4BAEjB,EACN,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CACzB,KAAC,GAAG,IAAkB,UAAU,EAAE,CAAC,YAChC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAC5B,8BACE,MAAC,IAAI,IAAC,KAAK,EAAC,OAAO,uBAAG,IAAI,IAAQ,EAClC,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,OAAO,CAAC,IAAI,GAAQ,IACxC,CACJ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAC7B,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,YAAE,OAAO,CAAC,IAAI,GAAQ,CACxC,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,cAAE,OAAO,CAAC,IAAI,GAAQ,CAC5B,IAVO,OAAO,CAAC,EAAE,CAWd,CACP,CAAC,IACE,CACP,IACG,CACP,CAAC;QACJ,CAAC,CAAC;QAEF,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,qEAAqE;QACrE,qBAAqB;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAC,GAAG,KAAG,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAExE,uEAAuE;QACvE,2EAA2E;QAC3E,wDAAwD;QACxD,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,EAAE;YACtC,aAAa,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,kBAAkB,EAAE;iBACtB,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;YAC9D,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,CAAC","sourcesContent":["import { Command, Flags } from \"@oclif/core\";\nimport { Box, render, Text } from \"ink\";\nimport { resolvePort } from \"../cli/detect-port.js\";\nimport { Header } from \"../cli/header.js\";\nimport { resolveViewsDir } from \"../cli/resolve-views-dir.js\";\nimport { startTunnelControlServer } from \"../cli/tunnel-control-server.js\";\nimport { useMessages } from \"../cli/use-messages.js\";\nimport { useNodemon } from \"../cli/use-nodemon.js\";\nimport { useOpenBrowser } from \"../cli/use-open-browser.js\";\nimport { useTunnel } from \"../cli/use-tunnel.js\";\nimport { useTypeScriptCheck } from \"../cli/use-typescript-check.js\";\nimport { scanAndWriteViewsDts } from \"../web/plugin/scan-views.js\";\n\nexport default class Dev extends Command {\n static override description = \"Start development server\";\n static override examples = [\"skybridge\"];\n static override flags = {\n port: Flags.integer({\n char: \"p\",\n description: \"Port to run the server on\",\n min: 1,\n }),\n tunnel: Flags.boolean({\n description: \"Open an Alpic tunnel for remote testing\",\n default: false,\n }),\n open: Flags.boolean({\n description: \"Open DevTools in the browser when the server is ready\",\n default: process.env.SKYBRIDGE_OPEN !== \"false\",\n allowNo: true,\n }),\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Show tunnel logs\",\n default: false,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(Dev);\n\n // Generate .skybridge/views.d.ts before render() spawns `tsc --noEmit\n // --watch`. Vite's plugin config hook writes the same file when nodemon\n // boots the server, but tsc starts in parallel — if .skybridge/ doesn't\n // exist at tsc startup, its watcher never picks up the late-created file\n // and the dev UI reports phantom TS errors forever.\n const root = process.cwd();\n try {\n scanAndWriteViewsDts(root, await resolveViewsDir(root));\n } catch {\n // Best-effort: if the scan fails (e.g. broken vite config, duplicate\n // view names) tsc may show phantom errors, but the dev server should\n // still start so the developer can fix the underlying issue.\n }\n\n const { port, fallback, envWarning } = await resolvePort(flags.port);\n if (envWarning) {\n this.warn(envWarning);\n }\n\n const {\n port: controlPort,\n manager: tunnelManager,\n close: closeTunnelControl,\n } = await startTunnelControlServer(() => port);\n\n const env = {\n ...process.env,\n __PORT: String(port),\n __TUNNEL_CONTROL_PORT: String(controlPort),\n };\n\n const App = () => {\n const tsErrors = useTypeScriptCheck();\n const [messages, pushMessage] = useMessages();\n useNodemon(env, pushMessage);\n useOpenBrowser(port, flags.open);\n const tunnelState = useTunnel(\n port,\n pushMessage,\n flags.verbose,\n flags.tunnel,\n );\n\n return (\n <Box flexDirection=\"column\" padding={1} marginLeft={1}>\n <Header version={this.config.version} />\n\n <Box>\n <Text>🏠{\" \"}</Text>\n {fallback ? (\n <Text color=\"yellow\">3000 in use, running on </Text>\n ) : (\n <Text>Running on </Text>\n )}\n <Text color=\"green\">{`http://localhost:${port}/mcp`}</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"white\" bold>\n Test locally with DevTools:{\" \"}\n </Text>\n <Text color=\"green\">{`http://localhost:${port}/`}</Text>\n </Box>\n\n {tunnelState.status === \"idle\" && (\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text>Get a public URL and LLM Playground access with </Text>\n <Text color=\"cyan\" bold>\n --tunnel\n </Text>\n <Text>.</Text>\n </Box>\n )}\n {tunnelState.status === \"starting\" && (\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text color=\"yellow\">{tunnelState.message}</Text>\n </Box>\n )}\n {tunnelState.status === \"connected\" && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text>Exposed on </Text>\n <Text color=\"green\">{`${tunnelState.url}/mcp`}</Text>\n </Box>\n <Box>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"white\" bold>\n Test with an LLM on Playground:{\" \"}\n </Text>\n <Text color=\"green\">{`${tunnelState.url}/try`}</Text>\n </Box>\n </Box>\n )}\n {tunnelState.status === \"error\" && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text>🌍{\" \"}</Text>\n <Text color=\"red\">\n Cannot open tunnel: {tunnelState.message}\n </Text>\n </Box>\n <Box>\n <Text color=\"#20a832\">→{\" \"}</Text>\n <Text color=\"red\">{`Try manually: npx alpic tunnel --port ${port}`}</Text>\n </Box>\n </Box>\n )}\n\n <Box>\n <Text>🛟{\" \"}</Text>\n <Text>Need help? Reach us on </Text>\n <Text color=\"white\" underline>\n https://discord.alpic.ai\n </Text>\n </Box>\n\n {tsErrors.length > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"red\" bold>\n ⚠️ TypeScript errors found:\n </Text>\n {tsErrors.map((error) => (\n <Box\n key={`${error.file}:${error.line}:${error.col}`}\n marginLeft={2}\n flexDirection=\"column\"\n >\n <Box>\n <Text color=\"white\">{error.file}</Text>\n <Text color=\"grey\">\n ({error.line},{error.col}):{\" \"}\n </Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"red\">{error.message}</Text>\n </Box>\n </Box>\n ))}\n </Box>\n )}\n {messages.length > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"white\" bold>\n Logs:\n </Text>\n {messages.map((message) => (\n <Box key={message.id} marginLeft={2}>\n {message.type === \"restart\" ? (\n <>\n <Text color=\"green\">✓{\" \"}</Text>\n <Text color=\"white\">{message.text}</Text>\n </>\n ) : message.type === \"error\" ? (\n <Text color=\"red\">{message.text}</Text>\n ) : (\n <Text>{message.text}</Text>\n )}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n );\n };\n\n // Note: `exitOnCtrlC: false` because we own SIGINT below to guarantee\n // alpic gets killed before we exit. If anything ever calls `useInput` or\n // puts stdin into raw mode, also wire an explicit `\\x03` keypress to the\n // shutdown function — Ink will otherwise swallow Ctrl-C without ever\n // delivering SIGINT.\n const ink = render(<App />, { exitOnCtrlC: false, patchConsole: true });\n\n // Synchronous-first shutdown: kill the alpic subprocess up front so we\n // can't leave it orphaned even if another SIGINT listener (e.g. nodemon's)\n // exits the process before our async cleanup completes.\n const shutdown = (code: number) => () => {\n tunnelManager.stop();\n void closeTunnelControl()\n .catch((err) => {\n console.error(\"Failed to close tunnel control server\", err);\n })\n .finally(() => {\n ink.unmount();\n process.exit(code);\n });\n };\n process.once(\"SIGINT\", shutdown(130));\n process.once(\"SIGTERM\", shutdown(143));\n }\n}\n"]}
|
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
import type { AudioContent, EmbeddedResource, ImageContent, ResourceLink, TextContent } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* MCP content annotations applied to any returned block.
|
|
4
|
+
*
|
|
5
|
+
* - `audience` — who is meant to see the content (`"user"`, `"assistant"`, or both).
|
|
6
|
+
* - `priority` — relative importance hint for the host.
|
|
7
|
+
* - `lastModified` — ISO timestamp for when the content was produced.
|
|
8
|
+
*/
|
|
2
9
|
type ContentAnnotations = {
|
|
3
10
|
audience?: ("user" | "assistant")[];
|
|
4
11
|
priority?: number;
|
|
5
12
|
lastModified?: string;
|
|
6
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* Build an MCP text content block.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* return { content: [text("Found 3 results.")] };
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
7
22
|
export declare function text(value: string, annotations?: ContentAnnotations): TextContent;
|
|
23
|
+
/**
|
|
24
|
+
* Build an MCP image content block.
|
|
25
|
+
*
|
|
26
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
27
|
+
* **already base64-encoded**. Passing a raw byte-string will produce invalid
|
|
28
|
+
* content.
|
|
29
|
+
*/
|
|
8
30
|
export declare function image(data: string | Uint8Array, mimeType: string, annotations?: ContentAnnotations): ImageContent;
|
|
31
|
+
/**
|
|
32
|
+
* Build an MCP audio content block.
|
|
33
|
+
*
|
|
34
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
35
|
+
* **already base64-encoded**.
|
|
36
|
+
*/
|
|
9
37
|
export declare function audio(data: string | Uint8Array, mimeType: string, annotations?: ContentAnnotations): AudioContent;
|
|
38
|
+
/**
|
|
39
|
+
* Build an MCP embedded resource — the full content travels inline. Use this
|
|
40
|
+
* when the client needs the bytes themselves rather than a link.
|
|
41
|
+
*
|
|
42
|
+
* Pass either `text` (UTF-8 string) or `blob` (base64-encoded bytes).
|
|
43
|
+
*/
|
|
10
44
|
export declare function embeddedResource(resource: {
|
|
11
45
|
uri: string;
|
|
12
46
|
mimeType?: string;
|
|
@@ -16,6 +50,12 @@ export declare function embeddedResource(resource: {
|
|
|
16
50
|
mimeType?: string;
|
|
17
51
|
blob: string;
|
|
18
52
|
}, annotations?: ContentAnnotations): EmbeddedResource;
|
|
53
|
+
/**
|
|
54
|
+
* Build an MCP resource link — a `type: "resource_link"` block carrying a URI
|
|
55
|
+
* the client can fetch (or subscribe to) on demand. Use a link when the
|
|
56
|
+
* client should retrieve the bytes itself; use {@link embeddedResource} when
|
|
57
|
+
* the content must travel inline with the response.
|
|
58
|
+
*/
|
|
19
59
|
export declare function resourceLink(link: {
|
|
20
60
|
uri: string;
|
|
21
61
|
name: string;
|
|
@@ -10,9 +10,24 @@ function toBase64(data) {
|
|
|
10
10
|
}
|
|
11
11
|
return Buffer.from(data).toString("base64");
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Build an MCP text content block.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* return { content: [text("Found 3 results.")] };
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
13
21
|
export function text(value, annotations) {
|
|
14
22
|
return { type: "text", text: value, ...(annotations && { annotations }) };
|
|
15
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Build an MCP image content block.
|
|
26
|
+
*
|
|
27
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
28
|
+
* **already base64-encoded**. Passing a raw byte-string will produce invalid
|
|
29
|
+
* content.
|
|
30
|
+
*/
|
|
16
31
|
export function image(data, mimeType, annotations) {
|
|
17
32
|
return {
|
|
18
33
|
type: "image",
|
|
@@ -21,6 +36,12 @@ export function image(data, mimeType, annotations) {
|
|
|
21
36
|
...(annotations && { annotations }),
|
|
22
37
|
};
|
|
23
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an MCP audio content block.
|
|
41
|
+
*
|
|
42
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
43
|
+
* **already base64-encoded**.
|
|
44
|
+
*/
|
|
24
45
|
export function audio(data, mimeType, annotations) {
|
|
25
46
|
return {
|
|
26
47
|
type: "audio",
|
|
@@ -29,6 +50,12 @@ export function audio(data, mimeType, annotations) {
|
|
|
29
50
|
...(annotations && { annotations }),
|
|
30
51
|
};
|
|
31
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Build an MCP embedded resource — the full content travels inline. Use this
|
|
55
|
+
* when the client needs the bytes themselves rather than a link.
|
|
56
|
+
*
|
|
57
|
+
* Pass either `text` (UTF-8 string) or `blob` (base64-encoded bytes).
|
|
58
|
+
*/
|
|
32
59
|
export function embeddedResource(resource, annotations) {
|
|
33
60
|
return {
|
|
34
61
|
type: "resource",
|
|
@@ -36,6 +63,12 @@ export function embeddedResource(resource, annotations) {
|
|
|
36
63
|
...(annotations && { annotations }),
|
|
37
64
|
};
|
|
38
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Build an MCP resource link — a `type: "resource_link"` block carrying a URI
|
|
68
|
+
* the client can fetch (or subscribe to) on demand. Use a link when the
|
|
69
|
+
* client should retrieve the bytes itself; use {@link embeddedResource} when
|
|
70
|
+
* the content must travel inline with the response.
|
|
71
|
+
*/
|
|
39
72
|
export function resourceLink(link, annotations) {
|
|
40
73
|
return {
|
|
41
74
|
type: "resource_link",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-helpers.js","sourceRoot":"","sources":["../../src/server/content-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"content-helpers.js","sourceRoot":"","sources":["../../src/server/content-helpers.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,IAAyB;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAClB,KAAa,EACb,WAAgC;IAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAEoD,EACpD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,IAOC,EACD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,GAAG,IAAI;QACP,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n AudioContent,\n EmbeddedResource,\n ImageContent,\n ResourceLink,\n TextContent,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * MCP content annotations applied to any returned block.\n *\n * - `audience` — who is meant to see the content (`\"user\"`, `\"assistant\"`, or both).\n * - `priority` — relative importance hint for the host.\n * - `lastModified` — ISO timestamp for when the content was produced.\n */\ntype ContentAnnotations = {\n audience?: (\"user\" | \"assistant\")[];\n priority?: number;\n lastModified?: string;\n};\n\n/**\n * Returns a base64-encoded string.\n * - `Uint8Array` input is encoded via `Buffer.toString(\"base64\")`.\n * - `string` input is assumed to be **already base64-encoded** and is returned\n * as-is. Passing raw/unencoded string bytes will produce invalid MCP content.\n */\nfunction toBase64(data: string | Uint8Array): string {\n if (typeof data === \"string\") {\n return data;\n }\n return Buffer.from(data).toString(\"base64\");\n}\n\n/**\n * Build an MCP text content block.\n *\n * @example\n * ```ts\n * return { content: [text(\"Found 3 results.\")] };\n * ```\n */\nexport function text(\n value: string,\n annotations?: ContentAnnotations,\n): TextContent {\n return { type: \"text\", text: value, ...(annotations && { annotations }) };\n}\n\n/**\n * Build an MCP image content block.\n *\n * `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is\n * **already base64-encoded**. Passing a raw byte-string will produce invalid\n * content.\n */\nexport function image(\n data: string | Uint8Array,\n mimeType: string,\n annotations?: ContentAnnotations,\n): ImageContent {\n return {\n type: \"image\",\n data: toBase64(data),\n mimeType,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP audio content block.\n *\n * `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is\n * **already base64-encoded**.\n */\nexport function audio(\n data: string | Uint8Array,\n mimeType: string,\n annotations?: ContentAnnotations,\n): AudioContent {\n return {\n type: \"audio\",\n data: toBase64(data),\n mimeType,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP embedded resource — the full content travels inline. Use this\n * when the client needs the bytes themselves rather than a link.\n *\n * Pass either `text` (UTF-8 string) or `blob` (base64-encoded bytes).\n */\nexport function embeddedResource(\n resource:\n | { uri: string; mimeType?: string; text: string }\n | { uri: string; mimeType?: string; blob: string },\n annotations?: ContentAnnotations,\n): EmbeddedResource {\n return {\n type: \"resource\",\n resource,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP resource link — a `type: \"resource_link\"` block carrying a URI\n * the client can fetch (or subscribe to) on demand. Use a link when the\n * client should retrieve the bytes itself; use {@link embeddedResource} when\n * the content must travel inline with the response.\n */\nexport function resourceLink(\n link: {\n uri: string;\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n size?: number;\n },\n annotations?: ContentAnnotations,\n): ResourceLink {\n return {\n type: \"resource_link\",\n ...link,\n ...(annotations && { annotations }),\n };\n}\n"]}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for a host-managed file reference passed between tools and views.
|
|
4
|
+
*
|
|
5
|
+
* Use it inside a tool's `inputSchema` or `outputSchema` so the host can
|
|
6
|
+
* recognize the field as a file and surface attach / preview affordances.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* server.registerTool({
|
|
11
|
+
* name: "summarize-document",
|
|
12
|
+
* inputSchema: { document: FileRef },
|
|
13
|
+
* }, async ({ document }) => {
|
|
14
|
+
* const res = await fetch(document.download_url);
|
|
15
|
+
* // …
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @see https://docs.skybridge.tech/api-reference/file-ref
|
|
20
|
+
*/
|
|
2
21
|
export declare const FileRef: z.ZodObject<{
|
|
3
22
|
file_id: z.ZodString;
|
|
4
23
|
download_url: z.ZodString;
|
|
5
24
|
mime_type: z.ZodOptional<z.ZodString>;
|
|
6
25
|
file_name: z.ZodOptional<z.ZodString>;
|
|
7
26
|
}, z.core.$strip>;
|
|
27
|
+
/** Inferred TypeScript type for {@link FileRef}. */
|
|
8
28
|
export type FileRef = z.infer<typeof FileRef>;
|
package/dist/server/file-ref.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for a host-managed file reference passed between tools and views.
|
|
4
|
+
*
|
|
5
|
+
* Use it inside a tool's `inputSchema` or `outputSchema` so the host can
|
|
6
|
+
* recognize the field as a file and surface attach / preview affordances.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* server.registerTool({
|
|
11
|
+
* name: "summarize-document",
|
|
12
|
+
* inputSchema: { document: FileRef },
|
|
13
|
+
* }, async ({ document }) => {
|
|
14
|
+
* const res = await fetch(document.download_url);
|
|
15
|
+
* // …
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @see https://docs.skybridge.tech/api-reference/file-ref
|
|
20
|
+
*/
|
|
2
21
|
export const FileRef = z.object({
|
|
3
22
|
file_id: z.string(),
|
|
4
23
|
download_url: z.string(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-ref.js","sourceRoot":"","sources":["../../src/server/file-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\nexport const FileRef = z.object({\n file_id: z.string(),\n download_url: z.string(),\n mime_type: z.string().optional(),\n file_name: z.string().optional(),\n});\n\nexport type FileRef = z.infer<typeof FileRef>;\n"]}
|
|
1
|
+
{"version":3,"file":"file-ref.js","sourceRoot":"","sources":["../../src/server/file-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Zod schema for a host-managed file reference passed between tools and views.\n *\n * Use it inside a tool's `inputSchema` or `outputSchema` so the host can\n * recognize the field as a file and surface attach / preview affordances.\n *\n * @example\n * ```ts\n * server.registerTool({\n * name: \"summarize-document\",\n * inputSchema: { document: FileRef },\n * }, async ({ document }) => {\n * const res = await fetch(document.download_url);\n * // …\n * });\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/file-ref\n */\nexport const FileRef = z.object({\n file_id: z.string(),\n download_url: z.string(),\n mime_type: z.string().optional(),\n file_name: z.string().optional(),\n});\n\n/** Inferred TypeScript type for {@link FileRef}. */\nexport type FileRef = z.infer<typeof FileRef>;\n"]}
|
|
@@ -19,7 +19,12 @@ export type McpMiddlewareFn = (request: {
|
|
|
19
19
|
* MCP methods the server handles (incoming from client).
|
|
20
20
|
*/
|
|
21
21
|
export type McpMethodString = ClientRequest["method"] | ClientNotification["method"];
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the `params` type for a specific MCP method (request or notification)
|
|
24
|
+
* from the SDK's typed unions. Falls back to `Record<string, unknown>` for
|
|
25
|
+
* unknown methods. Used by {@link McpTypedMiddlewareFn} to narrow the request
|
|
26
|
+
* shape in typed middleware.
|
|
27
|
+
*/
|
|
23
28
|
export type McpRequestParams<M extends string> = Extract<ClientRequest, {
|
|
24
29
|
method: M;
|
|
25
30
|
}> extends {
|
|
@@ -29,7 +34,11 @@ export type McpRequestParams<M extends string> = Extract<ClientRequest, {
|
|
|
29
34
|
}> extends {
|
|
30
35
|
params: infer P;
|
|
31
36
|
} ? P : Record<string, unknown>;
|
|
32
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the `extra` arg type for a specific MCP method: {@link McpExtra} for
|
|
39
|
+
* request methods, `undefined` for notification methods (the SDK does not
|
|
40
|
+
* pass extra context for notifications).
|
|
41
|
+
*/
|
|
33
42
|
export type McpExtraFor<M extends string> = M extends ClientRequest["method"] ? McpExtra : M extends ClientNotification["method"] ? undefined : McpExtra | undefined;
|
|
34
43
|
/** Maps each MCP request method to its SDK result type. */
|
|
35
44
|
interface McpResultMap {
|
|
@@ -59,7 +68,11 @@ interface McpResultMap {
|
|
|
59
68
|
* For unknown/unmatched methods, falls back to `ServerResult`.
|
|
60
69
|
*/
|
|
61
70
|
export type McpResultFor<M extends string> = M extends keyof McpResultMap ? McpResultMap[M] : M extends `${infer Prefix}/*` ? [McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]] extends [never] ? M extends ToWildcard<ClientNotification["method"]> ? undefined : ServerResult : McpResultMap[keyof McpResultMap & `${Prefix}/${string}`] : M extends ClientNotification["method"] ? undefined : ServerResult;
|
|
62
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Typed middleware function for a specific method. Narrows `request.params`
|
|
73
|
+
* via {@link McpRequestParams}, `extra` via {@link McpExtraFor}, and the
|
|
74
|
+
* resolved value of `next()` via {@link McpResultFor}.
|
|
75
|
+
*/
|
|
63
76
|
export type McpTypedMiddlewareFn<M extends string> = (request: {
|
|
64
77
|
method: M;
|
|
65
78
|
params: McpRequestParams<M>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolResult,\n CancelTaskResult,\n ClientNotification,\n ClientRequest,\n CompleteResult,\n EmptyResult,\n GetPromptResult,\n GetTaskPayloadResult,\n GetTaskResult,\n InitializeResult,\n ListPromptsResult,\n ListResourcesResult,\n ListResourceTemplatesResult,\n ListTasksResult,\n ListToolsResult,\n ReadResourceResult,\n ServerNotification,\n ServerRequest,\n ServerResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * The `extra` context object provided by the MCP SDK to request handlers.\n */\nexport type McpExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\n/**\n * A single MCP middleware function following the onion model.\n * Call `next()` to invoke the next middleware or the final handler.\n * For notifications, `extra` is `undefined` (SDK does not provide extra context)\n * and `next()` resolves to `undefined`.\n */\nexport type McpMiddlewareFn = (\n request: { method: string; params: Record<string, unknown> },\n extra: McpExtra | undefined,\n next: () => Promise<unknown>,\n) => Promise<unknown> | unknown;\n\n/**\n * MCP methods the server handles (incoming from client).\n */\nexport type McpMethodString =\n | ClientRequest[\"method\"]\n | ClientNotification[\"method\"];\n\n/** Extract params type for a specific MCP method from SDK unions. */\nexport type McpRequestParams<M extends string> =\n Extract<ClientRequest, { method: M }> extends { params: infer P }\n ? P\n : Extract<ClientNotification, { method: M }> extends { params: infer P }\n ? P\n : Record<string, unknown>;\n\n/** Resolve extra type: McpExtra for requests, undefined for notifications. */\nexport type McpExtraFor<M extends string> = M extends ClientRequest[\"method\"]\n ? McpExtra\n : M extends ClientNotification[\"method\"]\n ? undefined\n : McpExtra | undefined;\n\n/** Maps each MCP request method to its SDK result type. */\ninterface McpResultMap {\n ping: EmptyResult;\n initialize: InitializeResult;\n \"tools/list\": ListToolsResult;\n \"tools/call\": CallToolResult;\n \"resources/list\": ListResourcesResult;\n \"resources/templates/list\": ListResourceTemplatesResult;\n \"resources/read\": ReadResourceResult;\n \"resources/subscribe\": EmptyResult;\n \"resources/unsubscribe\": EmptyResult;\n \"prompts/list\": ListPromptsResult;\n \"prompts/get\": GetPromptResult;\n \"completion/complete\": CompleteResult;\n \"logging/setLevel\": EmptyResult;\n \"tasks/get\": GetTaskResult;\n \"tasks/result\": GetTaskPayloadResult;\n \"tasks/list\": ListTasksResult;\n \"tasks/cancel\": CancelTaskResult;\n}\n\n/**\n * Map an MCP method string to its corresponding result type.\n * For request methods, resolves to the specific SDK result type.\n * For wildcard patterns (e.g. `\"tools/*\"`), resolves to the union of matching result types.\n * For notification methods, resolves to `undefined`.\n * For unknown/unmatched methods, falls back to `ServerResult`.\n */\nexport type McpResultFor<M extends string> = M extends keyof McpResultMap\n ? McpResultMap[M]\n : M extends `${infer Prefix}/*`\n ? [McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]] extends [never]\n ? M extends ToWildcard<ClientNotification[\"method\"]>\n ? undefined\n : ServerResult\n : McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]\n : M extends ClientNotification[\"method\"]\n ? undefined\n : ServerResult;\n\n/** Typed middleware fn for a specific method — narrows params, extra, and next() result. */\nexport type McpTypedMiddlewareFn<M extends string> = (\n request: { method: M; params: McpRequestParams<M> },\n extra: McpExtraFor<M>,\n next: () => Promise<McpResultFor<M>>,\n) => Promise<unknown> | unknown;\n\n/** Extracts `\"prefix/*\"` from `\"prefix/anything\"` — distributive over unions. */\ntype ToWildcard<T extends string> = T extends `${infer Prefix}/${string}`\n ? `${Prefix}/*`\n : never;\n\n/** Wildcard prefixes derived from method strings (e.g. `\"tools/*\"` from `\"tools/call\"`). */\nexport type McpWildcard = ToWildcard<McpMethodString>;\n\n/** Category keywords matching all requests or all notifications. */\ntype McpCategory = \"request\" | \"notification\";\n\n/**\n * A single filter pattern for MCP middleware:\n * - Exact method: `\"tools/call\"`\n * - Wildcard: `\"tools/*\"`\n * - Category: `\"request\"` | `\"notification\"`\n * - Escape hatch: arbitrary string via `string & {}`\n */\ntype McpMiddlewareFilterPattern =\n | McpMethodString\n | McpWildcard\n | McpCategory\n | (string & {});\n\n/**\n * Filter determining which MCP methods a middleware applies to.\n * A single pattern or an array of patterns (OR logic).\n */\nexport type McpMiddlewareFilter =\n | McpMiddlewareFilterPattern\n | McpMiddlewareFilterPattern[];\n\n/**\n * Internal entry stored for each registered middleware.\n * `filter: null` means catch-all (matches everything).\n */\nexport type McpMiddlewareEntry = {\n filter: McpMiddlewareFilter | null;\n handler: McpMiddlewareFn;\n};\n\ntype HandlerMap = Map<string, (...args: unknown[]) => Promise<unknown>>;\n\n/**\n * Extract the TS-private `_requestHandlers` and `_notificationHandlers` maps\n * from the SDK's `Server` (extends `Protocol`). These are runtime-accessible\n * but declared `private` in TypeScript.\n *\n * Validates with `instanceof Map` so an incompatible SDK version fails fast\n * instead of silently breaking.\n */\nexport function getHandlerMaps(server: Server) {\n const obj: object = server;\n\n if (\n !(\"_requestHandlers\" in obj && obj._requestHandlers instanceof Map) ||\n !(\n \"_notificationHandlers\" in obj && obj._notificationHandlers instanceof Map\n )\n ) {\n throw new Error(\n \"Incompatible MCP SDK version: expected _requestHandlers and _notificationHandlers on Server\",\n );\n }\n\n return {\n requestHandlers: obj._requestHandlers as HandlerMap,\n notificationHandlers: obj._notificationHandlers as HandlerMap,\n };\n}\n\n/**\n * Check if a single filter pattern matches a given method.\n *\n * - Exact: `\"tools/call\"` matches only `\"tools/call\"`\n * - Wildcard: `\"tools/*\"` matches any method starting with `\"tools/\"`\n * - Category `\"request\"`: matches when `isNotification` is false\n * - Category `\"notification\"`: matches when `isNotification` is true\n */\nexport function matchesFilter(\n method: string,\n filter: string,\n isNotification: boolean,\n): boolean {\n if (filter === \"request\") {\n return !isNotification;\n }\n if (filter === \"notification\") {\n return isNotification;\n }\n if (filter.endsWith(\"/*\")) {\n const prefix = filter.slice(0, -1); // \"tools/*\" → \"tools/\"\n return method.startsWith(prefix);\n }\n return method === filter;\n}\n\nfunction matchesAnyFilter(\n method: string,\n filter: McpMiddlewareFilter | null,\n isNotification: boolean,\n): boolean {\n if (filter === null) {\n return true;\n }\n if (typeof filter === \"string\") {\n return matchesFilter(method, filter, isNotification);\n }\n return filter.some((pattern) =>\n matchesFilter(method, pattern, isNotification),\n );\n}\n\n/**\n * Build an onion-model middleware chain for a specific method.\n *\n * Filters `entries` to those matching `method`, then composes them\n * so the first registered middleware is the outermost layer.\n * `next()` is guarded against multiple calls within a single middleware.\n */\nexport function buildMiddlewareChain(\n method: string,\n isNotification: boolean,\n originalHandler: (...args: unknown[]) => Promise<unknown>,\n entries: McpMiddlewareEntry[],\n) {\n const applicable = entries.filter((entry) =>\n matchesAnyFilter(method, entry.filter, isNotification),\n );\n\n if (applicable.length === 0) {\n return originalHandler;\n }\n\n return (...args: unknown[]) => {\n const rawRequest = args[0] as Record<string, unknown> | undefined;\n // SDK calls request handlers as handler(request, extra) but\n // notification handlers as handler(notification) — no extra arg.\n const extra = isNotification ? undefined : (args[1] as McpExtra);\n const mcpRequest = {\n method,\n params: (rawRequest?.params as Record<string, unknown>) ?? {},\n };\n\n let index = 0;\n\n const executeLayer = (): Promise<unknown> => {\n const entry = applicable[index++];\n if (!entry) {\n if (rawRequest) {\n rawRequest.params = mcpRequest.params;\n }\n return originalHandler(...args);\n }\n\n let nextCalled = false;\n\n const next = (): Promise<unknown> => {\n if (nextCalled) {\n throw new Error(\n `next() called multiple times in middleware for \"${method}\"`,\n );\n }\n nextCalled = true;\n return executeLayer();\n };\n\n return Promise.resolve(entry.handler(mcpRequest, extra, next));\n };\n\n return executeLayer();\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolResult,\n CancelTaskResult,\n ClientNotification,\n ClientRequest,\n CompleteResult,\n EmptyResult,\n GetPromptResult,\n GetTaskPayloadResult,\n GetTaskResult,\n InitializeResult,\n ListPromptsResult,\n ListResourcesResult,\n ListResourceTemplatesResult,\n ListTasksResult,\n ListToolsResult,\n ReadResourceResult,\n ServerNotification,\n ServerRequest,\n ServerResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * The `extra` context object provided by the MCP SDK to request handlers.\n */\nexport type McpExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\n/**\n * A single MCP middleware function following the onion model.\n * Call `next()` to invoke the next middleware or the final handler.\n * For notifications, `extra` is `undefined` (SDK does not provide extra context)\n * and `next()` resolves to `undefined`.\n */\nexport type McpMiddlewareFn = (\n request: { method: string; params: Record<string, unknown> },\n extra: McpExtra | undefined,\n next: () => Promise<unknown>,\n) => Promise<unknown> | unknown;\n\n/**\n * MCP methods the server handles (incoming from client).\n */\nexport type McpMethodString =\n | ClientRequest[\"method\"]\n | ClientNotification[\"method\"];\n\n/**\n * Resolve the `params` type for a specific MCP method (request or notification)\n * from the SDK's typed unions. Falls back to `Record<string, unknown>` for\n * unknown methods. Used by {@link McpTypedMiddlewareFn} to narrow the request\n * shape in typed middleware.\n */\nexport type McpRequestParams<M extends string> =\n Extract<ClientRequest, { method: M }> extends { params: infer P }\n ? P\n : Extract<ClientNotification, { method: M }> extends { params: infer P }\n ? P\n : Record<string, unknown>;\n\n/**\n * Resolve the `extra` arg type for a specific MCP method: {@link McpExtra} for\n * request methods, `undefined` for notification methods (the SDK does not\n * pass extra context for notifications).\n */\nexport type McpExtraFor<M extends string> = M extends ClientRequest[\"method\"]\n ? McpExtra\n : M extends ClientNotification[\"method\"]\n ? undefined\n : McpExtra | undefined;\n\n/** Maps each MCP request method to its SDK result type. */\ninterface McpResultMap {\n ping: EmptyResult;\n initialize: InitializeResult;\n \"tools/list\": ListToolsResult;\n \"tools/call\": CallToolResult;\n \"resources/list\": ListResourcesResult;\n \"resources/templates/list\": ListResourceTemplatesResult;\n \"resources/read\": ReadResourceResult;\n \"resources/subscribe\": EmptyResult;\n \"resources/unsubscribe\": EmptyResult;\n \"prompts/list\": ListPromptsResult;\n \"prompts/get\": GetPromptResult;\n \"completion/complete\": CompleteResult;\n \"logging/setLevel\": EmptyResult;\n \"tasks/get\": GetTaskResult;\n \"tasks/result\": GetTaskPayloadResult;\n \"tasks/list\": ListTasksResult;\n \"tasks/cancel\": CancelTaskResult;\n}\n\n/**\n * Map an MCP method string to its corresponding result type.\n * For request methods, resolves to the specific SDK result type.\n * For wildcard patterns (e.g. `\"tools/*\"`), resolves to the union of matching result types.\n * For notification methods, resolves to `undefined`.\n * For unknown/unmatched methods, falls back to `ServerResult`.\n */\nexport type McpResultFor<M extends string> = M extends keyof McpResultMap\n ? McpResultMap[M]\n : M extends `${infer Prefix}/*`\n ? [McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]] extends [never]\n ? M extends ToWildcard<ClientNotification[\"method\"]>\n ? undefined\n : ServerResult\n : McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]\n : M extends ClientNotification[\"method\"]\n ? undefined\n : ServerResult;\n\n/**\n * Typed middleware function for a specific method. Narrows `request.params`\n * via {@link McpRequestParams}, `extra` via {@link McpExtraFor}, and the\n * resolved value of `next()` via {@link McpResultFor}.\n */\nexport type McpTypedMiddlewareFn<M extends string> = (\n request: { method: M; params: McpRequestParams<M> },\n extra: McpExtraFor<M>,\n next: () => Promise<McpResultFor<M>>,\n) => Promise<unknown> | unknown;\n\n/** Extracts `\"prefix/*\"` from `\"prefix/anything\"` — distributive over unions. */\ntype ToWildcard<T extends string> = T extends `${infer Prefix}/${string}`\n ? `${Prefix}/*`\n : never;\n\n/** Wildcard prefixes derived from method strings (e.g. `\"tools/*\"` from `\"tools/call\"`). */\nexport type McpWildcard = ToWildcard<McpMethodString>;\n\n/** Category keywords matching all requests or all notifications. */\ntype McpCategory = \"request\" | \"notification\";\n\n/**\n * A single filter pattern for MCP middleware:\n * - Exact method: `\"tools/call\"`\n * - Wildcard: `\"tools/*\"`\n * - Category: `\"request\"` | `\"notification\"`\n * - Escape hatch: arbitrary string via `string & {}`\n */\ntype McpMiddlewareFilterPattern =\n | McpMethodString\n | McpWildcard\n | McpCategory\n | (string & {});\n\n/**\n * Filter determining which MCP methods a middleware applies to.\n * A single pattern or an array of patterns (OR logic).\n */\nexport type McpMiddlewareFilter =\n | McpMiddlewareFilterPattern\n | McpMiddlewareFilterPattern[];\n\n/**\n * Internal entry stored for each registered middleware.\n * `filter: null` means catch-all (matches everything).\n */\nexport type McpMiddlewareEntry = {\n filter: McpMiddlewareFilter | null;\n handler: McpMiddlewareFn;\n};\n\ntype HandlerMap = Map<string, (...args: unknown[]) => Promise<unknown>>;\n\n/**\n * Extract the TS-private `_requestHandlers` and `_notificationHandlers` maps\n * from the SDK's `Server` (extends `Protocol`). These are runtime-accessible\n * but declared `private` in TypeScript.\n *\n * Validates with `instanceof Map` so an incompatible SDK version fails fast\n * instead of silently breaking.\n */\nexport function getHandlerMaps(server: Server) {\n const obj: object = server;\n\n if (\n !(\"_requestHandlers\" in obj && obj._requestHandlers instanceof Map) ||\n !(\n \"_notificationHandlers\" in obj && obj._notificationHandlers instanceof Map\n )\n ) {\n throw new Error(\n \"Incompatible MCP SDK version: expected _requestHandlers and _notificationHandlers on Server\",\n );\n }\n\n return {\n requestHandlers: obj._requestHandlers as HandlerMap,\n notificationHandlers: obj._notificationHandlers as HandlerMap,\n };\n}\n\n/**\n * Check if a single filter pattern matches a given method.\n *\n * - Exact: `\"tools/call\"` matches only `\"tools/call\"`\n * - Wildcard: `\"tools/*\"` matches any method starting with `\"tools/\"`\n * - Category `\"request\"`: matches when `isNotification` is false\n * - Category `\"notification\"`: matches when `isNotification` is true\n */\nexport function matchesFilter(\n method: string,\n filter: string,\n isNotification: boolean,\n): boolean {\n if (filter === \"request\") {\n return !isNotification;\n }\n if (filter === \"notification\") {\n return isNotification;\n }\n if (filter.endsWith(\"/*\")) {\n const prefix = filter.slice(0, -1); // \"tools/*\" → \"tools/\"\n return method.startsWith(prefix);\n }\n return method === filter;\n}\n\nfunction matchesAnyFilter(\n method: string,\n filter: McpMiddlewareFilter | null,\n isNotification: boolean,\n): boolean {\n if (filter === null) {\n return true;\n }\n if (typeof filter === \"string\") {\n return matchesFilter(method, filter, isNotification);\n }\n return filter.some((pattern) =>\n matchesFilter(method, pattern, isNotification),\n );\n}\n\n/**\n * Build an onion-model middleware chain for a specific method.\n *\n * Filters `entries` to those matching `method`, then composes them\n * so the first registered middleware is the outermost layer.\n * `next()` is guarded against multiple calls within a single middleware.\n */\nexport function buildMiddlewareChain(\n method: string,\n isNotification: boolean,\n originalHandler: (...args: unknown[]) => Promise<unknown>,\n entries: McpMiddlewareEntry[],\n) {\n const applicable = entries.filter((entry) =>\n matchesAnyFilter(method, entry.filter, isNotification),\n );\n\n if (applicable.length === 0) {\n return originalHandler;\n }\n\n return (...args: unknown[]) => {\n const rawRequest = args[0] as Record<string, unknown> | undefined;\n // SDK calls request handlers as handler(request, extra) but\n // notification handlers as handler(notification) — no extra arg.\n const extra = isNotification ? undefined : (args[1] as McpExtra);\n const mcpRequest = {\n method,\n params: (rawRequest?.params as Record<string, unknown>) ?? {},\n };\n\n let index = 0;\n\n const executeLayer = (): Promise<unknown> => {\n const entry = applicable[index++];\n if (!entry) {\n if (rawRequest) {\n rawRequest.params = mcpRequest.params;\n }\n return originalHandler(...args);\n }\n\n let nextCalled = false;\n\n const next = (): Promise<unknown> => {\n if (nextCalled) {\n throw new Error(\n `next() called multiple times in middleware for \"${method}\"`,\n );\n }\n nextCalled = true;\n return executeLayer();\n };\n\n return Promise.resolve(entry.handler(mcpRequest, extra, next));\n };\n\n return executeLayer();\n };\n}\n"]}
|