omkit 0.1.6 → 0.1.8
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.
|
@@ -70,7 +70,13 @@ export declare class ExecutionTree {
|
|
|
70
70
|
private drive;
|
|
71
71
|
private finalize;
|
|
72
72
|
private finalizeOnce;
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* The end-of-run recap: the run folder, sibling log paths, and the assert tally.
|
|
75
|
+
* Shared verbatim between the terminal (via {@link printSummary}) and the tail of
|
|
76
|
+
* `main.log` (as the footer passed to `writeNodeLogs`), so both close the same way.
|
|
77
|
+
*/
|
|
78
|
+
private summaryLines;
|
|
79
|
+
/** The recap to the terminal (files are already written). */
|
|
74
80
|
private printSummary;
|
|
75
81
|
private verdictOk;
|
|
76
82
|
private nodeView;
|
|
@@ -243,7 +243,8 @@ export class ExecutionTree {
|
|
|
243
243
|
const entries = this.store.entries();
|
|
244
244
|
const at = (name) => path.join(this.folder.path(), name);
|
|
245
245
|
const assertSummary = `⊨ ${this.assertionsPassed} passed · ⊭ ${this.assertionsFailed} failed`;
|
|
246
|
-
|
|
246
|
+
const summary = this.summaryLines(at, assertSummary);
|
|
247
|
+
await writeNodeLogs(flat, entries, summary);
|
|
247
248
|
await writeRollup(at("events.log"), flat, entries, (e) => e.level === "event");
|
|
248
249
|
await writeRollup(at("asserts.log"), flat, entries, (e) => e.level === "assert", assertSummary);
|
|
249
250
|
await writeRollup(at("snapshots.log"), flat, entries, (e) => e.level === "snapshot");
|
|
@@ -255,19 +256,30 @@ export class ExecutionTree {
|
|
|
255
256
|
process.off("unhandledRejection", this.onUnhandled);
|
|
256
257
|
this.consoleCapture.uninstall();
|
|
257
258
|
ExecutionTree.current = null;
|
|
258
|
-
this.printSummary(
|
|
259
|
+
this.printSummary(summary);
|
|
259
260
|
if (!this.verdictOk())
|
|
260
261
|
process.exitCode = 1;
|
|
261
262
|
}
|
|
262
|
-
/**
|
|
263
|
-
|
|
263
|
+
/**
|
|
264
|
+
* The end-of-run recap: the run folder, sibling log paths, and the assert tally.
|
|
265
|
+
* Shared verbatim between the terminal (via {@link printSummary}) and the tail of
|
|
266
|
+
* `main.log` (as the footer passed to `writeNodeLogs`), so both close the same way.
|
|
267
|
+
*/
|
|
268
|
+
summaryLines(at, assertSummary) {
|
|
269
|
+
return [
|
|
270
|
+
`om → ${this.folder.path()}`,
|
|
271
|
+
` main log → ${at("main.log")}`,
|
|
272
|
+
` events → ${at("events.log")}`,
|
|
273
|
+
` asserts → ${at("asserts.log")} ${assertSummary}`,
|
|
274
|
+
` snapshots → ${at("snapshots.log")}`,
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
/** The recap to the terminal (files are already written). */
|
|
278
|
+
printSummary(lines) {
|
|
264
279
|
const term = this.originalLog;
|
|
265
280
|
term("");
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
term(` events → ${at("events.log")}`);
|
|
269
|
-
term(` asserts → ${at("asserts.log")} ${assertSummary}`);
|
|
270
|
-
term(` snapshots → ${at("snapshots.log")}`);
|
|
281
|
+
for (const line of lines)
|
|
282
|
+
term(line);
|
|
271
283
|
}
|
|
272
284
|
verdictOk() {
|
|
273
285
|
return this.faults.length === 0;
|
|
@@ -3,6 +3,8 @@ import type { NodeView } from "./views.ts";
|
|
|
3
3
|
/**
|
|
4
4
|
* Writes each node's own human `.log`: a one-time `ActionRef` header, then that
|
|
5
5
|
* node's entries (by `nodeId`) prefixed `[seq iso-ts path]`. Rows never repeat
|
|
6
|
-
* name/tags — the header carries them.
|
|
6
|
+
* name/tags — the header carries them. The optional `footer` (the run's end-of-run
|
|
7
|
+
* recap — assert tally + sibling log paths) is appended to the **root** log only,
|
|
8
|
+
* so `main.log` closes with the same snippet shown live on the terminal.
|
|
7
9
|
*/
|
|
8
|
-
export declare function writeNodeLogs(nodes: readonly NodeView[], entries: readonly LogEntry[]): Promise<void>;
|
|
10
|
+
export declare function writeNodeLogs(nodes: readonly NodeView[], entries: readonly LogEntry[], footer?: readonly string[]): Promise<void>;
|
|
@@ -5,19 +5,24 @@ import { entryLine, groupByNode } from "./entryLine.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Writes each node's own human `.log`: a one-time `ActionRef` header, then that
|
|
7
7
|
* node's entries (by `nodeId`) prefixed `[seq iso-ts path]`. Rows never repeat
|
|
8
|
-
* name/tags — the header carries them.
|
|
8
|
+
* name/tags — the header carries them. The optional `footer` (the run's end-of-run
|
|
9
|
+
* recap — assert tally + sibling log paths) is appended to the **root** log only,
|
|
10
|
+
* so `main.log` closes with the same snippet shown live on the terminal.
|
|
9
11
|
*/
|
|
10
|
-
export async function writeNodeLogs(nodes, entries) {
|
|
12
|
+
export async function writeNodeLogs(nodes, entries, footer = []) {
|
|
11
13
|
const byNode = groupByNode(entries, () => true);
|
|
12
14
|
const refByNode = new Map(nodes.map((n) => [n.id, n]));
|
|
13
15
|
const refOf = (id) => refByNode.get(id);
|
|
14
16
|
await Promise.all(nodes.map(async (node) => {
|
|
15
17
|
const lines = (byNode.get(node.id) ?? []).map((e) => entryLine(e, refOf));
|
|
16
|
-
const
|
|
18
|
+
const foot = node.parentId === null && footer.length ? ["", summaryRule, ...footer] : [];
|
|
19
|
+
const body = [...header(node), "", ...lines, ...foot, ""].join("\n");
|
|
17
20
|
await mkdir(path.dirname(node.logFile), { recursive: true });
|
|
18
21
|
await writeFile(node.logFile, body, "utf8");
|
|
19
22
|
}));
|
|
20
23
|
}
|
|
24
|
+
/** The divider that opens the root log's end-of-run recap (mirrors the header's `── legend ──`). */
|
|
25
|
+
const summaryRule = "── summary ──────────────────────────────────────────────────────────";
|
|
21
26
|
/** The per-file metadata header: ref, defining script, args, start/finish times, status. */
|
|
22
27
|
const header = (node) => [
|
|
23
28
|
`# ${renderRef(node)}`,
|
|
@@ -27,7 +32,18 @@ const header = (node) => [
|
|
|
27
32
|
`# started: ${stamp(node.startedAt)}`,
|
|
28
33
|
`# finished: ${stamp(node.endedAt)} (${node.duration}ms)`,
|
|
29
34
|
`# status: ${node.status}`,
|
|
35
|
+
...(node.parentId === null ? legend : []), // the run's root log (main.log) only
|
|
30
36
|
].filter((line) => line !== null);
|
|
37
|
+
/** A compact reading guide, printed once on the root log so an agent can follow the run. */
|
|
38
|
+
const legend = [
|
|
39
|
+
"#",
|
|
40
|
+
"# ── legend ──────────────────────────────────────────────────────────",
|
|
41
|
+
"# [n] global sequence — the merge/order key across every .log",
|
|
42
|
+
"# indented rows a child action's milestones, bubbled up here",
|
|
43
|
+
"# name [tags] the action and its tags · defined … where its code lives",
|
|
44
|
+
"# launch · → ….log a child started — open that file for its full output",
|
|
45
|
+
"# ⚡ event ✓ done ok ⊘ cancelled ✗ failed ⊨/⊭ assert pass/fail 📸 snapshot ▪ output",
|
|
46
|
+
];
|
|
31
47
|
const stamp = (ms) => (ms ? new Date(ms).toISOString() : "-");
|
|
32
48
|
const renderArgs = (args) => {
|
|
33
49
|
try {
|
|
@@ -6,7 +6,11 @@ import { label } from "../../foundation/ActionRef.js";
|
|
|
6
6
|
* the child by its {@link label} (name + tags) via `refOf`; everything else keeps its
|
|
7
7
|
* `source` (proc name, `console`, …).
|
|
8
8
|
*/
|
|
9
|
-
export const entryLine = (e, refOf) =>
|
|
9
|
+
export const entryLine = (e, refOf) => {
|
|
10
|
+
// Bubbled child rows are nested a level in — the whole row, `[seq]` included.
|
|
11
|
+
const indent = e.level === "child" ? " " : "";
|
|
12
|
+
return `${indent}[${e.sequence}] ${bodyOf(e, refOf)}`;
|
|
13
|
+
};
|
|
10
14
|
const bodyOf = (e, refOf) => {
|
|
11
15
|
switch (e.level) {
|
|
12
16
|
case "event":
|
|
@@ -24,9 +28,9 @@ const bodyOf = (e, refOf) => {
|
|
|
24
28
|
// output. (Cancellation no longer logs at this level — it's a clean stop.)
|
|
25
29
|
return e.source === "error" ? `✗ ${e.message}` : `▪ ${e.message}`;
|
|
26
30
|
case "child": {
|
|
27
|
-
// A bubbled child
|
|
28
|
-
//
|
|
29
|
-
//
|
|
31
|
+
// A bubbled child line (indented at the row level by `entryLine`). The launch pointer
|
|
32
|
+
// (`source === "launch"`) keeps its `→ id · logfile` message; a milestone names the
|
|
33
|
+
// child by its label (name + tags) via `refOf`.
|
|
30
34
|
if (e.source === "launch")
|
|
31
35
|
return `${e.source} · ${e.message}`;
|
|
32
36
|
const ref = refOf?.(e.source);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "A tiny runtime for developer workflows: typed actions that pass runtime capabilities and emit typed events, under one run and one shared log. The operational/runtime layer to tskb's knowledge layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|