@ouro.bot/cli 0.1.0-alpha.207 → 0.1.0-alpha.208
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.json +6 -0
- package/dist/heart/daemon/cli-defaults.js +557 -0
- package/dist/heart/daemon/cli-exec.js +1847 -0
- package/dist/heart/daemon/cli-parse.js +759 -0
- package/dist/heart/daemon/cli-render.js +316 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +28 -3347
- package/dist/nerves/coverage/file-completeness.js +7 -3
- package/package.json +1 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI output formatting and rendering helpers.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions that transform data into human-readable CLI output.
|
|
6
|
+
* No side effects — callers handle writing to stdout.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.parseStatusPayload = parseStatusPayload;
|
|
43
|
+
exports.humanizeSenseName = humanizeSenseName;
|
|
44
|
+
exports.formatTable = formatTable;
|
|
45
|
+
exports.formatDaemonStatusOutput = formatDaemonStatusOutput;
|
|
46
|
+
exports.formatVersionOutput = formatVersionOutput;
|
|
47
|
+
exports.buildStoppedStatusPayload = buildStoppedStatusPayload;
|
|
48
|
+
exports.daemonUnavailableStatusOutput = daemonUnavailableStatusOutput;
|
|
49
|
+
exports.isDaemonUnavailableError = isDaemonUnavailableError;
|
|
50
|
+
exports.formatMcpResponse = formatMcpResponse;
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const runtime_metadata_1 = require("./runtime-metadata");
|
|
53
|
+
const runtime_mode_1 = require("./runtime-mode");
|
|
54
|
+
const identity_1 = require("../identity");
|
|
55
|
+
const daemon_tombstone_1 = require("./daemon-tombstone");
|
|
56
|
+
const daemon_health_1 = require("./daemon-health");
|
|
57
|
+
// ── Field extractors ──
|
|
58
|
+
function stringField(value) {
|
|
59
|
+
return typeof value === "string" ? value : null;
|
|
60
|
+
}
|
|
61
|
+
function numberField(value) {
|
|
62
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
63
|
+
}
|
|
64
|
+
function booleanField(value) {
|
|
65
|
+
return typeof value === "boolean" ? value : null;
|
|
66
|
+
}
|
|
67
|
+
// ── Parsers ──
|
|
68
|
+
function parseStatusPayload(data) {
|
|
69
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
70
|
+
return null;
|
|
71
|
+
const raw = data;
|
|
72
|
+
const overview = raw.overview;
|
|
73
|
+
const senses = raw.senses;
|
|
74
|
+
const workers = raw.workers;
|
|
75
|
+
if (!overview || typeof overview !== "object" || Array.isArray(overview))
|
|
76
|
+
return null;
|
|
77
|
+
if (!Array.isArray(senses) || !Array.isArray(workers))
|
|
78
|
+
return null;
|
|
79
|
+
const parsedOverview = {
|
|
80
|
+
daemon: stringField(overview.daemon) ?? "unknown",
|
|
81
|
+
health: stringField(overview.health) ?? "unknown",
|
|
82
|
+
socketPath: stringField(overview.socketPath) ?? "unknown",
|
|
83
|
+
outlookUrl: stringField(overview.outlookUrl) ?? "unavailable",
|
|
84
|
+
version: stringField(overview.version) ?? "unknown",
|
|
85
|
+
lastUpdated: stringField(overview.lastUpdated) ?? "unknown",
|
|
86
|
+
repoRoot: stringField(overview.repoRoot) ?? "unknown",
|
|
87
|
+
configFingerprint: stringField(overview.configFingerprint) ?? "unknown",
|
|
88
|
+
workerCount: numberField(overview.workerCount) ?? 0,
|
|
89
|
+
senseCount: numberField(overview.senseCount) ?? 0,
|
|
90
|
+
entryPath: stringField(overview.entryPath) ?? "unknown",
|
|
91
|
+
mode: stringField(overview.mode) ?? "unknown",
|
|
92
|
+
};
|
|
93
|
+
const parsedSenses = senses.map((entry) => {
|
|
94
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
95
|
+
return null;
|
|
96
|
+
const row = entry;
|
|
97
|
+
const agent = stringField(row.agent);
|
|
98
|
+
const sense = stringField(row.sense);
|
|
99
|
+
const status = stringField(row.status);
|
|
100
|
+
const detail = stringField(row.detail);
|
|
101
|
+
const enabled = booleanField(row.enabled);
|
|
102
|
+
if (!agent || !sense || !status || detail === null || enabled === null)
|
|
103
|
+
return null;
|
|
104
|
+
return {
|
|
105
|
+
agent,
|
|
106
|
+
sense,
|
|
107
|
+
label: stringField(row.label) ?? undefined,
|
|
108
|
+
enabled,
|
|
109
|
+
status,
|
|
110
|
+
detail,
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
const parsedWorkers = workers.map((entry) => {
|
|
114
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
115
|
+
return null;
|
|
116
|
+
const row = entry;
|
|
117
|
+
const agent = stringField(row.agent);
|
|
118
|
+
const worker = stringField(row.worker);
|
|
119
|
+
const status = stringField(row.status);
|
|
120
|
+
const restartCount = numberField(row.restartCount);
|
|
121
|
+
const hasPid = Object.prototype.hasOwnProperty.call(row, "pid");
|
|
122
|
+
const pid = row.pid === null ? null : numberField(row.pid);
|
|
123
|
+
const pidInvalid = !hasPid || (row.pid !== null && pid === null);
|
|
124
|
+
if (!agent || !worker || !status || restartCount === null || pidInvalid)
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
agent,
|
|
128
|
+
worker,
|
|
129
|
+
status,
|
|
130
|
+
pid,
|
|
131
|
+
restartCount,
|
|
132
|
+
lastExitCode: numberField(row.lastExitCode) ?? null,
|
|
133
|
+
lastSignal: stringField(row.lastSignal) ?? null,
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
if (parsedSenses.some((row) => row === null) || parsedWorkers.some((row) => row === null))
|
|
137
|
+
return null;
|
|
138
|
+
return {
|
|
139
|
+
overview: parsedOverview,
|
|
140
|
+
senses: parsedSenses,
|
|
141
|
+
workers: parsedWorkers,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// ── Formatters ──
|
|
145
|
+
function humanizeSenseName(sense, label) {
|
|
146
|
+
if (label)
|
|
147
|
+
return label;
|
|
148
|
+
if (sense === "cli")
|
|
149
|
+
return "CLI";
|
|
150
|
+
if (sense === "bluebubbles")
|
|
151
|
+
return "BlueBubbles";
|
|
152
|
+
if (sense === "teams")
|
|
153
|
+
return "Teams";
|
|
154
|
+
return sense;
|
|
155
|
+
}
|
|
156
|
+
function formatTable(headers, rows) {
|
|
157
|
+
const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index].length)));
|
|
158
|
+
const renderRow = (row) => `| ${row.map((cell, index) => (index === row.length - 1
|
|
159
|
+
? cell
|
|
160
|
+
: cell.padEnd(widths[index]))).join(" | ")} |`;
|
|
161
|
+
const divider = `|-${widths.map((width) => "-".repeat(width)).join("-|-")}-|`;
|
|
162
|
+
return [
|
|
163
|
+
renderRow(headers),
|
|
164
|
+
divider,
|
|
165
|
+
...rows.map(renderRow),
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
function formatDaemonStatusOutput(response, fallback) {
|
|
169
|
+
const payload = parseStatusPayload(response.data);
|
|
170
|
+
if (!payload)
|
|
171
|
+
return fallback;
|
|
172
|
+
const overviewRows = [
|
|
173
|
+
["Daemon", payload.overview.daemon],
|
|
174
|
+
["Socket", payload.overview.socketPath],
|
|
175
|
+
["Version", payload.overview.version],
|
|
176
|
+
["Last Updated", payload.overview.lastUpdated],
|
|
177
|
+
["Outlook", payload.overview.outlookUrl],
|
|
178
|
+
["Entry Path", payload.overview.entryPath],
|
|
179
|
+
["Mode", payload.overview.mode],
|
|
180
|
+
["Workers", String(payload.overview.workerCount)],
|
|
181
|
+
["Senses", String(payload.overview.senseCount)],
|
|
182
|
+
["Health", payload.overview.health],
|
|
183
|
+
];
|
|
184
|
+
const senseRows = payload.senses.map((row) => [
|
|
185
|
+
row.agent,
|
|
186
|
+
humanizeSenseName(row.sense, row.label),
|
|
187
|
+
row.enabled ? "ON" : "OFF",
|
|
188
|
+
row.status,
|
|
189
|
+
row.detail,
|
|
190
|
+
]);
|
|
191
|
+
const workerRows = payload.workers.map((row) => {
|
|
192
|
+
/* v8 ignore start — exit info branches tested via daemon-crash-context; v8 misreports conditional chains @preserve */
|
|
193
|
+
let exitInfo = "n/a";
|
|
194
|
+
if (row.lastExitCode !== null)
|
|
195
|
+
exitInfo = `code=${row.lastExitCode}`;
|
|
196
|
+
if (row.lastSignal !== null)
|
|
197
|
+
exitInfo = row.lastExitCode !== null ? `code=${row.lastExitCode} sig=${row.lastSignal}` : `sig=${row.lastSignal}`;
|
|
198
|
+
/* v8 ignore stop */
|
|
199
|
+
return [
|
|
200
|
+
row.agent,
|
|
201
|
+
row.worker,
|
|
202
|
+
row.status,
|
|
203
|
+
row.pid === null ? "n/a" : String(row.pid),
|
|
204
|
+
String(row.restartCount),
|
|
205
|
+
exitInfo,
|
|
206
|
+
];
|
|
207
|
+
});
|
|
208
|
+
return [
|
|
209
|
+
"Overview",
|
|
210
|
+
formatTable(["Item", "Value"], overviewRows),
|
|
211
|
+
"",
|
|
212
|
+
"Senses",
|
|
213
|
+
formatTable(["Agent", "Sense", "Enabled", "State", "Detail"], senseRows),
|
|
214
|
+
"",
|
|
215
|
+
"Workers",
|
|
216
|
+
formatTable(["Agent", "Worker", "State", "PID", "Restarts", "Last Exit"], workerRows),
|
|
217
|
+
].join("\n");
|
|
218
|
+
}
|
|
219
|
+
function formatVersionOutput() {
|
|
220
|
+
const version = (0, runtime_metadata_1.getRuntimeMetadata)().version;
|
|
221
|
+
const mode = (0, runtime_mode_1.detectRuntimeMode)((0, identity_1.getRepoRoot)());
|
|
222
|
+
/* v8 ignore start — cosmetic display toggle; dev mode always true in test env */
|
|
223
|
+
return mode === "dev" ? `${version} (dev)` : version;
|
|
224
|
+
/* v8 ignore stop */
|
|
225
|
+
}
|
|
226
|
+
function buildStoppedStatusPayload(socketPath) {
|
|
227
|
+
const metadata = (0, runtime_metadata_1.getRuntimeMetadata)();
|
|
228
|
+
const repoRoot = (0, identity_1.getRepoRoot)();
|
|
229
|
+
return {
|
|
230
|
+
overview: {
|
|
231
|
+
daemon: "stopped",
|
|
232
|
+
health: "warn",
|
|
233
|
+
socketPath,
|
|
234
|
+
outlookUrl: "unavailable",
|
|
235
|
+
version: metadata.version,
|
|
236
|
+
lastUpdated: metadata.lastUpdated,
|
|
237
|
+
repoRoot: metadata.repoRoot,
|
|
238
|
+
configFingerprint: metadata.configFingerprint,
|
|
239
|
+
workerCount: 0,
|
|
240
|
+
senseCount: 0,
|
|
241
|
+
entryPath: path.join(repoRoot, "dist", "heart", "daemon", "daemon-entry.js"),
|
|
242
|
+
mode: (0, runtime_mode_1.detectRuntimeMode)(repoRoot),
|
|
243
|
+
},
|
|
244
|
+
senses: [],
|
|
245
|
+
workers: [],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function daemonUnavailableStatusOutput(socketPath, healthFilePath) {
|
|
249
|
+
/* v8 ignore start — tombstone read tested in daemon-status-tombstone.test; branch misreported @preserve */
|
|
250
|
+
const tombstone = (0, daemon_tombstone_1.readDaemonTombstone)();
|
|
251
|
+
const deathLine = tombstone
|
|
252
|
+
? `Last death: ${tombstone.timestamp} -- ${tombstone.reason}: ${tombstone.message}`
|
|
253
|
+
: null;
|
|
254
|
+
/* v8 ignore stop */
|
|
255
|
+
const lines = [
|
|
256
|
+
formatDaemonStatusOutput({
|
|
257
|
+
ok: true,
|
|
258
|
+
summary: "daemon not running",
|
|
259
|
+
data: buildStoppedStatusPayload(socketPath),
|
|
260
|
+
}, "daemon not running"),
|
|
261
|
+
"",
|
|
262
|
+
];
|
|
263
|
+
/* v8 ignore start — tombstone presence requires real daemon crash @preserve */
|
|
264
|
+
if (deathLine) {
|
|
265
|
+
lines.push(deathLine);
|
|
266
|
+
lines.push("");
|
|
267
|
+
/* v8 ignore stop */
|
|
268
|
+
}
|
|
269
|
+
// Read health file for last-known state (best-effort)
|
|
270
|
+
const resolvedHealthPath = healthFilePath ?? (0, daemon_health_1.getDefaultHealthPath)();
|
|
271
|
+
const health = (0, daemon_health_1.readHealth)(resolvedHealthPath);
|
|
272
|
+
if (health) {
|
|
273
|
+
lines.push(`Last known status: ${health.status} (pid ${health.pid}, uptime ${health.uptimeSeconds}s)`);
|
|
274
|
+
if (health.safeMode?.active) {
|
|
275
|
+
lines.push(`SAFE MODE: ${health.safeMode.reason}`);
|
|
276
|
+
}
|
|
277
|
+
if (health.degraded.length > 0) {
|
|
278
|
+
lines.push("");
|
|
279
|
+
lines.push("Degraded:");
|
|
280
|
+
for (const d of health.degraded) {
|
|
281
|
+
lines.push(` ${d.component}: ${d.reason} (since ${d.since})`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
lines.push("");
|
|
285
|
+
}
|
|
286
|
+
lines.push("daemon not running; run `ouro up`");
|
|
287
|
+
return lines.join("\n");
|
|
288
|
+
}
|
|
289
|
+
function isDaemonUnavailableError(error) {
|
|
290
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
291
|
+
? String(error.code ?? "")
|
|
292
|
+
: "";
|
|
293
|
+
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
294
|
+
}
|
|
295
|
+
function formatMcpResponse(command, response) {
|
|
296
|
+
if (command.kind === "mcp.list") {
|
|
297
|
+
const allTools = response.data;
|
|
298
|
+
if (!allTools || allTools.length === 0) {
|
|
299
|
+
return response.message ?? "no tools available from connected MCP servers";
|
|
300
|
+
}
|
|
301
|
+
const lines = [];
|
|
302
|
+
for (const entry of allTools) {
|
|
303
|
+
lines.push(`[${entry.server}]`);
|
|
304
|
+
for (const tool of entry.tools) {
|
|
305
|
+
lines.push(` ${tool.name}: ${tool.description}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return lines.join("\n");
|
|
309
|
+
}
|
|
310
|
+
// mcp.call
|
|
311
|
+
const result = response.data;
|
|
312
|
+
if (!result) {
|
|
313
|
+
return response.message ?? "no result";
|
|
314
|
+
}
|
|
315
|
+
return result.content.map((c) => c.text).join("\n");
|
|
316
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI command types and dependency interface.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from daemon-cli.ts — the OuroCliCommand union and OuroCliDeps
|
|
6
|
+
* interface are the contract between parsing, execution, and default wiring.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|