theokit 0.21.0 → 0.22.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/{agent-76WK2CZJ.js → agent-7IGDT55V.js} +2 -2
- package/dist/{build-TBV4KVXI.js → build-G7WWVZTL.js} +2 -2
- package/dist/chunk-2CVV6CNN.js +174 -0
- package/dist/chunk-2CVV6CNN.js.map +1 -0
- package/dist/{chunk-3WOODHXK.js → chunk-AYZCGJXA.js} +12 -6
- package/dist/chunk-AYZCGJXA.js.map +1 -0
- package/dist/{chunk-7PFHQ56Q.js → chunk-BLL5LY7R.js} +39 -15
- package/dist/chunk-BLL5LY7R.js.map +1 -0
- package/dist/{chunk-23T4CO4M.js → chunk-JITNAZXE.js} +2 -2
- package/dist/{chunk-DXF64KS5.js → chunk-LGVVX3BV.js} +30 -12
- package/dist/chunk-LGVVX3BV.js.map +1 -0
- package/dist/{chunk-W4YO6S4I.js → chunk-W4XWVTN4.js} +112 -67
- package/dist/chunk-W4XWVTN4.js.map +1 -0
- package/dist/cli/index.js +5 -5
- package/dist/{dev-NHHTXOA4.js → dev-SHAN56K5.js} +4 -4
- package/dist/index.js +2 -2
- package/dist/{mcp-HP5RVOOZ.js → mcp-P4MHP3VA.js} +4 -4
- package/dist/{mcp-HP5RVOOZ.js.map → mcp-P4MHP3VA.js.map} +1 -1
- package/dist/server/index.d.ts +162 -1
- package/dist/server/index.js +60 -3
- package/dist/server/index.js.map +1 -1
- package/dist/{start-ILT6E25Q.js → start-RD5WQMIH.js} +6 -4
- package/dist/{start-ILT6E25Q.js.map → start-RD5WQMIH.js.map} +1 -1
- package/dist/vite-plugin/index.js +2 -2
- package/dist/{vite-plugin-3UDOJGO5.js → vite-plugin-A7WGYEMD.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-3WOODHXK.js.map +0 -1
- package/dist/chunk-7PFHQ56Q.js.map +0 -1
- package/dist/chunk-DMC6HYUR.js +0 -141
- package/dist/chunk-DMC6HYUR.js.map +0 -1
- package/dist/chunk-DXF64KS5.js.map +0 -1
- package/dist/chunk-W4YO6S4I.js.map +0 -1
- /package/dist/{agent-76WK2CZJ.js.map → agent-7IGDT55V.js.map} +0 -0
- /package/dist/{build-TBV4KVXI.js.map → build-G7WWVZTL.js.map} +0 -0
- /package/dist/{chunk-23T4CO4M.js.map → chunk-JITNAZXE.js.map} +0 -0
- /package/dist/{dev-NHHTXOA4.js.map → dev-SHAN56K5.js.map} +0 -0
- /package/dist/{vite-plugin-3UDOJGO5.js.map → vite-plugin-A7WGYEMD.js.map} +0 -0
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
} from "./chunk-HNBWZKIQ.js";
|
|
9
9
|
import {
|
|
10
10
|
theoPluginAsync
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-AYZCGJXA.js";
|
|
12
|
+
import "./chunk-LGVVX3BV.js";
|
|
13
|
+
import "./chunk-2CVV6CNN.js";
|
|
14
14
|
import "./chunk-M6NMS5LR.js";
|
|
15
15
|
import "./chunk-IEES3CHD.js";
|
|
16
16
|
import {
|
|
@@ -111,4 +111,4 @@ export {
|
|
|
111
111
|
devCommand,
|
|
112
112
|
startDevServer
|
|
113
113
|
};
|
|
114
|
-
//# sourceMappingURL=dev-
|
|
114
|
+
//# sourceMappingURL=dev-SHAN56K5.js.map
|
package/dist/index.js
CHANGED
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
scanRoutes,
|
|
9
9
|
theoConfigSchema,
|
|
10
10
|
theoPlugin
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-BLL5LY7R.js";
|
|
12
12
|
import "./chunk-D7ZH7DTG.js";
|
|
13
13
|
import "./chunk-RSVN727G.js";
|
|
14
14
|
import "./chunk-7BIM27LV.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-W4XWVTN4.js";
|
|
16
16
|
import "./chunk-5QW7IQQU.js";
|
|
17
17
|
import "./chunk-IAJ2JVEH.js";
|
|
18
18
|
import "./chunk-VMEWD57H.js";
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import "tsx/esm";
|
|
3
3
|
import {
|
|
4
4
|
createAgentSsrLoader
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JITNAZXE.js";
|
|
6
6
|
import {
|
|
7
7
|
extractAppResources,
|
|
8
8
|
handleMcpJsonRpc
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2CVV6CNN.js";
|
|
10
10
|
import "./chunk-M6NMS5LR.js";
|
|
11
11
|
import "./chunk-GDN3PXFH.js";
|
|
12
12
|
import {
|
|
@@ -31,7 +31,7 @@ async function handleMcpStdioLine(line, mod, name, appResources = []) {
|
|
|
31
31
|
error: { code: -32700, message: "Parse error" }
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
-
const response = handleMcpJsonRpc(mod, name, body, appResources);
|
|
34
|
+
const response = await handleMcpJsonRpc(mod, name, body, appResources);
|
|
35
35
|
const payload = await response.json();
|
|
36
36
|
return JSON.stringify(payload);
|
|
37
37
|
}
|
|
@@ -75,4 +75,4 @@ async function mcpCommand(name, deps = {}) {
|
|
|
75
75
|
export {
|
|
76
76
|
mcpCommand
|
|
77
77
|
};
|
|
78
|
-
//# sourceMappingURL=mcp-
|
|
78
|
+
//# sourceMappingURL=mcp-P4MHP3VA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/mcp.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * `theokit mcp <agent>` — expose a scanned agent as an MCP server over stdio.\n *\n * The stdio sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`): a desktop MCP client\n * (e.g. Claude Desktop) spawns this command and speaks newline-delimited JSON-RPC over the pipe.\n * It reuses the framework's OWN `serveMcpStdio` / `handleMcpJsonRpc` (no LLM call, no runtime — a\n * TRANSPORT, like the HTTP route). `deps` is injectable so the routing is tested without Vite/stdin.\n */\nimport { createInterface } from 'node:readline'\n\nimport { extractAppResources } from '../../server/agent/mcp-app-resources.js'\nimport { type StdioStreams, serveMcpStdio } from '../../server/agent/mcp-stdio.js'\nimport { scanAgents } from '../../server/scan/agent-scan.js'\n\nimport { createAgentSsrLoader } from './agent.js'\n\nexport interface McpCommandDeps {\n projectRoot?: string\n /** Load a TS agent module; defaults to the framework's Vite SSR loader (same transpile as dev). */\n loadModule?: (filePath: string) => Promise<Record<string, unknown>>\n /** stdio streams; defaults to `readline(process.stdin)` + `process.stdout`. Injected in tests. */\n streams?: StdioStreams\n /** Agents dir name (config `agentsDir`); defaults to the loaded config's value (\"agents\"). Tests inject it. */\n agentsDir?: string\n}\n\nexport async function mcpCommand(name: string, deps: McpCommandDeps = {}): Promise<void> {\n const projectRoot = deps.projectRoot ?? process.cwd()\n // #95 follow-up — resolve agents dir from config (default \"agents\") so `agentsDir` is honored here too.\n const agentsDir =\n deps.agentsDir ??\n (await (await import('../../config/load-config.js')).loadConfig(projectRoot)).agentsDir\n const agent = scanAgents(projectRoot, agentsDir).find((a) => a.name === name)\n if (!agent) {\n const names = scanAgents(projectRoot, agentsDir)\n .map((a) => a.name)\n .join(', ')\n throw new Error(\n `theokit mcp: agent '${name}' not found. Available: ${names || '(none in agents/)'}`,\n )\n }\n\n let loadModule = deps.loadModule\n let dispose: (() => Promise<void>) | undefined\n if (!loadModule) {\n const loader = await createAgentSsrLoader(projectRoot)\n loadModule = loader.load\n dispose = loader.dispose\n }\n\n const streams =\n deps.streams ??\n ({\n lines: createInterface({ input: process.stdin }),\n write: (line: string) => process.stdout.write(line),\n } satisfies StdioStreams)\n\n try {\n const mod = await loadModule(agent.filePath)\n await serveMcpStdio(mod, agent.name, extractAppResources(mod), streams)\n } finally {\n if (dispose) await dispose()\n }\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAQA,SAAS,uBAAuB;;;ACWhC,eAAsB,mBACpB,MACA,KACA,MACA,eAAuC,CAAC,GAChB;AACxB,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,KAAK,UAAU;AAAA,MACpB,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,OAAO,EAAE,MAAM,QAAQ,SAAS,cAAc;AAAA,IAChD,CAAC;AAAA,EACH;AACA,QAAM,WAAW,iBAAiB,KAAK,MAAM,MAAM,YAAY;
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/mcp.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * `theokit mcp <agent>` — expose a scanned agent as an MCP server over stdio.\n *\n * The stdio sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`): a desktop MCP client\n * (e.g. Claude Desktop) spawns this command and speaks newline-delimited JSON-RPC over the pipe.\n * It reuses the framework's OWN `serveMcpStdio` / `handleMcpJsonRpc` (no LLM call, no runtime — a\n * TRANSPORT, like the HTTP route). `deps` is injectable so the routing is tested without Vite/stdin.\n */\nimport { createInterface } from 'node:readline'\n\nimport { extractAppResources } from '../../server/agent/mcp-app-resources.js'\nimport { type StdioStreams, serveMcpStdio } from '../../server/agent/mcp-stdio.js'\nimport { scanAgents } from '../../server/scan/agent-scan.js'\n\nimport { createAgentSsrLoader } from './agent.js'\n\nexport interface McpCommandDeps {\n projectRoot?: string\n /** Load a TS agent module; defaults to the framework's Vite SSR loader (same transpile as dev). */\n loadModule?: (filePath: string) => Promise<Record<string, unknown>>\n /** stdio streams; defaults to `readline(process.stdin)` + `process.stdout`. Injected in tests. */\n streams?: StdioStreams\n /** Agents dir name (config `agentsDir`); defaults to the loaded config's value (\"agents\"). Tests inject it. */\n agentsDir?: string\n}\n\nexport async function mcpCommand(name: string, deps: McpCommandDeps = {}): Promise<void> {\n const projectRoot = deps.projectRoot ?? process.cwd()\n // #95 follow-up — resolve agents dir from config (default \"agents\") so `agentsDir` is honored here too.\n const agentsDir =\n deps.agentsDir ??\n (await (await import('../../config/load-config.js')).loadConfig(projectRoot)).agentsDir\n const agent = scanAgents(projectRoot, agentsDir).find((a) => a.name === name)\n if (!agent) {\n const names = scanAgents(projectRoot, agentsDir)\n .map((a) => a.name)\n .join(', ')\n throw new Error(\n `theokit mcp: agent '${name}' not found. Available: ${names || '(none in agents/)'}`,\n )\n }\n\n let loadModule = deps.loadModule\n let dispose: (() => Promise<void>) | undefined\n if (!loadModule) {\n const loader = await createAgentSsrLoader(projectRoot)\n loadModule = loader.load\n dispose = loader.dispose\n }\n\n const streams =\n deps.streams ??\n ({\n lines: createInterface({ input: process.stdin }),\n write: (line: string) => process.stdout.write(line),\n } satisfies StdioStreams)\n\n try {\n const mod = await loadModule(agent.filePath)\n await serveMcpStdio(mod, agent.name, extractAppResources(mod), streams)\n } finally {\n if (dispose) await dispose()\n }\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = await handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAQA,SAAS,uBAAuB;;;ACWhC,eAAsB,mBACpB,MACA,KACA,MACA,eAAuC,CAAC,GAChB;AACxB,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,KAAK,UAAU;AAAA,MACpB,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,OAAO,EAAE,MAAM,QAAQ,SAAS,cAAc;AAAA,IAChD,CAAC;AAAA,EACH;AACA,QAAM,WAAW,MAAM,iBAAiB,KAAK,MAAM,MAAM,YAAY;AACrE,QAAM,UAAmB,MAAM,SAAS,KAAK;AAC7C,SAAO,KAAK,UAAU,OAAO;AAC/B;AAcA,eAAsB,cACpB,KACA,MACA,cACA,SACe;AACf,mBAAiB,QAAQ,QAAQ,OAAO;AACtC,UAAM,MAAM,MAAM,mBAAmB,MAAM,KAAK,MAAM,YAAY;AAClE,QAAI,QAAQ,KAAM,SAAQ,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,EAC5C;AACF;;;ADtCA,eAAsB,WAAW,MAAc,OAAuB,CAAC,GAAkB;AACvF,QAAM,cAAc,KAAK,eAAe,QAAQ,IAAI;AAEpD,QAAM,YACJ,KAAK,cACJ,OAAO,MAAM,OAAO,2BAA6B,GAAG,WAAW,WAAW,GAAG;AAChF,QAAM,QAAQ,WAAW,aAAa,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC5E,MAAI,CAAC,OAAO;AACV,UAAM,QAAQ,WAAW,aAAa,SAAS,EAC5C,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,uBAAuB,IAAI,2BAA2B,SAAS,mBAAmB;AAAA,IACpF;AAAA,EACF;AAEA,MAAI,aAAa,KAAK;AACtB,MAAI;AACJ,MAAI,CAAC,YAAY;AACf,UAAM,SAAS,MAAM,qBAAqB,WAAW;AACrD,iBAAa,OAAO;AACpB,cAAU,OAAO;AAAA,EACnB;AAEA,QAAM,UACJ,KAAK,WACJ;AAAA,IACC,OAAO,gBAAgB,EAAE,OAAO,QAAQ,MAAM,CAAC;AAAA,IAC/C,OAAO,CAAC,SAAiB,QAAQ,OAAO,MAAM,IAAI;AAAA,EACpD;AAEF,MAAI;AACF,UAAM,MAAM,MAAM,WAAW,MAAM,QAAQ;AAC3C,UAAM,cAAc,KAAK,MAAM,MAAM,oBAAoB,GAAG,GAAG,OAAO;AAAA,EACxE,UAAE;AACA,QAAI,QAAS,OAAM,QAAQ;AAAA,EAC7B;AACF;","names":[]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1115,6 +1115,167 @@ interface ExecuteWebRequestOptions {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
declare function executeWebRequest(request: Request, routeModule: WebRouteModule, opts?: ExecuteWebRequestOptions): Promise<Response>;
|
|
1117
1117
|
|
|
1118
|
+
/**
|
|
1119
|
+
* M33 — the SINGLE route-input validation pipeline, shared by the HTTP path (`web-handler.ts`
|
|
1120
|
+
* `runHandler`) and the in-process caller (`in-process-caller.ts` `callProcedure`).
|
|
1121
|
+
*
|
|
1122
|
+
* Both surfaces validate the same three channels (`query`/`body`/`params`) against the same
|
|
1123
|
+
* `RouteConfig` Zod schemas. Extracting the logic here means there is ONE validation pipeline
|
|
1124
|
+
* (no duplication, no drift) — each caller only differs in how it MAPS a failure: the HTTP path
|
|
1125
|
+
* emits a 400 Response, the in-process path throws a typed error.
|
|
1126
|
+
*/
|
|
1127
|
+
|
|
1128
|
+
/** The three request-input channels a route may declare a Zod schema for. */
|
|
1129
|
+
type RouteInputChannel = 'query' | 'body' | 'params';
|
|
1130
|
+
/**
|
|
1131
|
+
* The minimal structural shape the validator needs: the three optional channel schemas. Both
|
|
1132
|
+
* `RouteConfig` (the 5-arity generic contract) and `WebRouteHandlerConfig` (the Web runtime shape)
|
|
1133
|
+
* satisfy this — the validator depends only on the schemas, not the full route generic.
|
|
1134
|
+
*/
|
|
1135
|
+
interface RouteInputSchemas {
|
|
1136
|
+
query?: z.ZodType;
|
|
1137
|
+
body?: z.ZodType;
|
|
1138
|
+
params?: z.ZodType;
|
|
1139
|
+
}
|
|
1140
|
+
/** Raw (pre-validation) channel values. A channel absent from the config passes through unchanged. */
|
|
1141
|
+
interface RawRouteInput {
|
|
1142
|
+
query?: unknown;
|
|
1143
|
+
body?: unknown;
|
|
1144
|
+
params?: unknown;
|
|
1145
|
+
}
|
|
1146
|
+
/** Discriminated result — each caller maps `ok:false` to its own surface (Response vs throw). */
|
|
1147
|
+
type RouteInputValidation = {
|
|
1148
|
+
ok: true;
|
|
1149
|
+
query: unknown;
|
|
1150
|
+
body: unknown;
|
|
1151
|
+
params: unknown;
|
|
1152
|
+
} | {
|
|
1153
|
+
ok: false;
|
|
1154
|
+
channel: RouteInputChannel;
|
|
1155
|
+
error: z.ZodError;
|
|
1156
|
+
};
|
|
1157
|
+
/**
|
|
1158
|
+
* Validate the three input channels against the route's declared Zod schemas. A channel with no
|
|
1159
|
+
* declared schema passes its raw value through. First failing channel short-circuits (query → body
|
|
1160
|
+
* → params order, matching `runHandler`).
|
|
1161
|
+
*/
|
|
1162
|
+
declare function validateRouteInput(config: RouteInputSchemas, raw: RawRouteInput): RouteInputValidation;
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* M33 Phase 1 — `callProcedure`, the in-process typed caller.
|
|
1166
|
+
*
|
|
1167
|
+
* The load-bearing seam for non-HTTP surfaces (TUI / Tauri / MCP-tools): invoke a route's shared
|
|
1168
|
+
* logic with STRUCTURED input, WITHOUT going through the HTTP transport (no URL/body parsing, no
|
|
1169
|
+
* middleware chain, no Response). It reuses the SAME Zod validation pipeline as the HTTP path
|
|
1170
|
+
* (`validateRouteInput`) so there is no drift, and reuses the SAME `config.response` server-fault
|
|
1171
|
+
* check. Failures throw typed errors (there is no HTTP status off-web).
|
|
1172
|
+
*
|
|
1173
|
+
* Prior art: tRPC `callProcedure` / `createCallerFactory`
|
|
1174
|
+
* (`.claude/knowledge-base/references/trpc/packages/server/src/unstable-core-do-not-import/router.ts:401,441`).
|
|
1175
|
+
*
|
|
1176
|
+
* Design (ADR-0044 / blueprint §5.4): the AUTHOR passes structured input `{query?, body?, params?}`
|
|
1177
|
+
* — never synthesizes a Request. A minimal in-process `Request` is provided to the handler ONLY so
|
|
1178
|
+
* handlers that read `request.headers`/`request.url` still work (opencode's proven pattern); the
|
|
1179
|
+
* input itself is NOT parsed from it.
|
|
1180
|
+
*/
|
|
1181
|
+
|
|
1182
|
+
/** Structured input for an in-process procedure call — one object per declared channel. */
|
|
1183
|
+
type ProcedureInput = RawRouteInput;
|
|
1184
|
+
/**
|
|
1185
|
+
* Thrown when structured input fails the route's Zod validation. The in-process analog of the HTTP
|
|
1186
|
+
* 400 — carries the failing channel + the Zod issues (typed error, not a magic value; Rule 8).
|
|
1187
|
+
*/
|
|
1188
|
+
declare class ProcedureInputError extends Error {
|
|
1189
|
+
readonly channel: RouteInputChannel;
|
|
1190
|
+
readonly issues: z.core.$ZodIssue[];
|
|
1191
|
+
constructor(channel: RouteInputChannel, error: z.ZodError);
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Thrown when the handler's plain-object return drifts from `config.response` — a SERVER fault
|
|
1195
|
+
* (the handler violated its own declared contract), mirroring the HTTP path's 500 as a throw.
|
|
1196
|
+
*/
|
|
1197
|
+
declare class ProcedureOutputError extends Error {
|
|
1198
|
+
readonly issues: z.core.$ZodIssue[];
|
|
1199
|
+
constructor(error: z.ZodError);
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Invoke a route/procedure's handler in-process with structured, Zod-validated input.
|
|
1203
|
+
*
|
|
1204
|
+
* @param config the `RouteConfig` (what `route().build()` produces).
|
|
1205
|
+
* @param input structured `{query?, body?, params?}` — validated by the route's schemas.
|
|
1206
|
+
* @param ctx the typed run-context the handler receives (built by the CALLER — a TUI/Tauri/MCP
|
|
1207
|
+
* surface supplies its own ctx factory; there is no HTTP middleware chain here).
|
|
1208
|
+
* @returns the handler's result (validated against `config.response` when declared).
|
|
1209
|
+
* @throws ProcedureInputError on invalid structured input (the off-web analog of a 400).
|
|
1210
|
+
* @throws ProcedureOutputError on a handler output that drifts from `config.response` (server fault).
|
|
1211
|
+
*/
|
|
1212
|
+
declare function callProcedure(config: RouteConfig<z.ZodType, z.ZodType, z.ZodType>, input?: ProcedureInput, ctx?: unknown): Promise<unknown>;
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* M33 Phase 1 — the ctx reconciliation contract (ADR-0044 D4 / blueprint §5.2, §8.5).
|
|
1216
|
+
*
|
|
1217
|
+
* ## The problem the deep research verified against our own code
|
|
1218
|
+
*
|
|
1219
|
+
* At runtime (`execute.ts:122-165`) the handler's `ctx` is written by THREE sources, not one:
|
|
1220
|
+
*
|
|
1221
|
+
* 1. **the user `context.ts` factory** — via `runMiddlewareAndContext` (`execute.ts:131`). This is
|
|
1222
|
+
* the ONLY writer the author controls + declares a shape for. **This is the typed surface.**
|
|
1223
|
+
* 2. **plugin decorations** — `pluginRunner.applyDecorations(ctx)` (`execute.ts:124,136`). Plugins
|
|
1224
|
+
* add arbitrary keys (`decorateRequest`) whose types the route author does not see.
|
|
1225
|
+
* 3. **the jobs backend** — `ctx.queue` injected when `jobs.backend` is configured (`execute.ts:141-150`).
|
|
1226
|
+
*
|
|
1227
|
+
* A naive "infer `TCtx` from everything on `ctx`" would LIE — it cannot see writers (2) and (3), so
|
|
1228
|
+
* `ctx.queue` / plugin keys would be typed as present when they are not (or vice-versa). That is the
|
|
1229
|
+
* exact Hono/global-augmentation failure the blueprint refuted (§8.5). oRPC/tRPC avoid it only
|
|
1230
|
+
* because middleware is their SOLE ctx writer; TheoKit is multi-writer.
|
|
1231
|
+
*
|
|
1232
|
+
* ## The reconciliation (what this contract locks)
|
|
1233
|
+
*
|
|
1234
|
+
* The typed `TCtx` a route handler sees reflects **only writer (1)** — the user `context.ts` factory
|
|
1235
|
+
* (`ContextValue` below). Writers (2) and (3) are **explicitly untyped** on the route surface:
|
|
1236
|
+
*
|
|
1237
|
+
* - `ctx.queue` (jobs) is reached through the dedicated {@link JobsAugmentedCtx} helper, NOT the
|
|
1238
|
+
* inferred `TCtx` — a handler that needs the queue opts into the augmented type explicitly.
|
|
1239
|
+
* - plugin-decorated keys are `unknown` by design (a plugin is a cross-cutting, per-app concern;
|
|
1240
|
+
* typing them into every route's `TCtx` would couple routes to plugin internals — G5).
|
|
1241
|
+
*
|
|
1242
|
+
* This keeps the LOCKED 5-arity `RouteConfig` generic (`route-config-generic-arity.test.ts`, GAP-4)
|
|
1243
|
+
* intact — `TCtx` stays the single typed ctx slot; we only define WHICH writer it corresponds to,
|
|
1244
|
+
* so `runtime ⊇ type` holds honestly (the type is a sound subset of the runtime ctx, never a lie).
|
|
1245
|
+
*
|
|
1246
|
+
* Type-tests proving this live in `tests/ctx-reconciliation.test-d.ts`.
|
|
1247
|
+
*/
|
|
1248
|
+
/**
|
|
1249
|
+
* The typed run-context a route handler sees = the return of the user `context.ts` factory.
|
|
1250
|
+
* `TValue` is inferred from that factory at the web adapter seam; it EXCLUDES plugin decorations and
|
|
1251
|
+
* `ctx.queue` (those are not part of the author-declared shape).
|
|
1252
|
+
*/
|
|
1253
|
+
type ContextValue<TValue extends Record<string, unknown> = Record<string, unknown>> = TValue;
|
|
1254
|
+
/** The `ctx.queue` client shape injected by the jobs backend (writer 3). Reached explicitly. */
|
|
1255
|
+
interface QueueClientLike {
|
|
1256
|
+
enqueue(name: string, input: unknown): void | Promise<void>;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Opt-in augmentation for handlers that read `ctx.queue`. A route that uses the jobs queue annotates
|
|
1260
|
+
* its ctx as `JobsAugmentedCtx<MyCtx>` — making the jobs dependency explicit in the type, instead of
|
|
1261
|
+
* silently assuming `ctx.queue` exists on every route (which would lie when `jobs.backend` is unset).
|
|
1262
|
+
*/
|
|
1263
|
+
type JobsAugmentedCtx<TValue extends Record<string, unknown> = Record<string, unknown>> = TValue & {
|
|
1264
|
+
queue: QueueClientLike;
|
|
1265
|
+
};
|
|
1266
|
+
/**
|
|
1267
|
+
* The three runtime ctx writers, named for documentation + the type-test. This is the reconciliation
|
|
1268
|
+
* artifact the plan requires as a deliverable (not a footnote).
|
|
1269
|
+
*/
|
|
1270
|
+
declare const CTX_WRITERS: {
|
|
1271
|
+
/** Writer 1 — user `context.ts` factory. THE typed surface (`TCtx`). execute.ts:131 */
|
|
1272
|
+
readonly contextFactory: "context.ts";
|
|
1273
|
+
/** Writer 2 — plugin `decorateRequest`. Untyped on the route surface. execute.ts:124,136 */
|
|
1274
|
+
readonly pluginDecorations: "pluginRunner.applyDecorations";
|
|
1275
|
+
/** Writer 3 — jobs backend `ctx.queue`. Reached via JobsAugmentedCtx, not TCtx. execute.ts:141-150 */
|
|
1276
|
+
readonly jobsQueue: "jobBackend";
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1118
1279
|
/**
|
|
1119
1280
|
* Types for `loadEnv()` — env auto-load utility (Phase 1 of
|
|
1120
1281
|
* docs/plans/framework-zero-config-polish-plan.md).
|
|
@@ -1156,4 +1317,4 @@ interface LoadEnvResult {
|
|
|
1156
1317
|
declare function _resetEnvCache(): void;
|
|
1157
1318
|
declare function loadEnv(options?: LoadEnvOptions): LoadEnvResult;
|
|
1158
1319
|
|
|
1159
|
-
export { type AcpToolConfig, ActionError, type ActionErrorCode, ActionInputError, type ActionManifestEntry, type ActionResult, type AppResource, type AppResourceInput, type BodyParserOptions, DEFAULT_MAX_AGE as CACHE_DEFAULT_MAX_AGE, DEFAULT_MAX_ENTRY_SIZE as CACHE_DEFAULT_MAX_ENTRY_SIZE, DEFAULT_SWR_MULTIPLIER as CACHE_DEFAULT_SWR_MULTIPLIER, CACHE_TAG_MAX_ITEMS, CACHE_TAG_MAX_LENGTH, type CacheControlInput, type CacheEngine, type CacheEngineOptions, type CacheEntry, type CacheStatus, type CacheStorageAdapter, type CacheStore, type CacheStoreAdmin, type ValidationResult as CacheValidationResult, type CachedFunction, type CachedRouteConfig, type ChannelMessage, type ChannelWebhookConfig, type CodeModeApi, type CodeModeConfig, type CodeModePermission, CodeModePermissionDeniedError, type CompiledRouteRule, CustomTool, DEFAULT_EXCLUDED_QUERY_PARAMS, type DefineCachedFunctionOptions, FileTooLargeError, type GetOrComputeOptions, InMemoryCacheAdapter, type InMemoryCacheAdapterOptions, type KeyDerivationOptions, type LoadEnvOptions, type LoadEnvResult, type McpResourceContents, type McpResourceDescriptor, NodeAcpTransport, type NormalizedCacheConfig, type OpenApiDocsOptions, type ParsedBody, type RevalidateResult, type RouteCacheOptions, RouteConfig, type RouteRule, type RouteRules, type Sandbox, type SerializedActionResult, type SerializedResponse, type StdioStreams, THEO_T_PREFIX, type UniversalZodIssue, type UploadedFile, type VendorAgentClient, type VendorAgentToolConfig, VerifyFn, type WorkflowLike, type WorkflowToolConfig, _resetCacheEngine, _resetEnvCache, buildResourceDescriptors, compileRouteRules, createACPTool, createCacheEngine, createCodeMode, createOpenApiHandler, createVendorAgentTool, createWorkflowTool, defineAppResource, defineCachedFunction, defineCachedRoute, deriveKey as deriveCacheKey, deserializeResponse, executeWebRequest, extractAppResources, extractUniversalIssues, getCacheControlHeader, getCacheEngine, handleChannelWebhook, handleMcpStdioLine, initCacheEngine, isActionError, isChannelPath, isInputError, loadEnv, parseChannelPath, parseRequestBody, readAppResource, resolveRouteRule, revalidatePath, revalidateTag, serializeResponse, serveMcpStdio, updateTag, validateExpire as validateCacheExpire, validateMaxAge as validateCacheMaxAge, validateTags as validateCacheTags };
|
|
1320
|
+
export { type AcpToolConfig, ActionError, type ActionErrorCode, ActionInputError, type ActionManifestEntry, type ActionResult, type AppResource, type AppResourceInput, type BodyParserOptions, DEFAULT_MAX_AGE as CACHE_DEFAULT_MAX_AGE, DEFAULT_MAX_ENTRY_SIZE as CACHE_DEFAULT_MAX_ENTRY_SIZE, DEFAULT_SWR_MULTIPLIER as CACHE_DEFAULT_SWR_MULTIPLIER, CACHE_TAG_MAX_ITEMS, CACHE_TAG_MAX_LENGTH, CTX_WRITERS, type CacheControlInput, type CacheEngine, type CacheEngineOptions, type CacheEntry, type CacheStatus, type CacheStorageAdapter, type CacheStore, type CacheStoreAdmin, type ValidationResult as CacheValidationResult, type CachedFunction, type CachedRouteConfig, type ChannelMessage, type ChannelWebhookConfig, type CodeModeApi, type CodeModeConfig, type CodeModePermission, CodeModePermissionDeniedError, type CompiledRouteRule, type ContextValue, CustomTool, DEFAULT_EXCLUDED_QUERY_PARAMS, type DefineCachedFunctionOptions, FileTooLargeError, type GetOrComputeOptions, InMemoryCacheAdapter, type InMemoryCacheAdapterOptions, type JobsAugmentedCtx, type KeyDerivationOptions, type LoadEnvOptions, type LoadEnvResult, type McpResourceContents, type McpResourceDescriptor, NodeAcpTransport, type NormalizedCacheConfig, type OpenApiDocsOptions, type ParsedBody, type ProcedureInput, ProcedureInputError, ProcedureOutputError, type QueueClientLike, type RawRouteInput, type RevalidateResult, type RouteCacheOptions, RouteConfig, type RouteInputChannel, type RouteInputSchemas, type RouteInputValidation, type RouteRule, type RouteRules, type Sandbox, type SerializedActionResult, type SerializedResponse, type StdioStreams, THEO_T_PREFIX, type UniversalZodIssue, type UploadedFile, type VendorAgentClient, type VendorAgentToolConfig, VerifyFn, type WorkflowLike, type WorkflowToolConfig, _resetCacheEngine, _resetEnvCache, buildResourceDescriptors, callProcedure, compileRouteRules, createACPTool, createCacheEngine, createCodeMode, createOpenApiHandler, createVendorAgentTool, createWorkflowTool, defineAppResource, defineCachedFunction, defineCachedRoute, deriveKey as deriveCacheKey, deserializeResponse, executeWebRequest, extractAppResources, extractUniversalIssues, getCacheControlHeader, getCacheEngine, handleChannelWebhook, handleMcpStdioLine, initCacheEngine, isActionError, isChannelPath, isInputError, loadEnv, parseChannelPath, parseRequestBody, readAppResource, resolveRouteRule, revalidatePath, revalidateTag, serializeResponse, serveMcpStdio, updateTag, validateExpire as validateCacheExpire, validateMaxAge as validateCacheMaxAge, validateTags as validateCacheTags, validateRouteInput };
|
package/dist/server/index.js
CHANGED
|
@@ -59,8 +59,9 @@ import {
|
|
|
59
59
|
loadEnv,
|
|
60
60
|
readAppResource,
|
|
61
61
|
resolveTransformer,
|
|
62
|
-
superjsonTransformer
|
|
63
|
-
|
|
62
|
+
superjsonTransformer,
|
|
63
|
+
validateRouteInput
|
|
64
|
+
} from "../chunk-W4XWVTN4.js";
|
|
64
65
|
import {
|
|
65
66
|
findSuggestion,
|
|
66
67
|
levenshtein
|
|
@@ -1328,7 +1329,7 @@ async function handleMcpStdioLine(line, mod, name, appResources = []) {
|
|
|
1328
1329
|
error: { code: -32700, message: "Parse error" }
|
|
1329
1330
|
});
|
|
1330
1331
|
}
|
|
1331
|
-
const response = handleMcpJsonRpc(mod, name, body, appResources);
|
|
1332
|
+
const response = await handleMcpJsonRpc(mod, name, body, appResources);
|
|
1332
1333
|
const payload = await response.json();
|
|
1333
1334
|
return JSON.stringify(payload);
|
|
1334
1335
|
}
|
|
@@ -1349,6 +1350,57 @@ function deserializeResponse(serialized) {
|
|
|
1349
1350
|
return superjson.deserialize(serialized);
|
|
1350
1351
|
}
|
|
1351
1352
|
|
|
1353
|
+
// src/server/http/in-process-caller.ts
|
|
1354
|
+
var ProcedureInputError = class extends Error {
|
|
1355
|
+
channel;
|
|
1356
|
+
issues;
|
|
1357
|
+
constructor(channel, error) {
|
|
1358
|
+
super(`Invalid ${channel} for in-process procedure call: ${error.message}`);
|
|
1359
|
+
this.name = "ProcedureInputError";
|
|
1360
|
+
this.channel = channel;
|
|
1361
|
+
this.issues = error.issues;
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
var ProcedureOutputError = class extends Error {
|
|
1365
|
+
issues;
|
|
1366
|
+
constructor(error) {
|
|
1367
|
+
super(`In-process procedure output failed its response schema: ${error.message}`);
|
|
1368
|
+
this.name = "ProcedureOutputError";
|
|
1369
|
+
this.issues = error.issues;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
function inProcessRequest() {
|
|
1373
|
+
return new Request("theo://in-process/procedure", { method: "POST" });
|
|
1374
|
+
}
|
|
1375
|
+
async function callProcedure(config, input = {}, ctx) {
|
|
1376
|
+
const validated = validateRouteInput(config, input);
|
|
1377
|
+
if (!validated.ok) throw new ProcedureInputError(validated.channel, validated.error);
|
|
1378
|
+
const result = await config.handler({
|
|
1379
|
+
query: validated.query,
|
|
1380
|
+
body: validated.body,
|
|
1381
|
+
params: validated.params,
|
|
1382
|
+
request: inProcessRequest(),
|
|
1383
|
+
ctx
|
|
1384
|
+
});
|
|
1385
|
+
const responseSchema = config.response;
|
|
1386
|
+
if (responseSchema !== void 0 && !(result instanceof Response)) {
|
|
1387
|
+
const parsed = responseSchema.safeParse(result);
|
|
1388
|
+
if (!parsed.success) throw new ProcedureOutputError(parsed.error);
|
|
1389
|
+
return parsed.data;
|
|
1390
|
+
}
|
|
1391
|
+
return result;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/server/http/ctx-reconciliation.ts
|
|
1395
|
+
var CTX_WRITERS = {
|
|
1396
|
+
/** Writer 1 — user `context.ts` factory. THE typed surface (`TCtx`). execute.ts:131 */
|
|
1397
|
+
contextFactory: "context.ts",
|
|
1398
|
+
/** Writer 2 — plugin `decorateRequest`. Untyped on the route surface. execute.ts:124,136 */
|
|
1399
|
+
pluginDecorations: "pluginRunner.applyDecorations",
|
|
1400
|
+
/** Writer 3 — jobs backend `ctx.queue`. Reached via JobsAugmentedCtx, not TCtx. execute.ts:141-150 */
|
|
1401
|
+
jobsQueue: "jobBackend"
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1352
1404
|
// src/server/index.ts
|
|
1353
1405
|
if (typeof globalThis !== "undefined") {
|
|
1354
1406
|
const WARN_KEY = "__theokit_server_umbrella_warn_emitted__";
|
|
@@ -1379,6 +1431,7 @@ export {
|
|
|
1379
1431
|
CSRF_READINESS_RESET_PATH,
|
|
1380
1432
|
CSRF_WARN_CODE,
|
|
1381
1433
|
CSRF_WARN_DOCS_URL,
|
|
1434
|
+
CTX_WRITERS,
|
|
1382
1435
|
ChannelManager,
|
|
1383
1436
|
CodeModePermissionDeniedError,
|
|
1384
1437
|
ConsoleObservabilityAdapter,
|
|
@@ -1410,6 +1463,8 @@ export {
|
|
|
1410
1463
|
NotFoundError,
|
|
1411
1464
|
PluginRunner,
|
|
1412
1465
|
PostgresJobBackend,
|
|
1466
|
+
ProcedureInputError,
|
|
1467
|
+
ProcedureOutputError,
|
|
1413
1468
|
READY_PATH,
|
|
1414
1469
|
STRIPPED_HEADERS,
|
|
1415
1470
|
SpanImpl,
|
|
@@ -1434,6 +1489,7 @@ export {
|
|
|
1434
1489
|
buildJobManifest,
|
|
1435
1490
|
buildResourceDescriptors,
|
|
1436
1491
|
buildSecurityHeaders,
|
|
1492
|
+
callProcedure,
|
|
1437
1493
|
checkThrottle,
|
|
1438
1494
|
clearOidcCache,
|
|
1439
1495
|
compilePattern,
|
|
@@ -1590,6 +1646,7 @@ export {
|
|
|
1590
1646
|
validateCronSchedule,
|
|
1591
1647
|
validateCsrf,
|
|
1592
1648
|
validateCsrfRequest,
|
|
1649
|
+
validateRouteInput,
|
|
1593
1650
|
verifyBackupCode,
|
|
1594
1651
|
verifyOAuthState,
|
|
1595
1652
|
verifyTotp,
|