@mono-agent/agent-app 0.4.0 → 0.4.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/README.md +15 -5
- package/dist/adapter-send-tools-main.js +2 -2
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +72 -6
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +413 -38
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts +48 -11
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +98 -24
- package/dist/app-config.js.map +1 -1
- package/dist/app.d.ts +10 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +659 -48
- package/dist/app.js.map +1 -1
- package/dist/artifact-retention.d.ts +32 -0
- package/dist/artifact-retention.d.ts.map +1 -0
- package/dist/artifact-retention.js +111 -0
- package/dist/artifact-retention.js.map +1 -0
- package/dist/audit-runs.d.ts +12 -0
- package/dist/audit-runs.d.ts.map +1 -0
- package/dist/audit-runs.js +68 -0
- package/dist/audit-runs.js.map +1 -0
- package/dist/backfill.d.ts +3 -7
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +115 -11
- package/dist/backfill.js.map +1 -1
- package/dist/background.d.ts +8 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +202 -4
- package/dist/background.js.map +1 -1
- package/dist/broadcast-recorder.d.ts +27 -0
- package/dist/broadcast-recorder.d.ts.map +1 -0
- package/dist/broadcast-recorder.js +82 -0
- package/dist/broadcast-recorder.js.map +1 -0
- package/dist/channel-config-view.d.ts +28 -0
- package/dist/channel-config-view.d.ts.map +1 -0
- package/dist/channel-config-view.js +79 -0
- package/dist/channel-config-view.js.map +1 -0
- package/dist/channel-gate.d.ts +22 -0
- package/dist/channel-gate.d.ts.map +1 -0
- package/dist/channel-gate.js +35 -0
- package/dist/channel-gate.js.map +1 -0
- package/dist/channel-plugins.d.ts +39 -0
- package/dist/channel-plugins.d.ts.map +1 -0
- package/dist/channel-plugins.js +346 -0
- package/dist/channel-plugins.js.map +1 -0
- package/dist/channels.d.ts +62 -73
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +833 -126
- package/dist/channels.js.map +1 -1
- package/dist/cli.d.ts +53 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +696 -28
- package/dist/cli.js.map +1 -1
- package/dist/config-reference.d.ts +24 -0
- package/dist/config-reference.d.ts.map +1 -0
- package/dist/config-reference.js +580 -0
- package/dist/config-reference.js.map +1 -0
- package/dist/configured-agent.d.ts +104 -0
- package/dist/configured-agent.d.ts.map +1 -0
- package/dist/configured-agent.js +578 -0
- package/dist/configured-agent.js.map +1 -0
- package/dist/consumer-contract.d.ts +36 -0
- package/dist/consumer-contract.d.ts.map +1 -0
- package/dist/consumer-contract.js +290 -0
- package/dist/consumer-contract.js.map +1 -0
- package/dist/doctor.d.ts +9 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +252 -55
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +23 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +91 -26
- package/dist/init.js.map +1 -1
- package/dist/install-skill.d.ts +1 -1
- package/dist/install-skill.js +2 -2
- package/dist/install-skill.js.map +1 -1
- package/dist/interaction-bridge.d.ts +72 -0
- package/dist/interaction-bridge.d.ts.map +1 -0
- package/dist/interaction-bridge.js +354 -0
- package/dist/interaction-bridge.js.map +1 -0
- package/dist/memory-command.d.ts +10 -0
- package/dist/memory-command.d.ts.map +1 -0
- package/dist/memory-command.js +649 -0
- package/dist/memory-command.js.map +1 -0
- package/dist/memory-recall-main.js +1 -1
- package/dist/memory-recall-main.js.map +1 -1
- package/dist/memory-recall.d.ts +41 -5
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +75 -5
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-rituals.d.ts +8 -11
- package/dist/memory-rituals.d.ts.map +1 -1
- package/dist/memory-rituals.js +26 -25
- package/dist/memory-rituals.js.map +1 -1
- package/dist/metrics.d.ts +13 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +105 -0
- package/dist/metrics.js.map +1 -0
- package/dist/notify-destinations.d.ts +23 -0
- package/dist/notify-destinations.d.ts.map +1 -0
- package/dist/notify-destinations.js +79 -0
- package/dist/notify-destinations.js.map +1 -0
- package/dist/posted-message-index.d.ts +61 -0
- package/dist/posted-message-index.d.ts.map +1 -0
- package/dist/posted-message-index.js +178 -0
- package/dist/posted-message-index.js.map +1 -0
- package/dist/proactive-notify.d.ts +32 -0
- package/dist/proactive-notify.d.ts.map +1 -0
- package/dist/proactive-notify.js +62 -0
- package/dist/proactive-notify.js.map +1 -0
- package/dist/recipes/base.d.ts +15 -0
- package/dist/recipes/base.d.ts.map +1 -0
- package/dist/recipes/base.js +51 -0
- package/dist/recipes/base.js.map +1 -0
- package/dist/recipes/catalog.d.ts +4 -0
- package/dist/recipes/catalog.d.ts.map +1 -0
- package/dist/recipes/catalog.js +509 -0
- package/dist/recipes/catalog.js.map +1 -0
- package/dist/recipes/index.d.ts +11 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +14 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/recipes/types.d.ts +70 -0
- package/dist/recipes/types.d.ts.map +1 -0
- package/dist/recipes/types.js +15 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/request-model-override.d.ts +67 -0
- package/dist/request-model-override.d.ts.map +1 -0
- package/dist/request-model-override.js +96 -0
- package/dist/request-model-override.js.map +1 -0
- package/dist/runs-health.d.ts +22 -0
- package/dist/runs-health.d.ts.map +1 -0
- package/dist/runs-health.js +144 -0
- package/dist/runs-health.js.map +1 -0
- package/dist/seen-conversations.d.ts +22 -0
- package/dist/seen-conversations.d.ts.map +1 -0
- package/dist/seen-conversations.js +120 -0
- package/dist/seen-conversations.js.map +1 -0
- package/dist/setup.d.ts +29 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +97 -0
- package/dist/setup.js.map +1 -0
- package/dist/trigger-overrides.d.ts +17 -0
- package/dist/trigger-overrides.d.ts.map +1 -0
- package/dist/trigger-overrides.js +30 -0
- package/dist/trigger-overrides.js.map +1 -0
- package/dist/tui-command.d.ts +56 -0
- package/dist/tui-command.d.ts.map +1 -0
- package/dist/tui-command.js +160 -0
- package/dist/tui-command.js.map +1 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +4 -0
- package/dist/ui.js.map +1 -1
- package/dist/web-command.d.ts +39 -0
- package/dist/web-command.d.ts.map +1 -0
- package/dist/web-command.js +151 -0
- package/dist/web-command.js.map +1 -0
- package/package.json +17 -17
- package/schema/mono-agent.config.schema.json +1686 -0
- package/skills/mono-agent-composer/SKILL.md +34 -9
- package/skills/mono-agent-composer/agents/openai.yaml +4 -0
- package/skills/mono-agent-composer/references/config-blueprint.md +52 -18
- package/skills/mono-agent-composer/references/discovery-questions.md +14 -16
- package/skills/mono-agent-composer/references/feature-coverage.md +19 -15
- package/skills/mono-agent-composer/references/package-map.md +17 -19
- package/skills/mono-agent-composer/references/playbooks.md +209 -0
- package/skills/mono-agent-composer/references/validation.md +9 -1
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
2
3
|
import { stat } from "node:fs/promises";
|
|
3
4
|
import { basename, resolve } from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { buildMonoAgentConfigView, findJsonSecretConfigWarnings, findRemovedConfigWarnings, readMonoAgentConfigJson, redactMonoAgentConfig, } from "@mono-agent/config";
|
|
8
|
+
import { listRecordedRuns } from "@mono-agent/observability";
|
|
9
|
+
import { describeSandboxEffectiveState, sandboxEffectiveStateWarning, } from "@mono-agent/runtime-adapter";
|
|
6
10
|
import { startMonoAgentApp } from "./app.js";
|
|
7
|
-
import { phoenixAppBaseUrl } from "./app-config.js";
|
|
11
|
+
import { describeSensitiveDataExportWarning, isAppCoreConfigError, loadAppCoreConfig, phoenixAppBaseUrl, } from "./app-config.js";
|
|
12
|
+
import { runAuditRuns } from "./audit-runs.js";
|
|
8
13
|
import { runBackfill } from "./backfill.js";
|
|
9
14
|
import { defaultBackgroundDeps, resolveInstanceTarget, restartBackground, startBackground, statusBackground, stopBackground, tailLogs, } from "./background.js";
|
|
15
|
+
import { collectChannelConfigViews } from "./channel-config-view.js";
|
|
16
|
+
import { resolveChannelDrivers } from "./channels.js";
|
|
17
|
+
import { findUnknownAppConfigWarnings } from "./config-reference.js";
|
|
10
18
|
import { validateMonoAgentFolder } from "./doctor.js";
|
|
11
|
-
import { initMonoAgentFolder } from "./init.js";
|
|
19
|
+
import { initMonoAgentFolder, isWithChannel } from "./init.js";
|
|
12
20
|
import { installComposerSkill } from "./install-skill.js";
|
|
21
|
+
import { runMetrics } from "./metrics.js";
|
|
22
|
+
import { findRecipe, RECIPE_CATALOG, recipeIds, resolveRecipeInputs } from "./recipes/index.js";
|
|
23
|
+
import { buildRunsHealthDisplay, RUNS_HEALTH_MAX_RUNS } from "./runs-health.js";
|
|
13
24
|
import { purgeSessions } from "./sessions.js";
|
|
25
|
+
import { collectSetupOptions } from "./setup.js";
|
|
14
26
|
import * as ui from "./ui.js";
|
|
15
27
|
const DEFAULT_LOG_LINES = 200;
|
|
16
28
|
// Node's maximum setInterval/setTimeout delay (2^31 - 1 ms, ~24.8 days). A
|
|
@@ -18,21 +30,26 @@ const DEFAULT_LOG_LINES = 200;
|
|
|
18
30
|
// busy-waiting; larger values silently overflow to a 1ms delay.
|
|
19
31
|
const KEEP_ALIVE_INTERVAL_MS = 2_147_483_647;
|
|
20
32
|
const BACKGROUND_COMMANDS = ["start", "restart", "stop", "status", "logs"];
|
|
21
|
-
const KNOWN_COMMANDS = ["init", "validate", "start", "restart", "stop", "status", "logs", "install-skill", "backfill"];
|
|
33
|
+
const KNOWN_COMMANDS = ["init", "setup", "validate", "doctor", "config", "recipes", "start", "restart", "stop", "status", "logs", "tui", "web", "install-skill", "backfill", "audit-runs", "metrics", "memory"];
|
|
22
34
|
export function parseCliArgs(argv) {
|
|
23
35
|
const [command, ...rest] = argv;
|
|
24
36
|
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
25
|
-
return { command: "help", force: false, foreground: false, follow: false, all: false, dryRun: false };
|
|
37
|
+
return { command: "help", positionals: [], force: false, foreground: false, follow: false, all: false, dryRun: false, includeMemory: false };
|
|
26
38
|
}
|
|
27
39
|
if (!KNOWN_COMMANDS.includes(command)) {
|
|
28
40
|
throw new Error(`Unknown command \`${command}\`. Expected ${KNOWN_COMMANDS.join(", ")}.`);
|
|
29
41
|
}
|
|
30
|
-
|
|
42
|
+
// `doctor` is an alias of `validate`; normalize here so every downstream
|
|
43
|
+
// validate path (env-file resolution, --consumer, routing) applies unchanged.
|
|
44
|
+
const cmd = (command === "doctor" ? "validate" : command);
|
|
31
45
|
const isLogs = cmd === "logs";
|
|
32
46
|
let configPath;
|
|
33
47
|
let model;
|
|
34
48
|
let fallbackModels;
|
|
35
49
|
let memory;
|
|
50
|
+
let recipe;
|
|
51
|
+
let withChannels;
|
|
52
|
+
const positionals = [];
|
|
36
53
|
let envFile;
|
|
37
54
|
let target;
|
|
38
55
|
let force = false;
|
|
@@ -44,6 +61,19 @@ export function parseCliArgs(argv) {
|
|
|
44
61
|
let since;
|
|
45
62
|
let until;
|
|
46
63
|
let dryRun = false;
|
|
64
|
+
let includeMemory = false;
|
|
65
|
+
let artifactDir;
|
|
66
|
+
let groupBy;
|
|
67
|
+
let consumerPath;
|
|
68
|
+
let agent;
|
|
69
|
+
let conversation;
|
|
70
|
+
let staleAfterMs;
|
|
71
|
+
let json = false;
|
|
72
|
+
let limit;
|
|
73
|
+
let host;
|
|
74
|
+
let port;
|
|
75
|
+
let open;
|
|
76
|
+
let allowNonLoopback;
|
|
47
77
|
for (let i = 0; i < rest.length; i += 1) {
|
|
48
78
|
const flag = rest[i];
|
|
49
79
|
switch (flag) {
|
|
@@ -65,6 +95,71 @@ export function parseCliArgs(argv) {
|
|
|
65
95
|
case "--dry-run":
|
|
66
96
|
dryRun = true;
|
|
67
97
|
break;
|
|
98
|
+
case "--include-memory":
|
|
99
|
+
includeMemory = true;
|
|
100
|
+
break;
|
|
101
|
+
case "--artifact-dir":
|
|
102
|
+
artifactDir = requireValue(rest, ++i, flag);
|
|
103
|
+
break;
|
|
104
|
+
case "--artifacts":
|
|
105
|
+
artifactDir = requireValue(rest, ++i, flag);
|
|
106
|
+
break;
|
|
107
|
+
case "--by": {
|
|
108
|
+
const raw = requireValue(rest, ++i, flag);
|
|
109
|
+
if (raw !== "model" && raw !== "channel" && raw !== "failureKind") {
|
|
110
|
+
throw new Error("--by must be model, channel, or failureKind.");
|
|
111
|
+
}
|
|
112
|
+
groupBy = raw;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "--consumer":
|
|
116
|
+
consumerPath = requireValue(rest, ++i, flag);
|
|
117
|
+
break;
|
|
118
|
+
case "--agent":
|
|
119
|
+
agent = requireValue(rest, ++i, flag);
|
|
120
|
+
break;
|
|
121
|
+
case "--conversation":
|
|
122
|
+
conversation = requireValue(rest, ++i, flag);
|
|
123
|
+
break;
|
|
124
|
+
case "--stale-after-ms": {
|
|
125
|
+
const raw = requireValue(rest, ++i, flag);
|
|
126
|
+
const parsed = Number(raw);
|
|
127
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
128
|
+
throw new Error("--stale-after-ms must be a positive integer.");
|
|
129
|
+
}
|
|
130
|
+
staleAfterMs = parsed;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case "--json":
|
|
134
|
+
json = true;
|
|
135
|
+
break;
|
|
136
|
+
case "--limit": {
|
|
137
|
+
const raw = requireValue(rest, ++i, flag);
|
|
138
|
+
const parsed = Number(raw);
|
|
139
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100) {
|
|
140
|
+
throw new Error("--limit must be an integer between 1 and 100.");
|
|
141
|
+
}
|
|
142
|
+
limit = parsed;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case "--host":
|
|
146
|
+
host = requireValue(rest, ++i, flag);
|
|
147
|
+
break;
|
|
148
|
+
case "--port": {
|
|
149
|
+
const raw = requireValue(rest, ++i, flag);
|
|
150
|
+
const parsed = Number(raw);
|
|
151
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
152
|
+
throw new Error("--port must be an integer between 0 and 65535.");
|
|
153
|
+
}
|
|
154
|
+
port = parsed;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case "--no-open":
|
|
158
|
+
open = false;
|
|
159
|
+
break;
|
|
160
|
+
case "--allow-non-loopback":
|
|
161
|
+
allowNonLoopback = true;
|
|
162
|
+
break;
|
|
68
163
|
case "--model":
|
|
69
164
|
model = requireValue(rest, ++i, flag);
|
|
70
165
|
break;
|
|
@@ -82,6 +177,15 @@ export function parseCliArgs(argv) {
|
|
|
82
177
|
memory = raw;
|
|
83
178
|
break;
|
|
84
179
|
}
|
|
180
|
+
case "--recipe":
|
|
181
|
+
recipe = requireValue(rest, ++i, flag);
|
|
182
|
+
break;
|
|
183
|
+
case "--with":
|
|
184
|
+
withChannels = requireValue(rest, ++i, flag)
|
|
185
|
+
.split(",")
|
|
186
|
+
.map((entry) => entry.trim())
|
|
187
|
+
.filter((entry) => entry.length > 0);
|
|
188
|
+
break;
|
|
85
189
|
case "--env-file":
|
|
86
190
|
envFile = requireValue(rest, ++i, flag);
|
|
87
191
|
break;
|
|
@@ -121,15 +225,38 @@ export function parseCliArgs(argv) {
|
|
|
121
225
|
break;
|
|
122
226
|
}
|
|
123
227
|
default:
|
|
124
|
-
|
|
228
|
+
if (flag === undefined) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
if (flag.startsWith("--")) {
|
|
232
|
+
throw new Error(`Unknown flag \`${flag}\` for \`mono-agent ${command}\`.`);
|
|
233
|
+
}
|
|
234
|
+
// Non-flag tokens are positional arguments (e.g. `recipes show <id>`).
|
|
235
|
+
positionals.push(flag);
|
|
236
|
+
break;
|
|
125
237
|
}
|
|
126
238
|
}
|
|
239
|
+
if (consumerPath !== undefined && cmd !== "validate" && cmd !== "audit-runs") {
|
|
240
|
+
throw new Error("--consumer is only supported for `mono-agent validate` and `mono-agent audit-runs`.");
|
|
241
|
+
}
|
|
242
|
+
if ((host !== undefined || port !== undefined || open !== undefined || allowNonLoopback !== undefined) && cmd !== "web") {
|
|
243
|
+
throw new Error("--host, --port, --no-open, and --allow-non-loopback are only supported for `mono-agent web`.");
|
|
244
|
+
}
|
|
245
|
+
if (includeMemory && cmd !== "audit-runs" && cmd !== "metrics" && cmd !== "backfill" && cmd !== "web") {
|
|
246
|
+
throw new Error("--include-memory is only supported for `mono-agent audit-runs`, `mono-agent metrics`, `mono-agent backfill`, and `mono-agent web`.");
|
|
247
|
+
}
|
|
248
|
+
if (limit !== undefined && cmd !== "memory") {
|
|
249
|
+
throw new Error("--limit is only supported for `mono-agent memory`.");
|
|
250
|
+
}
|
|
127
251
|
return {
|
|
128
252
|
command: cmd,
|
|
129
253
|
...(configPath === undefined ? {} : { configPath }),
|
|
130
254
|
...(model === undefined ? {} : { model }),
|
|
131
255
|
...(fallbackModels === undefined ? {} : { fallbackModels }),
|
|
132
256
|
...(memory === undefined ? {} : { memory }),
|
|
257
|
+
...(recipe === undefined ? {} : { recipe }),
|
|
258
|
+
...(withChannels === undefined ? {} : { withChannels }),
|
|
259
|
+
positionals,
|
|
133
260
|
...(envFile === undefined ? {} : { envFile }),
|
|
134
261
|
...(target === undefined ? {} : { target }),
|
|
135
262
|
force,
|
|
@@ -141,6 +268,19 @@ export function parseCliArgs(argv) {
|
|
|
141
268
|
...(since === undefined ? {} : { since }),
|
|
142
269
|
...(until === undefined ? {} : { until }),
|
|
143
270
|
dryRun,
|
|
271
|
+
includeMemory,
|
|
272
|
+
...(artifactDir === undefined ? {} : { artifactDir }),
|
|
273
|
+
...(groupBy === undefined ? {} : { groupBy }),
|
|
274
|
+
...(consumerPath === undefined ? {} : { consumerPath }),
|
|
275
|
+
...(staleAfterMs === undefined ? {} : { staleAfterMs }),
|
|
276
|
+
...(json ? { json } : {}),
|
|
277
|
+
...(limit === undefined ? {} : { limit }),
|
|
278
|
+
...(agent === undefined ? {} : { agent }),
|
|
279
|
+
...(conversation === undefined ? {} : { conversation }),
|
|
280
|
+
...(host === undefined ? {} : { host }),
|
|
281
|
+
...(port === undefined ? {} : { port }),
|
|
282
|
+
...(open === undefined ? {} : { open }),
|
|
283
|
+
...(allowNonLoopback === undefined ? {} : { allowNonLoopback }),
|
|
144
284
|
};
|
|
145
285
|
}
|
|
146
286
|
/**
|
|
@@ -157,6 +297,15 @@ export function loadCliEnvFile(path) {
|
|
|
157
297
|
return false;
|
|
158
298
|
}
|
|
159
299
|
}
|
|
300
|
+
function resolveValidateContext(args, invocationCwd) {
|
|
301
|
+
const cwd = resolve(invocationCwd, args.consumerPath ?? ".");
|
|
302
|
+
return {
|
|
303
|
+
cwd,
|
|
304
|
+
configPath: resolve(cwd, args.configPath ?? "mono-agent.config.json"),
|
|
305
|
+
envFilePath: resolve(cwd, args.envFile ?? ".env"),
|
|
306
|
+
allowFilesystemWrites: args.consumerPath === undefined,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
160
309
|
function requireValue(args, index, flag) {
|
|
161
310
|
const value = args[index];
|
|
162
311
|
if (value === undefined || value.startsWith("--")) {
|
|
@@ -166,15 +315,45 @@ function requireValue(args, index, flag) {
|
|
|
166
315
|
}
|
|
167
316
|
const HELP_COMMANDS = [
|
|
168
317
|
{
|
|
169
|
-
signature: "mono-agent init [--
|
|
318
|
+
signature: "mono-agent init [--recipe <id>] [--with <csv>] [--dry-run]\n" +
|
|
319
|
+
" [--model <ref>] [--fallback-models <csv>] [--memory lite|journal|bujo]",
|
|
170
320
|
lines: [
|
|
171
321
|
"Scaffold mono-agent.config.json, IDENTITY.md, and .mono-agent/ in the",
|
|
172
|
-
"current folder.
|
|
322
|
+
"current folder. With --recipe, build from a blueprint (+ .env.example);",
|
|
323
|
+
"--with adds channels, --dry-run previews. Existing files are never overwritten.",
|
|
173
324
|
],
|
|
174
325
|
},
|
|
175
326
|
{
|
|
176
|
-
signature: "mono-agent
|
|
177
|
-
|
|
327
|
+
signature: "mono-agent setup [--recipe <id>] [--with <csv>] [--dry-run]\n" +
|
|
328
|
+
" [--model <ref>] [--fallback-models <csv>] [--memory lite|journal|bujo]",
|
|
329
|
+
lines: [
|
|
330
|
+
"Guided setup when attached to a TTY: choose a recipe, answer non-secret",
|
|
331
|
+
"inputs, select channel add-ons, scaffold, validate, and print the secrets",
|
|
332
|
+
"checklist. In non-TTY contexts it falls back to init-style flags.",
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
signature: "mono-agent recipes list | show <id>",
|
|
337
|
+
lines: [
|
|
338
|
+
"List the executable config blueprints, or show one's generated config,",
|
|
339
|
+
".env.example, and follow-up checklist.",
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
signature: "mono-agent validate [--recipe <id>] [--consumer <path>] [--config <path>] [--env-file <path>]",
|
|
344
|
+
lines: [
|
|
345
|
+
"Load every config section and report what would run, wait, or fail.",
|
|
346
|
+
"--consumer validates another agent folder read-only, including its .env.",
|
|
347
|
+
"With --recipe, also report whether the recipe's capabilities are live.",
|
|
348
|
+
"`mono-agent doctor` is an alias for this command.",
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
signature: "mono-agent config [--config <path>] [--env-file <path>]",
|
|
353
|
+
lines: [
|
|
354
|
+
"Print the resolved config field-by-field, tagging each value with where",
|
|
355
|
+
"it came from (env / json / default), plus the channel summary. Read-only.",
|
|
356
|
+
],
|
|
178
357
|
},
|
|
179
358
|
{
|
|
180
359
|
signature: "mono-agent start [--config <path>] [--env-file <path>] [--foreground|-f]",
|
|
@@ -205,21 +384,68 @@ const HELP_COMMANDS = [
|
|
|
205
384
|
signature: "mono-agent logs [--config <path>] [--follow|-f] [--lines <n>]",
|
|
206
385
|
lines: ["Print (and optionally follow) the background instance's log files."],
|
|
207
386
|
},
|
|
387
|
+
{
|
|
388
|
+
signature: "mono-agent tui [--agent <label|sourceId>] [--conversation <id>]",
|
|
389
|
+
lines: [
|
|
390
|
+
"Open the operator console from any directory: live chat with full",
|
|
391
|
+
"thinking/tool/telemetry insight, recorded-run replay, and config view.",
|
|
392
|
+
"Discovers running agents via the trace-source registry; one running",
|
|
393
|
+
"agent connects directly, several open a picker.",
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
signature: "mono-agent web [--host <addr>] [--port <n>] [--no-open] [--allow-non-loopback] [--include-memory]",
|
|
398
|
+
lines: [
|
|
399
|
+
"Serve the read-only Session Recorder web PWA from any directory: a live",
|
|
400
|
+
"flight-recorder over every agent's runs (prompt, reasoning, tools, cost).",
|
|
401
|
+
"Discovers running agents via the trace-source registry — the same",
|
|
402
|
+
"mechanism as `tui` — and streams new/updated runs in real time.",
|
|
403
|
+
"--include-memory also shows memory-maintenance runs.",
|
|
404
|
+
],
|
|
405
|
+
},
|
|
208
406
|
{
|
|
209
407
|
signature: "mono-agent install-skill [--target claude|codex|both] [--force]",
|
|
210
408
|
lines: [
|
|
211
409
|
"Copy the bundled mono-agent-composer skill into ~/.claude/skills and",
|
|
212
|
-
"~/.
|
|
410
|
+
"~/.agents/skills (default: both). Refuses to overwrite without --force.",
|
|
213
411
|
],
|
|
214
412
|
},
|
|
215
413
|
{
|
|
216
414
|
signature: "mono-agent backfill (--run <id> | --all) [--since <iso>] [--until <iso>]\n" +
|
|
217
|
-
" [--dry-run] [--config <path>] [--env-file <path>]",
|
|
415
|
+
" [--include-memory] [--dry-run] [--config <path>] [--env-file <path>]",
|
|
218
416
|
lines: [
|
|
219
|
-
"Export already-recorded run artifacts to the configured Phoenix exporter",
|
|
417
|
+
"Export already-recorded agent-run artifacts to the configured Phoenix exporter",
|
|
220
418
|
"with their historical timestamps. Trace ids are deterministic per run, so",
|
|
221
419
|
"re-running overwrites rather than duplicating. --dry-run maps and",
|
|
222
|
-
"serializes without sending.",
|
|
420
|
+
"serializes without sending. --include-memory adds memory-run artifacts",
|
|
421
|
+
"for --all; explicit --run can target a memory run directly.",
|
|
422
|
+
],
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
signature: "mono-agent audit-runs [--artifact-dir <path> | --consumer <path>]\n" +
|
|
426
|
+
" [--include-memory] [--config <path>] [--env-file <path>] [--stale-after-ms <n>] [--json]",
|
|
427
|
+
lines: [
|
|
428
|
+
"Read local agent-run summary artifacts without rewriting them. Reports parse",
|
|
429
|
+
"failures, status and failure-kind histograms, stale running summaries,",
|
|
430
|
+
"and per-failure-kind rates. --include-memory includes memory-run artifacts.",
|
|
431
|
+
],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
signature: "mono-agent metrics [--artifacts <path>] [--since <iso>] [--until <iso>]\n" +
|
|
435
|
+
" [--include-memory] [--by model|channel|failureKind] [--json] [--config <path>] [--env-file <path>]",
|
|
436
|
+
lines: [
|
|
437
|
+
"Aggregate local agent-run summaries into status rates, failure-kind rates,",
|
|
438
|
+
"duration percentiles, and total/per-run cost. Read-only and offline.",
|
|
439
|
+
"--include-memory includes memory-run artifacts.",
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
signature: "mono-agent memory [stats|today|show <date>|search <query>|top]\n" +
|
|
444
|
+
" [--limit <n>] [--json] [--config <path>] [--env-file <path>]",
|
|
445
|
+
lines: [
|
|
446
|
+
"Preview the configured memory store from an agent folder. Reads the",
|
|
447
|
+
"memory block from mono-agent.config.json, not the standalone memory-bujo",
|
|
448
|
+
"env workflow. Human-first output by default; --json is for scripts.",
|
|
223
449
|
],
|
|
224
450
|
},
|
|
225
451
|
];
|
|
@@ -262,15 +488,24 @@ export async function runCli(argv) {
|
|
|
262
488
|
process.stdout.write(`\n${renderHelp()}`);
|
|
263
489
|
return 2;
|
|
264
490
|
}
|
|
265
|
-
|
|
491
|
+
const invocationCwd = process.cwd();
|
|
492
|
+
loadCliEnvFile(args.command === "validate"
|
|
493
|
+
? resolveValidateContext(args, invocationCwd).envFilePath
|
|
494
|
+
: resolve(invocationCwd, args.envFile ?? ".env"));
|
|
266
495
|
switch (args.command) {
|
|
267
496
|
case "help":
|
|
268
497
|
process.stdout.write(renderHelp());
|
|
269
498
|
return 0;
|
|
270
499
|
case "init":
|
|
271
500
|
return await runInit(args);
|
|
501
|
+
case "setup":
|
|
502
|
+
return await runSetup(args);
|
|
272
503
|
case "validate":
|
|
273
504
|
return await runValidate(args);
|
|
505
|
+
case "config":
|
|
506
|
+
return await runConfig(args);
|
|
507
|
+
case "recipes":
|
|
508
|
+
return runRecipes(args);
|
|
274
509
|
case "start":
|
|
275
510
|
return await runStart(args);
|
|
276
511
|
case "restart":
|
|
@@ -278,6 +513,31 @@ export async function runCli(argv) {
|
|
|
278
513
|
case "status":
|
|
279
514
|
case "logs":
|
|
280
515
|
return await runBackgroundCommand(args, args.command);
|
|
516
|
+
case "tui": {
|
|
517
|
+
// Lazy import: the operator console (and pi-tui) load only on demand.
|
|
518
|
+
const { runTui } = await import("./tui-command.js");
|
|
519
|
+
return await runTui({
|
|
520
|
+
configPath: resolve(process.cwd(), args.configPath ?? "mono-agent.config.json"),
|
|
521
|
+
cwd: process.cwd(),
|
|
522
|
+
env: process.env,
|
|
523
|
+
...(args.agent === undefined ? {} : { agent: args.agent }),
|
|
524
|
+
...(args.conversation === undefined ? {} : { conversationId: args.conversation }),
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
case "web": {
|
|
528
|
+
// Lazy import: the web server (and express/session-web) load only on demand.
|
|
529
|
+
const { runWeb } = await import("./web-command.js");
|
|
530
|
+
return await runWeb({
|
|
531
|
+
configPath: resolve(process.cwd(), args.configPath ?? "mono-agent.config.json"),
|
|
532
|
+
cwd: process.cwd(),
|
|
533
|
+
env: process.env,
|
|
534
|
+
...(args.host === undefined ? {} : { host: args.host }),
|
|
535
|
+
...(args.port === undefined ? {} : { port: args.port }),
|
|
536
|
+
...(args.open === undefined ? {} : { open: args.open }),
|
|
537
|
+
...(args.allowNonLoopback === undefined ? {} : { allowNonLoopback: args.allowNonLoopback }),
|
|
538
|
+
includeMemory: args.includeMemory,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
281
541
|
case "install-skill":
|
|
282
542
|
return await runInstallSkill(args);
|
|
283
543
|
case "backfill":
|
|
@@ -288,31 +548,182 @@ export async function runCli(argv) {
|
|
|
288
548
|
...(args.since === undefined ? {} : { since: args.since }),
|
|
289
549
|
...(args.until === undefined ? {} : { until: args.until }),
|
|
290
550
|
dryRun: args.dryRun,
|
|
551
|
+
includeMemory: args.includeMemory,
|
|
291
552
|
});
|
|
553
|
+
case "audit-runs":
|
|
554
|
+
return await runAuditRuns({
|
|
555
|
+
...(args.configPath === undefined ? {} : { configPath: args.configPath }),
|
|
556
|
+
...(args.artifactDir === undefined ? {} : { artifactDir: args.artifactDir }),
|
|
557
|
+
...(args.consumerPath === undefined ? {} : { consumerPath: args.consumerPath }),
|
|
558
|
+
...(args.staleAfterMs === undefined ? {} : { staleAfterMs: args.staleAfterMs }),
|
|
559
|
+
json: args.json === true,
|
|
560
|
+
includeMemory: args.includeMemory,
|
|
561
|
+
});
|
|
562
|
+
case "metrics":
|
|
563
|
+
return await runMetrics({
|
|
564
|
+
...(args.configPath === undefined ? {} : { configPath: args.configPath }),
|
|
565
|
+
...(args.artifactDir === undefined ? {} : { artifactDir: args.artifactDir }),
|
|
566
|
+
...(args.since === undefined ? {} : { since: args.since }),
|
|
567
|
+
...(args.until === undefined ? {} : { until: args.until }),
|
|
568
|
+
...(args.groupBy === undefined ? {} : { groupBy: args.groupBy }),
|
|
569
|
+
json: args.json === true,
|
|
570
|
+
includeMemory: args.includeMemory,
|
|
571
|
+
});
|
|
572
|
+
case "memory": {
|
|
573
|
+
// Lazy import: the memory preview path pulls SQLite/backend clients only on demand.
|
|
574
|
+
const { runMemoryCommand } = await import("./memory-command.js");
|
|
575
|
+
return await runMemoryCommand({
|
|
576
|
+
cwd: process.cwd(),
|
|
577
|
+
env: process.env,
|
|
578
|
+
...(args.configPath === undefined ? {} : { configPath: args.configPath }),
|
|
579
|
+
positionals: args.positionals,
|
|
580
|
+
json: args.json === true,
|
|
581
|
+
...(args.limit === undefined ? {} : { limit: args.limit }),
|
|
582
|
+
});
|
|
583
|
+
}
|
|
292
584
|
}
|
|
293
585
|
}
|
|
294
586
|
async function runInit(args) {
|
|
587
|
+
const recipe = resolveRecipeArg(args);
|
|
588
|
+
if (recipe === "unknown") {
|
|
589
|
+
return 1;
|
|
590
|
+
}
|
|
591
|
+
const withChannels = resolveWithChannels(args);
|
|
592
|
+
if (withChannels === "invalid") {
|
|
593
|
+
return 1;
|
|
594
|
+
}
|
|
295
595
|
const result = await initMonoAgentFolder({
|
|
296
596
|
dir: process.cwd(),
|
|
297
597
|
...(args.model === undefined ? {} : { model: args.model }),
|
|
298
598
|
...(args.fallbackModels === undefined ? {} : { fallbackModels: args.fallbackModels }),
|
|
299
599
|
...(args.memory === undefined ? {} : { memory: args.memory }),
|
|
600
|
+
...(recipe === undefined ? {} : { recipe }),
|
|
601
|
+
...(withChannels === undefined ? {} : { withChannels }),
|
|
602
|
+
dryRun: args.dryRun,
|
|
603
|
+
});
|
|
604
|
+
printInitResult(result, recipe);
|
|
605
|
+
printNextSteps(result.configPath, recipe);
|
|
606
|
+
return 0;
|
|
607
|
+
}
|
|
608
|
+
async function runSetup(args) {
|
|
609
|
+
if (process.stdin.isTTY !== true) {
|
|
610
|
+
return await runInit(args);
|
|
611
|
+
}
|
|
612
|
+
const recipeArg = resolveRecipeArg(args);
|
|
613
|
+
if (recipeArg === "unknown") {
|
|
614
|
+
return 1;
|
|
615
|
+
}
|
|
616
|
+
const withChannelsArg = resolveWithChannels(args);
|
|
617
|
+
if (withChannelsArg === "invalid") {
|
|
618
|
+
return 1;
|
|
619
|
+
}
|
|
620
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
621
|
+
let collected;
|
|
622
|
+
try {
|
|
623
|
+
collected = await collectSetupOptions({
|
|
624
|
+
prompt: { question: (prompt) => rl.question(prompt) },
|
|
625
|
+
...(recipeArg === undefined ? {} : { recipe: recipeArg }),
|
|
626
|
+
...(args.model === undefined ? {} : { model: args.model }),
|
|
627
|
+
...(args.fallbackModels === undefined ? {} : { fallbackModels: args.fallbackModels }),
|
|
628
|
+
...(withChannelsArg === undefined ? {} : { withChannels: withChannelsArg }),
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
finally {
|
|
632
|
+
rl.close();
|
|
633
|
+
}
|
|
634
|
+
const result = await initMonoAgentFolder({
|
|
635
|
+
dir: process.cwd(),
|
|
636
|
+
...(collected.fallbackModels.length === 0 ? {} : { fallbackModels: collected.fallbackModels }),
|
|
637
|
+
...(args.memory === undefined ? {} : { memory: args.memory }),
|
|
638
|
+
recipe: collected.recipe,
|
|
639
|
+
recipeInputs: collected.recipeInputs,
|
|
640
|
+
withChannels: collected.withChannels,
|
|
641
|
+
dryRun: args.dryRun,
|
|
300
642
|
});
|
|
643
|
+
printInitResult(result, collected.recipe);
|
|
644
|
+
if (!result.dryRun) {
|
|
645
|
+
const report = await validateMonoAgentFolder({
|
|
646
|
+
env: process.env,
|
|
647
|
+
cwd: process.cwd(),
|
|
648
|
+
configPath: result.configPath,
|
|
649
|
+
});
|
|
650
|
+
process.stdout.write("\n" + ui.heading("Validation"));
|
|
651
|
+
for (const section of report.sections) {
|
|
652
|
+
process.stdout.write(formatSection(section));
|
|
653
|
+
}
|
|
654
|
+
process.stdout.write(renderRecipeCompleteness(collected.recipe, report));
|
|
655
|
+
printSecretsChecklist(collected.secrets);
|
|
656
|
+
printNextSteps(result.configPath, collected.recipe);
|
|
657
|
+
return report.ok ? 0 : 1;
|
|
658
|
+
}
|
|
659
|
+
printSecretsChecklist(collected.secrets);
|
|
660
|
+
printNextSteps(result.configPath, collected.recipe);
|
|
661
|
+
return 0;
|
|
662
|
+
}
|
|
663
|
+
function resolveRecipeArg(args) {
|
|
664
|
+
if (args.recipe === undefined) {
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
667
|
+
const recipe = findRecipe(args.recipe);
|
|
668
|
+
if (recipe === undefined) {
|
|
669
|
+
process.stderr.write(ui.errorLine(`Unknown recipe \`${args.recipe}\`.`));
|
|
670
|
+
process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
|
|
671
|
+
return "unknown";
|
|
672
|
+
}
|
|
673
|
+
return recipe;
|
|
674
|
+
}
|
|
675
|
+
function resolveWithChannels(args) {
|
|
676
|
+
if (args.withChannels === undefined) {
|
|
677
|
+
return undefined;
|
|
678
|
+
}
|
|
679
|
+
const invalid = args.withChannels.filter((channel) => !isWithChannel(channel));
|
|
680
|
+
if (invalid.length > 0) {
|
|
681
|
+
process.stderr.write(ui.errorLine(`Unknown --with channel(s): ${invalid.join(", ")}.`));
|
|
682
|
+
process.stderr.write(ui.hint("Valid channels: telegram, slack, webhook, openaiApi, cron."));
|
|
683
|
+
return "invalid";
|
|
684
|
+
}
|
|
685
|
+
return args.withChannels.filter(isWithChannel);
|
|
686
|
+
}
|
|
687
|
+
function printInitResult(result, recipe) {
|
|
688
|
+
if (result.dryRun) {
|
|
689
|
+
process.stdout.write(ui.style.dim("Dry run — nothing was written.\n"));
|
|
690
|
+
}
|
|
691
|
+
const verb = result.dryRun ? "would create" : "created";
|
|
301
692
|
for (const path of result.created) {
|
|
302
|
-
process.stdout.write(`${ui.badge("ok")}${ui.style.green(
|
|
693
|
+
process.stdout.write(`${ui.badge("ok")}${ui.style.green(verb.padEnd(12))} ${path}\n`);
|
|
303
694
|
}
|
|
304
695
|
for (const path of result.skipped) {
|
|
305
|
-
process.stdout.write(ui.style.dim(` kept
|
|
696
|
+
process.stdout.write(ui.style.dim(` kept ${path}`) + "\n");
|
|
306
697
|
}
|
|
307
698
|
if (result.knowledgeFiles.length > 0) {
|
|
308
699
|
process.stdout.write(`\nIdentity references existing knowledge: ${ui.style.cyan(result.knowledgeFiles.join(", "))}\n`);
|
|
309
700
|
}
|
|
701
|
+
if (recipe !== undefined) {
|
|
702
|
+
process.stdout.write(`\n${ui.style.bold("Recipe:")} ${ui.style.cyan(recipe.id)} ${ui.style.dim(`(risk: ${recipe.riskLevel})`)}\n`);
|
|
703
|
+
if (recipe.envExample !== undefined) {
|
|
704
|
+
process.stdout.write(ui.style.dim("Fill the secret placeholders in .env.example, then copy it to .env.\n"));
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function printSecretsChecklist(secrets) {
|
|
709
|
+
process.stdout.write("\n" + ui.heading("Secrets checklist"));
|
|
710
|
+
if (secrets.length === 0) {
|
|
711
|
+
process.stdout.write(ui.style.dim("No secret recipe inputs were prompted. Review .env.example if the recipe created one.\n"));
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
process.stdout.write(ui.style.dim("Copy .env.example to .env, then fill these variables. Secret values were not prompted or written to JSON.\n"));
|
|
715
|
+
for (const secret of secrets) {
|
|
716
|
+
const key = secret.envVar ?? secret.id;
|
|
717
|
+
process.stdout.write(` ${ui.style.bold(key)} ${ui.style.dim(`- ${secret.label}: ${secret.description}`)}\n`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function printNextSteps(configPath, recipe) {
|
|
721
|
+
const validateCmd = recipe === undefined ? "mono-agent validate" : `mono-agent validate --recipe ${recipe.id}`;
|
|
310
722
|
process.stdout.write("\n" +
|
|
311
723
|
ui.heading("Next steps") +
|
|
312
|
-
` ${ui.style.bold("1.")} Edit ${
|
|
313
|
-
` ${ui.style.bold("2.")}
|
|
724
|
+
` ${ui.style.bold("1.")} Edit ${configPath} ${ui.style.dim("(model, channels, skills, memory, sandbox)")}\n` +
|
|
725
|
+
` ${ui.style.bold("2.")} ${validateCmd}\n` +
|
|
314
726
|
` ${ui.style.bold("3.")} mono-agent start\n`);
|
|
315
|
-
return 0;
|
|
316
727
|
}
|
|
317
728
|
async function runInstallSkill(args) {
|
|
318
729
|
let result;
|
|
@@ -332,20 +743,219 @@ async function runInstallSkill(args) {
|
|
|
332
743
|
return 0;
|
|
333
744
|
}
|
|
334
745
|
async function runValidate(args) {
|
|
335
|
-
const
|
|
746
|
+
const context = resolveValidateContext(args, process.cwd());
|
|
336
747
|
const report = await validateMonoAgentFolder({
|
|
337
748
|
env: process.env,
|
|
338
|
-
cwd,
|
|
339
|
-
configPath:
|
|
749
|
+
cwd: context.cwd,
|
|
750
|
+
configPath: context.configPath,
|
|
751
|
+
allowFilesystemWrites: context.allowFilesystemWrites,
|
|
340
752
|
});
|
|
341
753
|
for (const section of report.sections) {
|
|
342
754
|
process.stdout.write(formatSection(section));
|
|
343
755
|
}
|
|
756
|
+
if (args.recipe !== undefined) {
|
|
757
|
+
const recipe = findRecipe(args.recipe);
|
|
758
|
+
if (recipe === undefined) {
|
|
759
|
+
process.stderr.write(ui.errorLine(`Unknown recipe \`${args.recipe}\`.`));
|
|
760
|
+
process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
|
|
761
|
+
return 1;
|
|
762
|
+
}
|
|
763
|
+
process.stdout.write(renderRecipeCompleteness(recipe, report));
|
|
764
|
+
}
|
|
344
765
|
process.stdout.write(report.ok
|
|
345
|
-
? `\n${ui.style.green("✓ Config is ready to start.")}\n`
|
|
766
|
+
? `\n${ui.style.green("✓ Config is ready to start.")}\n${ui.style.dim("Run `mono-agent config` for the full field-by-field view.")}\n`
|
|
346
767
|
: `\n${ui.hint("Fix the errors above, then re-run mono-agent validate.")}`);
|
|
768
|
+
process.stdout.write(ui.style.dim("Core sections activate by presence; channels need `enabled: true` — see docs/config (How sections activate).\n"));
|
|
347
769
|
return report.ok ? 0 : 1;
|
|
348
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Capability-aware recipe check: for each capability the recipe promises, report
|
|
773
|
+
* whether the matching doctor section has reached the expected status. `waiting`
|
|
774
|
+
* stays non-fatal (it never changes the validate exit code) but is surfaced as
|
|
775
|
+
* "selected recipe incomplete" so the operator knows what is left to wire up.
|
|
776
|
+
*/
|
|
777
|
+
function renderRecipeCompleteness(recipe, report) {
|
|
778
|
+
let out = "\n" + ui.heading(`Recipe: ${recipe.id}`);
|
|
779
|
+
let incomplete = 0;
|
|
780
|
+
for (const expectation of recipe.validateExpectations) {
|
|
781
|
+
const section = report.sections.find((entry) => entry.id === expectation.sectionId);
|
|
782
|
+
const status = section?.status ?? "missing";
|
|
783
|
+
const met = status === expectation.mustBe;
|
|
784
|
+
if (!met) {
|
|
785
|
+
incomplete += 1;
|
|
786
|
+
}
|
|
787
|
+
const badge = met ? ui.badge("ok") : ui.badge(status === "error" ? "error" : "waiting");
|
|
788
|
+
out += `${badge}${ui.style.bold(expectation.sectionId)} ${ui.style.dim(`(${status}, expected ${expectation.mustBe})`)}\n`;
|
|
789
|
+
if (!met && expectation.note !== undefined) {
|
|
790
|
+
out += ` ${ui.style.dim(expectation.note)}\n`;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
out += incomplete === 0
|
|
794
|
+
? `${ui.style.green(`✓ Recipe ${recipe.id} is fully configured.`)}\n`
|
|
795
|
+
: ui.style.yellow(`⚠ Selected recipe incomplete: ${incomplete} capability(ies) not yet live.\n`);
|
|
796
|
+
return out;
|
|
797
|
+
}
|
|
798
|
+
function riskColor(risk) {
|
|
799
|
+
if (risk === "high") {
|
|
800
|
+
return ui.style.red(risk);
|
|
801
|
+
}
|
|
802
|
+
if (risk === "medium") {
|
|
803
|
+
return ui.style.yellow(risk);
|
|
804
|
+
}
|
|
805
|
+
return ui.style.green(risk);
|
|
806
|
+
}
|
|
807
|
+
/** `mono-agent recipes list` — one line per recipe. */
|
|
808
|
+
export function renderRecipeList() {
|
|
809
|
+
let out = ui.banner("mono-agent", "recipes") + "\n";
|
|
810
|
+
for (const recipe of RECIPE_CATALOG) {
|
|
811
|
+
out += `${ui.style.bold(ui.style.cyan(recipe.id))} ${ui.style.dim(`[${riskColor(recipe.riskLevel)}]`)}\n`;
|
|
812
|
+
out += ` ${recipe.title}\n`;
|
|
813
|
+
out += ` ${ui.style.dim(recipe.tags.join(", "))}\n`;
|
|
814
|
+
}
|
|
815
|
+
out += "\n" + ui.style.dim("Scaffold one with: mono-agent init --recipe <id> [--dry-run] [--with slack,cron]\n");
|
|
816
|
+
return out;
|
|
817
|
+
}
|
|
818
|
+
/** `mono-agent recipes show <id>` — description, generated JSON, env example, checklist. */
|
|
819
|
+
export function renderRecipeShow(recipe) {
|
|
820
|
+
const inputs = resolveRecipeInputs(recipe);
|
|
821
|
+
let out = ui.banner("mono-agent", `recipe: ${recipe.id}`) + "\n";
|
|
822
|
+
out += `${ui.style.bold(recipe.title)} ${ui.style.dim(`(risk: ${riskColor(recipe.riskLevel)})`)}\n`;
|
|
823
|
+
out += `${recipe.description}\n`;
|
|
824
|
+
if (recipe.playbook !== undefined) {
|
|
825
|
+
out += ui.style.dim(`Playbook: docs/playbooks/${recipe.playbook}\n`);
|
|
826
|
+
}
|
|
827
|
+
out += "\n" + ui.heading("Generated mono-agent.config.json");
|
|
828
|
+
out += JSON.stringify(recipe.config(inputs), null, 2) + "\n";
|
|
829
|
+
const envExample = recipe.envExample?.(inputs);
|
|
830
|
+
if (envExample !== undefined && envExample.trim().length > 0) {
|
|
831
|
+
out += "\n" + ui.heading(".env.example");
|
|
832
|
+
out += envExample.endsWith("\n") ? envExample : envExample + "\n";
|
|
833
|
+
}
|
|
834
|
+
const files = recipe.files?.(inputs) ?? [];
|
|
835
|
+
if (files.length > 0) {
|
|
836
|
+
out += "\n" + ui.heading("Scaffolded files");
|
|
837
|
+
for (const file of files) {
|
|
838
|
+
out += ` ${ui.style.cyan(file.path)}\n`;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (recipe.validateExpectations.length > 0) {
|
|
842
|
+
out += "\n" + ui.heading("Follow-up checklist");
|
|
843
|
+
for (const expectation of recipe.validateExpectations) {
|
|
844
|
+
const note = expectation.note === undefined ? "" : ` — ${expectation.note}`;
|
|
845
|
+
out += ` ${ui.style.gray("•")} ${expectation.sectionId} ${ui.style.dim(`must be ${expectation.mustBe}`)}${ui.style.dim(note)}\n`;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return out;
|
|
849
|
+
}
|
|
850
|
+
/** Dispatch `mono-agent recipes list|show <id>`. */
|
|
851
|
+
function runRecipes(args) {
|
|
852
|
+
const [sub, id] = args.positionals;
|
|
853
|
+
if (sub === undefined || sub === "list") {
|
|
854
|
+
process.stdout.write(renderRecipeList());
|
|
855
|
+
return 0;
|
|
856
|
+
}
|
|
857
|
+
if (sub === "show") {
|
|
858
|
+
if (id === undefined) {
|
|
859
|
+
process.stderr.write(ui.errorLine("Usage: mono-agent recipes show <id>."));
|
|
860
|
+
process.stderr.write(ui.hint(`Available: ${recipeIds().join(", ")}.`));
|
|
861
|
+
return 2;
|
|
862
|
+
}
|
|
863
|
+
const recipe = findRecipe(id);
|
|
864
|
+
if (recipe === undefined) {
|
|
865
|
+
process.stderr.write(ui.errorLine(`Unknown recipe \`${id}\`.`));
|
|
866
|
+
process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
|
|
867
|
+
return 1;
|
|
868
|
+
}
|
|
869
|
+
process.stdout.write(renderRecipeShow(recipe));
|
|
870
|
+
return 0;
|
|
871
|
+
}
|
|
872
|
+
process.stderr.write(ui.errorLine(`Unknown recipes subcommand \`${sub}\`. Expected list or show.`));
|
|
873
|
+
return 2;
|
|
874
|
+
}
|
|
875
|
+
const SOURCE_TAG = {
|
|
876
|
+
env: ui.style.green("[env]"),
|
|
877
|
+
json: ui.style.cyan("[json]"),
|
|
878
|
+
default: ui.style.dim("[default]"),
|
|
879
|
+
};
|
|
880
|
+
/**
|
|
881
|
+
* Render the complete, source-annotated config view: every core section and
|
|
882
|
+
* field with its resolved value and whether it came from an env var, the JSON
|
|
883
|
+
* file, or the built-in default. This is the single discovery surface that
|
|
884
|
+
* replaced the old partial config panes.
|
|
885
|
+
*/
|
|
886
|
+
export function renderConfigView(sections) {
|
|
887
|
+
let out = "";
|
|
888
|
+
for (const section of sections) {
|
|
889
|
+
const badgeStatus = section.status === "active" ? "ok" : "disabled";
|
|
890
|
+
out += `${ui.badge(badgeStatus)}${ui.style.bold(section.label)}\n`;
|
|
891
|
+
const width = section.fields.reduce((max, field) => Math.max(max, field.label.length), 0);
|
|
892
|
+
for (const field of section.fields) {
|
|
893
|
+
const tag = SOURCE_TAG[field.source];
|
|
894
|
+
const lock = field.redacted === true ? ` ${ui.style.dim("(secret)")}` : "";
|
|
895
|
+
const defaultRestatement = field.restatesDefault === true ? ` ${ui.style.dim("(same as default)")}` : "";
|
|
896
|
+
out += ` ${ui.style.gray(field.label.padEnd(width))} ${field.value}${lock}${defaultRestatement} ${tag}\n`;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return out;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* `mono-agent config`: print the resolved configuration field-by-field with the
|
|
903
|
+
* source (env / json / default) of every value, then the channel summary. Read
|
|
904
|
+
* only — edits go in mono-agent.config.json and take effect on the next restart.
|
|
905
|
+
*/
|
|
906
|
+
async function runConfig(args) {
|
|
907
|
+
const cwd = process.cwd();
|
|
908
|
+
const configPath = resolve(cwd, args.configPath ?? "mono-agent.config.json");
|
|
909
|
+
const env = process.env;
|
|
910
|
+
const jsonResult = await readMonoAgentConfigJson(configPath);
|
|
911
|
+
let config;
|
|
912
|
+
try {
|
|
913
|
+
config = await loadAppCoreConfig({ env, cwd, configPath });
|
|
914
|
+
}
|
|
915
|
+
catch (error) {
|
|
916
|
+
if (isAppCoreConfigError(error)) {
|
|
917
|
+
process.stderr.write(ui.errorLine(error.message));
|
|
918
|
+
if (jsonResult.missing) {
|
|
919
|
+
process.stderr.write(ui.hint(`No mono-agent config found at ${configPath}. Run \`mono-agent init\` to scaffold one.`));
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
process.stderr.write(ui.hint("Fix the config above, then re-run `mono-agent config`."));
|
|
923
|
+
}
|
|
924
|
+
return 1;
|
|
925
|
+
}
|
|
926
|
+
throw error;
|
|
927
|
+
}
|
|
928
|
+
const sections = buildMonoAgentConfigView({
|
|
929
|
+
redacted: redactMonoAgentConfig(config),
|
|
930
|
+
json: jsonResult.json,
|
|
931
|
+
env,
|
|
932
|
+
});
|
|
933
|
+
const drivers = await resolveChannelDrivers({ env, cwd, configPath });
|
|
934
|
+
const channelViews = await collectChannelConfigViews(drivers, { env, cwd, configPath });
|
|
935
|
+
process.stdout.write(ui.banner("mono-agent", "resolved config") + "\n");
|
|
936
|
+
process.stdout.write(renderConfigView(sections));
|
|
937
|
+
if (channelViews.length > 0) {
|
|
938
|
+
process.stdout.write("\n" + ui.heading("Channels"));
|
|
939
|
+
process.stdout.write(renderConfigView(channelViews));
|
|
940
|
+
}
|
|
941
|
+
for (const warning of [
|
|
942
|
+
...findUnknownAppConfigWarnings(jsonResult.json),
|
|
943
|
+
...findJsonSecretConfigWarnings([...sections, ...channelViews]),
|
|
944
|
+
...findRemovedConfigWarnings({ json: jsonResult.json, env }),
|
|
945
|
+
]) {
|
|
946
|
+
process.stdout.write(`${ui.style.yellow(warning)}\n`);
|
|
947
|
+
}
|
|
948
|
+
const report = await validateMonoAgentFolder({ env, cwd, configPath, liveness: false, drivers });
|
|
949
|
+
const channels = report.sections.filter((section) => section.id.startsWith("channel:"));
|
|
950
|
+
if (channels.length > 0) {
|
|
951
|
+
process.stdout.write("\n" + ui.heading("Channel status"));
|
|
952
|
+
for (const section of channels) {
|
|
953
|
+
process.stdout.write(formatSection(section));
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
process.stdout.write("\n" + ui.style.dim("Source precedence: [env] > [json] > [default]. Edit mono-agent.config.json and run `mono-agent restart` to apply.") + "\n");
|
|
957
|
+
return 0;
|
|
958
|
+
}
|
|
349
959
|
/** Render one validation section: a status badge, a bold label, and its details. */
|
|
350
960
|
function formatSection(section) {
|
|
351
961
|
let out = `${ui.badge(section.status)}${ui.style.bold(section.label)}\n`;
|
|
@@ -358,7 +968,7 @@ function colorDetail(status, detail) {
|
|
|
358
968
|
if (status === "error") {
|
|
359
969
|
return ui.style.red(detail);
|
|
360
970
|
}
|
|
361
|
-
if (detail.startsWith("[WARN]")) {
|
|
971
|
+
if (detail.startsWith("[WARN]") || detail.startsWith("WARNING:")) {
|
|
362
972
|
return ui.style.yellow(detail);
|
|
363
973
|
}
|
|
364
974
|
return ui.style.dim(detail);
|
|
@@ -429,7 +1039,7 @@ async function runForeground(args) {
|
|
|
429
1039
|
...(args.configPath === undefined ? {} : { configPath: args.configPath }),
|
|
430
1040
|
logger: consoleLogger(),
|
|
431
1041
|
});
|
|
432
|
-
printAppStatus(app);
|
|
1042
|
+
await printAppStatus(app);
|
|
433
1043
|
// Block until a shutdown signal. Returning here (the old behavior) let the
|
|
434
1044
|
// process exit immediately whenever no channel owned a live handle — e.g. a
|
|
435
1045
|
// traceability-only config, now that the operator console is retired and the
|
|
@@ -508,7 +1118,7 @@ function requireDarwin(command) {
|
|
|
508
1118
|
process.stderr.write(ui.hint("Run `mono-agent start --foreground` to run in the foreground on this platform."));
|
|
509
1119
|
return 1;
|
|
510
1120
|
}
|
|
511
|
-
export function printAppStatus(app) {
|
|
1121
|
+
export async function printAppStatus(app, options = {}) {
|
|
512
1122
|
const trace = app.traceabilityStatus;
|
|
513
1123
|
process.stdout.write(ui.rule("instance"));
|
|
514
1124
|
process.stdout.write(ui.keyValue([
|
|
@@ -519,6 +1129,8 @@ export function printAppStatus(app) {
|
|
|
519
1129
|
],
|
|
520
1130
|
], 2));
|
|
521
1131
|
const artifactDir = app.traceabilityStatus.kind === "running" ? app.traceabilityStatus.artifactDir : undefined;
|
|
1132
|
+
process.stdout.write(ui.rule("sandbox"));
|
|
1133
|
+
process.stdout.write(` ${describeSandboxStatus(app.sandboxStatus)}\n`);
|
|
522
1134
|
process.stdout.write(ui.rule("observability"));
|
|
523
1135
|
process.stdout.write(` ${describeExporter(app.exporterStatus, artifactDir)}\n`);
|
|
524
1136
|
const channels = [...app.channelStatuses()];
|
|
@@ -528,6 +1140,43 @@ export function printAppStatus(app) {
|
|
|
528
1140
|
process.stdout.write(` ${ui.channelBadge(status.kind)}${ui.style.bold(id.padEnd(11))} ${describeChannelStatus(status)}\n`);
|
|
529
1141
|
}
|
|
530
1142
|
}
|
|
1143
|
+
await writeAppRunsHealthDetail(app, options);
|
|
1144
|
+
}
|
|
1145
|
+
async function writeAppRunsHealthDetail(app, options) {
|
|
1146
|
+
const artifactDir = app.traceabilityStatus.kind === "running" ? app.traceabilityStatus.artifactDir : undefined;
|
|
1147
|
+
if (artifactDir === undefined || artifactDir.trim().length === 0) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const reader = options.listRecordedRuns ?? listRecordedRuns;
|
|
1151
|
+
let result;
|
|
1152
|
+
try {
|
|
1153
|
+
result = await reader({ artifactDir, maxRuns: RUNS_HEALTH_MAX_RUNS, scope: "agent" });
|
|
1154
|
+
}
|
|
1155
|
+
catch (error) {
|
|
1156
|
+
result = {
|
|
1157
|
+
totalRuns: 0,
|
|
1158
|
+
runs: [],
|
|
1159
|
+
warnings: [`Unable to read run summaries: ${reasonOf(error)}`],
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
const display = buildRunsHealthDisplay({
|
|
1163
|
+
artifactDir,
|
|
1164
|
+
totalRuns: result.totalRuns,
|
|
1165
|
+
runs: result.runs,
|
|
1166
|
+
warnings: result.warnings,
|
|
1167
|
+
includeSelectedSkills: true,
|
|
1168
|
+
runOwnerAlive: true,
|
|
1169
|
+
maxRuns: RUNS_HEALTH_MAX_RUNS,
|
|
1170
|
+
...(app.selectedSkills === undefined ? {} : { selectedSkills: app.selectedSkills }),
|
|
1171
|
+
...(options.nowMs === undefined ? {} : { nowMs: options.nowMs }),
|
|
1172
|
+
});
|
|
1173
|
+
process.stdout.write(ui.rule("runs health"));
|
|
1174
|
+
for (const detail of display.details) {
|
|
1175
|
+
process.stdout.write(` ${detail}\n`);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
function reasonOf(error) {
|
|
1179
|
+
return error instanceof Error ? error.message : String(error);
|
|
531
1180
|
}
|
|
532
1181
|
function describeExporter(status, artifactDir) {
|
|
533
1182
|
if (status.kind !== "configured") {
|
|
@@ -539,7 +1188,7 @@ function describeExporter(status, artifactDir) {
|
|
|
539
1188
|
parts.push(`app ${appUrl}`);
|
|
540
1189
|
}
|
|
541
1190
|
if (status.includeSensitiveData) {
|
|
542
|
-
parts.push(
|
|
1191
|
+
parts.push(ui.style.yellow(describeSensitiveDataExportWarning(status.endpoint)));
|
|
543
1192
|
}
|
|
544
1193
|
if (status.lastWarning !== undefined) {
|
|
545
1194
|
parts.push(`last warning: ${status.lastWarning}`);
|
|
@@ -552,6 +1201,25 @@ function describeExporter(status, artifactDir) {
|
|
|
552
1201
|
: `JSONL artifacts remain local at ${artifactDir}`);
|
|
553
1202
|
return parts.join("; ");
|
|
554
1203
|
}
|
|
1204
|
+
function describeSandboxStatus(status) {
|
|
1205
|
+
const engineAvailability = status.engineAvailable === true
|
|
1206
|
+
? "present"
|
|
1207
|
+
: status.engineAvailable === false
|
|
1208
|
+
? "absent"
|
|
1209
|
+
: "not checked";
|
|
1210
|
+
const parts = [
|
|
1211
|
+
`effective: ${status.effective}`,
|
|
1212
|
+
`engine: ${status.engine ?? "none"} (${engineAvailability})`,
|
|
1213
|
+
...(status.fallback === undefined ? [] : [`fallback: ${status.fallback}`]),
|
|
1214
|
+
`fallback active: ${status.fallbackActive ? "yes" : "no"}`,
|
|
1215
|
+
status.detail,
|
|
1216
|
+
];
|
|
1217
|
+
const warning = status.warning ?? sandboxEffectiveStateWarning(status);
|
|
1218
|
+
if (warning !== undefined) {
|
|
1219
|
+
parts.push(ui.style.yellow(warning));
|
|
1220
|
+
}
|
|
1221
|
+
return parts.join("; ");
|
|
1222
|
+
}
|
|
555
1223
|
function describeChannelStatus(status) {
|
|
556
1224
|
if (status.kind === "running") {
|
|
557
1225
|
const facts = Object.entries(status.summary)
|