@hilbras/omninode 0.10.0 → 1.0.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/README.md +33 -7
- package/dist/cli/index.js +168 -35
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +46 -2
- package/dist/index.js +103 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,9 +18,26 @@ existing intelligence; it does not try to become another model.
|
|
|
18
18
|
|
|
19
19
|
## Status
|
|
20
20
|
|
|
21
|
-
**
|
|
22
|
-
|
|
23
|
-
`User → project context → memory → research agents → reports → aggregation →
|
|
21
|
+
**v1.0.0 — Phases 10 + 11: Production Release.** The plan is complete: the
|
|
22
|
+
full loop from §28 runs in one command —
|
|
23
|
+
`User → project context → memory → research agents → reports → aggregation →
|
|
24
|
+
planner → plan → execution → result` — with audit logging, error recovery and
|
|
25
|
+
production hardening (see [docs/DEVELOPMENT_PLAN.md](docs/DEVELOPMENT_PLAN.md)
|
|
26
|
+
for the plan and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the design).
|
|
27
|
+
|
|
28
|
+
### What's in v1.0
|
|
29
|
+
|
|
30
|
+
| Capability | Where |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| Provider abstraction, custom providers, model discovery | `src/providers/`, `src/registry/` |
|
|
33
|
+
| OmniHilbras integration (optional) | `src/providers/omnihilbras/` |
|
|
34
|
+
| CLI agents (stdin/arg/protocol), agent registry | `src/agents/` |
|
|
35
|
+
| Roles, projects, tasks (lifecycle + retry) | `src/roles/`, `src/tasks/` |
|
|
36
|
+
| Pipelines (DAG, parallel, retries, conditions) | `src/pipelines/` |
|
|
37
|
+
| Multi-AI research + report aggregation | `src/reports/` |
|
|
38
|
+
| Planner layer (model + heuristic) | `src/planner/` |
|
|
39
|
+
| Remembera / local memory | `src/memory/` |
|
|
40
|
+
| CLI, configuration, security foundations, docs | `src/cli/`, `src/config/`, `docs/` |
|
|
24
41
|
|
|
25
42
|
What exists today:
|
|
26
43
|
|
|
@@ -251,18 +268,27 @@ Phases 0–11 are laid out in [docs/DEVELOPMENT_PLAN.md](docs/DEVELOPMENT_PLAN.m
|
|
|
251
268
|
| v0.3.0 | OmniHilbras Integration (optional, like every provider) |
|
|
252
269
|
| v0.4.0 | Agent System |
|
|
253
270
|
| v0.5.0 | Roles & Tasks |
|
|
254
|
-
| v0.
|
|
271
|
+
| v0.6.0 | Pipeline Engine |
|
|
255
272
|
| v0.7.0 | Multi-AI Reports |
|
|
256
273
|
| v0.8.0 | Remembera / Memory |
|
|
257
|
-
| v0.8.0 | Remembera / Memory |
|
|
258
274
|
| v0.9.0 | Planner |
|
|
259
|
-
|
|
|
260
|
-
| v1.0.0 | Production Release |
|
|
275
|
+
| v0.10.0 | End-to-End Workflow (current) |
|
|
276
|
+
| **v1.0.0** | **Production Release — Phases 10 + 11 (current, final)** |
|
|
261
277
|
|
|
262
278
|
Core architectural rule: OmniNode must run successfully with **zero Hilbras
|
|
263
279
|
dependencies**. OmniHilbras and Remembera make it more powerful, but neither is
|
|
264
280
|
required for the core engine.
|
|
265
281
|
|
|
282
|
+
## Documentation
|
|
283
|
+
|
|
284
|
+
- [Development plan](docs/DEVELOPMENT_PLAN.md) — the full 12-phase roadmap
|
|
285
|
+
- [Architecture](docs/ARCHITECTURE.md) — module map, workflow, local state
|
|
286
|
+
- [Security model](docs/SECURITY.md) — secrets, process execution, env isolation, audit log
|
|
287
|
+
- [Example project](examples/demo/) — a runnable end-to-end demo (`./demo.sh`)
|
|
288
|
+
|
|
289
|
+
Prefer a container? `docker build -t omninode .` then
|
|
290
|
+
`docker run --rm -v "$PWD:/work" -w /work omninode run my-pipeline "audit the repo"`.
|
|
291
|
+
|
|
266
292
|
## Install
|
|
267
293
|
|
|
268
294
|
```bash
|
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { realpathSync } from "fs";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/version.ts
|
|
9
|
-
var OMNINODE_VERSION = "0.
|
|
9
|
+
var OMNINODE_VERSION = "1.0.0";
|
|
10
10
|
|
|
11
11
|
// src/errors/index.ts
|
|
12
12
|
var OmniNodeError = class extends Error {
|
|
@@ -87,6 +87,7 @@ var agentConfigSchema = z.object({
|
|
|
87
87
|
env: z.record(z.string()).optional(),
|
|
88
88
|
input_mode: z.enum(["stdin", "arg", "protocol"]).default("stdin"),
|
|
89
89
|
timeout_ms: z.number().int().positive().optional(),
|
|
90
|
+
inherit_env: z.boolean().optional(),
|
|
90
91
|
metadata: z.record(z.unknown()).optional()
|
|
91
92
|
}).strict();
|
|
92
93
|
var roleConfigSchema = z.object({
|
|
@@ -260,6 +261,7 @@ function toAgentConfig(raw) {
|
|
|
260
261
|
env: raw.env,
|
|
261
262
|
inputMode: raw.input_mode,
|
|
262
263
|
timeoutMs: raw.timeout_ms,
|
|
264
|
+
inheritEnv: raw.inherit_env,
|
|
263
265
|
metadata: raw.metadata
|
|
264
266
|
};
|
|
265
267
|
}
|
|
@@ -897,6 +899,17 @@ function registerModelsCommand(program) {
|
|
|
897
899
|
import { spawn } from "child_process";
|
|
898
900
|
var DEFAULT_TIMEOUT_MS2 = 6e5;
|
|
899
901
|
var KILL_GRACE_MS = 5e3;
|
|
902
|
+
function buildChildEnv(config) {
|
|
903
|
+
const configured = config.env ?? {};
|
|
904
|
+
if (config.inheritEnv !== false) {
|
|
905
|
+
return { ...process.env, ...configured };
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
PATH: process.env.PATH ?? "",
|
|
909
|
+
HOME: process.env.HOME ?? "",
|
|
910
|
+
...configured
|
|
911
|
+
};
|
|
912
|
+
}
|
|
900
913
|
function composeTaskText(input) {
|
|
901
914
|
const sections = [`# Objective
|
|
902
915
|
${input.objective}`];
|
|
@@ -963,7 +976,7 @@ var ProcessAgent = class {
|
|
|
963
976
|
[...this.config.args ?? [], ...mode === "arg" ? [composeTaskText(input)] : []],
|
|
964
977
|
{
|
|
965
978
|
cwd: this.config.cwd,
|
|
966
|
-
env:
|
|
979
|
+
env: buildChildEnv(this.config),
|
|
967
980
|
stdio: ["pipe", "pipe", "pipe"]
|
|
968
981
|
}
|
|
969
982
|
);
|
|
@@ -1400,6 +1413,7 @@ var TaskEngine = class {
|
|
|
1400
1413
|
};
|
|
1401
1414
|
await this.options.store.save(task);
|
|
1402
1415
|
this.log.info(`Created task ${task.id}.`);
|
|
1416
|
+
await this.audit("task.created", task.id, { objective: task.objective, project: task.project });
|
|
1403
1417
|
return task;
|
|
1404
1418
|
}
|
|
1405
1419
|
async queue(id) {
|
|
@@ -1491,6 +1505,7 @@ var TaskEngine = class {
|
|
|
1491
1505
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1492
1506
|
};
|
|
1493
1507
|
await this.options.store.save(reset);
|
|
1508
|
+
await this.audit("task.retry", id, { from: task.status });
|
|
1494
1509
|
this.log.info(`Task ${id} reset for retry.`);
|
|
1495
1510
|
return reset;
|
|
1496
1511
|
}
|
|
@@ -1519,16 +1534,68 @@ var TaskEngine = class {
|
|
|
1519
1534
|
...extra.result !== void 0 ? { result: { ...task.result, ...extra.result } } : {}
|
|
1520
1535
|
};
|
|
1521
1536
|
await this.options.store.save(updated);
|
|
1537
|
+
await this.audit(`task.${to === "queued" ? "queued" : to}`, updated.id, {
|
|
1538
|
+
from: task.status
|
|
1539
|
+
});
|
|
1522
1540
|
return updated;
|
|
1523
1541
|
}
|
|
1542
|
+
async audit(action, id, detail) {
|
|
1543
|
+
await this.options.audit?.record({
|
|
1544
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1545
|
+
action,
|
|
1546
|
+
id,
|
|
1547
|
+
...detail !== void 0 ? { detail } : {}
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1524
1550
|
};
|
|
1525
1551
|
function generateTaskId() {
|
|
1526
1552
|
return `task-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
|
|
1527
1553
|
}
|
|
1528
1554
|
|
|
1529
|
-
// src/
|
|
1530
|
-
import { mkdir as mkdir2
|
|
1555
|
+
// src/audit/index.ts
|
|
1556
|
+
import { appendFile, mkdir as mkdir2 } from "fs/promises";
|
|
1531
1557
|
import { dirname as dirname2 } from "path";
|
|
1558
|
+
var FileAuditLog = class {
|
|
1559
|
+
filePath;
|
|
1560
|
+
log;
|
|
1561
|
+
constructor(directory = `${process.cwd()}/.omninode`, log = logger) {
|
|
1562
|
+
this.filePath = `${directory}/audit.jsonl`;
|
|
1563
|
+
this.log = log.child({ component: "audit" });
|
|
1564
|
+
}
|
|
1565
|
+
async record(event) {
|
|
1566
|
+
try {
|
|
1567
|
+
await mkdir2(dirname2(this.filePath), { recursive: true });
|
|
1568
|
+
await appendFile(this.filePath, `${JSON.stringify(event)}
|
|
1569
|
+
`, "utf8");
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
this.log.warn(
|
|
1572
|
+
`Audit write failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
/** Recent events, oldest first; used by the CLI. */
|
|
1577
|
+
async recent(limit = 20) {
|
|
1578
|
+
const { readFile: readFile6 } = await import("fs/promises");
|
|
1579
|
+
let raw;
|
|
1580
|
+
try {
|
|
1581
|
+
raw = await readFile6(this.filePath, "utf8");
|
|
1582
|
+
} catch {
|
|
1583
|
+
return [];
|
|
1584
|
+
}
|
|
1585
|
+
const events = raw.split("\n").filter((line) => line.length > 0).map((line) => {
|
|
1586
|
+
try {
|
|
1587
|
+
return JSON.parse(line);
|
|
1588
|
+
} catch {
|
|
1589
|
+
return void 0;
|
|
1590
|
+
}
|
|
1591
|
+
}).filter((event) => event !== void 0);
|
|
1592
|
+
return events.slice(-limit);
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
// src/memory/local.ts
|
|
1597
|
+
import { mkdir as mkdir3, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
1598
|
+
import { dirname as dirname3 } from "path";
|
|
1532
1599
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
1533
1600
|
"the",
|
|
1534
1601
|
"and",
|
|
@@ -1641,7 +1708,7 @@ var LocalMemoryProvider = class {
|
|
|
1641
1708
|
}
|
|
1642
1709
|
async saveAll(entries) {
|
|
1643
1710
|
const tmp = `${this.filePath}.tmp`;
|
|
1644
|
-
await
|
|
1711
|
+
await mkdir3(dirname3(this.filePath), { recursive: true });
|
|
1645
1712
|
await writeFile2(tmp, `${JSON.stringify(entries, null, 2)}
|
|
1646
1713
|
`, "utf8");
|
|
1647
1714
|
await rename2(tmp, this.filePath);
|
|
@@ -1848,6 +1915,7 @@ function createTaskEngine(config, log = logger, directory) {
|
|
|
1848
1915
|
roles,
|
|
1849
1916
|
store: new FileTaskStore(directory),
|
|
1850
1917
|
...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
|
|
1918
|
+
audit: new FileAuditLog(),
|
|
1851
1919
|
log
|
|
1852
1920
|
});
|
|
1853
1921
|
}
|
|
@@ -2160,8 +2228,8 @@ var ReportService = class {
|
|
|
2160
2228
|
};
|
|
2161
2229
|
|
|
2162
2230
|
// src/reports/store.ts
|
|
2163
|
-
import { mkdir as
|
|
2164
|
-
import { dirname as
|
|
2231
|
+
import { mkdir as mkdir4, readFile as readFile3, rename as rename3, writeFile as writeFile3 } from "fs/promises";
|
|
2232
|
+
import { dirname as dirname4 } from "path";
|
|
2165
2233
|
var FileReportStore = class {
|
|
2166
2234
|
filePath;
|
|
2167
2235
|
queue = Promise.resolve();
|
|
@@ -2234,7 +2302,7 @@ var FileReportStore = class {
|
|
|
2234
2302
|
}
|
|
2235
2303
|
async write(entries) {
|
|
2236
2304
|
const tmp = `${this.filePath}.tmp`;
|
|
2237
|
-
await
|
|
2305
|
+
await mkdir4(dirname4(this.filePath), { recursive: true });
|
|
2238
2306
|
await writeFile3(tmp, `${JSON.stringify(entries, null, 2)}
|
|
2239
2307
|
`, "utf8");
|
|
2240
2308
|
await rename3(tmp, this.filePath);
|
|
@@ -2676,6 +2744,12 @@ var PipelineEngine = class {
|
|
|
2676
2744
|
};
|
|
2677
2745
|
await this.options.store.save(run2);
|
|
2678
2746
|
this.log.info(`Running pipeline ${def.id} as ${run2.id}.`);
|
|
2747
|
+
await this.options.audit?.record({
|
|
2748
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2749
|
+
action: "pipeline.run.started",
|
|
2750
|
+
id: run2.id,
|
|
2751
|
+
detail: { pipeline: def.id, objective }
|
|
2752
|
+
});
|
|
2679
2753
|
const deps = this.effectiveDependencies(def);
|
|
2680
2754
|
const outcomes = /* @__PURE__ */ new Map();
|
|
2681
2755
|
const pending = new Set(def.steps.map((s) => s.id));
|
|
@@ -2766,6 +2840,12 @@ ${contribution}`);
|
|
|
2766
2840
|
};
|
|
2767
2841
|
await this.options.store.save(finished);
|
|
2768
2842
|
this.log.info(`Pipeline ${def.id} finished: ${finalStatus}.`);
|
|
2843
|
+
await this.options.audit?.record({
|
|
2844
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2845
|
+
action: "pipeline.run.finished",
|
|
2846
|
+
id: run2.id,
|
|
2847
|
+
detail: { pipeline: def.id, status: finalStatus }
|
|
2848
|
+
});
|
|
2769
2849
|
return finished;
|
|
2770
2850
|
}
|
|
2771
2851
|
async get(runId) {
|
|
@@ -2910,6 +2990,12 @@ ${contribution}`);
|
|
|
2910
2990
|
};
|
|
2911
2991
|
}
|
|
2912
2992
|
await this.options.plans?.save(plan);
|
|
2993
|
+
await this.options.audit?.record({
|
|
2994
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2995
|
+
action: "plan.generated",
|
|
2996
|
+
id: plan.id,
|
|
2997
|
+
detail: { generatedBy: plan.generatedBy, steps: plan.steps.length }
|
|
2998
|
+
});
|
|
2913
2999
|
return {
|
|
2914
3000
|
status: "completed",
|
|
2915
3001
|
summary: plan.summary,
|
|
@@ -2967,8 +3053,8 @@ function createDefaultChatFn(providers) {
|
|
|
2967
3053
|
}
|
|
2968
3054
|
|
|
2969
3055
|
// src/pipelines/store.ts
|
|
2970
|
-
import { mkdir as
|
|
2971
|
-
import { dirname as
|
|
3056
|
+
import { mkdir as mkdir5, readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "fs/promises";
|
|
3057
|
+
import { dirname as dirname5 } from "path";
|
|
2972
3058
|
var FilePipelineRunStore = class {
|
|
2973
3059
|
filePath;
|
|
2974
3060
|
queue = Promise.resolve();
|
|
@@ -3015,7 +3101,7 @@ var FilePipelineRunStore = class {
|
|
|
3015
3101
|
}
|
|
3016
3102
|
async write(runs) {
|
|
3017
3103
|
const tmp = `${this.filePath}.tmp`;
|
|
3018
|
-
await
|
|
3104
|
+
await mkdir5(dirname5(this.filePath), { recursive: true });
|
|
3019
3105
|
await writeFile4(tmp, `${JSON.stringify(runs, null, 2)}
|
|
3020
3106
|
`, "utf8");
|
|
3021
3107
|
await rename4(tmp, this.filePath);
|
|
@@ -3023,8 +3109,8 @@ var FilePipelineRunStore = class {
|
|
|
3023
3109
|
};
|
|
3024
3110
|
|
|
3025
3111
|
// src/planner/store.ts
|
|
3026
|
-
import { mkdir as
|
|
3027
|
-
import { dirname as
|
|
3112
|
+
import { mkdir as mkdir6, readFile as readFile5, rename as rename5, writeFile as writeFile5 } from "fs/promises";
|
|
3113
|
+
import { dirname as dirname6 } from "path";
|
|
3028
3114
|
var FilePlanStore = class {
|
|
3029
3115
|
filePath;
|
|
3030
3116
|
queue = Promise.resolve();
|
|
@@ -3071,7 +3157,7 @@ var FilePlanStore = class {
|
|
|
3071
3157
|
}
|
|
3072
3158
|
async write(plans) {
|
|
3073
3159
|
const tmp = `${this.filePath}.tmp`;
|
|
3074
|
-
await
|
|
3160
|
+
await mkdir6(dirname6(this.filePath), { recursive: true });
|
|
3075
3161
|
await writeFile5(tmp, `${JSON.stringify(plans, null, 2)}
|
|
3076
3162
|
`, "utf8");
|
|
3077
3163
|
await rename5(tmp, this.filePath);
|
|
@@ -3109,6 +3195,7 @@ function buildPipelineEngine(config, log = logger) {
|
|
|
3109
3195
|
roles,
|
|
3110
3196
|
store: new FileTaskStore(),
|
|
3111
3197
|
...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
|
|
3198
|
+
audit: new FileAuditLog(),
|
|
3112
3199
|
log
|
|
3113
3200
|
});
|
|
3114
3201
|
return new PipelineEngine({
|
|
@@ -3121,6 +3208,7 @@ function buildPipelineEngine(config, log = logger) {
|
|
|
3121
3208
|
...config.project.planner !== void 0 ? { planner: buildPlanner(config.project.planner, chat, log) } : {},
|
|
3122
3209
|
plans: new FilePlanStore(),
|
|
3123
3210
|
reports: new ReportService(new FileReportStore(), log),
|
|
3211
|
+
audit: new FileAuditLog(),
|
|
3124
3212
|
log
|
|
3125
3213
|
});
|
|
3126
3214
|
}
|
|
@@ -3344,6 +3432,14 @@ function registerPlanCommands(program) {
|
|
|
3344
3432
|
}
|
|
3345
3433
|
|
|
3346
3434
|
// src/cli/commands/status.ts
|
|
3435
|
+
function groupBy(items, key) {
|
|
3436
|
+
const counts = {};
|
|
3437
|
+
for (const item of items) {
|
|
3438
|
+
const k = key(item);
|
|
3439
|
+
counts[k] = (counts[k] ?? 0) + 1;
|
|
3440
|
+
}
|
|
3441
|
+
return counts;
|
|
3442
|
+
}
|
|
3347
3443
|
function countBy(items, key) {
|
|
3348
3444
|
const counts = /* @__PURE__ */ new Map();
|
|
3349
3445
|
for (const item of items) {
|
|
@@ -3354,41 +3450,77 @@ function countBy(items, key) {
|
|
|
3354
3450
|
return `${items.length} (${[...counts.entries()].map(([k, v]) => `${k} ${v}`).join(", ")})`;
|
|
3355
3451
|
}
|
|
3356
3452
|
function registerStatusCommand(program) {
|
|
3357
|
-
program.command("status").description("Show the project's OmniNode state overview (config, tasks, runs, reports, plans, memory).").action(async () => {
|
|
3453
|
+
program.command("status").description("Show the project's OmniNode state overview (config, tasks, runs, reports, plans, memory).").option("--json", "Emit machine-readable JSON (for scripts and CI).").action(async (options) => {
|
|
3358
3454
|
const config = loadConfig();
|
|
3359
|
-
console.log(`OmniNode v${OMNINODE_VERSION} \u2014 project "${config.project.name}"`);
|
|
3360
|
-
console.log(
|
|
3361
|
-
`providers: ${config.project.providers.length > 0 ? config.project.providers.map((p) => p.name).join(", ") : "none"}`
|
|
3362
|
-
);
|
|
3363
|
-
console.log(
|
|
3364
|
-
`agents: ${config.project.agents.length > 0 ? config.project.agents.map((a) => a.name).join(", ") : "none"}`
|
|
3365
|
-
);
|
|
3366
|
-
console.log(
|
|
3367
|
-
`roles: ${config.project.roles.length > 0 ? config.project.roles.map((r) => r.id).join(", ") : "none"}`
|
|
3368
|
-
);
|
|
3369
|
-
console.log(
|
|
3370
|
-
`pipelines: ${config.project.pipelines.length > 0 ? config.project.pipelines.map((p) => p.id).join(", ") : "none"}`
|
|
3371
|
-
);
|
|
3372
3455
|
const tasks = await new FileTaskStore().list();
|
|
3373
|
-
console.log(`tasks: ${countBy(tasks, (t) => t.status)}`);
|
|
3374
3456
|
const runs = await new FilePipelineRunStore().list();
|
|
3375
|
-
console.log(`runs: ${countBy(runs, (r) => r.status)}`);
|
|
3376
3457
|
const reportStore = new FileReportStore();
|
|
3377
3458
|
const reports = await reportStore.listReports();
|
|
3378
3459
|
const combined = await reportStore.listCombined();
|
|
3379
|
-
console.log(`reports: ${reports.length} raw, ${combined.length} combined`);
|
|
3380
3460
|
const plans = await new FilePlanStore().list();
|
|
3381
|
-
|
|
3461
|
+
let memory;
|
|
3382
3462
|
if (config.project.memory !== void 0) {
|
|
3383
|
-
const
|
|
3384
|
-
|
|
3385
|
-
|
|
3463
|
+
const service2 = new MemoryService(createMemoryProvider(config.project.memory));
|
|
3464
|
+
memory = {
|
|
3465
|
+
provider: service2.providerName,
|
|
3466
|
+
entries: (await service2.providerRef.query({ limit: 1e4 })).length
|
|
3467
|
+
};
|
|
3468
|
+
}
|
|
3469
|
+
if (options.json) {
|
|
3470
|
+
console.log(
|
|
3471
|
+
JSON.stringify(
|
|
3472
|
+
{
|
|
3473
|
+
version: OMNINODE_VERSION,
|
|
3474
|
+
project: config.project.name,
|
|
3475
|
+
providers: config.project.providers.map((p) => p.name),
|
|
3476
|
+
agents: config.project.agents.map((a) => a.name),
|
|
3477
|
+
roles: config.project.roles.map((r) => r.id),
|
|
3478
|
+
pipelines: config.project.pipelines.map((p) => p.id),
|
|
3479
|
+
tasks: { total: tasks.length, byStatus: groupBy(tasks, (t) => t.status) },
|
|
3480
|
+
runs: { total: runs.length, byStatus: groupBy(runs, (r) => r.status) },
|
|
3481
|
+
reports: { raw: reports.length, combined: combined.length },
|
|
3482
|
+
plans: plans.length,
|
|
3483
|
+
...memory !== void 0 ? { memory } : {}
|
|
3484
|
+
},
|
|
3485
|
+
null,
|
|
3486
|
+
2
|
|
3487
|
+
)
|
|
3488
|
+
);
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
console.log(`OmniNode v${OMNINODE_VERSION} \u2014 project "${config.project.name}"`);
|
|
3492
|
+
console.log(`providers: ${config.project.providers.map((p) => p.name).join(", ") || "none"}`);
|
|
3493
|
+
console.log(`agents: ${config.project.agents.map((a) => a.name).join(", ") || "none"}`);
|
|
3494
|
+
console.log(`roles: ${config.project.roles.map((r) => r.id).join(", ") || "none"}`);
|
|
3495
|
+
console.log(`pipelines: ${config.project.pipelines.map((p) => p.id).join(", ") || "none"}`);
|
|
3496
|
+
console.log(`tasks: ${countBy(tasks, (t) => t.status)}`);
|
|
3497
|
+
console.log(`runs: ${countBy(runs, (r) => r.status)}`);
|
|
3498
|
+
console.log(`reports: ${reports.length} raw, ${combined.length} combined`);
|
|
3499
|
+
console.log(`plans: ${plans.length}`);
|
|
3500
|
+
if (memory) {
|
|
3501
|
+
console.log(`memory: ${memory.provider}, ${memory.entries} entr(ies)`);
|
|
3386
3502
|
} else {
|
|
3387
3503
|
console.log("memory: not configured (local provider available \u2014 add a `memory:` section)");
|
|
3388
3504
|
}
|
|
3389
3505
|
});
|
|
3390
3506
|
}
|
|
3391
3507
|
|
|
3508
|
+
// src/cli/commands/audit.ts
|
|
3509
|
+
function registerAuditCommand(program) {
|
|
3510
|
+
program.command("audit").description("Show the recent audit log \u2014 task and pipeline lifecycle events (\xA724).").option("-n, --limit <count>", "Number of events to show (default 20).", (value) => Number(value), 20).action(async (options) => {
|
|
3511
|
+
const events = await new FileAuditLog().recent(options.limit);
|
|
3512
|
+
if (events.length === 0) {
|
|
3513
|
+
console.log("No audit events recorded yet.");
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
for (const event of events) {
|
|
3517
|
+
const id = event.id ? ` ${event.id}` : "";
|
|
3518
|
+
const detail = event.detail && Object.keys(event.detail).length > 0 ? ` ${JSON.stringify(event.detail)}` : "";
|
|
3519
|
+
console.log(`${event.at} ${event.action}${id}${detail}`);
|
|
3520
|
+
}
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3392
3524
|
// src/cli/index.ts
|
|
3393
3525
|
function createProgram() {
|
|
3394
3526
|
const program = new Command();
|
|
@@ -3407,6 +3539,7 @@ function createProgram() {
|
|
|
3407
3539
|
registerMemoryCommands(program);
|
|
3408
3540
|
registerPlanCommands(program);
|
|
3409
3541
|
registerStatusCommand(program);
|
|
3542
|
+
registerAuditCommand(program);
|
|
3410
3543
|
registerRunAlias(program);
|
|
3411
3544
|
return program;
|
|
3412
3545
|
}
|