@nebutra/event-log 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/event-log",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Append-only event log with content-addressed snapshots",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -11,22 +11,31 @@
11
11
  "surface": "persistence",
12
12
  "summary": "Immutable event log, rollback dry-runs, and branches"
13
13
  },
14
- "main": "./src/index.ts",
15
- "types": "./src/index.ts",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
16
  "exports": {
17
- ".": "./src/index.ts"
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "test": "vitest run",
26
+ "typecheck": "tsc --noEmit"
18
27
  },
19
28
  "dependencies": {
20
- "@noble/hashes": "2.0.1",
21
- "@nebutra/capability-kit": "0.2.2",
22
- "@nebutra/content-store": "0.1.2",
23
- "@nebutra/errors": "0.1.2"
29
+ "@nebutra/capability-kit": "workspace:*",
30
+ "@nebutra/content-store": "workspace:*",
31
+ "@nebutra/errors": "workspace:*",
32
+ "@noble/hashes": "2.0.1"
24
33
  },
25
34
  "devDependencies": {
26
- "@types/node": "^25.9.1",
27
- "tsup": "^8.5.1",
28
- "typescript": "^5.9.3",
29
- "vitest": "^4.1.4"
35
+ "@types/node": "catalog:",
36
+ "tsup": "catalog:",
37
+ "typescript": "catalog:",
38
+ "vitest": "catalog:"
30
39
  },
31
40
  "homepage": "https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/ai/event-log#readme",
32
41
  "repository": {
@@ -40,9 +49,10 @@
40
49
  "publishConfig": {
41
50
  "access": "public"
42
51
  },
43
- "scripts": {
44
- "build": "tsup",
45
- "test": "vitest run",
46
- "typecheck": "tsc --noEmit"
47
- }
48
- }
52
+ "files": [
53
+ "dist",
54
+ "README.md",
55
+ "LICENSE",
56
+ "CHANGELOG.md"
57
+ ]
58
+ }
@@ -1,8 +0,0 @@
1
- {"at":"2026-05-19T05:44:37.050Z","traceId":"thread_1","kind":"tool_call","affected":["hello.md"],"snapshot":{"hello.md":"hi"},"parent":null,"id":"98448a866e126cc20a0c7fba8c687a14ffbe548b223f45d54877e100bed92087","tenantId":"tenant_a","summary":"wrote hello","objectHashes":["85052e9aab1b67b6622d94a08441b09fd5b7aca61ee360416d70de5da67d86ca"]}
2
- {"at":"2026-05-19T05:44:37.070Z","traceId":"t","kind":"tool_call","affected":[],"parent":null,"id":"33e13801c909cea3b507648b5153d6b63a1c32ff2f80c78c0e68bf61ec65769b","tenantId":"local","summary":"tool_call touched 0 path(s)","objectHashes":[]}
3
- {"at":"2026-05-19T05:47:30.443Z","traceId":"thread_1","kind":"tool_call","affected":["hello.md"],"snapshot":{"hello.md":"hi"},"parent":null,"id":"d3a5af5efbbfdcd34e07ea0b5e781fc9cb49d5ce07e8df679ac99bd86052f333","tenantId":"tenant_a","summary":"wrote hello","objectHashes":["85052e9aab1b67b6622d94a08441b09fd5b7aca61ee360416d70de5da67d86ca"]}
4
- {"at":"2026-05-19T05:47:30.446Z","traceId":"t","kind":"tool_call","affected":[],"parent":null,"id":"69b235a6db11d0b524d1b3b2dddf4b7cad9349fa87010681c7180bb968bddfc4","tenantId":"local","summary":"tool_call touched 0 path(s)","objectHashes":[]}
5
- {"at":"2026-05-30T21:06:21.198Z","traceId":"thread_1","kind":"tool_call","affected":["hello.md"],"snapshot":{"hello.md":"hi"},"parent":null,"id":"03884f8cfd0e65d3f8a2f323bb670d4eca830f52c5da33944ed3b6c0cc190a64","tenantId":"tenant_a","summary":"wrote hello","objectHashes":["85052e9aab1b67b6622d94a08441b09fd5b7aca61ee360416d70de5da67d86ca"]}
6
- {"at":"2026-05-30T21:06:21.209Z","traceId":"t","kind":"tool_call","affected":[],"parent":null,"id":"d45c25d4d45205fe8952c4606c2baceae705d74a5db379df35be85e7def0576c","tenantId":"local","summary":"tool_call touched 0 path(s)","objectHashes":[]}
7
- {"at":"2026-07-01T01:38:43.284Z","traceId":"thread_1","kind":"tool_call","affected":["hello.md"],"snapshot":{"hello.md":"hi"},"parent":null,"id":"9e6e195ea1e6edd3c71821ff3fdd694df885adcd359d28a0b74ec83bc6b6db03","tenantId":"tenant_a","summary":"wrote hello","objectHashes":["85052e9aab1b67b6622d94a08441b09fd5b7aca61ee360416d70de5da67d86ca"]}
8
- {"at":"2026-07-01T01:38:43.301Z","traceId":"t","kind":"tool_call","affected":[],"parent":null,"id":"28d45da6eee7f0df21606ec4213d9fd44fa456e8a417a20968566f13bd944f1a","tenantId":"local","summary":"tool_call touched 0 path(s)","objectHashes":[]}
@@ -1,22 +0,0 @@
1
-
2
- > @nebutra/event-log@0.1.2 build /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/event-log
3
- > tsup
4
-
5
- CLI Building entry: src/cli.ts, src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Using tsup config: /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/event-log/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/chunk-L5NN4BRT.js 3.50 KB
13
- ESM dist/index.js 106.00 B
14
- ESM dist/cli.js 890.00 B
15
- ESM dist/cli.js.map 1.54 KB
16
- ESM dist/index.js.map 71.00 B
17
- ESM dist/chunk-L5NN4BRT.js.map 7.63 KB
18
- ESM ⚡️ Build success in 46ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 2447ms
21
- DTS dist/cli.d.ts 13.00 B
22
- DTS dist/index.d.ts 1.44 KB
@@ -1,14 +0,0 @@
1
-
2
- > @nebutra/event-log@0.1.1 test /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/event-log
3
- > vitest run
4
-
5
-
6
- RUN v4.1.4 /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/event-log
7
-
8
- ✓ src/index.test.ts (2 tests) 46ms
9
-
10
- Test Files 1 passed (1)
11
- Tests 2 passed (2)
12
- Start at 09:38:42
13
- Duration 969ms (transform 106ms, setup 0ms, import 143ms, tests 46ms, environment 0ms)
14
-
@@ -1,4 +0,0 @@
1
-
2
- > @nebutra/event-log@0.1.2 typecheck /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/event-log
3
- > tsc --noEmit
4
-
@@ -1,17 +0,0 @@
1
- import { mkdtemp } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { EventLog } from "../src/index";
5
-
6
- const root = await mkdtemp(join(tmpdir(), "event-log-"));
7
- const log = await EventLog.open(root, { tenantId: "demo" });
8
- const id = await log.commit({
9
- traceId: "trace_1",
10
- kind: "content_write",
11
- affected: ["hello.md"],
12
- parent: null,
13
- snapshot: { "hello.md": "hi" },
14
- });
15
-
16
- const branch = await log.branchFrom(id, "purple-version");
17
- process.stdout.write(`${JSON.stringify(branch, null, 2)}\n`);
@@ -1,17 +0,0 @@
1
- import { mkdtemp } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { EventLog } from "../src/index";
5
-
6
- const root = await mkdtemp(join(tmpdir(), "event-log-"));
7
- const log = await EventLog.open(root, { tenantId: "demo" });
8
- const id = await log.commit({
9
- traceId: "trace_1",
10
- kind: "tool_call",
11
- affected: ["hello.md"],
12
- parent: null,
13
- snapshot: { "hello.md": "hi" },
14
- });
15
-
16
- const rollback = await log.rollbackTo(id);
17
- process.stdout.write(`${JSON.stringify(rollback, null, 2)}\n`);
@@ -1,12 +0,0 @@
1
- import { mkdtemp } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { EventLog } from "../src/index";
5
-
6
- const root = await mkdtemp(join(tmpdir(), "event-log-"));
7
- const log = await EventLog.open(root, { tenantId: "demo" });
8
-
9
- await log.commit({ traceId: "trace_1", kind: "llm_call", affected: [], parent: null });
10
- await log.commit({ traceId: "trace_1", kind: "sandbox_exec", affected: [], parent: null });
11
-
12
- process.stdout.write(`${JSON.stringify(await log.timeline(), null, 2)}\n`);
package/src/cli.ts DELETED
@@ -1,29 +0,0 @@
1
- import { EventLog } from "./index";
2
-
3
- const command = process.argv[2] ?? "doctor";
4
- const log = await EventLog.open(process.env.EVENT_LOG_ROOT ?? ".nebutra/.chronos");
5
-
6
- if (command === "doctor") {
7
- const timeline = await log.timeline();
8
- process.stdout.write(
9
- `${JSON.stringify(
10
- {
11
- capability: "event-log",
12
- ok: timeline.length > 0,
13
- events: timeline.length,
14
- ...(timeline.length === 0 && {
15
- suggestion: "Run the Layer 0 demo to create the first event.",
16
- }),
17
- },
18
- null,
19
- 2,
20
- )}\n`,
21
- );
22
- } else if (command === "timeline" || command === "debug") {
23
- process.stdout.write(
24
- `${JSON.stringify({ capability: "event-log", events: await log.timeline() }, null, 2)}\n`,
25
- );
26
- } else {
27
- process.stderr.write(`Unknown event-log command: ${command}\n`);
28
- process.exitCode = 1;
29
- }
package/src/index.test.ts DELETED
@@ -1,40 +0,0 @@
1
- import { mkdtemp, rm } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterEach, describe, expect, it } from "vitest";
5
- import { EventLog } from "./index";
6
-
7
- let root: string | undefined;
8
-
9
- afterEach(async () => {
10
- if (root) await rm(root, { recursive: true, force: true });
11
- root = undefined;
12
- });
13
-
14
- describe("EventLog", () => {
15
- it("commits immutable events and returns a dry-run rollback plan", async () => {
16
- root = await mkdtemp(join(tmpdir(), "event-log-"));
17
- const log = await EventLog.open(root, {
18
- tenantId: "tenant_a",
19
- summarize: async () => "wrote hello",
20
- });
21
- const id = await log.commit({
22
- traceId: "thread_1",
23
- kind: "tool_call",
24
- affected: ["hello.md"],
25
- snapshot: { "hello.md": "hi" },
26
- parent: null,
27
- });
28
-
29
- expect((await log.timeline()).at(0)).toMatchObject({ id, summary: "wrote hello" });
30
- await expect(log.rollbackTo(id)).resolves.toMatchObject({ dryRun: true, target: id });
31
- });
32
-
33
- it("branches from an existing event", async () => {
34
- root = await mkdtemp(join(tmpdir(), "event-log-"));
35
- const log = await EventLog.open(root);
36
- const id = await log.commit({ traceId: "t", kind: "tool_call", affected: [], parent: null });
37
-
38
- await expect(log.branchFrom(id, "purple")).resolves.toMatchObject({ name: "purple", from: id });
39
- });
40
- });
package/src/index.ts DELETED
@@ -1,167 +0,0 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { dirname, join } from "node:path";
3
- import { appendCapabilityDebug } from "@nebutra/capability-kit/debug";
4
- import { CapabilityError } from "@nebutra/errors";
5
- import { blake3 } from "@noble/hashes/blake3.js";
6
- import { bytesToHex } from "@noble/hashes/utils.js";
7
-
8
- export type EventKind =
9
- | "llm_call"
10
- | "tool_call"
11
- | "sub_agent_call"
12
- | "content_write"
13
- | "sandbox_exec";
14
-
15
- export interface EventInput {
16
- readonly traceId: string;
17
- readonly kind: EventKind;
18
- readonly affected: readonly string[];
19
- readonly parent: string | null;
20
- readonly snapshot?: Record<string, string>;
21
- }
22
-
23
- export interface EventRecord extends EventInput {
24
- readonly id: string;
25
- readonly tenantId: string;
26
- readonly at: string;
27
- readonly summary: string;
28
- readonly objectHashes: readonly string[];
29
- }
30
-
31
- export interface BranchRecord {
32
- readonly name: string;
33
- readonly from: string;
34
- readonly at: string;
35
- }
36
-
37
- export interface RollbackPlan {
38
- readonly dryRun: true;
39
- readonly target: string;
40
- readonly affected: readonly string[];
41
- readonly suggestion: string;
42
- }
43
-
44
- export interface EventLogOptions {
45
- readonly tenantId?: string;
46
- readonly summarize?: (event: EventInput) => Promise<string>;
47
- }
48
-
49
- function hashContent(content: string): string {
50
- return bytesToHex(blake3(new TextEncoder().encode(content)));
51
- }
52
-
53
- async function appendJsonl(path: string, value: unknown): Promise<void> {
54
- await mkdir(dirname(path), { recursive: true });
55
- await writeFile(path, `${JSON.stringify(value)}\n`, { flag: "a" });
56
- }
57
-
58
- async function readJsonl<T>(path: string): Promise<T[]> {
59
- try {
60
- const raw = await readFile(path, "utf8");
61
- return raw
62
- .trim()
63
- .split("\n")
64
- .filter(Boolean)
65
- .map((line) => JSON.parse(line) as T);
66
- } catch {
67
- return [];
68
- }
69
- }
70
-
71
- export class EventLog {
72
- readonly #root: string;
73
- readonly #tenantId: string;
74
- readonly #summarize: (event: EventInput) => Promise<string>;
75
-
76
- private constructor(
77
- root: string,
78
- tenantId: string,
79
- summarize: (event: EventInput) => Promise<string>,
80
- ) {
81
- this.#root = root;
82
- this.#tenantId = tenantId;
83
- this.#summarize = summarize;
84
- }
85
-
86
- static async open(root: string, options: EventLogOptions = {}): Promise<EventLog> {
87
- const log = new EventLog(
88
- root,
89
- options.tenantId ?? "local",
90
- options.summarize ??
91
- (async (event) => `${event.kind} touched ${event.affected.length} path(s)`),
92
- );
93
- await mkdir(log.objectsRoot(), { recursive: true });
94
- return log;
95
- }
96
-
97
- objectsRoot(): string {
98
- return join(this.#root, "objects", this.#tenantId);
99
- }
100
-
101
- eventsPath(): string {
102
- return join(this.#root, "events", `${this.#tenantId}.jsonl`);
103
- }
104
-
105
- branchesPath(): string {
106
- return join(this.#root, "branches", `${this.#tenantId}.jsonl`);
107
- }
108
-
109
- async commit(event: EventInput): Promise<string> {
110
- const objectHashes: string[] = [];
111
- for (const [path, content] of Object.entries(event.snapshot ?? {})) {
112
- const hash = hashContent(content);
113
- objectHashes.push(hash);
114
- await writeFile(join(this.objectsRoot(), hash), content, "utf8");
115
- await appendJsonl(join(this.objectsRoot(), "index.jsonl"), { hash, path });
116
- }
117
- const id = hashContent(
118
- JSON.stringify({ event, objectHashes, tenantId: this.#tenantId, at: Date.now() }),
119
- );
120
- const record: EventRecord = {
121
- ...event,
122
- id,
123
- tenantId: this.#tenantId,
124
- at: new Date().toISOString(),
125
- summary: await this.#summarize(event),
126
- objectHashes,
127
- };
128
- await appendJsonl(this.eventsPath(), record);
129
- await appendCapabilityDebug("event-log", { ...record });
130
- return id;
131
- }
132
-
133
- async timeline(): Promise<readonly EventRecord[]> {
134
- return readJsonl<EventRecord>(this.eventsPath());
135
- }
136
-
137
- async rollbackTo(id: string): Promise<RollbackPlan> {
138
- const target = (await this.timeline()).find((event) => event.id === id);
139
- if (!target) {
140
- throw new CapabilityError("event-log", "Rollback target not found", {
141
- suggestion: "Run `pnpm chronos:timeline` and choose an event id from the current tenant.",
142
- metadata: { id },
143
- statusCode: 404,
144
- });
145
- }
146
- return {
147
- dryRun: true,
148
- target: id,
149
- affected: target.affected,
150
- suggestion: "Review this plan, then call an explicit apply API in a higher layer.",
151
- };
152
- }
153
-
154
- async branchFrom(id: string, name: string): Promise<BranchRecord> {
155
- const exists = (await this.timeline()).some((event) => event.id === id);
156
- if (!exists) {
157
- throw new CapabilityError("event-log", "Branch source not found", {
158
- suggestion: "Run `pnpm chronos:timeline` and branch from an existing event id.",
159
- metadata: { id },
160
- statusCode: 404,
161
- });
162
- }
163
- const branch = { name, from: id, at: new Date().toISOString() };
164
- await appendJsonl(this.branchesPath(), branch);
165
- return branch;
166
- }
167
- }
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "target": "esnext",
7
- "types": ["node"],
8
- "incremental": false
9
- },
10
- "include": ["src", "examples"],
11
- "exclude": ["node_modules", "dist"]
12
- }
package/tsup.config.ts DELETED
@@ -1,11 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts", "src/cli.ts"],
5
- format: ["esm"],
6
- dts: true,
7
- sourcemap: true,
8
- clean: true,
9
- target: "es2022",
10
- external: ["@nebutra/content-store", "@nebutra/errors", "@noble/hashes"],
11
- });