@agent-native/core 0.52.0 → 0.53.0
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 +41 -95
- package/blueprints/action/crud.md +98 -0
- package/blueprints/channel/discord.md +74 -0
- package/blueprints/provider/stripe.md +87 -0
- package/blueprints/sandbox/docker.md +78 -0
- package/dist/action.d.ts +24 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +4 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/observational-memory/compactor.d.ts +43 -0
- package/dist/agent/observational-memory/compactor.d.ts.map +1 -0
- package/dist/agent/observational-memory/compactor.js +50 -0
- package/dist/agent/observational-memory/compactor.js.map +1 -0
- package/dist/agent/observational-memory/config.d.ts +37 -0
- package/dist/agent/observational-memory/config.d.ts.map +1 -0
- package/dist/agent/observational-memory/config.js +48 -0
- package/dist/agent/observational-memory/config.js.map +1 -0
- package/dist/agent/observational-memory/index.d.ts +26 -0
- package/dist/agent/observational-memory/index.d.ts.map +1 -0
- package/dist/agent/observational-memory/index.js +25 -0
- package/dist/agent/observational-memory/index.js.map +1 -0
- package/dist/agent/observational-memory/internal-run.d.ts +37 -0
- package/dist/agent/observational-memory/internal-run.d.ts.map +1 -0
- package/dist/agent/observational-memory/internal-run.js +59 -0
- package/dist/agent/observational-memory/internal-run.js.map +1 -0
- package/dist/agent/observational-memory/message-text.d.ts +13 -0
- package/dist/agent/observational-memory/message-text.d.ts.map +1 -0
- package/dist/agent/observational-memory/message-text.js +46 -0
- package/dist/agent/observational-memory/message-text.js.map +1 -0
- package/dist/agent/observational-memory/migrations.d.ts +13 -0
- package/dist/agent/observational-memory/migrations.d.ts.map +1 -0
- package/dist/agent/observational-memory/migrations.js +43 -0
- package/dist/agent/observational-memory/migrations.js.map +1 -0
- package/dist/agent/observational-memory/observer.d.ts +37 -0
- package/dist/agent/observational-memory/observer.d.ts.map +1 -0
- package/dist/agent/observational-memory/observer.js +82 -0
- package/dist/agent/observational-memory/observer.js.map +1 -0
- package/dist/agent/observational-memory/plugin.d.ts +16 -0
- package/dist/agent/observational-memory/plugin.d.ts.map +1 -0
- package/dist/agent/observational-memory/plugin.js +26 -0
- package/dist/agent/observational-memory/plugin.js.map +1 -0
- package/dist/agent/observational-memory/prompts.d.ts +27 -0
- package/dist/agent/observational-memory/prompts.d.ts.map +1 -0
- package/dist/agent/observational-memory/prompts.js +42 -0
- package/dist/agent/observational-memory/prompts.js.map +1 -0
- package/dist/agent/observational-memory/read.d.ts +47 -0
- package/dist/agent/observational-memory/read.d.ts.map +1 -0
- package/dist/agent/observational-memory/read.js +99 -0
- package/dist/agent/observational-memory/read.js.map +1 -0
- package/dist/agent/observational-memory/reflector.d.ts +31 -0
- package/dist/agent/observational-memory/reflector.d.ts.map +1 -0
- package/dist/agent/observational-memory/reflector.js +76 -0
- package/dist/agent/observational-memory/reflector.js.map +1 -0
- package/dist/agent/observational-memory/schema.d.ts +267 -0
- package/dist/agent/observational-memory/schema.d.ts.map +1 -0
- package/dist/agent/observational-memory/schema.js +48 -0
- package/dist/agent/observational-memory/schema.js.map +1 -0
- package/dist/agent/observational-memory/store.d.ts +52 -0
- package/dist/agent/observational-memory/store.d.ts.map +1 -0
- package/dist/agent/observational-memory/store.js +197 -0
- package/dist/agent/observational-memory/store.js.map +1 -0
- package/dist/agent/observational-memory/types.d.ts +61 -0
- package/dist/agent/observational-memory/types.d.ts.map +1 -0
- package/dist/agent/observational-memory/types.js +9 -0
- package/dist/agent/observational-memory/types.js.map +1 -0
- package/dist/agent/production-agent.d.ts +15 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +240 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +49 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +55 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/runtime-context.d.ts +30 -0
- package/dist/agent/runtime-context.d.ts.map +1 -1
- package/dist/agent/runtime-context.js +54 -1
- package/dist/agent/runtime-context.js.map +1 -1
- package/dist/agent/tool-call-journal.d.ts +101 -0
- package/dist/agent/tool-call-journal.d.ts.map +1 -0
- package/dist/agent/tool-call-journal.js +214 -0
- package/dist/agent/tool-call-journal.js.map +1 -0
- package/dist/agent/types.d.ts +24 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/add.d.ts +109 -0
- package/dist/cli/add.d.ts.map +1 -0
- package/dist/cli/add.js +352 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/connect.d.ts +2 -2
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +92 -24
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/eval.d.ts +17 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +121 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/index.js +44 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +11 -5
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/plan-local.d.ts +66 -5
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +495 -19
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +70 -59
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +118 -92
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +16 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +20 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +32 -7
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +13 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +21 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/db/client.d.ts +4 -2
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +6 -4
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/route-discovery.d.ts.map +1 -1
- package/dist/deploy/route-discovery.js +1 -0
- package/dist/deploy/route-discovery.js.map +1 -1
- package/dist/eval/agent-runner.d.ts +63 -0
- package/dist/eval/agent-runner.d.ts.map +1 -0
- package/dist/eval/agent-runner.js +142 -0
- package/dist/eval/agent-runner.js.map +1 -0
- package/dist/eval/define-eval.d.ts +29 -0
- package/dist/eval/define-eval.d.ts.map +1 -0
- package/dist/eval/define-eval.js +43 -0
- package/dist/eval/define-eval.js.map +1 -0
- package/dist/eval/index.d.ts +18 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +17 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/report.d.ts +8 -0
- package/dist/eval/report.d.ts.map +1 -0
- package/dist/eval/report.js +44 -0
- package/dist/eval/report.js.map +1 -0
- package/dist/eval/runner.d.ts +67 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +256 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/eval/scorer.d.ts +83 -0
- package/dist/eval/scorer.d.ts.map +1 -0
- package/dist/eval/scorer.js +195 -0
- package/dist/eval/scorer.js.map +1 -0
- package/dist/eval/types.d.ts +162 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +20 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +100 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/observability/tracing.d.ts +73 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +126 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +4 -1
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +10 -3
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +9 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +118 -110
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts +62 -0
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +99 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +7 -4
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +2 -0
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +33 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +10 -0
- package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/docs/content/agent-teams.md +32 -0
- package/docs/content/blueprint-installer.md +73 -0
- package/docs/content/evals.md +141 -0
- package/docs/content/pr-visual-recap.md +7 -4
- package/docs/content/sandbox-adapters.md +134 -0
- package/docs/content/template-plan.md +20 -8
- package/package.json +5 -1
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +10 -0
- package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +20 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
package/dist/cli/plan-local.js
CHANGED
|
@@ -2,17 +2,29 @@
|
|
|
2
2
|
* Plan helper commands.
|
|
3
3
|
*
|
|
4
4
|
* The `plan local` commands are intentionally separate from the Plan app
|
|
5
|
-
* actions. They do not call MCP,
|
|
6
|
-
* they only read
|
|
7
|
-
* auditable no-DB path. The top-level
|
|
8
|
-
* no-auth helper for fetching the
|
|
9
|
-
* MDX; it never sends plan content.
|
|
5
|
+
* actions. They do not call MCP, hosted write actions, SQLite, or hosted
|
|
6
|
+
* storage; they only read local files or serve them from a localhost bridge so
|
|
7
|
+
* privacy-focused users have an auditable no-DB path. The top-level
|
|
8
|
+
* `plan blocks` command is a schema-only, no-auth helper for fetching the
|
|
9
|
+
* public block catalog before authoring local MDX; it never sends plan content.
|
|
10
10
|
*/
|
|
11
11
|
import fs from "node:fs";
|
|
12
|
+
import crypto from "node:crypto";
|
|
13
|
+
import http from "node:http";
|
|
12
14
|
import path from "node:path";
|
|
13
15
|
import { spawnSync } from "node:child_process";
|
|
14
16
|
import { pathToFileURL } from "node:url";
|
|
15
17
|
import { DEFAULT_PLAN_APP_URL, defaultPlanBlocksOut, fetchPlanBlockCatalog, normalizePlanBlockFormat, } from "./plan-blocks.js";
|
|
18
|
+
const LOCAL_PLAN_ASSET_MAX_SINGLE_BYTES = 2 * 1024 * 1024;
|
|
19
|
+
const LOCAL_PLAN_ASSET_MAX_TOTAL_BYTES = 10 * 1024 * 1024;
|
|
20
|
+
const LOCAL_PLAN_ASSET_EXTENSIONS = new Set([
|
|
21
|
+
"png",
|
|
22
|
+
"jpg",
|
|
23
|
+
"jpeg",
|
|
24
|
+
"gif",
|
|
25
|
+
"webp",
|
|
26
|
+
"svg",
|
|
27
|
+
]);
|
|
16
28
|
function parseArgs(argv) {
|
|
17
29
|
const out = {};
|
|
18
30
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -70,12 +82,23 @@ function defaultLocalPlanAppUrl() {
|
|
|
70
82
|
process.env.PLAN_BASE_URL ||
|
|
71
83
|
"http://localhost:8096");
|
|
72
84
|
}
|
|
85
|
+
function defaultLocalPlanBridgeAppUrl() {
|
|
86
|
+
return (process.env.PLAN_LOCAL_BRIDGE_APP_URL ||
|
|
87
|
+
process.env.PLAN_BASE_URL ||
|
|
88
|
+
DEFAULT_PLAN_APP_URL);
|
|
89
|
+
}
|
|
73
90
|
function normalizeAppUrl(value) {
|
|
74
91
|
return (value || defaultLocalPlanAppUrl()).replace(/\/+$/, "");
|
|
75
92
|
}
|
|
93
|
+
function normalizeBridgeAppUrl(value) {
|
|
94
|
+
return (value || defaultLocalPlanBridgeAppUrl()).replace(/\/+$/, "");
|
|
95
|
+
}
|
|
76
96
|
function localPlanPreviewUrl(dir, appUrl) {
|
|
77
97
|
return `${normalizeAppUrl(appUrl)}/local-plans/${encodeURIComponent(path.basename(path.resolve(dir)))}`;
|
|
78
98
|
}
|
|
99
|
+
function localPlanBridgePageUrl(input) {
|
|
100
|
+
return `${normalizeBridgeAppUrl(input.appUrl)}/local-plans/${encodeURIComponent(path.basename(path.resolve(input.dir)))}?bridge=${encodeURIComponent(input.bridgeUrl)}`;
|
|
101
|
+
}
|
|
79
102
|
function openLocalUrl(url) {
|
|
80
103
|
const platform = process.platform;
|
|
81
104
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
@@ -241,6 +264,33 @@ function renderMarkdownish(source) {
|
|
|
241
264
|
flushList();
|
|
242
265
|
return html.join("\n");
|
|
243
266
|
}
|
|
267
|
+
function readLocalPlanAssets(dir) {
|
|
268
|
+
const assetsDir = path.join(dir, "assets");
|
|
269
|
+
if (!fs.existsSync(assetsDir))
|
|
270
|
+
return undefined;
|
|
271
|
+
const assets = {};
|
|
272
|
+
let totalBytes = 0;
|
|
273
|
+
for (const entry of fs.readdirSync(assetsDir, { withFileTypes: true })) {
|
|
274
|
+
if (!entry.isFile())
|
|
275
|
+
continue;
|
|
276
|
+
const filename = path.basename(entry.name);
|
|
277
|
+
if (!filename || filename !== entry.name)
|
|
278
|
+
continue;
|
|
279
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
280
|
+
if (!LOCAL_PLAN_ASSET_EXTENSIONS.has(ext))
|
|
281
|
+
continue;
|
|
282
|
+
const abs = path.join(assetsDir, filename);
|
|
283
|
+
const bytes = fs.readFileSync(abs);
|
|
284
|
+
if (bytes.byteLength > LOCAL_PLAN_ASSET_MAX_SINGLE_BYTES)
|
|
285
|
+
continue;
|
|
286
|
+
if (totalBytes + bytes.byteLength > LOCAL_PLAN_ASSET_MAX_TOTAL_BYTES) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
totalBytes += bytes.byteLength;
|
|
290
|
+
assets[filename] = bytes.toString("base64");
|
|
291
|
+
}
|
|
292
|
+
return Object.keys(assets).length > 0 ? assets : undefined;
|
|
293
|
+
}
|
|
244
294
|
export function readLocalPlanFiles(dir) {
|
|
245
295
|
const resolved = path.resolve(dir);
|
|
246
296
|
const planPath = path.join(resolved, "plan.mdx");
|
|
@@ -257,10 +307,244 @@ export function readLocalPlanFiles(dir) {
|
|
|
257
307
|
canvasMdx: readOptional("canvas.mdx"),
|
|
258
308
|
prototypeMdx: readOptional("prototype.mdx"),
|
|
259
309
|
stateJson: readOptional(".plan-state.json"),
|
|
310
|
+
assets: readLocalPlanAssets(resolved),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function localPlanMdxFolder(files) {
|
|
314
|
+
return {
|
|
315
|
+
"plan.mdx": files.planMdx,
|
|
316
|
+
...(files.canvasMdx ? { "canvas.mdx": files.canvasMdx } : {}),
|
|
317
|
+
...(files.prototypeMdx ? { "prototype.mdx": files.prototypeMdx } : {}),
|
|
318
|
+
...(files.stateJson ? { ".plan-state.json": files.stateJson } : {}),
|
|
319
|
+
...(files.assets ? { "assets/": files.assets } : {}),
|
|
260
320
|
};
|
|
261
321
|
}
|
|
322
|
+
function localPlanFileList(files) {
|
|
323
|
+
return [
|
|
324
|
+
"plan.mdx",
|
|
325
|
+
...(files.canvasMdx ? ["canvas.mdx"] : []),
|
|
326
|
+
...(files.prototypeMdx ? ["prototype.mdx"] : []),
|
|
327
|
+
...(files.stateJson ? [".plan-state.json"] : []),
|
|
328
|
+
...Object.keys(files.assets ?? {}).map((filename) => `assets/${filename}`),
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
function localPlanSourceEntries(files) {
|
|
332
|
+
return [
|
|
333
|
+
{ file: "plan.mdx", source: files.planMdx },
|
|
334
|
+
...(files.canvasMdx
|
|
335
|
+
? [{ file: "canvas.mdx", source: files.canvasMdx }]
|
|
336
|
+
: []),
|
|
337
|
+
...(files.prototypeMdx
|
|
338
|
+
? [{ file: "prototype.mdx", source: files.prototypeMdx }]
|
|
339
|
+
: []),
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
function lineNumberAt(source, index) {
|
|
343
|
+
let line = 1;
|
|
344
|
+
for (let i = 0; i < index; i += 1) {
|
|
345
|
+
if (source.charCodeAt(i) === 10)
|
|
346
|
+
line += 1;
|
|
347
|
+
}
|
|
348
|
+
return line;
|
|
349
|
+
}
|
|
350
|
+
function maskFencedCode(source) {
|
|
351
|
+
return source.replace(/(^|\n)(```|~~~)[\s\S]*?(\n\2[^\n]*(?=\n|$))/g, (match) => match.replace(/[^\n]/g, " "));
|
|
352
|
+
}
|
|
353
|
+
function findJsxOpeningTagEnd(source, start) {
|
|
354
|
+
let quote = null;
|
|
355
|
+
let braceDepth = 0;
|
|
356
|
+
for (let i = start; i < source.length; i += 1) {
|
|
357
|
+
const char = source[i];
|
|
358
|
+
if (quote) {
|
|
359
|
+
if (char === "\\" && i + 1 < source.length) {
|
|
360
|
+
i += 1;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (char === quote)
|
|
364
|
+
quote = null;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
368
|
+
quote = char;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (char === "{") {
|
|
372
|
+
braceDepth += 1;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (char === "}") {
|
|
376
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (char === ">" && braceDepth === 0)
|
|
380
|
+
return i;
|
|
381
|
+
}
|
|
382
|
+
return -1;
|
|
383
|
+
}
|
|
384
|
+
function addValidationIssue(issues, file, source, index, message) {
|
|
385
|
+
issues.push({ file, line: lineNumberAt(source, index), message });
|
|
386
|
+
}
|
|
387
|
+
const ENTITY_RE = /&(?:[a-z][a-z0-9]+|#[0-9]+|#x[0-9a-f]+);/gi;
|
|
388
|
+
const HTML_TEXT_ATTR_RE = /\b(?:aria-label|alt|placeholder|title|value)=\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`)/gi;
|
|
389
|
+
const WIREFRAME_TEXT_ATTR_RE = /\b(?:text|value|label|placeholder|title|note|due)=\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`)/gi;
|
|
390
|
+
function normalizeVisibleText(value) {
|
|
391
|
+
return value
|
|
392
|
+
.replace(/ | |�*a0;/gi, " ")
|
|
393
|
+
.replace(ENTITY_RE, "x")
|
|
394
|
+
.replace(/\s+/g, " ")
|
|
395
|
+
.trim();
|
|
396
|
+
}
|
|
397
|
+
function meaningfulTextLength(value) {
|
|
398
|
+
return normalizeVisibleText(value ?? "").length;
|
|
399
|
+
}
|
|
400
|
+
function htmlMeaningfulTextLength(html) {
|
|
401
|
+
let length = 0;
|
|
402
|
+
for (const match of html.matchAll(HTML_TEXT_ATTR_RE)) {
|
|
403
|
+
length += meaningfulTextLength(match[1] ?? match[2] ?? match[3]);
|
|
404
|
+
}
|
|
405
|
+
const visibleText = html
|
|
406
|
+
.replace(/<!--[\s\S]*?-->/g, " ")
|
|
407
|
+
.replace(/<script\b[\s\S]*?<\/script>/gi, " ")
|
|
408
|
+
.replace(/<style\b[\s\S]*?<\/style>/gi, " ")
|
|
409
|
+
.replace(/<[^>]+>/g, " ");
|
|
410
|
+
length += meaningfulTextLength(visibleText);
|
|
411
|
+
return length;
|
|
412
|
+
}
|
|
413
|
+
function hasSkeletonGeometry(html) {
|
|
414
|
+
return (/<(?:div|span|section|main|article|ul|li)\b/i.test(html) &&
|
|
415
|
+
/\b(?:height|width|background|border|padding|wf-card|wf-box|wf-pill|wf-chip)\b/i.test(html));
|
|
416
|
+
}
|
|
417
|
+
function stringAttributeValues(source, name) {
|
|
418
|
+
const values = [];
|
|
419
|
+
const re = new RegExp(`\\b${name}\\s*=\\s*(?:\\{\\s*\`([\\s\\S]*?)\`\\s*\\}|\\{\\s*"([^"]*)"\\s*\\}|\\{\\s*'([^']*)'\\s*\\}|"([^"]*)"|'([^']*)')`, "g");
|
|
420
|
+
for (const match of source.matchAll(re)) {
|
|
421
|
+
const value = match[1] ?? match[2] ?? match[3] ?? match[4] ?? match[5];
|
|
422
|
+
if (value !== undefined)
|
|
423
|
+
values.push(value);
|
|
424
|
+
}
|
|
425
|
+
return values;
|
|
426
|
+
}
|
|
427
|
+
function hasUnparsedAttributeExpression(source, name) {
|
|
428
|
+
return new RegExp(`\\b${name}\\s*=\\s*\\{`).test(source);
|
|
429
|
+
}
|
|
430
|
+
function hasMeaningfulWireframeHtml(screenOpening) {
|
|
431
|
+
const htmlValues = stringAttributeValues(screenOpening, "html");
|
|
432
|
+
if (htmlValues.length === 0) {
|
|
433
|
+
return hasUnparsedAttributeExpression(screenOpening, "html") ? null : false;
|
|
434
|
+
}
|
|
435
|
+
return htmlValues.some((html) => htmlMeaningfulTextLength(html) >= 2 || hasSkeletonGeometry(html));
|
|
436
|
+
}
|
|
437
|
+
function hasMeaningfulKitScreen(screenSource) {
|
|
438
|
+
for (const match of screenSource.matchAll(WIREFRAME_TEXT_ATTR_RE)) {
|
|
439
|
+
if (meaningfulTextLength(match[1] ?? match[2] ?? match[3]) >= 2) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (/\bitems\s*=\s*\{[\s\S]*?\blabel\s*:/i.test(screenSource))
|
|
444
|
+
return true;
|
|
445
|
+
if (/\brows\s*=\s*\{[\s\S]*?\b[klv]\s*:/i.test(screenSource))
|
|
446
|
+
return true;
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
function hasMeaningfulWireframeScreen(blockSource) {
|
|
450
|
+
const screenMatch = /<Screen\b/.exec(blockSource);
|
|
451
|
+
if (!screenMatch)
|
|
452
|
+
return false;
|
|
453
|
+
const screenStart = screenMatch.index;
|
|
454
|
+
const screenOpeningEnd = findJsxOpeningTagEnd(blockSource, screenStart);
|
|
455
|
+
if (screenOpeningEnd < 0)
|
|
456
|
+
return false;
|
|
457
|
+
const screenOpening = blockSource.slice(screenStart, screenOpeningEnd + 1);
|
|
458
|
+
const htmlMeaningful = hasMeaningfulWireframeHtml(screenOpening);
|
|
459
|
+
if (htmlMeaningful === true)
|
|
460
|
+
return true;
|
|
461
|
+
const selfClosing = /\/\s*>$/.test(screenOpening);
|
|
462
|
+
const closeIndex = selfClosing
|
|
463
|
+
? -1
|
|
464
|
+
: blockSource.indexOf("</Screen>", screenOpeningEnd + 1);
|
|
465
|
+
const screenSource = closeIndex >= 0
|
|
466
|
+
? blockSource.slice(screenStart, closeIndex + "</Screen>".length)
|
|
467
|
+
: screenOpening;
|
|
468
|
+
if (hasMeaningfulKitScreen(screenSource))
|
|
469
|
+
return true;
|
|
470
|
+
return htmlMeaningful === null ? null : false;
|
|
471
|
+
}
|
|
472
|
+
function lintWireframeBlocks(file, source, issues) {
|
|
473
|
+
const scanSource = maskFencedCode(source);
|
|
474
|
+
const re = /<WireframeBlock\b/g;
|
|
475
|
+
let match;
|
|
476
|
+
while ((match = re.exec(scanSource))) {
|
|
477
|
+
const start = match.index;
|
|
478
|
+
const openingEnd = findJsxOpeningTagEnd(scanSource, start);
|
|
479
|
+
if (openingEnd < 0) {
|
|
480
|
+
addValidationIssue(issues, file, source, start, "WireframeBlock opening tag is not closed.");
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
const opening = scanSource.slice(start, openingEnd + 1);
|
|
484
|
+
const unsupportedAttr = opening.match(/\b(data|screens|screen|elements)\s*=/);
|
|
485
|
+
if (unsupportedAttr) {
|
|
486
|
+
addValidationIssue(issues, file, source, start, `WireframeBlock uses unsupported "${unsupportedAttr[1]}" prop. Put content inside a <Screen> child instead.`);
|
|
487
|
+
}
|
|
488
|
+
const selfClosing = /\/\s*>$/.test(opening);
|
|
489
|
+
const closeTag = "</WireframeBlock>";
|
|
490
|
+
const closeIndex = selfClosing
|
|
491
|
+
? -1
|
|
492
|
+
: scanSource.indexOf(closeTag, openingEnd + 1);
|
|
493
|
+
const blockSource = selfClosing
|
|
494
|
+
? opening
|
|
495
|
+
: closeIndex >= 0
|
|
496
|
+
? scanSource.slice(start, closeIndex + closeTag.length)
|
|
497
|
+
: scanSource.slice(start, openingEnd + 1);
|
|
498
|
+
if (!selfClosing && closeIndex < 0) {
|
|
499
|
+
addValidationIssue(issues, file, source, start, "WireframeBlock must have a closing </WireframeBlock> tag.");
|
|
500
|
+
}
|
|
501
|
+
if (selfClosing || !/<Screen\b/.test(blockSource)) {
|
|
502
|
+
addValidationIssue(issues, file, source, start, 'WireframeBlock must wrap a <Screen> child; self-closing wireframes render empty. Use <WireframeBlock><Screen surface="browser">...</Screen></WireframeBlock>.');
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const meaningfulScreen = hasMeaningfulWireframeScreen(blockSource);
|
|
506
|
+
if (meaningfulScreen === false) {
|
|
507
|
+
addValidationIssue(issues, file, source, start, 'WireframeBlock contains an empty <Screen>; local previews render blank wireframes. Add visible html text/controls or kit nodes such as <Title text="Checkout" /> and <Btn label="Pay" />.');
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function lintColumnsBlocks(file, source, issues) {
|
|
512
|
+
const scanSource = maskFencedCode(source);
|
|
513
|
+
const re = /<Columns\b/g;
|
|
514
|
+
let match;
|
|
515
|
+
while ((match = re.exec(scanSource))) {
|
|
516
|
+
const start = match.index;
|
|
517
|
+
const openingEnd = findJsxOpeningTagEnd(scanSource, start);
|
|
518
|
+
if (openingEnd < 0)
|
|
519
|
+
continue;
|
|
520
|
+
const opening = scanSource.slice(start, openingEnd + 1);
|
|
521
|
+
if (/\bcolumns\s*=/.test(opening)) {
|
|
522
|
+
addValidationIssue(issues, file, source, start, 'Columns must use <Column> children, not a columns= prop. Use <Columns><Column label="Before">...</Column><Column label="After">...</Column></Columns>.');
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
export function validateLocalPlanFiles(files) {
|
|
527
|
+
const issues = [];
|
|
528
|
+
for (const entry of localPlanSourceEntries(files)) {
|
|
529
|
+
lintWireframeBlocks(entry.file, entry.source, issues);
|
|
530
|
+
lintColumnsBlocks(entry.file, entry.source, issues);
|
|
531
|
+
}
|
|
532
|
+
return issues;
|
|
533
|
+
}
|
|
534
|
+
export function assertLocalPlanFilesValid(files) {
|
|
535
|
+
const issues = validateLocalPlanFiles(files);
|
|
536
|
+
if (issues.length === 0)
|
|
537
|
+
return;
|
|
538
|
+
const details = issues
|
|
539
|
+
.slice(0, 8)
|
|
540
|
+
.map((issue) => `${issue.file}:${issue.line} ${issue.message}`)
|
|
541
|
+
.join("\n");
|
|
542
|
+
const overflow = issues.length > 8 ? `\n...plus ${issues.length - 8} more issues` : "";
|
|
543
|
+
throw new Error(`Local plan source validation failed:\n${details}${overflow}\nRun \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\` and update the MDX to the documented block shapes.`);
|
|
544
|
+
}
|
|
262
545
|
export function buildLocalPlanPreviewHtml(input) {
|
|
263
546
|
const files = readLocalPlanFiles(input.dir);
|
|
547
|
+
assertLocalPlanFilesValid(files);
|
|
264
548
|
const parsed = stripFrontmatter(files.planMdx);
|
|
265
549
|
const title = input.title ||
|
|
266
550
|
parsed.frontmatter.title ||
|
|
@@ -358,7 +642,9 @@ export function buildLocalPlanPreviewHtml(input) {
|
|
|
358
642
|
}
|
|
359
643
|
export function writeLocalPlanPreview(input) {
|
|
360
644
|
const dir = path.resolve(input.dir);
|
|
361
|
-
const
|
|
645
|
+
const files = readLocalPlanFiles(dir);
|
|
646
|
+
assertLocalPlanFilesValid(files);
|
|
647
|
+
const parsed = stripFrontmatter(files.planMdx);
|
|
362
648
|
const kind = input.kind || normalizeKind(parsed.frontmatter.kind);
|
|
363
649
|
const title = input.title ||
|
|
364
650
|
parsed.frontmatter.title ||
|
|
@@ -369,12 +655,6 @@ export function writeLocalPlanPreview(input) {
|
|
|
369
655
|
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
370
656
|
fs.writeFileSync(out, buildLocalPlanPreviewHtml({ ...input, dir, kind }));
|
|
371
657
|
}
|
|
372
|
-
const files = [
|
|
373
|
-
"plan.mdx",
|
|
374
|
-
"canvas.mdx",
|
|
375
|
-
"prototype.mdx",
|
|
376
|
-
".plan-state.json",
|
|
377
|
-
].filter((file) => fs.existsSync(path.join(dir, file)));
|
|
378
658
|
const result = {
|
|
379
659
|
ok: true,
|
|
380
660
|
dir,
|
|
@@ -382,7 +662,7 @@ export function writeLocalPlanPreview(input) {
|
|
|
382
662
|
url: out ? pathToFileURL(out).href : localPlanPreviewUrl(dir, input.appUrl),
|
|
383
663
|
title,
|
|
384
664
|
kind,
|
|
385
|
-
files,
|
|
665
|
+
files: localPlanFileList(files),
|
|
386
666
|
};
|
|
387
667
|
if (!input.open)
|
|
388
668
|
return result;
|
|
@@ -394,6 +674,160 @@ export function writeLocalPlanPreview(input) {
|
|
|
394
674
|
...(openResult.error ? { openError: openResult.error } : {}),
|
|
395
675
|
};
|
|
396
676
|
}
|
|
677
|
+
function buildLocalPlanBridgePayload(input) {
|
|
678
|
+
const dir = path.resolve(input.dir);
|
|
679
|
+
const files = readLocalPlanFiles(dir);
|
|
680
|
+
assertLocalPlanFilesValid(files);
|
|
681
|
+
const parsed = stripFrontmatter(files.planMdx);
|
|
682
|
+
const kind = input.kind || normalizeKind(parsed.frontmatter.kind);
|
|
683
|
+
const title = input.title ||
|
|
684
|
+
parsed.frontmatter.title ||
|
|
685
|
+
firstHeading(parsed.body) ||
|
|
686
|
+
path.basename(dir);
|
|
687
|
+
const brief = input.brief || parsed.frontmatter.brief || "";
|
|
688
|
+
return {
|
|
689
|
+
ok: true,
|
|
690
|
+
version: 1,
|
|
691
|
+
source: "agent-native-local-bridge",
|
|
692
|
+
localOnly: true,
|
|
693
|
+
slug: path.basename(dir),
|
|
694
|
+
dir,
|
|
695
|
+
title,
|
|
696
|
+
brief,
|
|
697
|
+
kind,
|
|
698
|
+
updatedAt: latestLocalPlanMtime(dir, files),
|
|
699
|
+
files: localPlanFileList(files),
|
|
700
|
+
mdx: localPlanMdxFolder(files),
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function latestLocalPlanMtime(dir, files) {
|
|
704
|
+
const candidates = [
|
|
705
|
+
path.join(dir, "plan.mdx"),
|
|
706
|
+
...(files.canvasMdx ? [path.join(dir, "canvas.mdx")] : []),
|
|
707
|
+
...(files.prototypeMdx ? [path.join(dir, "prototype.mdx")] : []),
|
|
708
|
+
...(files.stateJson ? [path.join(dir, ".plan-state.json")] : []),
|
|
709
|
+
...Object.keys(files.assets ?? {}).map((filename) => path.join(dir, "assets", filename)),
|
|
710
|
+
];
|
|
711
|
+
let latest = 0;
|
|
712
|
+
for (const file of candidates) {
|
|
713
|
+
try {
|
|
714
|
+
latest = Math.max(latest, fs.statSync(file).mtimeMs);
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
// Ignore files deleted between the read and stat passes.
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return new Date(latest || Date.now()).toISOString();
|
|
721
|
+
}
|
|
722
|
+
function sendBridgeJson(res, status, payload) {
|
|
723
|
+
res.writeHead(status, {
|
|
724
|
+
"access-control-allow-origin": "*",
|
|
725
|
+
"access-control-allow-methods": "GET, OPTIONS",
|
|
726
|
+
"access-control-allow-headers": "content-type",
|
|
727
|
+
"cache-control": "no-store",
|
|
728
|
+
"content-type": "application/json; charset=utf-8",
|
|
729
|
+
"x-agent-native-local-bridge": "1",
|
|
730
|
+
});
|
|
731
|
+
res.end(`${JSON.stringify(payload)}\n`);
|
|
732
|
+
}
|
|
733
|
+
function bridgeRequestUrl(req) {
|
|
734
|
+
return new URL(req.url || "/", "http://127.0.0.1");
|
|
735
|
+
}
|
|
736
|
+
function bridgeHostForUrl(host) {
|
|
737
|
+
if (host === "0.0.0.0" || host === "::")
|
|
738
|
+
return "127.0.0.1";
|
|
739
|
+
return host;
|
|
740
|
+
}
|
|
741
|
+
export async function startLocalPlanBridge(input) {
|
|
742
|
+
const dir = path.resolve(input.dir);
|
|
743
|
+
const initialPayload = buildLocalPlanBridgePayload({
|
|
744
|
+
dir,
|
|
745
|
+
kind: input.kind,
|
|
746
|
+
title: input.title,
|
|
747
|
+
brief: input.brief,
|
|
748
|
+
});
|
|
749
|
+
const token = input.token || crypto.randomBytes(24).toString("base64url");
|
|
750
|
+
const host = input.host || "127.0.0.1";
|
|
751
|
+
const appUrl = normalizeBridgeAppUrl(input.appUrl);
|
|
752
|
+
const server = http.createServer((req, res) => {
|
|
753
|
+
if (req.method === "OPTIONS") {
|
|
754
|
+
sendBridgeJson(res, 204, "");
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (req.method !== "GET") {
|
|
758
|
+
sendBridgeJson(res, 405, { ok: false, error: "Method not allowed." });
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const url = bridgeRequestUrl(req);
|
|
762
|
+
if (url.pathname !== "/local-plan.json") {
|
|
763
|
+
sendBridgeJson(res, 404, { ok: false, error: "Not found." });
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
if (url.searchParams.get("token") !== token) {
|
|
767
|
+
sendBridgeJson(res, 403, { ok: false, error: "Invalid bridge token." });
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
sendBridgeJson(res, 200, buildLocalPlanBridgePayload({
|
|
772
|
+
dir,
|
|
773
|
+
kind: input.kind,
|
|
774
|
+
title: input.title,
|
|
775
|
+
brief: input.brief,
|
|
776
|
+
}));
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
sendBridgeJson(res, 500, {
|
|
780
|
+
ok: false,
|
|
781
|
+
error: error instanceof Error ? error.message : String(error),
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
await new Promise((resolve, reject) => {
|
|
786
|
+
const onError = (error) => {
|
|
787
|
+
server.off("listening", onListening);
|
|
788
|
+
reject(error);
|
|
789
|
+
};
|
|
790
|
+
const onListening = () => {
|
|
791
|
+
server.off("error", onError);
|
|
792
|
+
resolve();
|
|
793
|
+
};
|
|
794
|
+
server.once("error", onError);
|
|
795
|
+
server.once("listening", onListening);
|
|
796
|
+
server.listen(input.port ?? 0, host);
|
|
797
|
+
});
|
|
798
|
+
const address = server.address();
|
|
799
|
+
if (!address || typeof address === "string") {
|
|
800
|
+
server.close();
|
|
801
|
+
throw new Error("Local plan bridge did not bind to a TCP port.");
|
|
802
|
+
}
|
|
803
|
+
const bridgeUrl = `http://${bridgeHostForUrl(host)}:${address.port}/local-plan.json?token=${encodeURIComponent(token)}`;
|
|
804
|
+
const url = localPlanBridgePageUrl({ dir, bridgeUrl, appUrl });
|
|
805
|
+
const openResult = input.open
|
|
806
|
+
? (input.openUrl || openLocalUrl)(url)
|
|
807
|
+
: undefined;
|
|
808
|
+
return {
|
|
809
|
+
server,
|
|
810
|
+
result: {
|
|
811
|
+
ok: true,
|
|
812
|
+
dir,
|
|
813
|
+
url,
|
|
814
|
+
bridgeUrl,
|
|
815
|
+
appUrl,
|
|
816
|
+
title: initialPayload.title,
|
|
817
|
+
kind: initialPayload.kind,
|
|
818
|
+
files: initialPayload.files,
|
|
819
|
+
host,
|
|
820
|
+
port: address.port,
|
|
821
|
+
...(openResult
|
|
822
|
+
? {
|
|
823
|
+
opened: openResult.ok,
|
|
824
|
+
openCommand: openResult.command,
|
|
825
|
+
...(openResult.error ? { openError: openResult.error } : {}),
|
|
826
|
+
}
|
|
827
|
+
: {}),
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
}
|
|
397
831
|
function writeLocalPlanSkeleton(input) {
|
|
398
832
|
const dir = path.resolve(input.dir || path.join(defaultPlansDir(), localPlanFolderName(input.title)));
|
|
399
833
|
const planPath = path.join(dir, "plan.mdx");
|
|
@@ -421,9 +855,9 @@ function writeLocalPlanSkeleton(input) {
|
|
|
421
855
|
"## Review Surface",
|
|
422
856
|
"",
|
|
423
857
|
"Author the structured plan or recap here. You can add Agent-Native Plan MDX",
|
|
424
|
-
|
|
425
|
-
"`<FileTree />`, or `<Diff />`; the local
|
|
426
|
-
"without publishing it to the Plan app.",
|
|
858
|
+
'blocks such as `<WireframeBlock><Screen surface="browser">...</Screen></WireframeBlock>`,',
|
|
859
|
+
"`<Diagram />`, `<TabsBlock />`, `<FileTree />`, or `<Diff />`; the local",
|
|
860
|
+
"preview will show the source without publishing it to the Plan app.",
|
|
427
861
|
"",
|
|
428
862
|
].join("\n");
|
|
429
863
|
fs.writeFileSync(planPath, mdx, "utf-8");
|
|
@@ -448,10 +882,12 @@ function runInit(args) {
|
|
|
448
882
|
function runCheck(args) {
|
|
449
883
|
const dir = stringArg(args, "dir");
|
|
450
884
|
const files = readLocalPlanFiles(dir);
|
|
885
|
+
assertLocalPlanFilesValid(files);
|
|
451
886
|
const parsed = stripFrontmatter(files.planMdx);
|
|
452
887
|
const result = {
|
|
453
888
|
ok: true,
|
|
454
889
|
noDb: true,
|
|
890
|
+
validation: "passed",
|
|
455
891
|
dir: files.dir,
|
|
456
892
|
title: parsed.frontmatter.title || firstHeading(parsed.body),
|
|
457
893
|
kind: normalizeKind(parsed.frontmatter.kind),
|
|
@@ -466,6 +902,12 @@ function runCheck(args) {
|
|
|
466
902
|
...(files.stateJson
|
|
467
903
|
? { ".plan-state.json": Buffer.byteLength(files.stateJson) }
|
|
468
904
|
: {}),
|
|
905
|
+
...(files.assets
|
|
906
|
+
? Object.fromEntries(Object.entries(files.assets).map(([filename, base64]) => [
|
|
907
|
+
`assets/${filename}`,
|
|
908
|
+
Buffer.byteLength(base64, "base64"),
|
|
909
|
+
]))
|
|
910
|
+
: {}),
|
|
469
911
|
},
|
|
470
912
|
};
|
|
471
913
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -484,6 +926,34 @@ function runPreview(args) {
|
|
|
484
926
|
});
|
|
485
927
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
486
928
|
}
|
|
929
|
+
async function runServe(args) {
|
|
930
|
+
const portValue = optionalArg(args, "port");
|
|
931
|
+
const port = portValue ? Number(portValue) : undefined;
|
|
932
|
+
if (portValue && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
933
|
+
throw new Error("--port must be an integer between 0 and 65535.");
|
|
934
|
+
}
|
|
935
|
+
const bridge = await startLocalPlanBridge({
|
|
936
|
+
dir: stringArg(args, "dir"),
|
|
937
|
+
appUrl: optionalArg(args, "app-url"),
|
|
938
|
+
title: optionalArg(args, "title"),
|
|
939
|
+
brief: optionalArg(args, "brief"),
|
|
940
|
+
host: optionalArg(args, "host"),
|
|
941
|
+
port,
|
|
942
|
+
open: boolArg(args, "open"),
|
|
943
|
+
kind: optionalArg(args, "kind")
|
|
944
|
+
? normalizeKind(optionalArg(args, "kind"))
|
|
945
|
+
: undefined,
|
|
946
|
+
});
|
|
947
|
+
process.stdout.write(`${JSON.stringify(bridge.result, null, 2)}\n`);
|
|
948
|
+
process.stderr.write(`Local Plan bridge running at ${bridge.result.bridgeUrl}\nPress Ctrl+C to stop.\n`);
|
|
949
|
+
await new Promise((resolve) => {
|
|
950
|
+
const stop = () => {
|
|
951
|
+
bridge.server.close(() => resolve());
|
|
952
|
+
};
|
|
953
|
+
process.once("SIGINT", stop);
|
|
954
|
+
process.once("SIGTERM", stop);
|
|
955
|
+
});
|
|
956
|
+
}
|
|
487
957
|
async function runBlocks(args) {
|
|
488
958
|
const format = normalizePlanBlockFormat(optionalArg(args, "format"));
|
|
489
959
|
const appUrl = optionalArg(args, "app-url") ||
|
|
@@ -536,6 +1006,7 @@ Usage:
|
|
|
536
1006
|
agent-native plan blocks [--format reference|schema] [--app-url <url>] [--out <file>] [--json]
|
|
537
1007
|
agent-native plan local init --title <title> [--brief <text>] [--kind plan|recap] [--dir <folder>] [--force]
|
|
538
1008
|
agent-native plan local check --dir <folder>
|
|
1009
|
+
agent-native plan local serve --dir <folder> [--app-url <url>] [--kind plan|recap] [--open] [--port <port>]
|
|
539
1010
|
agent-native plan local preview --dir <folder> [--app-url <url>] [--kind plan|recap] [--open] [--out preview.html]
|
|
540
1011
|
|
|
541
1012
|
The blocks command fetches the no-auth, read-only get-plan-blocks catalog from
|
|
@@ -552,10 +1023,12 @@ write actions, hosted storage, or SQLite.
|
|
|
552
1023
|
Common flow:
|
|
553
1024
|
agent-native plan blocks --out plan-blocks.md
|
|
554
1025
|
agent-native plan local init --title "Checkout review" --kind plan
|
|
555
|
-
agent-native plan local
|
|
1026
|
+
agent-native plan local serve --dir plans/checkout-review --open
|
|
556
1027
|
|
|
557
|
-
\`plan local
|
|
558
|
-
|
|
1028
|
+
\`plan local serve\` starts a tiny localhost bridge and opens the hosted Plan UI
|
|
1029
|
+
against that local-only source. The hosted app fetches the MDX from localhost in
|
|
1030
|
+
the browser; it does not write plan content to the hosted database. Use
|
|
1031
|
+
\`plan local preview\` for a local Plan dev server route. \`preview --out\` is a
|
|
559
1032
|
legacy/debug escape hatch that writes a standalone static HTML file.
|
|
560
1033
|
`;
|
|
561
1034
|
export async function runPlan(argv) {
|
|
@@ -592,6 +1065,9 @@ export async function runPlan(argv) {
|
|
|
592
1065
|
case "preview":
|
|
593
1066
|
runPreview(args);
|
|
594
1067
|
return;
|
|
1068
|
+
case "serve":
|
|
1069
|
+
await runServe(args);
|
|
1070
|
+
return;
|
|
595
1071
|
case "help":
|
|
596
1072
|
case "--help":
|
|
597
1073
|
case "-h":
|