@plasm_lang/vercel-agent 0.3.77 → 0.3.80
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/README.md +9 -2
- package/agent/instructions.md +1 -2
- package/bin/plasm-agent.mjs +5 -7
- package/package.json +29 -19
- package/scripts/build-stubs.ts +27 -0
- package/scripts/plasm-cli.ts +162 -0
- package/scripts/register-plasm-loader.mjs +6 -0
- package/scripts/run-plasm-cli.mjs +41 -0
- package/src/cli/init-scaffold.ts +198 -0
- package/src/cli/init.ts +20 -208
- package/src/cli/nitro-dev.ts +69 -0
- package/src/cli/node-dev-imports.ts +14 -0
- package/src/engine/create-host-transport.ts +7 -0
- package/src/index.ts +1 -1
- package/src/package-version.ts +14 -0
- package/src/project-info.ts +2 -1
- package/src/stubs/catalog-client.ts +2 -2
- package/src/tools/harness-tools.ts +42 -41
- package/src/tools/plasm-tools.ts +64 -55
- package/src/tools/tool-input.ts +9 -0
- package/templates/mcp-radar/.vercelignore +3 -0
- package/templates/mcp-radar/README.md +139 -0
- package/templates/mcp-radar/agent/agent.ts +38 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/README.md +23 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/domain.yaml +329 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/eval/cases.yaml +112 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/mappings.yaml +129 -0
- package/templates/mcp-radar/agent/catalogs/tavily/README.md +218 -0
- package/templates/mcp-radar/agent/catalogs/tavily/domain.yaml +373 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/cases.yaml +56 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.human.txt +67 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.json +363 -0
- package/templates/mcp-radar/agent/catalogs/tavily/mappings.yaml +171 -0
- package/templates/mcp-radar/agent/channels/mcp-radar.ts +85 -0
- package/templates/mcp-radar/agent/hooks/proof-audit.ts +10 -0
- package/templates/mcp-radar/agent/instructions.md +24 -0
- package/templates/mcp-radar/agent/research/mcp-innovations-proof.md +4 -0
- package/templates/mcp-radar/agent/schedules/mcp-radar-scan.ts +14 -0
- package/templates/mcp-radar/agent/skills/mcp-proof-format.md +19 -0
- package/templates/mcp-radar/api/[[...path]].ts +23 -0
- package/templates/mcp-radar/evals/mcp-radar-discover.eval.ts +14 -0
- package/templates/mcp-radar/lib/proof-store.ts +265 -0
- package/templates/mcp-radar/lib/run-radar.ts +214 -0
- package/templates/mcp-radar/nitro.config.ts +17 -0
- package/templates/mcp-radar/package.json +14 -0
- package/templates/mcp-radar/public/index.html +10 -0
- package/templates/mcp-radar/routes/[...path].ts +29 -0
- package/templates/mcp-radar/scripts/run-evals.ts +46 -0
- package/templates/mcp-radar/scripts/smoke-channel.ts +66 -0
- package/templates/mcp-radar/vercel.json +15 -0
- package/templates/scaffold/.vercelignore +3 -0
- package/templates/scaffold/api/[[...path]].ts +23 -0
- package/templates/scaffold/nitro.config.ts +17 -0
- package/templates/scaffold/public/index.html +10 -0
- package/templates/scaffold/public/index.mcp-radar.html +10 -0
- package/templates/scaffold/routes/[...path].ts +29 -0
- package/templates/scaffold/vercel.default.json +4 -0
- package/templates/scaffold/vercel.mcp-radar.json +15 -0
package/README.md
CHANGED
|
@@ -152,9 +152,16 @@ npm run smoke:stubs:matrix
|
|
|
152
152
|
npm run agent -- "your prompt" # requires AI_GATEWAY_API_KEY (Vercel AI Gateway)
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
### Dev server (`
|
|
155
|
+
### Dev server (`plasm-agent dev`)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
**Default:** Nitro dev server with **Vercel routing parity** (channels, cron, `/plasm/v1/info`) — same `vercelPlasmHandler` as deploy. Init scaffolds `nitro.config.ts` + `routes/[...path].ts` and adds `nitropack` as a devDependency.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
plasm-agent dev # Nitro (default)
|
|
161
|
+
plasm-agent dev --interactive # in-process server + TUI when TTY
|
|
162
|
+
plasm-agent dev --interactive --no-tui
|
|
163
|
+
plasm-agent chat --url http://127.0.0.1:3000
|
|
164
|
+
```
|
|
158
165
|
|
|
159
166
|
Slash commands in the TUI: `/help`, `/info`, `/model`, `/channels`, `/catalogs`, `/new`, `/quit`.
|
|
160
167
|
|
package/agent/instructions.md
CHANGED
|
@@ -13,8 +13,7 @@ You operate external APIs through **Plasm catalogs**, not ad-hoc REST tools.
|
|
|
13
13
|
|
|
14
14
|
- **One user goal → one stable `intent` string → one `logical_session_ref`.**
|
|
15
15
|
- Do not rotate `intent` per message (e.g. `"msg 3: sort moves"` breaks symbol reuse).
|
|
16
|
-
- Copy symbols from the **left column** of teaching TSV into programs — never invent `e#` / `p#` values.
|
|
17
|
-
- Federated catalogs: use opaque symbols from the table; never bare homonym wire names.
|
|
16
|
+
- Copy symbols from the **left column** of teaching TSV into programs — never invent `e#` / `p#` values or catalog English names (`Issue`, `create`).
|
|
18
17
|
|
|
19
18
|
## Programs
|
|
20
19
|
|
package/bin/plasm-agent.mjs
CHANGED
|
@@ -4,14 +4,12 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
6
|
const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
7
|
-
const runner = path.join(packageRoot, "scripts", "plasm-
|
|
8
|
-
const cli = path.join(packageRoot, "scripts", "plasm-cli.ts");
|
|
7
|
+
const runner = path.join(packageRoot, "scripts", "run-plasm-cli.mjs");
|
|
9
8
|
|
|
10
|
-
const child = spawn(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
9
|
+
const child = spawn(process.execPath, [runner, ...process.argv.slice(2)], {
|
|
10
|
+
stdio: "inherit",
|
|
11
|
+
env: process.env,
|
|
12
|
+
});
|
|
15
13
|
|
|
16
14
|
child.on("exit", (code) => {
|
|
17
15
|
process.exit(code ?? 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.80",
|
|
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": {
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": "./src/index.ts",
|
|
18
|
+
"./build": "./src/cli/build.ts",
|
|
19
|
+
"./server": "./src/server/plasm-handler.ts",
|
|
18
20
|
"./instrumentation": "./src/instrumentation.ts",
|
|
19
21
|
"./operator": "./src/operator/routes.ts",
|
|
20
22
|
"./agent": "./agent/agent.ts"
|
|
@@ -25,36 +27,43 @@
|
|
|
25
27
|
"files": [
|
|
26
28
|
"src",
|
|
27
29
|
"bin",
|
|
30
|
+
"templates",
|
|
28
31
|
"scripts/plasm-node.mjs",
|
|
32
|
+
"scripts/plasm-cli.ts",
|
|
33
|
+
"scripts/run-plasm-cli.mjs",
|
|
34
|
+
"scripts/build-stubs.ts",
|
|
35
|
+
"scripts/register-plasm-loader.mjs",
|
|
29
36
|
"scripts/resolve-ts-extension.mjs",
|
|
30
37
|
"agent",
|
|
31
38
|
"README.md"
|
|
32
39
|
],
|
|
33
40
|
"scripts": {
|
|
34
41
|
"typecheck": "tsc --noEmit",
|
|
35
|
-
"plasm-agent": "node
|
|
42
|
+
"plasm-agent": "node scripts/run-plasm-cli.mjs",
|
|
36
43
|
"build:engine": "npm run build --prefix ../plasm-engine",
|
|
37
|
-
"build:stubs": "node
|
|
38
|
-
"smoke:tools": "node
|
|
39
|
-
"smoke:async-transport": "node
|
|
40
|
-
"smoke:operator": "node
|
|
41
|
-
"smoke:stubs": "node
|
|
42
|
-
"smoke:stubs:matrix": "node
|
|
43
|
-
"eval": "node
|
|
44
|
-
"eval:catalog": "node
|
|
45
|
-
"smoke:hot-reload": "node
|
|
46
|
-
"smoke:sessions": "node
|
|
47
|
-
"smoke:tui-async": "node
|
|
48
|
-
"smoke:bootstrap": "node
|
|
49
|
-
"smoke:
|
|
50
|
-
"
|
|
51
|
-
"
|
|
44
|
+
"build:stubs": "node scripts/run-plasm-cli.mjs scripts/build-stubs.ts",
|
|
45
|
+
"smoke:tools": "node scripts/run-plasm-cli.mjs scripts/smoke-agent-tools.ts",
|
|
46
|
+
"smoke:async-transport": "node scripts/run-plasm-cli.mjs scripts/smoke-async-transport.ts",
|
|
47
|
+
"smoke:operator": "node scripts/run-plasm-cli.mjs scripts/smoke-operator.ts",
|
|
48
|
+
"smoke:stubs": "node scripts/run-plasm-cli.mjs scripts/smoke-stubs.ts",
|
|
49
|
+
"smoke:stubs:matrix": "node scripts/run-plasm-cli.mjs scripts/smoke-stubs-matrix.ts",
|
|
50
|
+
"eval": "node scripts/run-plasm-cli.mjs scripts/run-evals.ts",
|
|
51
|
+
"eval:catalog": "node scripts/run-plasm-cli.mjs scripts/run-catalog-eval.ts",
|
|
52
|
+
"smoke:hot-reload": "node scripts/run-plasm-cli.mjs scripts/smoke-hot-reload.ts",
|
|
53
|
+
"smoke:sessions": "node scripts/run-plasm-cli.mjs scripts/smoke-sessions.ts",
|
|
54
|
+
"smoke:tui-async": "node scripts/run-plasm-cli.mjs scripts/smoke-tui-async.ts",
|
|
55
|
+
"smoke:bootstrap": "node scripts/run-plasm-cli.mjs scripts/smoke-bootstrap-devx.ts",
|
|
56
|
+
"smoke:bootstrap:npm": "node scripts/run-plasm-cli.mjs scripts/smoke-bootstrap-npm.ts",
|
|
57
|
+
"smoke:vercel-build": "node scripts/run-plasm-cli.mjs scripts/smoke-vercel-build.ts",
|
|
58
|
+
"smoke:vercel-handler": "node scripts/run-plasm-cli.mjs scripts/smoke-vercel-handler.ts",
|
|
59
|
+
"agent": "node scripts/run-plasm-cli.mjs scripts/run-agent.ts",
|
|
60
|
+
"dev": "node scripts/run-plasm-cli.mjs scripts/dev-server.ts",
|
|
52
61
|
"plasm:dev": "npm run dev"
|
|
53
62
|
},
|
|
54
63
|
"dependencies": {
|
|
55
64
|
"@ai-sdk/otel": "^1.0.0-beta.6",
|
|
56
65
|
"@opentelemetry/api": "^1.9.0",
|
|
57
|
-
"@plasm_lang/engine": "^0.3.
|
|
66
|
+
"@plasm_lang/engine": "^0.3.80",
|
|
58
67
|
"@vercel/blob": "^0.27.3",
|
|
59
68
|
"@vercel/connect": "^0.2.6",
|
|
60
69
|
"@vercel/kv": "^3.0.0",
|
|
@@ -63,7 +72,8 @@
|
|
|
63
72
|
"js-yaml": "^4.1.0",
|
|
64
73
|
"pg": "^8.22.0",
|
|
65
74
|
"workflow": "^4.5.0",
|
|
66
|
-
"zod": "^3.
|
|
75
|
+
"zod": "^3.25.76",
|
|
76
|
+
"tsx": "^4.19.4"
|
|
67
77
|
},
|
|
68
78
|
"devDependencies": {
|
|
69
79
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Scan agent/catalogs/ and emit TypeScript stubs to agent/.plasm/stubs/<entry_id>.ts
|
|
4
|
+
*/
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { generateAllStubs } from "../src/stubs/generator.js";
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const agentRoot = path.join(packageRoot, "agent");
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const results = await generateAllStubs(agentRoot);
|
|
15
|
+
if (!results.length) {
|
|
16
|
+
console.error("no catalogs found under agent/catalogs/");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
for (const result of results) {
|
|
20
|
+
console.log(`${result.entryId}\t${result.catalogCgsHash}\t${result.outPath}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
main().catch((err) => {
|
|
25
|
+
console.error(err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TypeScript CLI for @plasm_lang/vercel-agent (not the Rust plasm-server TUI).
|
|
4
|
+
*/
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { runPlasmBuild } from "../src/cli/build.js";
|
|
8
|
+
import {
|
|
9
|
+
installDevServerShutdown,
|
|
10
|
+
startDevServerForProject,
|
|
11
|
+
} from "../src/cli/dev.js";
|
|
12
|
+
import { startNitroDevForProject } from "../src/cli/nitro-dev.js";
|
|
13
|
+
import { runDevTui } from "../src/dev/client/repl.js";
|
|
14
|
+
import { collectProjectInfo, formatPlasmInfoHuman } from "../src/cli/info.js";
|
|
15
|
+
import { runPlasmInit, formatInitSuccess } from "../src/cli/init.js";
|
|
16
|
+
import { runPlasmLink } from "../src/cli/link.js";
|
|
17
|
+
import {
|
|
18
|
+
requireAgentProject,
|
|
19
|
+
readPackageName,
|
|
20
|
+
resolveAgentProject,
|
|
21
|
+
} from "../src/cli/project-root.js";
|
|
22
|
+
import { loadAgentEnv } from "../src/load-env.js";
|
|
23
|
+
|
|
24
|
+
loadAgentEnv();
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const command = args[0];
|
|
28
|
+
|
|
29
|
+
function flag(name: string): boolean {
|
|
30
|
+
return args.includes(name);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function optionValue(flagName: string): string | undefined {
|
|
34
|
+
const index = args.indexOf(flagName);
|
|
35
|
+
if (index < 0) return undefined;
|
|
36
|
+
const value = args[index + 1];
|
|
37
|
+
if (!value || value.startsWith("-")) return undefined;
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function initTargetDir(): string {
|
|
42
|
+
for (let i = 1; i < args.length; i++) {
|
|
43
|
+
const arg = args[i];
|
|
44
|
+
if (arg.startsWith("-")) {
|
|
45
|
+
if (arg === "--template") i++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
return arg;
|
|
49
|
+
}
|
|
50
|
+
return process.cwd();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function cmdInit(): Promise<void> {
|
|
54
|
+
const template = optionValue("--template");
|
|
55
|
+
const npm = flag("--npm");
|
|
56
|
+
const target = initTargetDir();
|
|
57
|
+
const project = await runPlasmInit(target, { template, npm: npm || undefined });
|
|
58
|
+
console.log(formatInitSuccess(project, { template, npm: npm || undefined }));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function cmdInfo(): Promise<void> {
|
|
62
|
+
const project = await requireAgentProject();
|
|
63
|
+
const packageName = await readPackageName(project.projectRoot);
|
|
64
|
+
const info = await collectProjectInfo({
|
|
65
|
+
projectRoot: project.projectRoot,
|
|
66
|
+
agentRoot: project.agentRoot,
|
|
67
|
+
packageName,
|
|
68
|
+
});
|
|
69
|
+
if (flag("--json")) {
|
|
70
|
+
console.log(JSON.stringify(info, null, 2));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log(formatPlasmInfoHuman(info));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function cmdLink(): Promise<void> {
|
|
77
|
+
const project = await requireAgentProject();
|
|
78
|
+
const result = await runPlasmLink(project.projectRoot);
|
|
79
|
+
for (const line of result.messages) console.log(line);
|
|
80
|
+
if (!result.linked && !result.envPulled) process.exitCode = 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function cmdBuild(): Promise<void> {
|
|
84
|
+
const project = await requireAgentProject();
|
|
85
|
+
const result = await runPlasmBuild(project);
|
|
86
|
+
console.log(`Built ${result.stubs.length} stub(s)`);
|
|
87
|
+
console.log(`manifest: ${result.manifestPath}`);
|
|
88
|
+
for (const stub of result.stubs) {
|
|
89
|
+
console.log(` - ${stub.entryId} → ${path.relative(project.projectRoot, stub.outPath)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function cmdDev(): Promise<void> {
|
|
94
|
+
const project = await requireAgentProject();
|
|
95
|
+
if (flag("--interactive")) {
|
|
96
|
+
const handle = await startDevServerForProject({
|
|
97
|
+
project,
|
|
98
|
+
tui: flag("--no-tui") ? false : "auto",
|
|
99
|
+
});
|
|
100
|
+
installDevServerShutdown(handle);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await startNitroDevForProject(project);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function cmdChat(): Promise<void> {
|
|
107
|
+
const urlFlag = args.find((a, i) => args[i - 1] === "--url");
|
|
108
|
+
const baseUrl = urlFlag ?? process.env.PLASM_DEV_URL ?? "http://127.0.0.1:3000";
|
|
109
|
+
await runDevTui({ baseUrl });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function main(): Promise<void> {
|
|
113
|
+
if (!command || command === "--help" || command === "-h") {
|
|
114
|
+
console.log(`plasm-agent — @plasm_lang/vercel-agent CLI
|
|
115
|
+
|
|
116
|
+
Commands:
|
|
117
|
+
init [dir] [--template NAME] [--npm] Scaffold agent project (templates: mcp-radar)
|
|
118
|
+
info [--json] Project + catalog diagnostics
|
|
119
|
+
link vercel link + env pull (AI Gateway key)
|
|
120
|
+
build CGS stubs + .plasm/discovery/manifest.json
|
|
121
|
+
dev [--interactive] [--no-tui] Nitro dev (Vercel routing parity; default). --interactive for TUI/sessions.
|
|
122
|
+
chat [--url URL] Terminal client for a running dev server
|
|
123
|
+
`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
switch (command) {
|
|
128
|
+
case "init":
|
|
129
|
+
await cmdInit();
|
|
130
|
+
break;
|
|
131
|
+
case "info": {
|
|
132
|
+
const resolved = await resolveAgentProject();
|
|
133
|
+
if (!resolved) {
|
|
134
|
+
console.error("No agent project found. Run `plasm-agent init` first.");
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await cmdInfo();
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case "link":
|
|
142
|
+
await cmdLink();
|
|
143
|
+
break;
|
|
144
|
+
case "build":
|
|
145
|
+
await cmdBuild();
|
|
146
|
+
break;
|
|
147
|
+
case "dev":
|
|
148
|
+
await cmdDev();
|
|
149
|
+
break;
|
|
150
|
+
case "chat":
|
|
151
|
+
await cmdChat();
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
console.error(`Unknown command: ${command}`);
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await main().catch((err) => {
|
|
160
|
+
console.error(err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Run TypeScript via tsx (works on Node 20–26 and Vercel build).
|
|
4
|
+
* With no .ts first arg, runs plasm-cli.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { createRequire } from "node:module";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
const scriptArg = args[0];
|
|
16
|
+
const isScriptEntry = scriptArg?.endsWith(".ts");
|
|
17
|
+
const entry = isScriptEntry
|
|
18
|
+
? path.resolve(packageRoot, scriptArg)
|
|
19
|
+
: path.join(packageRoot, "scripts", "plasm-cli.ts");
|
|
20
|
+
const entryArgs = isScriptEntry ? args.slice(1) : args;
|
|
21
|
+
|
|
22
|
+
function runNode(nodeArgs) {
|
|
23
|
+
const result = spawnSync(process.execPath, [...nodeArgs, entry, ...entryArgs], {
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
env: process.env,
|
|
26
|
+
});
|
|
27
|
+
process.exit(result.status ?? 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const tsxCli = require.resolve("tsx/cli");
|
|
32
|
+
runNode([tsxCli]);
|
|
33
|
+
} catch {
|
|
34
|
+
const plasmNode = path.join(packageRoot, "scripts", "plasm-node.mjs");
|
|
35
|
+
const result = spawnSync(
|
|
36
|
+
process.execPath,
|
|
37
|
+
["--experimental-strip-types", "--experimental-transform-types", plasmNode, entry, ...entryArgs],
|
|
38
|
+
{ stdio: "inherit", env: process.env },
|
|
39
|
+
);
|
|
40
|
+
process.exit(result.status ?? 1);
|
|
41
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { frameworkPackageVersion } from "../package-version.js";
|
|
6
|
+
import type { ResolvedAgentProject } from "./project-root.js";
|
|
7
|
+
|
|
8
|
+
export interface InitOptions {
|
|
9
|
+
template?: string;
|
|
10
|
+
/** Use npm semver deps instead of monorepo file: links (default: auto-detect). */
|
|
11
|
+
npm?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm", ".nitro", ".output", "server"]);
|
|
15
|
+
const SKIP_TEMPLATE_FILES = new Set(["package-lock.json", "vercel-build.ts"]);
|
|
16
|
+
|
|
17
|
+
function plasmAgentPackageRoot(): string {
|
|
18
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function scaffoldRoot(): string {
|
|
22
|
+
return path.join(plasmAgentPackageRoot(), "templates", "scaffold");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveTemplateDir(template: string): string {
|
|
26
|
+
const dir = path.join(plasmAgentPackageRoot(), "templates", template);
|
|
27
|
+
return dir;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function shouldCopyTemplateEntry(src: string, templateRoot: string): boolean {
|
|
31
|
+
const rel = path.relative(templateRoot, src);
|
|
32
|
+
if (!rel || rel === "") return true;
|
|
33
|
+
const parts = rel.split(path.sep);
|
|
34
|
+
if (parts.some((part) => SKIP_TEMPLATE_DIRS.has(part))) return false;
|
|
35
|
+
if (SKIP_TEMPLATE_FILES.has(path.basename(src))) return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function exists(p: string): Promise<boolean> {
|
|
40
|
+
try {
|
|
41
|
+
await access(p);
|
|
42
|
+
return true;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function copyTemplate(templateRoot: string, projectRoot: string): Promise<void> {
|
|
49
|
+
await cp(templateRoot, projectRoot, {
|
|
50
|
+
recursive: true,
|
|
51
|
+
filter: (src) => src === templateRoot || shouldCopyTemplateEntry(src, templateRoot),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function copyScaffoldFile(relativePath: string, projectRoot: string): Promise<void> {
|
|
56
|
+
const src = path.join(scaffoldRoot(), relativePath);
|
|
57
|
+
const dest = path.join(projectRoot, relativePath);
|
|
58
|
+
await mkdir(path.dirname(dest), { recursive: true });
|
|
59
|
+
await cp(src, dest);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function writeDeployScaffold(
|
|
63
|
+
projectRoot: string,
|
|
64
|
+
template?: string,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const vercelJsonName =
|
|
67
|
+
template === "mcp-radar" ? "vercel.mcp-radar.json" : "vercel.default.json";
|
|
68
|
+
await copyScaffoldFile("api/[[...path]].ts", projectRoot);
|
|
69
|
+
await copyScaffoldFile("routes/[...path].ts", projectRoot);
|
|
70
|
+
await copyScaffoldFile("nitro.config.ts", projectRoot);
|
|
71
|
+
await copyScaffoldFile(".vercelignore", projectRoot);
|
|
72
|
+
const publicIndex =
|
|
73
|
+
template === "mcp-radar" ? "public/index.mcp-radar.html" : "public/index.html";
|
|
74
|
+
const publicDest = path.join(projectRoot, "public", "index.html");
|
|
75
|
+
await mkdir(path.dirname(publicDest), { recursive: true });
|
|
76
|
+
await cp(path.join(scaffoldRoot(), publicIndex), publicDest);
|
|
77
|
+
await cp(
|
|
78
|
+
path.join(scaffoldRoot(), vercelJsonName),
|
|
79
|
+
path.join(projectRoot, "vercel.json"),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function monorepoEngineRoot(packageRoot: string): string {
|
|
84
|
+
return path.resolve(packageRoot, "../plasm-engine");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function isMonorepoDevPackage(packageRoot: string): Promise<boolean> {
|
|
88
|
+
return exists(path.join(monorepoEngineRoot(packageRoot), "package.json"));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function patchProjectPackageJson(
|
|
92
|
+
projectRoot: string,
|
|
93
|
+
packageRoot: string,
|
|
94
|
+
options?: { npm?: boolean },
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
97
|
+
const raw = await readFile(pkgPath, "utf8");
|
|
98
|
+
const pkg = JSON.parse(raw) as {
|
|
99
|
+
name?: string;
|
|
100
|
+
scripts?: Record<string, string>;
|
|
101
|
+
dependencies?: Record<string, string>;
|
|
102
|
+
devDependencies?: Record<string, string>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const version = frameworkPackageVersion();
|
|
106
|
+
const useNpm =
|
|
107
|
+
options?.npm === true
|
|
108
|
+
? true
|
|
109
|
+
: options?.npm === false
|
|
110
|
+
? false
|
|
111
|
+
: !(await isMonorepoDevPackage(packageRoot));
|
|
112
|
+
|
|
113
|
+
pkg.name = path.basename(projectRoot);
|
|
114
|
+
if (useNpm) {
|
|
115
|
+
pkg.dependencies = {
|
|
116
|
+
...pkg.dependencies,
|
|
117
|
+
"@plasm_lang/vercel-agent": `^${version}`,
|
|
118
|
+
"@plasm_lang/engine": `^${version}`,
|
|
119
|
+
"@vercel/blob": "^0.27.3",
|
|
120
|
+
"@vercel/functions": "^3.4.3",
|
|
121
|
+
"@vercel/kv": "^3.0.0",
|
|
122
|
+
};
|
|
123
|
+
} else {
|
|
124
|
+
pkg.dependencies = {
|
|
125
|
+
...pkg.dependencies,
|
|
126
|
+
"@plasm_lang/vercel-agent": `file:${path.resolve(packageRoot)}`,
|
|
127
|
+
"@plasm_lang/engine": `file:${monorepoEngineRoot(packageRoot)}`,
|
|
128
|
+
"@vercel/blob": "^0.27.3",
|
|
129
|
+
"@vercel/functions": "^3.4.3",
|
|
130
|
+
"@vercel/kv": "^3.0.0",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pkg.scripts = {
|
|
135
|
+
build: "plasm-agent build",
|
|
136
|
+
dev: "plasm-agent dev",
|
|
137
|
+
"dev:interactive": "plasm-agent dev --interactive",
|
|
138
|
+
info: "plasm-agent info",
|
|
139
|
+
deploy: "vercel deploy",
|
|
140
|
+
...pkg.scripts,
|
|
141
|
+
};
|
|
142
|
+
delete pkg.scripts["vercel-build"];
|
|
143
|
+
|
|
144
|
+
pkg.devDependencies = {
|
|
145
|
+
...pkg.devDependencies,
|
|
146
|
+
nitropack: "^2.13.4",
|
|
147
|
+
tsx: "^4.19.4",
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function blankPackageJsonScaffold(): Record<string, unknown> {
|
|
154
|
+
const version = frameworkPackageVersion();
|
|
155
|
+
return {
|
|
156
|
+
name: "my-plasm-agent",
|
|
157
|
+
private: true,
|
|
158
|
+
type: "module",
|
|
159
|
+
scripts: {
|
|
160
|
+
dev: "plasm-agent dev",
|
|
161
|
+
"dev:interactive": "plasm-agent dev --interactive",
|
|
162
|
+
build: "plasm-agent build",
|
|
163
|
+
info: "plasm-agent info",
|
|
164
|
+
deploy: "vercel deploy",
|
|
165
|
+
},
|
|
166
|
+
dependencies: {
|
|
167
|
+
"@plasm_lang/vercel-agent": `^${version}`,
|
|
168
|
+
"@plasm_lang/engine": `^${version}`,
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
nitropack: "^2.13.4",
|
|
172
|
+
tsx: "^4.19.4",
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function runTemplateInit(
|
|
178
|
+
targetDir: string,
|
|
179
|
+
template: string,
|
|
180
|
+
options?: InitOptions,
|
|
181
|
+
): Promise<ResolvedAgentProject> {
|
|
182
|
+
const projectRoot = path.resolve(targetDir);
|
|
183
|
+
const agentRoot = path.join(projectRoot, "agent");
|
|
184
|
+
if (await exists(path.join(agentRoot, "agent.ts"))) {
|
|
185
|
+
throw new Error(`agent/agent.ts already exists in ${projectRoot}`);
|
|
186
|
+
}
|
|
187
|
+
const templateRoot = resolveTemplateDir(template);
|
|
188
|
+
if (!(await exists(templateRoot))) {
|
|
189
|
+
throw new Error(`Unknown template "${template}" or missing source: ${templateRoot}`);
|
|
190
|
+
}
|
|
191
|
+
await mkdir(projectRoot, { recursive: true });
|
|
192
|
+
await copyTemplate(templateRoot, projectRoot);
|
|
193
|
+
await patchProjectPackageJson(projectRoot, plasmAgentPackageRoot(), { npm: options?.npm });
|
|
194
|
+
await writeDeployScaffold(projectRoot, template);
|
|
195
|
+
return { projectRoot, agentRoot };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { exists, plasmAgentPackageRoot, resolveTemplateDir };
|