@monster-orange/den 0.1.0 → 0.2.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/CHANGELOG.md +204 -0
- package/README.md +1 -1
- package/bin/den +8 -0
- package/docs/distributions.md +283 -12
- package/package.json +4 -2
- package/src/pillars/journeys/sandbox.ts +2 -5
- package/src/pillars/security/discover.ts +17 -1
- package/src/pillars/security/run.ts +31 -3
- package/src/shared/agent-actor.ts +77 -0
- package/src/shared/den-config.ts +122 -0
- package/src/shared/edge/status.ts +74 -0
- package/src/shared/environment-manager/health.ts +54 -8
- package/src/shared/environment-manager/load-applications.ts +31 -1
- package/src/shared/environment-manager/log-tail.ts +5 -4
- package/src/shared/environment-manager/opctl.ts +2 -1
- package/src/shared/environment-manager/service-container.ts +47 -0
- package/src/shared/environment-manager/service-runner.ts +19 -2
- package/src/shared/environment-manager/sync-targets.ts +66 -0
- package/src/shared/environment-manager/up-plan.ts +25 -1
- package/src/shared/manifest/merge-override.ts +75 -0
- package/src/shared/manifest/placement.ts +333 -0
- package/src/shared/manifest/read-manifest.ts +156 -38
- package/src/shared/manifest/schema.ts +14 -0
- package/src/shared/manifest/validate-layers.ts +229 -0
- package/src/shared/manifest/validate-manifest.ts +66 -6
- package/src/shared/paths.ts +12 -0
- package/src/shared/redact.ts +107 -0
- package/src/shared/stacks/manifest-commands.ts +30 -0
- package/src/shared/stacks/status.ts +99 -0
- package/src/shared/types/index.ts +23 -0
- package/src/ui/cli/applications-command.ts +34 -2
- package/src/ui/cli/edge-command.ts +9 -19
- package/src/ui/cli/graph-command.ts +50 -4
- package/src/ui/cli/index.ts +184 -55
- package/src/ui/cli/mcp-connect.ts +350 -0
- package/src/ui/cli/mcp-main.ts +13 -0
- package/src/ui/cli/mcp-stdout-guard.ts +10 -0
- package/src/ui/cli/named-target.ts +11 -0
- package/src/ui/cli/stack-command.ts +13 -40
- package/src/ui/cli/test-runner.ts +8 -1
- package/src/ui/mcp/api.ts +286 -0
- package/src/ui/mcp/connect.ts +304 -0
- package/src/ui/mcp/guards.ts +145 -0
- package/src/ui/mcp/server.ts +149 -0
- package/src/ui/mcp/tools.ts +644 -0
- package/src/ui/server/audit.ts +137 -0
- package/src/ui/server/changelog-parser.ts +11 -0
- package/src/ui/server/graph-prefs.ts +89 -0
- package/src/ui/server/health-row.ts +15 -0
- package/src/ui/server/index.ts +256 -61
- package/src/ui/server/lifecycle-exec.ts +188 -0
- package/src/ui/server/log-history.ts +4 -3
- package/src/ui/server/public/environments.html +4 -2
- package/src/ui/server/public/graph-model.js +416 -0
- package/src/ui/server/public/graph.css +980 -0
- package/src/ui/server/public/graph.js +986 -0
- package/src/ui/server/public/header.js +7 -5
- package/src/ui/server/public/local-dev.html +130 -2191
- package/src/ui/server/public/logs.html +11 -4
- package/src/ui/server/public/panel.js +790 -0
- package/src/ui/server/public/services.html +6 -1
- package/src/ui/server/readonly-routes.ts +209 -0
- package/src/ui/server/ui-assets.ts +73 -0
- package/src/ui/server/workspace-routes.ts +39 -8
package/src/ui/cli/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Main entry point for the interactive CLI
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { isNamedTarget } from "./named-target.js";
|
|
8
9
|
import chalk from "chalk";
|
|
9
10
|
import oraSpinner from "ora";
|
|
10
11
|
|
|
@@ -101,6 +102,7 @@ import {
|
|
|
101
102
|
getReposRoot,
|
|
102
103
|
mergeUserConfig,
|
|
103
104
|
readDenConfig,
|
|
105
|
+
isProductionEnv,
|
|
104
106
|
readUserConfig,
|
|
105
107
|
} from "../../shared/den-config.js";
|
|
106
108
|
import { deriveGraph } from "../../shared/environment-manager/derivation/derive-graph.js";
|
|
@@ -120,7 +122,7 @@ import {
|
|
|
120
122
|
} from "./applications-command.js";
|
|
121
123
|
import { loadApplications } from "../../shared/environment-manager/load-applications.js";
|
|
122
124
|
import { hasRuntime, repoKind } from "../../shared/repo-kind.js";
|
|
123
|
-
import type { RepoKind } from "../../shared/types/index.js";
|
|
125
|
+
import type { RepoKind, ValidationResult } from "../../shared/types/index.js";
|
|
124
126
|
import { localCommandsFor } from "../../shared/stacks/manifest-commands.js";
|
|
125
127
|
import {
|
|
126
128
|
buildUpPlan,
|
|
@@ -131,7 +133,15 @@ import * as fs from "fs";
|
|
|
131
133
|
import { readManifest } from "../../shared/manifest/read-manifest.js";
|
|
132
134
|
import * as nodePath from "path";
|
|
133
135
|
import { runInit } from "./init.js";
|
|
134
|
-
import {
|
|
136
|
+
import {
|
|
137
|
+
validateManifest,
|
|
138
|
+
validateManifestLayers,
|
|
139
|
+
} from "../../shared/manifest/validate-manifest.js";
|
|
140
|
+
import {
|
|
141
|
+
placementRefusal,
|
|
142
|
+
placementSuffix,
|
|
143
|
+
servicePlacement,
|
|
144
|
+
} from "../../shared/manifest/placement.js";
|
|
135
145
|
import {
|
|
136
146
|
getDistRoot,
|
|
137
147
|
getEngineRoot,
|
|
@@ -290,6 +300,13 @@ const COMMAND_HELP: Record<string, { usage: string; lines: string[] }> = {
|
|
|
290
300
|
"This org's HTTPS edge: own CA + cert, routes at <name>.<edge.domain>.",
|
|
291
301
|
],
|
|
292
302
|
},
|
|
303
|
+
mcp: {
|
|
304
|
+
usage: "mcp [--port <web port>]",
|
|
305
|
+
lines: [
|
|
306
|
+
"An MCP server over stdio for agents: a client of the running web server",
|
|
307
|
+
`(\`${product().cli} web up\`). Read tools always; actions only as named in mcp.actions.`,
|
|
308
|
+
],
|
|
309
|
+
},
|
|
293
310
|
"local-dev": {
|
|
294
311
|
usage: "local-dev [on|off]",
|
|
295
312
|
lines: [
|
|
@@ -345,6 +362,7 @@ const TELEMETRY_COMMANDS = new Set<string>([
|
|
|
345
362
|
"stack",
|
|
346
363
|
"apps",
|
|
347
364
|
"applications",
|
|
365
|
+
"mcp",
|
|
348
366
|
]);
|
|
349
367
|
|
|
350
368
|
/** Map a raw command token to a PII-safe, registered telemetry command name. */
|
|
@@ -362,6 +380,52 @@ function telemetryReason(error: unknown): string {
|
|
|
362
380
|
return name && name !== "Object" ? name : "Error";
|
|
363
381
|
}
|
|
364
382
|
|
|
383
|
+
/** Print one manifest's validation: a status line, then each issue. */
|
|
384
|
+
function printValidation(
|
|
385
|
+
label: string,
|
|
386
|
+
result: ValidationResult,
|
|
387
|
+
layer?: string,
|
|
388
|
+
): void {
|
|
389
|
+
const tag = layer ? `${layer}, ` : "";
|
|
390
|
+
const plural = (n: number, word: string): string =>
|
|
391
|
+
`${n} ${word}${n === 1 ? "" : "s"}`;
|
|
392
|
+
if (result.valid && result.warnings.length === 0) {
|
|
393
|
+
console.log(
|
|
394
|
+
chalk.green(` ✓ ${label}`) +
|
|
395
|
+
chalk.gray(` (${tag}v${result.version}, no issues)`),
|
|
396
|
+
);
|
|
397
|
+
} else if (result.valid) {
|
|
398
|
+
console.log(
|
|
399
|
+
chalk.yellow(` ⚠ ${label}`) +
|
|
400
|
+
chalk.gray(
|
|
401
|
+
` (${tag}v${result.version}, ${plural(result.warnings.length, "warning")})`,
|
|
402
|
+
),
|
|
403
|
+
);
|
|
404
|
+
} else {
|
|
405
|
+
console.log(
|
|
406
|
+
chalk.red(` ✗ ${label}`) +
|
|
407
|
+
chalk.gray(
|
|
408
|
+
` (${tag}v${result.version}, ${plural(result.errors.length, "error")}${result.warnings.length > 0 ? `, ${plural(result.warnings.length, "warning")}` : ""})`,
|
|
409
|
+
),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
for (const err of result.errors) {
|
|
414
|
+
const path = err.path ? chalk.gray(err.path) + " " : "";
|
|
415
|
+
console.log(` ${chalk.red("ERROR")} ${path}${err.message}`);
|
|
416
|
+
if (err.suggestion) {
|
|
417
|
+
console.log(` ${chalk.gray("→ " + err.suggestion)}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
for (const warn of result.warnings) {
|
|
421
|
+
const path = warn.path ? chalk.gray(warn.path) + " " : "";
|
|
422
|
+
console.log(` ${chalk.yellow("WARNING")} ${path}${warn.message}`);
|
|
423
|
+
if (warn.suggestion) {
|
|
424
|
+
console.log(` ${chalk.gray("→ " + warn.suggestion)}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
365
429
|
class DenCLI {
|
|
366
430
|
private static readonly serviceCommands = new Set([
|
|
367
431
|
"up",
|
|
@@ -631,6 +695,26 @@ class DenCLI {
|
|
|
631
695
|
cmdApps(args.slice(1));
|
|
632
696
|
break;
|
|
633
697
|
|
|
698
|
+
case "mcp": {
|
|
699
|
+
// Setup subcommands: register the server with agents.
|
|
700
|
+
const { MCP_CONNECT_SUBCOMMANDS, runMcpConnect } =
|
|
701
|
+
await import("./mcp-connect.js");
|
|
702
|
+
if (
|
|
703
|
+
(MCP_CONNECT_SUBCOMMANDS as readonly string[]).includes(
|
|
704
|
+
args[1] ?? "",
|
|
705
|
+
)
|
|
706
|
+
) {
|
|
707
|
+
process.exitCode = await runMcpConnect(args.slice(1));
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
// bin/den routes `mcp` to its own entry (stdout is the protocol);
|
|
711
|
+
// this covers the CLI run directly from a checkout.
|
|
712
|
+
await import("./mcp-stdout-guard.js");
|
|
713
|
+
const { runMcp } = await import("../mcp/server.js");
|
|
714
|
+
await runMcp(args.slice(1));
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
|
|
634
718
|
case "--help":
|
|
635
719
|
case "-h":
|
|
636
720
|
this.displayHelp();
|
|
@@ -1226,7 +1310,15 @@ class DenCLI {
|
|
|
1226
1310
|
/** Skip ANY already-healthy node (derived `up` starts only what's down). */
|
|
1227
1311
|
skipHealthyAll?: boolean;
|
|
1228
1312
|
},
|
|
1229
|
-
opts?: {
|
|
1313
|
+
opts?: {
|
|
1314
|
+
force?: boolean;
|
|
1315
|
+
/**
|
|
1316
|
+
* What the user named on the command line (repo or address). A remote
|
|
1317
|
+
* service among them is refused with a non-zero exit; one reached only
|
|
1318
|
+
* as part of a workspace or app run is said and skipped.
|
|
1319
|
+
*/
|
|
1320
|
+
named?: readonly string[];
|
|
1321
|
+
},
|
|
1230
1322
|
): Promise<void> {
|
|
1231
1323
|
// How long to show startup output before handing control back to the menu
|
|
1232
1324
|
// for detachable (long-running) commands like `up`.
|
|
@@ -1304,6 +1396,30 @@ class DenCLI {
|
|
|
1304
1396
|
printPartialState();
|
|
1305
1397
|
return;
|
|
1306
1398
|
}
|
|
1399
|
+
// R13: a remote service is never started or stopped from here. Said,
|
|
1400
|
+
// not failed, so a workspace or app up/down carries on past it.
|
|
1401
|
+
const placed = resolveService(address, workspace);
|
|
1402
|
+
const remote = placed
|
|
1403
|
+
? placementRefusal(
|
|
1404
|
+
address,
|
|
1405
|
+
servicePlacement(
|
|
1406
|
+
readManifest(placed.repoPath),
|
|
1407
|
+
placed.subsystem,
|
|
1408
|
+
"local",
|
|
1409
|
+
),
|
|
1410
|
+
(e) => isProductionEnv(e),
|
|
1411
|
+
command,
|
|
1412
|
+
)
|
|
1413
|
+
: null;
|
|
1414
|
+
if (remote) {
|
|
1415
|
+
if (isNamedTarget(address, opts?.named ?? [])) {
|
|
1416
|
+
console.log(chalk.red(` ✗ ${remote}`));
|
|
1417
|
+
process.exitCode = 1;
|
|
1418
|
+
} else {
|
|
1419
|
+
console.log(chalk.gray(` • ${remote}`));
|
|
1420
|
+
}
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1307
1423
|
if (command === "up" && !seedAddresses.has(address)) {
|
|
1308
1424
|
const resolved = resolveService(address, workspace);
|
|
1309
1425
|
if (resolved) {
|
|
@@ -1641,6 +1757,7 @@ class DenCLI {
|
|
|
1641
1757
|
plan = false,
|
|
1642
1758
|
pull = false,
|
|
1643
1759
|
): Promise<void> {
|
|
1760
|
+
const named = [seed];
|
|
1644
1761
|
const full = deriveGraph({ scope: "all" });
|
|
1645
1762
|
// Same positional closure the web "up with deps" uses: everything the seed
|
|
1646
1763
|
// sits ON TOP OF, never anything above it. Keeps the two entry points from
|
|
@@ -1722,12 +1839,17 @@ class DenCLI {
|
|
|
1722
1839
|
);
|
|
1723
1840
|
return;
|
|
1724
1841
|
}
|
|
1725
|
-
await this.runServicesCommand(
|
|
1842
|
+
await this.runServicesCommand(
|
|
1726
1843
|
order,
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1844
|
+
"up",
|
|
1845
|
+
{
|
|
1846
|
+
order,
|
|
1847
|
+
waitDepsFor: (address) => hardDepsOf(graph, address),
|
|
1848
|
+
failFast: true,
|
|
1849
|
+
skipHealthyAll: true,
|
|
1850
|
+
},
|
|
1851
|
+
{ named },
|
|
1852
|
+
);
|
|
1731
1853
|
}
|
|
1732
1854
|
|
|
1733
1855
|
/**
|
|
@@ -1783,7 +1905,12 @@ class DenCLI {
|
|
|
1783
1905
|
console.log(chalk.gray(" (--dry-run: not stopping anything)\n"));
|
|
1784
1906
|
return;
|
|
1785
1907
|
}
|
|
1786
|
-
await this.runServicesCommand(
|
|
1908
|
+
await this.runServicesCommand(
|
|
1909
|
+
order,
|
|
1910
|
+
"down",
|
|
1911
|
+
{ order },
|
|
1912
|
+
{ force, named: [seed] },
|
|
1913
|
+
);
|
|
1787
1914
|
}
|
|
1788
1915
|
|
|
1789
1916
|
/** Workspace repos with no runtime (modules, libraries) → their kind. */
|
|
@@ -2301,7 +2428,9 @@ class DenCLI {
|
|
|
2301
2428
|
return;
|
|
2302
2429
|
}
|
|
2303
2430
|
preflightSecrets();
|
|
2304
|
-
await this.runServicesCommand(targets, "up"
|
|
2431
|
+
await this.runServicesCommand(targets, "up", undefined, {
|
|
2432
|
+
named: addresses,
|
|
2433
|
+
});
|
|
2305
2434
|
}
|
|
2306
2435
|
|
|
2307
2436
|
private async cmdServiceDown(args: string[]): Promise<void> {
|
|
@@ -2361,7 +2490,10 @@ class DenCLI {
|
|
|
2361
2490
|
addresses.length > 0
|
|
2362
2491
|
? addresses
|
|
2363
2492
|
: this.resolveNoArgServiceTargets("down");
|
|
2364
|
-
await this.runServicesCommand(targets, "down", undefined, {
|
|
2493
|
+
await this.runServicesCommand(targets, "down", undefined, {
|
|
2494
|
+
force,
|
|
2495
|
+
named: addresses,
|
|
2496
|
+
});
|
|
2365
2497
|
}
|
|
2366
2498
|
|
|
2367
2499
|
private async cmdServiceRestart(argv: string[]): Promise<void> {
|
|
@@ -2380,7 +2512,9 @@ class DenCLI {
|
|
|
2380
2512
|
filtered.length > 0
|
|
2381
2513
|
? filtered
|
|
2382
2514
|
: this.resolveNoArgServiceTargets("restart");
|
|
2383
|
-
await this.runServicesCommand(addresses, "restart"
|
|
2515
|
+
await this.runServicesCommand(addresses, "restart", undefined, {
|
|
2516
|
+
named: filtered,
|
|
2517
|
+
});
|
|
2384
2518
|
}
|
|
2385
2519
|
|
|
2386
2520
|
/** Service address with a ⏸ marker when the repo is auto-start off. */
|
|
@@ -2442,9 +2576,14 @@ class DenCLI {
|
|
|
2442
2576
|
for (const r of results) {
|
|
2443
2577
|
const healthStr = formatHealthLabel(r.health);
|
|
2444
2578
|
const reasonStr = formatHealthReason(r);
|
|
2579
|
+
const placed = placementSuffix({
|
|
2580
|
+
runs: r.runs ?? "none",
|
|
2581
|
+
target: r.target ?? null,
|
|
2582
|
+
});
|
|
2445
2583
|
|
|
2446
2584
|
table.push([
|
|
2447
|
-
this.labelWithAutostart(r.address, noAutostart)
|
|
2585
|
+
this.labelWithAutostart(r.address, noAutostart) +
|
|
2586
|
+
(placed ? chalk.cyan(` ${placed}`) : ""),
|
|
2448
2587
|
r.mode === "dev-managed"
|
|
2449
2588
|
? chalk.blue("dev-managed")
|
|
2450
2589
|
: chalk.magenta("managed"),
|
|
@@ -2606,13 +2745,19 @@ class DenCLI {
|
|
|
2606
2745
|
/* ignore */
|
|
2607
2746
|
}
|
|
2608
2747
|
|
|
2748
|
+
const placed = placementSuffix({
|
|
2749
|
+
runs: r.runs ?? "none",
|
|
2750
|
+
target: r.target ?? null,
|
|
2751
|
+
});
|
|
2609
2752
|
table.push([
|
|
2610
|
-
this.labelWithAutostart(r.address, noAutostart)
|
|
2753
|
+
this.labelWithAutostart(r.address, noAutostart) +
|
|
2754
|
+
(placed ? chalk.cyan(` ${placed}`) : ""),
|
|
2611
2755
|
r.mode === "dev-managed"
|
|
2612
2756
|
? chalk.blue("dev-managed")
|
|
2613
2757
|
: chalk.magenta("managed"),
|
|
2614
2758
|
healthStr,
|
|
2615
|
-
|
|
2759
|
+
// A remote service's commands are never run from here.
|
|
2760
|
+
placed ? chalk.gray("—") : cmds.join(", "),
|
|
2616
2761
|
]);
|
|
2617
2762
|
}
|
|
2618
2763
|
|
|
@@ -3415,10 +3560,26 @@ class DenCLI {
|
|
|
3415
3560
|
req.on("error", reject);
|
|
3416
3561
|
});
|
|
3417
3562
|
} else {
|
|
3418
|
-
const
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3563
|
+
const isDir = fs.existsSync(target) && fs.lstatSync(target).isDirectory();
|
|
3564
|
+
// A repo folder: validate each layer that applies to it (its own or the
|
|
3565
|
+
// held manifest, then manifests/<repo>.override.yml, then the merge).
|
|
3566
|
+
if (isDir) {
|
|
3567
|
+
const layered = validateManifestLayers(nodePath.resolve(target));
|
|
3568
|
+
if (layered.layers.length > 0) {
|
|
3569
|
+
console.log();
|
|
3570
|
+
for (const l of layered.layers)
|
|
3571
|
+
printValidation(
|
|
3572
|
+
chalk.bold(l.file ?? "merged"),
|
|
3573
|
+
l.result,
|
|
3574
|
+
l.layer === "merged" ? "merged" : l.layer,
|
|
3575
|
+
);
|
|
3576
|
+
console.log();
|
|
3577
|
+
if (!layered.valid) process.exit(1);
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
const filePath = isDir
|
|
3582
|
+
? nodePath.join(target, `${product().manifestYml}`)
|
|
3422
3583
|
: target;
|
|
3423
3584
|
|
|
3424
3585
|
if (!fs.existsSync(filePath)) {
|
|
@@ -3434,42 +3595,7 @@ class DenCLI {
|
|
|
3434
3595
|
);
|
|
3435
3596
|
|
|
3436
3597
|
console.log();
|
|
3437
|
-
|
|
3438
|
-
console.log(
|
|
3439
|
-
chalk.green(` ✓ ${label}`) +
|
|
3440
|
-
chalk.gray(` (v${result.version}, no issues)`),
|
|
3441
|
-
);
|
|
3442
|
-
} else if (result.valid) {
|
|
3443
|
-
console.log(
|
|
3444
|
-
chalk.yellow(` ⚠ ${label}`) +
|
|
3445
|
-
chalk.gray(
|
|
3446
|
-
` (v${result.version}, ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"})`,
|
|
3447
|
-
),
|
|
3448
|
-
);
|
|
3449
|
-
} else {
|
|
3450
|
-
console.log(
|
|
3451
|
-
chalk.red(` ✗ ${label}`) +
|
|
3452
|
-
chalk.gray(
|
|
3453
|
-
` (v${result.version}, ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}${result.warnings.length > 0 ? `, ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}` : ""})`,
|
|
3454
|
-
),
|
|
3455
|
-
);
|
|
3456
|
-
}
|
|
3457
|
-
|
|
3458
|
-
for (const err of result.errors) {
|
|
3459
|
-
const path = err.path ? chalk.gray(err.path) + " " : "";
|
|
3460
|
-
console.log(` ${chalk.red("ERROR")} ${path}${err.message}`);
|
|
3461
|
-
if (err.suggestion) {
|
|
3462
|
-
console.log(` ${chalk.gray("→ " + err.suggestion)}`);
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
for (const warn of result.warnings) {
|
|
3466
|
-
const path = warn.path ? chalk.gray(warn.path) + " " : "";
|
|
3467
|
-
console.log(` ${chalk.yellow("WARNING")} ${path}${warn.message}`);
|
|
3468
|
-
if (warn.suggestion) {
|
|
3469
|
-
console.log(` ${chalk.gray("→ " + warn.suggestion)}`);
|
|
3470
|
-
}
|
|
3471
|
-
}
|
|
3472
|
-
|
|
3598
|
+
printValidation(label, result);
|
|
3473
3599
|
console.log();
|
|
3474
3600
|
if (!result.valid) process.exit(1);
|
|
3475
3601
|
}
|
|
@@ -3559,6 +3685,9 @@ class DenCLI {
|
|
|
3559
3685
|
console.log(
|
|
3560
3686
|
" create <name> [dir] Scaffold a new distribution of your own (--title, --cli, --env-prefix)",
|
|
3561
3687
|
);
|
|
3688
|
+
console.log(
|
|
3689
|
+
" mcp [--port <n>] MCP server (stdio) for agents, over the running web server",
|
|
3690
|
+
);
|
|
3562
3691
|
console.log();
|
|
3563
3692
|
console.log(" workspace list List workspaces");
|
|
3564
3693
|
console.log(
|