@plasm_lang/vercel-agent 0.3.91 → 0.3.92
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/package.json +3 -2
- package/src/authoring/context.ts +4 -1
- package/src/cli/build-manifest.ts +1 -0
- package/src/cli/build.ts +9 -0
- package/src/cli/compile-authored-slots.ts +11 -0
- package/src/cli/compile-generated-stubs.ts +97 -0
- package/src/nitro/create-plasm-nitro.ts +37 -3
- package/src/nitro/write-workflow-dispatch-route.ts +2 -2
- package/src/server/plasm-handler.ts +1 -0
- package/src/stubs/generator.ts +20 -1
- package/src/stubs/stub-runtime.ts +10 -0
- package/templates/mcp-radar/lib/start-mcp-radar.ts +8 -2
- package/templates/mcp-radar/workflows/mcp-radar-scan.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasm_lang/vercel-agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.92",
|
|
4
4
|
"description": "Catalog-native TypeScript agent framework (Plasm CGS/CML, Vercel AI SDK, Nitro-oriented)",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"./server": "./src/server/plasm-handler.ts",
|
|
20
20
|
"./instrumentation": "./src/instrumentation.ts",
|
|
21
21
|
"./operator": "./src/operator/routes.ts",
|
|
22
|
+
"./stub-runtime": "./src/stubs/stub-runtime.ts",
|
|
22
23
|
"./agent": "./agent/agent.ts"
|
|
23
24
|
},
|
|
24
25
|
"bin": {
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"dependencies": {
|
|
64
65
|
"@ai-sdk/otel": "^1.0.3",
|
|
65
66
|
"@opentelemetry/api": "^1.9.0",
|
|
66
|
-
"@plasm_lang/engine": "^0.3.
|
|
67
|
+
"@plasm_lang/engine": "^0.3.92",
|
|
67
68
|
"@vercel/blob": "^0.27.3",
|
|
68
69
|
"@vercel/connect": "^0.2.6",
|
|
69
70
|
"@vercel/kv": "^3.0.0",
|
package/src/authoring/context.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface CreateAuthoringContextOptions {
|
|
|
14
14
|
agentRoot: string;
|
|
15
15
|
getAgent: () => Promise<PlasmAgent>;
|
|
16
16
|
importCacheBust?: number;
|
|
17
|
+
/** Generated stub extension — `.mjs` on Vercel (bundled at build); `.ts` in dev. */
|
|
18
|
+
stubImportExt?: "ts" | "mjs";
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function createAuthoringContext(
|
|
@@ -21,12 +23,13 @@ export function createAuthoringContext(
|
|
|
21
23
|
): AuthoringContext {
|
|
22
24
|
const agentRoot = path.resolve(options.agentRoot);
|
|
23
25
|
const bust = options.importCacheBust ?? Date.now();
|
|
26
|
+
const stubExt = options.stubImportExt ?? "ts";
|
|
24
27
|
|
|
25
28
|
return {
|
|
26
29
|
agentRoot,
|
|
27
30
|
getAgent: options.getAgent,
|
|
28
31
|
importStub: async (entryId: string) => {
|
|
29
|
-
const stubPath = path.join(agentRoot, ".plasm", "stubs", `${entryId}
|
|
32
|
+
const stubPath = path.join(agentRoot, ".plasm", "stubs", `${entryId}.${stubExt}`);
|
|
30
33
|
const url = `${pathToFileURL(stubPath).href}?t=${bust}`;
|
|
31
34
|
return import(url);
|
|
32
35
|
},
|
package/src/cli/build.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { readBuildManifest } from "./build-manifest.js";
|
|
5
|
+
import { compileGeneratedStubs } from "./compile-generated-stubs.js";
|
|
5
6
|
import { compileAuthoredSlots } from "./compile-authored-slots.js";
|
|
6
7
|
import type { ResolvedAgentProject } from "./project-root.js";
|
|
7
8
|
import { isNativeEngineAvailable } from "../engine/napi-binding.js";
|
|
@@ -15,6 +16,7 @@ export interface PlasmBuildResult {
|
|
|
15
16
|
discoveryDir: string;
|
|
16
17
|
manifestPath: string;
|
|
17
18
|
stubs: Array<{ entryId: string; catalogCgsHash: string; outPath: string }>;
|
|
19
|
+
compiledStubs: Record<string, string>;
|
|
18
20
|
outputDir?: string;
|
|
19
21
|
agentSummaryPath?: string;
|
|
20
22
|
vercelOutput?: boolean;
|
|
@@ -22,6 +24,11 @@ export interface PlasmBuildResult {
|
|
|
22
24
|
|
|
23
25
|
export async function runPlasmBuild(project: ResolvedAgentProject): Promise<PlasmBuildResult> {
|
|
24
26
|
const stubs = await generateAllStubs(project.agentRoot);
|
|
27
|
+
const { compiledStubs } = await compileGeneratedStubs(
|
|
28
|
+
project.projectRoot,
|
|
29
|
+
project.agentRoot,
|
|
30
|
+
stubs,
|
|
31
|
+
);
|
|
25
32
|
const discovery = await walkAgentProject(project.agentRoot);
|
|
26
33
|
const { compiledSlots } = await compileAuthoredSlots(
|
|
27
34
|
project.projectRoot,
|
|
@@ -38,6 +45,7 @@ export async function runPlasmBuild(project: ResolvedAgentProject): Promise<Plas
|
|
|
38
45
|
projectRoot: project.projectRoot,
|
|
39
46
|
agentRoot: project.agentRoot,
|
|
40
47
|
compiledSlots,
|
|
48
|
+
compiledStubs,
|
|
41
49
|
stubs: stubs.map((s) => ({
|
|
42
50
|
entryId: s.entryId,
|
|
43
51
|
catalogCgsHash: s.catalogCgsHash,
|
|
@@ -74,6 +82,7 @@ export async function runPlasmBuild(project: ResolvedAgentProject): Promise<Plas
|
|
|
74
82
|
discoveryDir,
|
|
75
83
|
manifestPath,
|
|
76
84
|
stubs,
|
|
85
|
+
compiledStubs,
|
|
77
86
|
outputDir: appBuild.outputDir,
|
|
78
87
|
agentSummaryPath: appBuild.agentSummaryPath,
|
|
79
88
|
vercelOutput: appBuild.vercelOutput,
|
|
@@ -71,6 +71,17 @@ export async function compileAuthoredSlots(
|
|
|
71
71
|
"workflow",
|
|
72
72
|
"workflow/api",
|
|
73
73
|
],
|
|
74
|
+
plugins: [
|
|
75
|
+
{
|
|
76
|
+
name: "external-workflows",
|
|
77
|
+
setup(build) {
|
|
78
|
+
build.onResolve({ filter: /\/workflows\// }, (args) => ({
|
|
79
|
+
path: args.path,
|
|
80
|
+
external: true,
|
|
81
|
+
}));
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
74
85
|
logLevel: "silent",
|
|
75
86
|
});
|
|
76
87
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import * as esbuild from "esbuild";
|
|
6
|
+
|
|
7
|
+
import type { StubGenerationResult } from "../stubs/generator.js";
|
|
8
|
+
|
|
9
|
+
const ESBUILD_BANNER =
|
|
10
|
+
'import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);';
|
|
11
|
+
|
|
12
|
+
const STUB_RUNTIME_SOURCE = path.join(
|
|
13
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
14
|
+
"../stubs/stub-runtime.ts",
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const STUB_RUNTIME_BASENAME = "_stub-runtime.mjs";
|
|
18
|
+
|
|
19
|
+
/** Native engine stays external; runtime is a shared sibling module on Vercel. */
|
|
20
|
+
const STUB_RUNTIME_EXTERNAL = ["@plasm_lang/engine"];
|
|
21
|
+
|
|
22
|
+
export interface CompileGeneratedStubsResult {
|
|
23
|
+
/** entry_id → projectRoot-relative compiled `.mjs` path */
|
|
24
|
+
compiledStubs: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function stubRuntimeExternalPlugin(): esbuild.Plugin {
|
|
28
|
+
return {
|
|
29
|
+
name: "stub-runtime-sibling",
|
|
30
|
+
setup(build) {
|
|
31
|
+
build.onResolve({ filter: /^\.\/_stub-runtime\.js$/ }, () => ({
|
|
32
|
+
path: "./_stub-runtime.mjs",
|
|
33
|
+
external: true,
|
|
34
|
+
}));
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function compileStubRuntime(stubsDir: string): Promise<string> {
|
|
40
|
+
const runtimePath = path.join(stubsDir, STUB_RUNTIME_BASENAME);
|
|
41
|
+
await esbuild.build({
|
|
42
|
+
entryPoints: [STUB_RUNTIME_SOURCE],
|
|
43
|
+
bundle: true,
|
|
44
|
+
platform: "node",
|
|
45
|
+
target: "node22",
|
|
46
|
+
format: "esm",
|
|
47
|
+
outfile: runtimePath,
|
|
48
|
+
banner: { js: ESBUILD_BANNER },
|
|
49
|
+
external: STUB_RUNTIME_EXTERNAL,
|
|
50
|
+
logLevel: "silent",
|
|
51
|
+
});
|
|
52
|
+
return runtimePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Compile generated CGS stubs to `.mjs` for serverless dynamic import. */
|
|
56
|
+
export async function compileGeneratedStubs(
|
|
57
|
+
projectRoot: string,
|
|
58
|
+
agentRoot: string,
|
|
59
|
+
stubs: StubGenerationResult[],
|
|
60
|
+
): Promise<CompileGeneratedStubsResult> {
|
|
61
|
+
const stubsDir = path.join(agentRoot, ".plasm", "stubs");
|
|
62
|
+
await mkdir(stubsDir, { recursive: true });
|
|
63
|
+
await compileStubRuntime(stubsDir);
|
|
64
|
+
|
|
65
|
+
const compiledStubs: Record<string, string> = {};
|
|
66
|
+
|
|
67
|
+
await Promise.all(
|
|
68
|
+
stubs.map(async (stub) => {
|
|
69
|
+
const entryId = stub.entryId;
|
|
70
|
+
const tsPath = stub.outPath;
|
|
71
|
+
const mjsPath = tsPath.replace(/\.ts$/, ".mjs");
|
|
72
|
+
|
|
73
|
+
await esbuild.build({
|
|
74
|
+
entryPoints: [tsPath],
|
|
75
|
+
bundle: true,
|
|
76
|
+
platform: "node",
|
|
77
|
+
target: "node22",
|
|
78
|
+
format: "esm",
|
|
79
|
+
outfile: mjsPath,
|
|
80
|
+
banner: { js: ESBUILD_BANNER },
|
|
81
|
+
external: STUB_RUNTIME_EXTERNAL,
|
|
82
|
+
plugins: [stubRuntimeExternalPlugin()],
|
|
83
|
+
logLevel: "silent",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
compiledStubs[entryId] = path.relative(projectRoot, mjsPath);
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
await writeFile(
|
|
91
|
+
path.join(stubsDir, "package.json"),
|
|
92
|
+
`${JSON.stringify({ type: "module" }, null, 2)}\n`,
|
|
93
|
+
"utf8",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return { compiledStubs };
|
|
97
|
+
};
|
|
@@ -44,6 +44,42 @@ async function publicDirExists(projectRoot: string): Promise<boolean> {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
async function pathExists(p: string): Promise<boolean> {
|
|
48
|
+
try {
|
|
49
|
+
await access(p);
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function plasmNitroHandlers(
|
|
57
|
+
projectRoot: string,
|
|
58
|
+
workflowEnabled: boolean,
|
|
59
|
+
): Promise<NonNullable<NitroConfig["handlers"]>> {
|
|
60
|
+
const catchAllHandler = path.join(plasmNitroRoutesDir(projectRoot), "[[path]].ts");
|
|
61
|
+
const handlers: NonNullable<NitroConfig["handlers"]> = [];
|
|
62
|
+
|
|
63
|
+
if (workflowEnabled) {
|
|
64
|
+
const dispatchHandler = path.join(
|
|
65
|
+
plasmNitroRoutesDir(projectRoot),
|
|
66
|
+
"internal",
|
|
67
|
+
"workflow",
|
|
68
|
+
"dispatch.post.ts",
|
|
69
|
+
);
|
|
70
|
+
if (await pathExists(dispatchHandler)) {
|
|
71
|
+
handlers.push({
|
|
72
|
+
route: "/internal/workflow/dispatch",
|
|
73
|
+
handler: dispatchHandler,
|
|
74
|
+
method: "post",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handlers.push({ route: "/**", handler: catchAllHandler });
|
|
80
|
+
return handlers;
|
|
81
|
+
}
|
|
82
|
+
|
|
47
83
|
export async function createPlasmNitro(
|
|
48
84
|
host: PreparedPlasmHost,
|
|
49
85
|
dev: boolean,
|
|
@@ -59,8 +95,6 @@ export async function createPlasmNitro(
|
|
|
59
95
|
|
|
60
96
|
const traceDeps = [...new Set([...SERVER_TRACE_DEPS, ...host.externalDeps])];
|
|
61
97
|
|
|
62
|
-
const catchAllHandler = path.join(plasmNitroRoutesDir(host.projectRoot), "[[path]].ts");
|
|
63
|
-
|
|
64
98
|
const publicAssets = (await publicDirExists(host.projectRoot))
|
|
65
99
|
? [
|
|
66
100
|
{
|
|
@@ -80,7 +114,7 @@ export async function createPlasmNitro(
|
|
|
80
114
|
dev,
|
|
81
115
|
serverDir: false,
|
|
82
116
|
ignore: ["api/**", "server/**", "routes/**", "nitro.config.ts"],
|
|
83
|
-
handlers:
|
|
117
|
+
handlers: await plasmNitroHandlers(host.projectRoot, host.workflowEnabled),
|
|
84
118
|
publicAssets,
|
|
85
119
|
modules,
|
|
86
120
|
experimental: {
|
|
@@ -11,7 +11,7 @@ export async function writeWorkflowDispatchRoute(projectRoot: string): Promise<s
|
|
|
11
11
|
const source = `import path from "node:path";
|
|
12
12
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
13
13
|
|
|
14
|
-
import agentDefinition from "
|
|
14
|
+
import agentDefinition from "../../../../../agent/agent.js";
|
|
15
15
|
import { createPlasmApp } from "@plasm_lang/vercel-agent/server";
|
|
16
16
|
|
|
17
17
|
const agentRoot = path.join(process.cwd(), "agent");
|
|
@@ -53,7 +53,7 @@ export default async (event: NitroNodeEvent) => {
|
|
|
53
53
|
mode: "prod",
|
|
54
54
|
sessions: false,
|
|
55
55
|
});
|
|
56
|
-
const { runRadar } = await import("
|
|
56
|
+
const { runRadar } = await import("../../../../../lib/run-radar.js");
|
|
57
57
|
const result = await runRadar(app.getAuthoringContext(), { force });
|
|
58
58
|
|
|
59
59
|
res.statusCode = 200;
|
|
@@ -229,6 +229,7 @@ export async function createPlasmApp(options: PlasmAppOptions): Promise<PlasmApp
|
|
|
229
229
|
agentRoot,
|
|
230
230
|
getAgent: async () => agent ?? bootstrap(),
|
|
231
231
|
importCacheBust,
|
|
232
|
+
stubImportExt: mode === "prod" ? "mjs" : "ts",
|
|
232
233
|
});
|
|
233
234
|
|
|
234
235
|
const bootstrap = async (): Promise<PlasmAgent> => {
|
package/src/stubs/generator.ts
CHANGED
|
@@ -59,6 +59,23 @@ export function computeCatalogCgsHash(domainYaml: string, mappingsYaml: string):
|
|
|
59
59
|
return createHash("sha256").update(domainYaml).update("\n").update(mappingsYaml).digest("hex");
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/** Dev-time shim beside generated stubs; prod uses `_stub-runtime.mjs` from compile. */
|
|
63
|
+
export async function writeStubRuntimeShim(outDir: string): Promise<void> {
|
|
64
|
+
await mkdir(outDir, { recursive: true });
|
|
65
|
+
const source = `/** @generated plasm-agent stub runtime shim (dev only) */
|
|
66
|
+
export {
|
|
67
|
+
buildDottedArgs,
|
|
68
|
+
createCatalogClient,
|
|
69
|
+
executeRows,
|
|
70
|
+
plasmBoolean,
|
|
71
|
+
plasmLiteral,
|
|
72
|
+
plasmNumber,
|
|
73
|
+
type StubInvokeOptions,
|
|
74
|
+
} from "@plasm_lang/vercel-agent/stub-runtime";
|
|
75
|
+
`;
|
|
76
|
+
await writeFile(path.join(outDir, "_stub-runtime.ts"), source, "utf8");
|
|
77
|
+
}
|
|
78
|
+
|
|
62
79
|
/** Legacy parser — delegates to {@link parseCgsDomain}. */
|
|
63
80
|
export function parseDomainYaml(raw: string, fallbackEntryId: string): ParsedDomain {
|
|
64
81
|
return toLegacyParsedDomain(parseCgsDomain(raw, fallbackEntryId));
|
|
@@ -154,7 +171,7 @@ export function renderStubModule(
|
|
|
154
171
|
bindings?: Map<string, CapabilityBinding>,
|
|
155
172
|
): string {
|
|
156
173
|
const exportName = toExportName(catalog.entry_id);
|
|
157
|
-
const clientImport = "
|
|
174
|
+
const clientImport = "./_stub-runtime.js";
|
|
158
175
|
|
|
159
176
|
const entityTypes = new Set<string>();
|
|
160
177
|
const inputTypes = new Set<string>();
|
|
@@ -288,6 +305,8 @@ export async function generateAllStubs(
|
|
|
288
305
|
const outDir = path.join(agentRoot, ".plasm", "stubs");
|
|
289
306
|
const { access, readdir, stat } = await import("node:fs/promises");
|
|
290
307
|
|
|
308
|
+
await writeStubRuntimeShim(outDir);
|
|
309
|
+
|
|
291
310
|
const engine = options?.engine ?? createEngine();
|
|
292
311
|
|
|
293
312
|
let entries;
|
|
@@ -2,11 +2,17 @@ import type { AuthoringContext } from "@plasm_lang/vercel-agent";
|
|
|
2
2
|
|
|
3
3
|
import type { RadarRunOptions } from "./run-radar.js";
|
|
4
4
|
|
|
5
|
+
/** Must match @workflow/nitro manifest for `workflows/mcp-radar-scan.ts`. */
|
|
6
|
+
const MCP_RADAR_SCAN_WORKFLOW_ID =
|
|
7
|
+
"workflow//./workflows/mcp-radar-scan//mcpRadarScanWorkflow";
|
|
8
|
+
|
|
5
9
|
export async function startMcpRadarRun(
|
|
6
10
|
ctx: AuthoringContext,
|
|
7
11
|
options: RadarRunOptions = {},
|
|
8
12
|
): Promise<unknown> {
|
|
9
13
|
const { start } = await import("workflow/api");
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
return start({ workflowId: MCP_RADAR_SCAN_WORKFLOW_ID }, [
|
|
15
|
+
ctx.agentRoot,
|
|
16
|
+
options.force === true,
|
|
17
|
+
]);
|
|
12
18
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
function workflowDispatchUrl(): string {
|
|
2
2
|
const explicit = process.env.PLASM_WORKFLOW_DISPATCH_URL?.trim();
|
|
3
3
|
if (explicit) return explicit;
|
|
4
|
+
const production = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
5
|
+
if (production) return `https://${production}/internal/workflow/dispatch`;
|
|
4
6
|
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
5
7
|
if (vercelUrl) return `https://${vercelUrl}/internal/workflow/dispatch`;
|
|
6
8
|
const port = process.env.PORT ?? "3000";
|