@monster-orange/den 0.2.8 → 0.4.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 +1731 -0
- package/README.md +4 -2
- package/bin/den +2 -1
- package/docs/distributions.md +730 -6
- package/make/den.mk +19 -4
- package/make/journeys.mk +2 -1
- package/make/local_dev.mk +6 -13
- package/make/secrets-1password.mk +18 -0
- package/make/test.mk +1 -1
- package/package.json +12 -2
- package/schemas/den.plugin.v1.json +156 -0
- package/schemas/den.theme.v1.json +269 -0
- package/scripts/auth-configure.ts +16 -8
- package/scripts/heal-config.ts +0 -5
- package/scripts/setup-1password.ts +41 -19
- package/scripts/setup-secrets.ts +67 -101
- package/src/pillars/api/global-setup.ts +1 -1
- package/src/pillars/journeys/sandbox.ts +23 -18
- package/src/pillars/journeys/tools/browser-session.ts +2 -2
- package/src/scripts/setup-default-workspace.ts +2 -2
- package/src/shared/agents/default-pack/be-dev.md +20 -0
- package/src/shared/agents/default-pack/devops.md +14 -0
- package/src/shared/agents/default-pack/fe-dev.md +20 -0
- package/src/shared/agents/default-pack/orchestrator.md +16 -0
- package/src/shared/agents/default-pack/planner.md +17 -0
- package/src/shared/agents/default-pack/reviewer.md +14 -0
- package/src/shared/agents/default-pack/ux-designer.md +15 -0
- package/src/shared/agents/den-claude.ts +173 -0
- package/src/shared/agents/generic.ts +52 -0
- package/src/shared/agents/personas.ts +218 -0
- package/src/shared/agents/records.ts +118 -0
- package/src/shared/agents/runtime.ts +192 -0
- package/src/shared/agents/runtimes.ts +47 -0
- package/src/shared/auth/headless-login.ts +1 -1
- package/src/shared/auth/sudo-vault.ts +93 -31
- package/src/shared/auth/token-providers/index.ts +7 -3
- package/src/shared/brand.ts +7 -0
- package/src/shared/config-error.ts +33 -3
- package/src/shared/constants.ts +3 -24
- package/src/shared/den-config.ts +257 -5
- package/src/shared/edge/config.ts +147 -21
- package/src/shared/edge/exec.ts +31 -0
- package/src/shared/edge/included.ts +169 -0
- package/src/shared/edge/render.ts +69 -14
- package/src/shared/edge/runtime.ts +128 -121
- package/src/shared/edge/status.ts +68 -8
- package/src/shared/edge/tls.ts +328 -0
- package/src/shared/edge/types.ts +25 -9
- package/src/shared/engine.ts +23 -0
- package/src/shared/environment-manager/default-workspace.ts +16 -1
- package/src/shared/environment-manager/health.ts +73 -38
- package/src/shared/environment-manager/scope.ts +168 -0
- package/src/shared/environment-manager/service-runner.ts +789 -43
- package/src/shared/environment-manager/up-targets.ts +132 -0
- package/src/shared/environment-manager/workspaces.ts +19 -0
- package/src/shared/health/rollup.ts +48 -0
- package/src/shared/open-url.ts +21 -2
- package/src/shared/org-sources.ts +78 -59
- package/src/shared/org.ts +53 -5
- package/src/shared/plugins/manifest.ts +406 -0
- package/src/shared/plugins/store.ts +358 -0
- package/src/shared/process/legacy-adopt.ts +285 -0
- package/src/shared/process/process-group.ts +202 -0
- package/src/shared/process/spawn-streaming.ts +7 -0
- package/src/shared/secret-env.ts +70 -0
- package/src/shared/secrets/doppler.ts +1 -1
- package/src/shared/secrets/index.ts +279 -50
- package/src/shared/secrets/none.ts +56 -0
- package/src/shared/secrets/strategies.ts +202 -0
- package/src/shared/secrets/types.ts +1 -1
- package/src/shared/sessions/client.ts +277 -0
- package/src/shared/sessions/den-pty.ts +97 -0
- package/src/shared/sessions/env.ts +79 -0
- package/src/shared/sessions/host.ts +667 -0
- package/src/shared/sessions/names.ts +85 -0
- package/src/shared/sessions/paths.ts +27 -0
- package/src/shared/sessions/protocol.ts +72 -0
- package/src/shared/sessions/pty.ts +141 -0
- package/src/shared/sessions/ring-buffer.ts +160 -0
- package/src/shared/sessions/types.ts +81 -0
- package/src/shared/smoke/git-remote.ts +40 -0
- package/src/shared/stacks/manifest-commands.ts +1 -1
- package/src/shared/stacks/namespace.ts +178 -0
- package/src/shared/stacks/owners.ts +303 -0
- package/src/shared/stacks/render.ts +8 -3
- package/src/shared/stacks/runtime.ts +100 -16
- package/src/shared/stacks/status.ts +63 -12
- package/src/shared/strategies/registry.ts +89 -0
- package/src/shared/theme/contract.ts +538 -0
- package/src/shared/types/index.ts +12 -3
- package/src/ui/cli/agents-command.ts +812 -0
- package/src/ui/cli/create.ts +24 -7
- package/src/ui/cli/doctor-command.ts +260 -0
- package/src/ui/cli/edge-command.ts +56 -10
- package/src/ui/cli/env-probe.ts +266 -0
- package/src/ui/cli/flag-parser.ts +18 -5
- package/src/ui/cli/health-display.ts +25 -0
- package/src/ui/cli/index.ts +580 -60
- package/src/ui/cli/notice.ts +20 -0
- package/src/ui/cli/plugin-command.ts +849 -0
- package/src/ui/cli/sessions-command.ts +718 -0
- package/src/ui/cli/stack-command.ts +63 -2
- package/src/ui/cli/test-runner.ts +11 -9
- package/src/ui/cli/theme-command.ts +136 -0
- package/src/ui/cli/web-command.ts +47 -23
- package/src/ui/mcp/api.ts +28 -2
- package/src/ui/mcp/scope.ts +145 -20
- package/src/ui/mcp/server.ts +27 -2
- package/src/ui/mcp/tools.ts +94 -12
- package/src/ui/server/appearance-prefs.ts +95 -0
- package/src/ui/server/audit.ts +35 -0
- package/src/ui/server/daemon-reaper.ts +29 -1
- package/src/ui/server/engine-stamp.ts +83 -0
- package/src/ui/server/github-service.ts +23 -44
- package/src/ui/server/graph-page.ts +20 -0
- package/src/ui/server/graph-prefs.ts +44 -1
- package/src/ui/server/http-errors.ts +293 -0
- package/src/ui/server/http-helpers.ts +22 -3
- package/src/ui/server/index.ts +539 -51
- package/src/ui/server/public/appearance.js +531 -0
- package/src/ui/server/public/edge-page.js +248 -0
- package/src/ui/server/public/edge.html +251 -0
- package/src/ui/server/public/environments.html +65 -83
- package/src/ui/server/public/error-page.js +247 -0
- package/src/ui/server/public/error.html +260 -0
- package/src/ui/server/public/graph-model.js +332 -14
- package/src/ui/server/public/graph.css +141 -47
- package/src/ui/server/public/graph.js +223 -44
- package/src/ui/server/public/header.js +396 -600
- package/src/ui/server/public/health-fit.js +248 -0
- package/src/ui/server/public/health-rollup.js +128 -0
- package/src/ui/server/public/i18n/es.js +287 -0
- package/src/ui/server/public/i18n/ja.js +276 -0
- package/src/ui/server/public/index.html +139 -438
- package/src/ui/server/public/local-dev.html +3468 -4044
- package/src/ui/server/public/logs.html +71 -208
- package/src/ui/server/public/panel.js +14 -2
- package/src/ui/server/public/release.html +175 -389
- package/src/ui/server/public/services.html +596 -770
- package/src/ui/server/public/settings-page.js +759 -0
- package/src/ui/server/public/settings-registry.js +521 -0
- package/src/ui/server/public/settings.html +590 -0
- package/src/ui/server/public/shell.js +3869 -0
- package/src/ui/server/public/topbar.js +1413 -0
- package/src/ui/server/public/version-label.js +12 -0
- package/src/ui/server/public/workspace-picker.js +74 -34
- package/src/ui/server/readonly-routes.ts +37 -3
- package/src/ui/server/services-list.ts +94 -17
- package/src/ui/server/settings-distribution.ts +74 -0
- package/src/ui/server/shell-prefs.ts +192 -0
- package/src/ui/server/theme-routes.ts +292 -0
- package/src/ui/server/theme-runtime.ts +407 -0
- package/src/ui/server/ui-assets.ts +16 -1
- package/src/ui/server/ui-language.ts +50 -0
- package/src/ui/server/workspace-routes.ts +48 -18
- package/themes/den/den-theme.json +72 -0
- package/themes/den/favicon.ico +0 -0
- package/themes/den/mark.svg +6 -0
package/src/ui/cli/create.ts
CHANGED
|
@@ -146,17 +146,26 @@ export function renderDistribution(
|
|
|
146
146
|
...bannerLines(title).map((l) => ` - "${l}"`),
|
|
147
147
|
"",
|
|
148
148
|
"# The GitHub org (or user) that owns the repos Den clones and lists.",
|
|
149
|
-
"
|
|
149
|
+
"# Unset, Den lists no GitHub repos (and never calls gh for them). E.g.",
|
|
150
|
+
"# github_org: example-org",
|
|
150
151
|
"",
|
|
151
|
-
"# Secrets backend:
|
|
152
|
-
"#
|
|
152
|
+
"# Secrets backend: none (this distribution needs no secrets store), or",
|
|
153
|
+
"# env (plain environment variables), doppler or 1password. A backend's",
|
|
154
|
+
"# refs say where Den's own features find their secrets, e.g.",
|
|
155
|
+
`# refs: { sudo: { key: ${prefix}_LOCAL_ADMIN_PWD } }`,
|
|
153
156
|
"secrets:",
|
|
154
|
-
" provider:
|
|
157
|
+
" provider: none",
|
|
155
158
|
"",
|
|
156
|
-
"# This instance's
|
|
159
|
+
"# This instance's edge: <service>.<domain>, one Traefik for its routes.",
|
|
157
160
|
"edge:",
|
|
158
161
|
` org: ${name}`,
|
|
159
162
|
` domain: ${name}.localhost`,
|
|
163
|
+
" # TLS: none serves http only. For https, pick a strategy:",
|
|
164
|
+
" # tls: { strategy: mkcert } # a local CA, per machine",
|
|
165
|
+
" # tls: { strategy: mkcert, ca: ~/path/to/ca } # an existing mkcert CA",
|
|
166
|
+
" # tls: { strategy: files, include: edge/dynamic, mounts: { <host>: <container> } }",
|
|
167
|
+
" # # your own certs, in included Traefik config",
|
|
168
|
+
" tls: none",
|
|
160
169
|
"",
|
|
161
170
|
"environments: [local]",
|
|
162
171
|
"",
|
|
@@ -344,13 +353,21 @@ export function runCreate(argv: string[], cwd: string): number {
|
|
|
344
353
|
denVersion: runningDenVersion(),
|
|
345
354
|
});
|
|
346
355
|
const cli = args.cli ?? args.name;
|
|
347
|
-
|
|
356
|
+
// Relative only when the new directory is under the caller's cwd; a
|
|
357
|
+
// path that climbs out (../../..) is harder to read than the absolute one.
|
|
358
|
+
const rel = path.relative(cwd, args.dir);
|
|
359
|
+
const cdPath =
|
|
360
|
+
rel === ""
|
|
361
|
+
? "."
|
|
362
|
+
: rel.startsWith("..") || path.isAbsolute(rel)
|
|
363
|
+
? args.dir
|
|
364
|
+
: rel;
|
|
348
365
|
console.log(
|
|
349
366
|
`Created ${args.name} in ${args.dir} (${plural(written.length, "file")}).`,
|
|
350
367
|
);
|
|
351
368
|
console.log("");
|
|
352
369
|
console.log("Next:");
|
|
353
|
-
console.log(` cd ${
|
|
370
|
+
console.log(` cd ${cdPath}`);
|
|
354
371
|
console.log(" npm install");
|
|
355
372
|
console.log(` ./bin/${cli} --help`);
|
|
356
373
|
return 0;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `doctor`: every strategy kind in the registry (secrets, tls,
|
|
3
|
+
* session-backend, runtime), the strategy this distribution selects for it, whether
|
|
4
|
+
* nothing chose it (implicit), and its detect() result with the reason.
|
|
5
|
+
*
|
|
6
|
+
* Read-only: detect() is a PATH check (session-backend loads the optional
|
|
7
|
+
* PTY module, which runs nothing; runtime reads a CLI's version from its
|
|
8
|
+
* install, never by running it), so doctor never spawns a backend CLI,
|
|
9
|
+
* never prompts, and never prints a secret value. Exit 1 when a strategy
|
|
10
|
+
* that will be used is unavailable or its config is invalid. When agents
|
|
11
|
+
* are configured, a note says to run `<cli> doctor --probe`.
|
|
12
|
+
*
|
|
13
|
+
* `--probe` (opt-in, AG spec §5a-env) is the one exception to never
|
|
14
|
+
* spawning: it runs a short probe session through the session host for a
|
|
15
|
+
* plain session and each available runtime's agent env (env-probe.ts), and
|
|
16
|
+
* exits 1 when a runtime's scrubEnv name or a credential-shaped variable
|
|
17
|
+
* reaches one. Without the PTY module the probe is unavailable, not failed.
|
|
18
|
+
*/
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
import { readDenConfig, type DenConfig } from "../../shared/den-config.js";
|
|
21
|
+
import { getStateDir } from "../../shared/paths.js";
|
|
22
|
+
import { product } from "../../shared/product.js";
|
|
23
|
+
import { listRecords } from "../../shared/agents/records.js";
|
|
24
|
+
import { readBrand } from "../../shared/brand.js";
|
|
25
|
+
import { hasEdgeBlock, readEdgeConfig } from "../../shared/edge/config.js";
|
|
26
|
+
import { edgeTlsWarnings, tlsStrategyFor } from "../../shared/edge/tls.js";
|
|
27
|
+
import {
|
|
28
|
+
readSecretsConfig,
|
|
29
|
+
secretsStatus,
|
|
30
|
+
} from "../../shared/secrets/index.js";
|
|
31
|
+
import {
|
|
32
|
+
DEN_PTY_ID,
|
|
33
|
+
SESSION_BACKEND_KIND,
|
|
34
|
+
} from "../../shared/sessions/den-pty.js";
|
|
35
|
+
import {
|
|
36
|
+
getStrategy,
|
|
37
|
+
strategyKinds,
|
|
38
|
+
} from "../../shared/strategies/registry.js";
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_RUNTIME_ID,
|
|
41
|
+
RUNTIME_KIND,
|
|
42
|
+
} from "../../shared/agents/runtimes.js";
|
|
43
|
+
|
|
44
|
+
export interface DoctorRow {
|
|
45
|
+
kind: string;
|
|
46
|
+
/** The selected strategy id ("" when the config could not be read). */
|
|
47
|
+
selected: string;
|
|
48
|
+
implicit: boolean;
|
|
49
|
+
available: boolean;
|
|
50
|
+
reason?: string;
|
|
51
|
+
/** True when it will be used and cannot work: doctor exits 1. */
|
|
52
|
+
problem: boolean;
|
|
53
|
+
warnings: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type Selector = (env: NodeJS.ProcessEnv) => DoctorRow;
|
|
57
|
+
|
|
58
|
+
const SELECTORS: Record<string, Selector> = {
|
|
59
|
+
secrets(env) {
|
|
60
|
+
const st = secretsStatus(readSecretsConfig(), env);
|
|
61
|
+
return {
|
|
62
|
+
kind: "secrets",
|
|
63
|
+
selected: st.backend,
|
|
64
|
+
implicit: st.implicit,
|
|
65
|
+
available: st.detect.available,
|
|
66
|
+
reason: st.detect.reason,
|
|
67
|
+
// The implicit default is used only when a secret is asked for.
|
|
68
|
+
problem: !st.detect.available && !st.implicit,
|
|
69
|
+
warnings: [],
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
tls(env) {
|
|
73
|
+
if (!hasEdgeBlock())
|
|
74
|
+
return {
|
|
75
|
+
kind: "tls",
|
|
76
|
+
selected: "none",
|
|
77
|
+
implicit: false,
|
|
78
|
+
available: true,
|
|
79
|
+
reason: "no edge: block",
|
|
80
|
+
problem: false,
|
|
81
|
+
warnings: [],
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
const cfg = readEdgeConfig();
|
|
85
|
+
const d = tlsStrategyFor(cfg).detect(env);
|
|
86
|
+
return {
|
|
87
|
+
kind: "tls",
|
|
88
|
+
selected: cfg.tls.strategy,
|
|
89
|
+
implicit: cfg.tlsFrom === "implicit",
|
|
90
|
+
available: d.available,
|
|
91
|
+
reason: d.reason,
|
|
92
|
+
problem: !d.available,
|
|
93
|
+
warnings: edgeTlsWarnings(cfg),
|
|
94
|
+
};
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return {
|
|
97
|
+
kind: "tls",
|
|
98
|
+
selected: "",
|
|
99
|
+
implicit: false,
|
|
100
|
+
available: false,
|
|
101
|
+
reason: (err as Error).message,
|
|
102
|
+
problem: true,
|
|
103
|
+
warnings: [],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
// Agents' terminals (AG-3a). Only den-pty exists, so it is the implicit
|
|
108
|
+
// choice; unavailable is never a problem (nothing needs a session until
|
|
109
|
+
// one is started, and `sessions start` says why).
|
|
110
|
+
[SESSION_BACKEND_KIND](env) {
|
|
111
|
+
const d = getStrategy(SESSION_BACKEND_KIND, DEN_PTY_ID)?.detect(env) ?? {
|
|
112
|
+
available: false,
|
|
113
|
+
reason: "not registered",
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
kind: SESSION_BACKEND_KIND,
|
|
117
|
+
selected: DEN_PTY_ID,
|
|
118
|
+
implicit: true,
|
|
119
|
+
available: d.available,
|
|
120
|
+
reason: d.reason,
|
|
121
|
+
problem: false,
|
|
122
|
+
warnings: [],
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
// Agents' CLIs (AG-2a): the default runtime, chosen per persona or agent,
|
|
126
|
+
// so it is implicit and its absence is never a problem (`agents start`
|
|
127
|
+
// says why it can't run).
|
|
128
|
+
[RUNTIME_KIND](env) {
|
|
129
|
+
const d = getStrategy(RUNTIME_KIND, DEFAULT_RUNTIME_ID)?.detect(env) ?? {
|
|
130
|
+
available: false,
|
|
131
|
+
reason: "not registered",
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
kind: RUNTIME_KIND,
|
|
135
|
+
selected: DEFAULT_RUNTIME_ID,
|
|
136
|
+
implicit: true,
|
|
137
|
+
available: d.available,
|
|
138
|
+
reason: d.reason,
|
|
139
|
+
problem: false,
|
|
140
|
+
warnings: [],
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* One row per registered kind (secrets, then tls, then any other kind in
|
|
147
|
+
* registration order). Never spawns, never throws.
|
|
148
|
+
*/
|
|
149
|
+
export function doctorRows(env: NodeJS.ProcessEnv = process.env): DoctorRow[] {
|
|
150
|
+
const kinds = strategyKinds();
|
|
151
|
+
const ordered = [
|
|
152
|
+
...Object.keys(SELECTORS).filter((k) => kinds.includes(k)),
|
|
153
|
+
...kinds.filter((k) => !(k in SELECTORS)),
|
|
154
|
+
];
|
|
155
|
+
return ordered.map(
|
|
156
|
+
(kind) =>
|
|
157
|
+
SELECTORS[kind]?.(env) ?? {
|
|
158
|
+
kind,
|
|
159
|
+
selected: "",
|
|
160
|
+
implicit: false,
|
|
161
|
+
available: false,
|
|
162
|
+
reason: "no selector for this kind",
|
|
163
|
+
problem: false,
|
|
164
|
+
warnings: [],
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function formatDoctor(rows: DoctorRow[], title: string): string[] {
|
|
170
|
+
const lines = [chalk.bold(`${title} doctor: strategies`), ""];
|
|
171
|
+
const width = Math.max(...rows.map((r) => r.kind.length));
|
|
172
|
+
for (const r of rows) {
|
|
173
|
+
const sel = r.selected
|
|
174
|
+
? `${r.selected}${r.implicit ? " (implicit)" : ""}`
|
|
175
|
+
: "(invalid config)";
|
|
176
|
+
const state = r.available
|
|
177
|
+
? chalk.green("✓ available")
|
|
178
|
+
: (r.problem ? chalk.red : chalk.yellow)("✗ unavailable");
|
|
179
|
+
lines.push(
|
|
180
|
+
` ${r.kind.padEnd(width)} ${sel.padEnd(20)} ${state}${r.reason ? `: ${r.reason}` : ""}`,
|
|
181
|
+
);
|
|
182
|
+
for (const w of r.warnings) lines.push(chalk.yellow(` ⚠ ${w}`));
|
|
183
|
+
}
|
|
184
|
+
return lines;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Whether agents are configured here: an `agents:` block with anything in
|
|
189
|
+
* it (max_concurrent, persona_packs, an agent id), or an agent record.
|
|
190
|
+
*/
|
|
191
|
+
export function agentsConfigured(
|
|
192
|
+
config: DenConfig | null,
|
|
193
|
+
stateDir: string,
|
|
194
|
+
): boolean {
|
|
195
|
+
const raw = config?.agents as unknown;
|
|
196
|
+
if (raw && typeof raw === "object" && Object.keys(raw).length) return true;
|
|
197
|
+
try {
|
|
198
|
+
return listRecords(stateDir).length > 0;
|
|
199
|
+
} catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Informational lines after the rows: never a failure. */
|
|
205
|
+
export function doctorNotes(cli: string, configured: boolean): string[] {
|
|
206
|
+
return configured
|
|
207
|
+
? [
|
|
208
|
+
"",
|
|
209
|
+
` ℹ agents are configured or have run here: run \`${cli} doctor --probe\` to verify agent env isolation`,
|
|
210
|
+
]
|
|
211
|
+
: [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function readConfigOrNull(): DenConfig | null {
|
|
215
|
+
try {
|
|
216
|
+
return readDenConfig();
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function cmdDoctor(args: string[]): Promise<number> {
|
|
223
|
+
const probe = args.includes("--probe");
|
|
224
|
+
const unknown = args.filter((a) => a !== "--probe");
|
|
225
|
+
if (unknown.length) {
|
|
226
|
+
console.error(
|
|
227
|
+
`✗ doctor: unknown argument ${unknown[0]} (doctor [--probe])`,
|
|
228
|
+
);
|
|
229
|
+
return 2;
|
|
230
|
+
}
|
|
231
|
+
const rows = doctorRows();
|
|
232
|
+
for (const l of formatDoctor(rows, readBrand().name)) console.log(l);
|
|
233
|
+
const cli = product().cli;
|
|
234
|
+
if (!probe) {
|
|
235
|
+
for (const l of doctorNotes(
|
|
236
|
+
cli,
|
|
237
|
+
agentsConfigured(readConfigOrNull(), getStateDir()),
|
|
238
|
+
))
|
|
239
|
+
console.log(l);
|
|
240
|
+
return rows.some((r) => r.problem) ? 1 : 0;
|
|
241
|
+
}
|
|
242
|
+
const { runEnvProbe, formatProbe } = await import("./env-probe.js");
|
|
243
|
+
const { resolveDeps, plainSessionEnv } =
|
|
244
|
+
await import("./sessions-command.js");
|
|
245
|
+
const d = resolveDeps({});
|
|
246
|
+
const results = await runEnvProbe({
|
|
247
|
+
dist: d.dist,
|
|
248
|
+
cli: d.cli,
|
|
249
|
+
env: d.env,
|
|
250
|
+
pty: d.pty,
|
|
251
|
+
connect: d.connect,
|
|
252
|
+
plainEnv: (caller) => plainSessionEnv(caller),
|
|
253
|
+
});
|
|
254
|
+
console.log("");
|
|
255
|
+
console.log(chalk.bold(`${readBrand().name} doctor: session env probe`));
|
|
256
|
+
console.log("");
|
|
257
|
+
for (const l of formatProbe(results)) console.log(l);
|
|
258
|
+
const found = results.some((r) => r.findings.length);
|
|
259
|
+
return rows.some((r) => r.problem) || found ? 1 : 0;
|
|
260
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `edge up | down | status | trust | untrust | routes`
|
|
2
|
+
* `edge up [--take-over] | down | status | trust | untrust | routes`
|
|
3
3
|
*
|
|
4
|
-
* This org's
|
|
4
|
+
* This org's edge: routes at `<name>.<edge.domain>`, TLS as `edge.tls` says
|
|
5
|
+
* (none, mkcert or files). `trust` / `untrust` exist only under mkcert.
|
|
6
|
+
* `routes` lists Den's own routes, then the routers in the included
|
|
7
|
+
* dynamic files, each with its source file, then the stacks' routers
|
|
8
|
+
* (`stack-*.yml`, 0.3.0-dev.32), each marked "stack" with its file.
|
|
5
9
|
*/
|
|
6
10
|
import chalk from "chalk";
|
|
7
11
|
import { readBrand } from "../../shared/brand.js";
|
|
@@ -15,18 +19,37 @@ import {
|
|
|
15
19
|
trustCa,
|
|
16
20
|
untrustCa,
|
|
17
21
|
} from "../../shared/edge/runtime.js";
|
|
18
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
includedRouters,
|
|
24
|
+
stackRouters,
|
|
25
|
+
routerUrls,
|
|
26
|
+
type IncludedRouter,
|
|
27
|
+
} from "../../shared/edge/included.js";
|
|
28
|
+
import { edgeRoutes, edgeStatus } from "../../shared/edge/status.js";
|
|
29
|
+
import { tlsYaml } from "../../shared/edge/tls.js";
|
|
30
|
+
import { rejoinRunningStacks } from "../../shared/stacks/status.js";
|
|
31
|
+
|
|
32
|
+
/** An included router's URLs (the hosts its rule names), else its name. */
|
|
33
|
+
function includedUrl(r: IncludedRouter): string {
|
|
34
|
+
return r.hosts.length ? routerUrls(r).join(", ") : r.name;
|
|
35
|
+
}
|
|
19
36
|
|
|
20
37
|
export async function cmdEdge(args: string[]): Promise<void> {
|
|
21
38
|
const sub = args[0] ?? "status";
|
|
22
39
|
const cfg = readEdgeConfig();
|
|
23
40
|
const n = edgeNames(cfg.org);
|
|
24
41
|
const published = cfg.ports
|
|
25
|
-
?
|
|
42
|
+
? cfg.tls.strategy === "none"
|
|
43
|
+
? `host :${cfg.ports.http} (http only)`
|
|
44
|
+
: `host :${cfg.ports.http} / :${cfg.ports.https}`
|
|
26
45
|
: `not published (reach it on Docker network ${n.network})`;
|
|
27
46
|
switch (sub) {
|
|
28
47
|
case "up": {
|
|
29
|
-
|
|
48
|
+
// --take-over: the only way to replace an edge another state dir
|
|
49
|
+
// owns (0.3.0-dev.40); never implied.
|
|
50
|
+
const { certs } = await edgeUp(cfg, {
|
|
51
|
+
takeOver: args.includes("--take-over"),
|
|
52
|
+
});
|
|
30
53
|
console.log(
|
|
31
54
|
chalk.green(
|
|
32
55
|
`✓ ${n.container} up — ${edgeDomains(cfg)
|
|
@@ -37,7 +60,7 @@ export async function cmdEdge(args: string[]): Promise<void> {
|
|
|
37
60
|
console.log(` ports ${published}`);
|
|
38
61
|
if (certs.issued)
|
|
39
62
|
console.log(` cert issued for ${certs.names.join(", ")}`);
|
|
40
|
-
if (caTrusted(cfg) === false)
|
|
63
|
+
if (cfg.tls.strategy === "mkcert" && caTrusted(cfg) === false)
|
|
41
64
|
console.log(
|
|
42
65
|
chalk.yellow(
|
|
43
66
|
" CA not trusted yet — run `edge trust` (asks for your password once)",
|
|
@@ -45,6 +68,8 @@ export async function cmdEdge(args: string[]): Promise<void> {
|
|
|
45
68
|
);
|
|
46
69
|
for (const name of ["edge", ...Object.keys(cfg.routes)])
|
|
47
70
|
console.log(` ${routeUrl(name, cfg)}`);
|
|
71
|
+
// Running stacks' routes stay up: the edge rejoins their networks.
|
|
72
|
+
rejoinRunningStacks();
|
|
48
73
|
return;
|
|
49
74
|
}
|
|
50
75
|
case "down":
|
|
@@ -57,10 +82,25 @@ export async function cmdEdge(args: string[]): Promise<void> {
|
|
|
57
82
|
case "untrust":
|
|
58
83
|
process.exitCode = untrustCa(cfg);
|
|
59
84
|
return;
|
|
60
|
-
case "routes":
|
|
61
|
-
for (const r of
|
|
85
|
+
case "routes": {
|
|
86
|
+
for (const r of edgeRoutes(cfg))
|
|
62
87
|
console.log(`${r.url.padEnd(52)} → ${r.upstream}`);
|
|
88
|
+
// Routers in the included dynamic files: Traefik serves them too.
|
|
89
|
+
const inc = includedRouters(cfg);
|
|
90
|
+
for (const r of inc.routers)
|
|
91
|
+
console.log(
|
|
92
|
+
`${includedUrl(r).padEnd(52)} → ${r.upstream || "?"} (${r.file})`,
|
|
93
|
+
);
|
|
94
|
+
// Routers the stacks wrote (stack up): neither Den's nor included.
|
|
95
|
+
const stk = stackRouters(cfg);
|
|
96
|
+
for (const r of stk.routers)
|
|
97
|
+
console.log(
|
|
98
|
+
`${includedUrl(r).padEnd(52)} → ${r.upstream || "?"} (stack: ${r.file})`,
|
|
99
|
+
);
|
|
100
|
+
for (const e of [...inc.errors, ...stk.errors])
|
|
101
|
+
console.log(chalk.yellow(`${e.file}: not read (${e.error})`));
|
|
63
102
|
return;
|
|
103
|
+
}
|
|
64
104
|
case "status": {
|
|
65
105
|
const st = edgeStatus(cfg);
|
|
66
106
|
console.log(chalk.bold(`${readBrand().name} edge — ${st.org}`));
|
|
@@ -68,14 +108,20 @@ export async function cmdEdge(args: string[]): Promise<void> {
|
|
|
68
108
|
console.log(` ports ${published}`);
|
|
69
109
|
console.log(` domains ${st.domains.join(", ")} (+ every subdomain)`);
|
|
70
110
|
console.log(
|
|
71
|
-
`
|
|
111
|
+
` tls ${tlsYaml(cfg.tls).replace(/^tls: /, "")}${cfg.tlsFrom === "implicit" ? " (implicit: edge.tls is not set)" : ""}`,
|
|
72
112
|
);
|
|
113
|
+
if (cfg.tls.strategy !== "none")
|
|
114
|
+
console.log(
|
|
115
|
+
// Fronted (edge.publish: false): the proxy in front owns
|
|
116
|
+
// browser trust (BOOT-1), so this machine's trust is moot.
|
|
117
|
+
` CA ${caDir(cfg)} — ${!st.ports ? "not needed: fronted" : st.caTrusted === null ? "unknown" : st.caTrusted ? "trusted" : "NOT trusted"}`,
|
|
118
|
+
);
|
|
73
119
|
if (st.certIssued) console.log(` cert ${st.certNames.join(", ")}`);
|
|
74
120
|
return;
|
|
75
121
|
}
|
|
76
122
|
default:
|
|
77
123
|
throw new Error(
|
|
78
|
-
`Unknown edge command "${sub}". Use: up | down | status | trust | untrust | routes`,
|
|
124
|
+
`Unknown edge command "${sub}". Use: up [--take-over] | down | status | trust | untrust | routes`,
|
|
79
125
|
);
|
|
80
126
|
}
|
|
81
127
|
}
|