@monster-orange/den 0.1.0 → 0.2.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/CHANGELOG.md +137 -0
- package/README.md +1 -1
- package/bin/den +7 -0
- package/docs/distributions.md +255 -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 +16 -1
- 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 +321 -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 +131 -45
- package/src/ui/cli/mcp-main.ts +13 -0
- package/src/ui/cli/mcp-stdout-guard.ts +10 -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 +284 -0
- package/src/ui/mcp/guards.ts +141 -0
- package/src/ui/mcp/server.ts +147 -0
- package/src/ui/mcp/tools.ts +565 -0
- package/src/ui/server/audit.ts +71 -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 +175 -61
- 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 +976 -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 +787 -0
- package/src/ui/server/public/services.html +6 -1
- package/src/ui/server/readonly-routes.ts +200 -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
|
@@ -101,6 +101,7 @@ import {
|
|
|
101
101
|
getReposRoot,
|
|
102
102
|
mergeUserConfig,
|
|
103
103
|
readDenConfig,
|
|
104
|
+
isProductionEnv,
|
|
104
105
|
readUserConfig,
|
|
105
106
|
} from "../../shared/den-config.js";
|
|
106
107
|
import { deriveGraph } from "../../shared/environment-manager/derivation/derive-graph.js";
|
|
@@ -120,7 +121,7 @@ import {
|
|
|
120
121
|
} from "./applications-command.js";
|
|
121
122
|
import { loadApplications } from "../../shared/environment-manager/load-applications.js";
|
|
122
123
|
import { hasRuntime, repoKind } from "../../shared/repo-kind.js";
|
|
123
|
-
import type { RepoKind } from "../../shared/types/index.js";
|
|
124
|
+
import type { RepoKind, ValidationResult } from "../../shared/types/index.js";
|
|
124
125
|
import { localCommandsFor } from "../../shared/stacks/manifest-commands.js";
|
|
125
126
|
import {
|
|
126
127
|
buildUpPlan,
|
|
@@ -131,7 +132,15 @@ import * as fs from "fs";
|
|
|
131
132
|
import { readManifest } from "../../shared/manifest/read-manifest.js";
|
|
132
133
|
import * as nodePath from "path";
|
|
133
134
|
import { runInit } from "./init.js";
|
|
134
|
-
import {
|
|
135
|
+
import {
|
|
136
|
+
validateManifest,
|
|
137
|
+
validateManifestLayers,
|
|
138
|
+
} from "../../shared/manifest/validate-manifest.js";
|
|
139
|
+
import {
|
|
140
|
+
placementRefusal,
|
|
141
|
+
placementSuffix,
|
|
142
|
+
servicePlacement,
|
|
143
|
+
} from "../../shared/manifest/placement.js";
|
|
135
144
|
import {
|
|
136
145
|
getDistRoot,
|
|
137
146
|
getEngineRoot,
|
|
@@ -290,6 +299,13 @@ const COMMAND_HELP: Record<string, { usage: string; lines: string[] }> = {
|
|
|
290
299
|
"This org's HTTPS edge: own CA + cert, routes at <name>.<edge.domain>.",
|
|
291
300
|
],
|
|
292
301
|
},
|
|
302
|
+
mcp: {
|
|
303
|
+
usage: "mcp [--port <web port>]",
|
|
304
|
+
lines: [
|
|
305
|
+
"An MCP server over stdio for agents: a client of the running web server",
|
|
306
|
+
`(\`${product().cli} web up\`). Read tools always; actions only as named in mcp.actions.`,
|
|
307
|
+
],
|
|
308
|
+
},
|
|
293
309
|
"local-dev": {
|
|
294
310
|
usage: "local-dev [on|off]",
|
|
295
311
|
lines: [
|
|
@@ -345,6 +361,7 @@ const TELEMETRY_COMMANDS = new Set<string>([
|
|
|
345
361
|
"stack",
|
|
346
362
|
"apps",
|
|
347
363
|
"applications",
|
|
364
|
+
"mcp",
|
|
348
365
|
]);
|
|
349
366
|
|
|
350
367
|
/** Map a raw command token to a PII-safe, registered telemetry command name. */
|
|
@@ -362,6 +379,52 @@ function telemetryReason(error: unknown): string {
|
|
|
362
379
|
return name && name !== "Object" ? name : "Error";
|
|
363
380
|
}
|
|
364
381
|
|
|
382
|
+
/** Print one manifest's validation: a status line, then each issue. */
|
|
383
|
+
function printValidation(
|
|
384
|
+
label: string,
|
|
385
|
+
result: ValidationResult,
|
|
386
|
+
layer?: string,
|
|
387
|
+
): void {
|
|
388
|
+
const tag = layer ? `${layer}, ` : "";
|
|
389
|
+
const plural = (n: number, word: string): string =>
|
|
390
|
+
`${n} ${word}${n === 1 ? "" : "s"}`;
|
|
391
|
+
if (result.valid && result.warnings.length === 0) {
|
|
392
|
+
console.log(
|
|
393
|
+
chalk.green(` ✓ ${label}`) +
|
|
394
|
+
chalk.gray(` (${tag}v${result.version}, no issues)`),
|
|
395
|
+
);
|
|
396
|
+
} else if (result.valid) {
|
|
397
|
+
console.log(
|
|
398
|
+
chalk.yellow(` ⚠ ${label}`) +
|
|
399
|
+
chalk.gray(
|
|
400
|
+
` (${tag}v${result.version}, ${plural(result.warnings.length, "warning")})`,
|
|
401
|
+
),
|
|
402
|
+
);
|
|
403
|
+
} else {
|
|
404
|
+
console.log(
|
|
405
|
+
chalk.red(` ✗ ${label}`) +
|
|
406
|
+
chalk.gray(
|
|
407
|
+
` (${tag}v${result.version}, ${plural(result.errors.length, "error")}${result.warnings.length > 0 ? `, ${plural(result.warnings.length, "warning")}` : ""})`,
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
for (const err of result.errors) {
|
|
413
|
+
const path = err.path ? chalk.gray(err.path) + " " : "";
|
|
414
|
+
console.log(` ${chalk.red("ERROR")} ${path}${err.message}`);
|
|
415
|
+
if (err.suggestion) {
|
|
416
|
+
console.log(` ${chalk.gray("→ " + err.suggestion)}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const warn of result.warnings) {
|
|
420
|
+
const path = warn.path ? chalk.gray(warn.path) + " " : "";
|
|
421
|
+
console.log(` ${chalk.yellow("WARNING")} ${path}${warn.message}`);
|
|
422
|
+
if (warn.suggestion) {
|
|
423
|
+
console.log(` ${chalk.gray("→ " + warn.suggestion)}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
365
428
|
class DenCLI {
|
|
366
429
|
private static readonly serviceCommands = new Set([
|
|
367
430
|
"up",
|
|
@@ -631,6 +694,15 @@ class DenCLI {
|
|
|
631
694
|
cmdApps(args.slice(1));
|
|
632
695
|
break;
|
|
633
696
|
|
|
697
|
+
case "mcp": {
|
|
698
|
+
// bin/den routes `mcp` to its own entry (stdout is the protocol);
|
|
699
|
+
// this covers the CLI run directly from a checkout.
|
|
700
|
+
await import("./mcp-stdout-guard.js");
|
|
701
|
+
const { runMcp } = await import("../mcp/server.js");
|
|
702
|
+
await runMcp(args.slice(1));
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
|
|
634
706
|
case "--help":
|
|
635
707
|
case "-h":
|
|
636
708
|
this.displayHelp();
|
|
@@ -1304,6 +1376,25 @@ class DenCLI {
|
|
|
1304
1376
|
printPartialState();
|
|
1305
1377
|
return;
|
|
1306
1378
|
}
|
|
1379
|
+
// R13: a remote service is never started or stopped from here. Said,
|
|
1380
|
+
// not failed, so a workspace or app up/down carries on past it.
|
|
1381
|
+
const placed = resolveService(address, workspace);
|
|
1382
|
+
const remote = placed
|
|
1383
|
+
? placementRefusal(
|
|
1384
|
+
address,
|
|
1385
|
+
servicePlacement(
|
|
1386
|
+
readManifest(placed.repoPath),
|
|
1387
|
+
placed.subsystem,
|
|
1388
|
+
"local",
|
|
1389
|
+
),
|
|
1390
|
+
(e) => isProductionEnv(e),
|
|
1391
|
+
command === "down" ? "stop" : "start",
|
|
1392
|
+
)
|
|
1393
|
+
: null;
|
|
1394
|
+
if (remote) {
|
|
1395
|
+
console.log(chalk.gray(` • ${remote}`));
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1307
1398
|
if (command === "up" && !seedAddresses.has(address)) {
|
|
1308
1399
|
const resolved = resolveService(address, workspace);
|
|
1309
1400
|
if (resolved) {
|
|
@@ -2442,9 +2533,14 @@ class DenCLI {
|
|
|
2442
2533
|
for (const r of results) {
|
|
2443
2534
|
const healthStr = formatHealthLabel(r.health);
|
|
2444
2535
|
const reasonStr = formatHealthReason(r);
|
|
2536
|
+
const placed = placementSuffix({
|
|
2537
|
+
runs: r.runs ?? "none",
|
|
2538
|
+
target: r.target ?? null,
|
|
2539
|
+
});
|
|
2445
2540
|
|
|
2446
2541
|
table.push([
|
|
2447
|
-
this.labelWithAutostart(r.address, noAutostart)
|
|
2542
|
+
this.labelWithAutostart(r.address, noAutostart) +
|
|
2543
|
+
(placed ? chalk.cyan(` ${placed}`) : ""),
|
|
2448
2544
|
r.mode === "dev-managed"
|
|
2449
2545
|
? chalk.blue("dev-managed")
|
|
2450
2546
|
: chalk.magenta("managed"),
|
|
@@ -2606,13 +2702,19 @@ class DenCLI {
|
|
|
2606
2702
|
/* ignore */
|
|
2607
2703
|
}
|
|
2608
2704
|
|
|
2705
|
+
const placed = placementSuffix({
|
|
2706
|
+
runs: r.runs ?? "none",
|
|
2707
|
+
target: r.target ?? null,
|
|
2708
|
+
});
|
|
2609
2709
|
table.push([
|
|
2610
|
-
this.labelWithAutostart(r.address, noAutostart)
|
|
2710
|
+
this.labelWithAutostart(r.address, noAutostart) +
|
|
2711
|
+
(placed ? chalk.cyan(` ${placed}`) : ""),
|
|
2611
2712
|
r.mode === "dev-managed"
|
|
2612
2713
|
? chalk.blue("dev-managed")
|
|
2613
2714
|
: chalk.magenta("managed"),
|
|
2614
2715
|
healthStr,
|
|
2615
|
-
|
|
2716
|
+
// A remote service's commands are never run from here.
|
|
2717
|
+
placed ? chalk.gray("—") : cmds.join(", "),
|
|
2616
2718
|
]);
|
|
2617
2719
|
}
|
|
2618
2720
|
|
|
@@ -3415,10 +3517,26 @@ class DenCLI {
|
|
|
3415
3517
|
req.on("error", reject);
|
|
3416
3518
|
});
|
|
3417
3519
|
} else {
|
|
3418
|
-
const
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3520
|
+
const isDir = fs.existsSync(target) && fs.lstatSync(target).isDirectory();
|
|
3521
|
+
// A repo folder: validate each layer that applies to it (its own or the
|
|
3522
|
+
// held manifest, then manifests/<repo>.override.yml, then the merge).
|
|
3523
|
+
if (isDir) {
|
|
3524
|
+
const layered = validateManifestLayers(nodePath.resolve(target));
|
|
3525
|
+
if (layered.layers.length > 0) {
|
|
3526
|
+
console.log();
|
|
3527
|
+
for (const l of layered.layers)
|
|
3528
|
+
printValidation(
|
|
3529
|
+
chalk.bold(l.file ?? "merged"),
|
|
3530
|
+
l.result,
|
|
3531
|
+
l.layer === "merged" ? "merged" : l.layer,
|
|
3532
|
+
);
|
|
3533
|
+
console.log();
|
|
3534
|
+
if (!layered.valid) process.exit(1);
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
const filePath = isDir
|
|
3539
|
+
? nodePath.join(target, `${product().manifestYml}`)
|
|
3422
3540
|
: target;
|
|
3423
3541
|
|
|
3424
3542
|
if (!fs.existsSync(filePath)) {
|
|
@@ -3434,42 +3552,7 @@ class DenCLI {
|
|
|
3434
3552
|
);
|
|
3435
3553
|
|
|
3436
3554
|
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
|
-
|
|
3555
|
+
printValidation(label, result);
|
|
3473
3556
|
console.log();
|
|
3474
3557
|
if (!result.valid) process.exit(1);
|
|
3475
3558
|
}
|
|
@@ -3559,6 +3642,9 @@ class DenCLI {
|
|
|
3559
3642
|
console.log(
|
|
3560
3643
|
" create <name> [dir] Scaffold a new distribution of your own (--title, --cli, --env-prefix)",
|
|
3561
3644
|
);
|
|
3645
|
+
console.log(
|
|
3646
|
+
" mcp [--port <n>] MCP server (stdio) for agents, over the running web server",
|
|
3647
|
+
);
|
|
3562
3648
|
console.log();
|
|
3563
3649
|
console.log(" workspace list List workspaces");
|
|
3564
3650
|
console.log(
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry point for `<cli> mcp` (bin/den routes `mcp` here). Kept apart from
|
|
3
|
+
* the main CLI so nothing but the MCP protocol is ever written to stdout.
|
|
4
|
+
*/
|
|
5
|
+
import "./mcp-stdout-guard.js";
|
|
6
|
+
import { runMcp } from "../mcp/server.js";
|
|
7
|
+
|
|
8
|
+
runMcp(process.argv.slice(2)).catch((error: unknown) => {
|
|
9
|
+
process.stderr.write(
|
|
10
|
+
`mcp: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
11
|
+
);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imported first by the `mcp` entry: stdout carries the MCP protocol only,
|
|
3
|
+
* so anything that logs through console goes to stderr instead.
|
|
4
|
+
*/
|
|
5
|
+
const toStderr = (...args: unknown[]): void => console.error(...args);
|
|
6
|
+
console.log = toStderr;
|
|
7
|
+
console.info = toStderr;
|
|
8
|
+
console.debug = toStderr;
|
|
9
|
+
|
|
10
|
+
export {};
|
|
@@ -6,15 +6,8 @@
|
|
|
6
6
|
* https://<route>.<stack>.<edge.domain>.
|
|
7
7
|
*/
|
|
8
8
|
import chalk from "chalk";
|
|
9
|
-
import { readEdgeConfig } from "../../shared/edge/config.js";
|
|
10
9
|
import { routeUrl } from "../../shared/edge/render.js";
|
|
11
10
|
import { edgeUp } from "../../shared/edge/runtime.js";
|
|
12
|
-
import { readStacks } from "../../shared/stacks/config.js";
|
|
13
|
-
import {
|
|
14
|
-
getFocusedWorkspaceName,
|
|
15
|
-
getWorkspaceByName,
|
|
16
|
-
} from "../../shared/environment-manager/workspaces.js";
|
|
17
|
-
import { stackWorkspace } from "../../shared/stacks/render.js";
|
|
18
11
|
import { projectName, startOrder } from "../../shared/stacks/render.js";
|
|
19
12
|
import {
|
|
20
13
|
stackDown,
|
|
@@ -22,44 +15,26 @@ import {
|
|
|
22
15
|
stackLogs,
|
|
23
16
|
stackPs,
|
|
24
17
|
stackUp,
|
|
25
|
-
type StackContext,
|
|
26
18
|
} from "../../shared/stacks/runtime.js";
|
|
19
|
+
import { loadStackContext, stackStatuses } from "../../shared/stacks/status.js";
|
|
27
20
|
|
|
28
|
-
function
|
|
29
|
-
fkWs: string;
|
|
30
|
-
ws: string;
|
|
31
|
-
service?: string;
|
|
32
|
-
rest: string[];
|
|
33
|
-
} {
|
|
21
|
+
function serviceFlag(args: string[]): { service?: string; rest: string[] } {
|
|
34
22
|
const rest: string[] = [];
|
|
35
|
-
// The global -w/--workspace is parsed (and checked) before we run and
|
|
36
|
-
// arrives as DEN_WORKSPACE_OVERRIDE.
|
|
37
|
-
const fkWs =
|
|
38
|
-
process.env.DEN_WORKSPACE_OVERRIDE ||
|
|
39
|
-
getFocusedWorkspaceName() ||
|
|
40
|
-
"default";
|
|
41
23
|
let service: string | undefined;
|
|
42
24
|
for (let i = 0; i < args.length; i++) {
|
|
43
25
|
if (args[i] === "--service") service = args[++i];
|
|
44
26
|
else rest.push(args[i]);
|
|
45
27
|
}
|
|
46
|
-
|
|
47
|
-
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(ws))
|
|
48
|
-
throw new Error(
|
|
49
|
-
`workspace "${ws}" must be lowercase letters, digits and dashes`,
|
|
50
|
-
);
|
|
51
|
-
return { fkWs, ws, service, rest };
|
|
28
|
+
return { service, rest };
|
|
52
29
|
}
|
|
53
30
|
|
|
54
31
|
export async function cmdStack(argv: string[]): Promise<void> {
|
|
55
|
-
const {
|
|
32
|
+
const { service, rest } = serviceFlag(argv);
|
|
56
33
|
const [sub = "ls", ...args] = rest;
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
const ctx: StackContext = { edge, stacks, workspace: ws };
|
|
34
|
+
// The global -w/--workspace is parsed (and checked) before we run and
|
|
35
|
+
// arrives as DEN_WORKSPACE_OVERRIDE; loadStackContext reads it.
|
|
36
|
+
const ctx = loadStackContext();
|
|
37
|
+
const { edge, stacks } = ctx;
|
|
63
38
|
const names = args.filter((a) => !a.startsWith("-"));
|
|
64
39
|
const pick = () => {
|
|
65
40
|
if (!names.length)
|
|
@@ -71,17 +46,15 @@ export async function cmdStack(argv: string[]): Promise<void> {
|
|
|
71
46
|
|
|
72
47
|
switch (sub) {
|
|
73
48
|
case "ls":
|
|
74
|
-
for (const s of
|
|
49
|
+
for (const s of stackStatuses(ctx)) {
|
|
75
50
|
console.log(
|
|
76
51
|
chalk.bold(`${s.name}`) +
|
|
77
52
|
chalk.gray(
|
|
78
|
-
` ${s.repo}${s.dir ? `/${s.dir}` : ""} project ${
|
|
53
|
+
` ${s.repo}${s.dir ? `/${s.dir}` : ""} project ${s.project}`,
|
|
79
54
|
),
|
|
80
55
|
);
|
|
81
|
-
for (const route of
|
|
82
|
-
console.log(
|
|
83
|
-
` ${routeUrl(`${route}.${s.name}`, edge)} → ${s.routes[route]}`,
|
|
84
|
-
);
|
|
56
|
+
for (const route of s.routes)
|
|
57
|
+
console.log(` ${route.url} → ${route.target}`);
|
|
85
58
|
if (s.uses.length)
|
|
86
59
|
console.log(chalk.gray(` uses ${s.uses.join(", ")}`));
|
|
87
60
|
}
|
|
@@ -90,7 +63,7 @@ export async function cmdStack(argv: string[]): Promise<void> {
|
|
|
90
63
|
// Re-render the edge so its cert covers every stack, then start the stacks.
|
|
91
64
|
await edgeUp(edge);
|
|
92
65
|
for (const n of startOrder(pick(), stacks)) {
|
|
93
|
-
console.log(chalk.cyan(`\n▶ ${n} (${projectName(n,
|
|
66
|
+
console.log(chalk.cyan(`\n▶ ${n} (${projectName(n, ctx.workspace)})`));
|
|
94
67
|
stackUp(ctx, stacks[n], { build: args.includes("--build") });
|
|
95
68
|
for (const route of Object.keys(stacks[n].routes))
|
|
96
69
|
console.log(chalk.green(` ✓ ${routeUrl(`${route}.${n}`, edge)}`));
|
|
@@ -447,10 +447,16 @@ export class TestRunner {
|
|
|
447
447
|
): CommandStep[] {
|
|
448
448
|
const names = options.modules ?? [];
|
|
449
449
|
const script = "src/pillars/security/run.ts";
|
|
450
|
+
// An explicitly chosen stronger mode is passed on; the entrypoint's
|
|
451
|
+
// per-env ceiling refuses it where it isn't allowed.
|
|
452
|
+
const mode =
|
|
453
|
+
options.securityMode && options.securityMode !== "baseline"
|
|
454
|
+
? [`--mode=${options.securityMode}`]
|
|
455
|
+
: [];
|
|
450
456
|
if (names.length === 0) {
|
|
451
457
|
return [
|
|
452
458
|
{
|
|
453
|
-
...tsxCommand(script, `--env=${options.environment}
|
|
459
|
+
...tsxCommand(script, `--env=${options.environment}`, ...mode),
|
|
454
460
|
env: baseEnv,
|
|
455
461
|
label: "Run all security scans",
|
|
456
462
|
},
|
|
@@ -461,6 +467,7 @@ export class TestRunner {
|
|
|
461
467
|
script,
|
|
462
468
|
`--env=${options.environment}`,
|
|
463
469
|
`--service=${name}`,
|
|
470
|
+
...mode,
|
|
464
471
|
),
|
|
465
472
|
env: baseEnv,
|
|
466
473
|
label: `Run security scan ${name}`,
|