@byok-sdk/client 0.1.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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/adapters/claude/claude-adapter.d.ts +155 -0
  4. package/dist/adapters/claude/events.d.ts +91 -0
  5. package/dist/adapters/claude/permission-mapping.d.ts +142 -0
  6. package/dist/adapters/claude/process-client.d.ts +103 -0
  7. package/dist/adapters/claude/resolve-approval-mcp-bin.d.ts +45 -0
  8. package/dist/adapters/claude/resolve-bin.d.ts +27 -0
  9. package/dist/adapters/codex/codex-adapter.d.ts +97 -0
  10. package/dist/adapters/codex/events.d.ts +41 -0
  11. package/dist/adapters/codex/permission-mapping.d.ts +107 -0
  12. package/dist/adapters/codex/process-runner.d.ts +86 -0
  13. package/dist/adapters/codex/resolve-bin.d.ts +25 -0
  14. package/dist/adapters/pi/events.d.ts +58 -0
  15. package/dist/adapters/pi/permission-mapping.d.ts +46 -0
  16. package/dist/adapters/pi/pi-adapter.d.ts +29 -0
  17. package/dist/adapters/pi/resolve-bin.d.ts +44 -0
  18. package/dist/adapters/pi/rpc-client.d.ts +101 -0
  19. package/dist/bin/approval-mcp-server.d.ts +69 -0
  20. package/dist/bin/audit-log.d.ts +212 -0
  21. package/dist/bin/byok-agent.d.ts +2 -0
  22. package/dist/bin/byok-agent.js +12347 -0
  23. package/dist/bin/byok-agent.js.map +1 -0
  24. package/dist/bin/byok-approval-mcp.d.ts +2 -0
  25. package/dist/bin/byok-approval-mcp.js +498 -0
  26. package/dist/bin/byok-approval-mcp.js.map +1 -0
  27. package/dist/bin/commands/approvals.d.ts +31 -0
  28. package/dist/bin/commands/approve-reject.d.ts +29 -0
  29. package/dist/bin/commands/doctor.d.ts +15 -0
  30. package/dist/bin/commands/pair.d.ts +7 -0
  31. package/dist/bin/commands/runtimes.d.ts +7 -0
  32. package/dist/bin/commands/service.d.ts +54 -0
  33. package/dist/bin/commands/start.d.ts +21 -0
  34. package/dist/bin/commands/status.d.ts +25 -0
  35. package/dist/bin/commands/support-bundle.d.ts +7 -0
  36. package/dist/bin/commands/tasks.d.ts +27 -0
  37. package/dist/bin/commands/unpair.d.ts +124 -0
  38. package/dist/bin/commands/workspaces.d.ts +16 -0
  39. package/dist/bin/config.d.ts +41 -0
  40. package/dist/bin/control-client.d.ts +50 -0
  41. package/dist/bin/format.d.ts +67 -0
  42. package/dist/bin/runtime-probe.d.ts +34 -0
  43. package/dist/bin/tasks-view.d.ts +49 -0
  44. package/dist/daemon/approvals.d.ts +87 -0
  45. package/dist/daemon/auth-manager.d.ts +52 -0
  46. package/dist/daemon/blob-client.d.ts +22 -0
  47. package/dist/daemon/connection-manager.d.ts +502 -0
  48. package/dist/daemon/control-protocol.d.ts +299 -0
  49. package/dist/daemon/control-server.d.ts +61 -0
  50. package/dist/daemon/create-daemon.d.ts +364 -0
  51. package/dist/daemon/cursor-store.d.ts +30 -0
  52. package/dist/daemon/daemon-owner.d.ts +24 -0
  53. package/dist/daemon/deterministic-jitter.d.ts +20 -0
  54. package/dist/daemon/device-keys.d.ts +45 -0
  55. package/dist/daemon/device-proof-signer.d.ts +41 -0
  56. package/dist/daemon/environment.d.ts +96 -0
  57. package/dist/daemon/git-workspace-store.d.ts +49 -0
  58. package/dist/daemon/git-workspace.d.ts +80 -0
  59. package/dist/daemon/http-client.d.ts +8 -0
  60. package/dist/daemon/journal/journal.d.ts +342 -0
  61. package/dist/daemon/journal/sqlite-journal.d.ts +173 -0
  62. package/dist/daemon/journal/sqlite-support.d.ts +96 -0
  63. package/dist/daemon/journal/storage-policy.d.ts +368 -0
  64. package/dist/daemon/long-poll-transport.d.ts +142 -0
  65. package/dist/daemon/observer.d.ts +345 -0
  66. package/dist/daemon/operational-health.d.ts +87 -0
  67. package/dist/daemon/policy.d.ts +16 -0
  68. package/dist/daemon/progress-batcher.d.ts +31 -0
  69. package/dist/daemon/runtime-capabilities.d.ts +36 -0
  70. package/dist/daemon/session-workspace-store.d.ts +75 -0
  71. package/dist/daemon/store.d.ts +54 -0
  72. package/dist/daemon/task-runner.d.ts +862 -0
  73. package/dist/daemon/truth-memory-client.d.ts +94 -0
  74. package/dist/daemon/url.d.ts +60 -0
  75. package/dist/daemon/ws-transport.d.ts +110 -0
  76. package/dist/diagnostics/diagnostics.d.ts +114 -0
  77. package/dist/diagnostics/support-bundle.d.ts +81 -0
  78. package/dist/index.d.ts +42 -0
  79. package/dist/index.js +10056 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/lifecycle/create-service-lifecycle.d.ts +59 -0
  82. package/dist/lifecycle/exec-runner.d.ts +141 -0
  83. package/dist/lifecycle/launchd.d.ts +55 -0
  84. package/dist/lifecycle/service-types.d.ts +177 -0
  85. package/dist/lifecycle/systemd.d.ts +46 -0
  86. package/dist/lifecycle/winsw.d.ts +64 -0
  87. package/dist/types.d.ts +178 -0
  88. package/dist/util/async-queue.d.ts +45 -0
  89. package/dist/util/atomic-write.d.ts +86 -0
  90. package/dist/util/secure-dir.d.ts +124 -0
  91. package/package.json +66 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * M4 Phase 3: the testable core of `byok-approval-mcp` (`byok-approval-mcp.ts`
3
+ * is the thin stdio-wiring entry point — mirrors this repo's existing
4
+ * `bin/commands/*.ts` split: real logic lives in a plain module, the bin
5
+ * script itself is glue no test ever imports directly).
6
+ *
7
+ * `byok-approval-mcp` is the MCP stdio server `claude`'s own
8
+ * `--permission-prompt-tool` spawns AS ITS OWN CHILD PROCESS when the claude
9
+ * adapter runs a task under `PermissionPolicy.mode: 'confirm'` (see
10
+ * `../adapters/claude/permission-mapping.ts`'s `confirm`-mode doc comment).
11
+ * It implements just enough of the MCP stdio transport (JSON-RPC 2.0,
12
+ * newline-delimited, per the spec) to expose ONE tool — empirically
13
+ * confirmed end-to-end against the real installed claude 2.1.216 binary
14
+ * (M4 Phase 3 STEP 0): `initialize` -> `notifications/initialized` ->
15
+ * `tools/list` -> `tools/call`, with the tool's arguments shaped exactly
16
+ * `{tool_name, input, tool_use_id}` (claude's own real wire shape,
17
+ * live-captured) and its expected response shaped
18
+ * `{content:[{type:'text', text: JSON.stringify({behavior:'allow',
19
+ * updatedInput} | {behavior:'deny', message})}]}` — the SAME shape the
20
+ * Claude Agent SDK's in-process `canUseTool` callback returns (see
21
+ * platform.claude.com/docs/en/agent-sdk/user-input), just crossing a
22
+ * process boundary via MCP instead of an in-process function call.
23
+ */
24
+ export declare const APPROVAL_TOOL_NAME = "approval_prompt";
25
+ /** Bound on how much of a tool call's `input` gets folded into the wire `task.await_approval.summary` — mirrors `events.ts`'s `RESULT_DIAGNOSTIC_MAX_CHARS`/`truncateResultDiagnostic` convention: a human-facing summary, not a full audit record. */
26
+ export declare const APPROVAL_SUMMARY_MAX_CHARS = 500;
27
+ /** `${toolName}: ${input}`, bounded — the human-readable description carried into `task.await_approval.summary` and (via the daemon/CLI's own rendering) whatever a real approver actually reads before deciding. */
28
+ export declare function summarizeToolCall(toolName: string, input: unknown): string;
29
+ export interface ApprovalOutcome {
30
+ approved: boolean;
31
+ reason?: string;
32
+ }
33
+ /** What `byok-approval-mcp.ts` (the real entry point) injects — the one real dependency this module has on the outside world. */
34
+ export interface ApprovalMcpDeps {
35
+ /** Requests a decision from the daemon this task is running on. Any rejection/throw here is treated as fail-closed (deny) by `handleMcpRequest` — never surfaced to claude as a raw protocol error. */
36
+ requestApproval(taskId: string, summary: string): Promise<ApprovalOutcome>;
37
+ }
38
+ interface JsonRpcRequestLike {
39
+ jsonrpc?: unknown;
40
+ id?: unknown;
41
+ method?: unknown;
42
+ params?: unknown;
43
+ }
44
+ /**
45
+ * Handles exactly one already-parsed JSON-RPC request object and returns the
46
+ * exact response object to write back (`undefined` for a notification that
47
+ * expects no reply, e.g. `notifications/initialized`) — no stdio/process
48
+ * concerns at all, so tests call this directly with a stub {@link
49
+ * ApprovalMcpDeps} instead of spawning a real process or a real control
50
+ * socket. `serveApprovalMcpOverStdio` below is the only caller in production.
51
+ */
52
+ export declare function handleMcpRequest(req: JsonRpcRequestLike, deps: ApprovalMcpDeps, taskId: string): Promise<Record<string, unknown> | undefined>;
53
+ export interface ServeApprovalMcpOptions {
54
+ taskId: string;
55
+ deps: ApprovalMcpDeps;
56
+ /** Defaults to `process.stdin`/`process.stdout` — overridable so tests can drive this over in-memory streams. */
57
+ input?: NodeJS.ReadableStream;
58
+ output?: NodeJS.WritableStream;
59
+ }
60
+ /**
61
+ * Wires {@link handleMcpRequest} to real NDJSON stdio (the MCP stdio
62
+ * transport: one JSON-RPC message per line, both directions) — the only
63
+ * genuinely process-shaped piece of this module. Each line is handled
64
+ * independently and asynchronously (never serialized against the others),
65
+ * since claude's own parallel-tool-use can legitimately fire more than one
66
+ * concurrent `tools/call` over the same connection.
67
+ */
68
+ export declare function serveApprovalMcpOverStdio(opts: ServeApprovalMcpOptions): void;
69
+ export {};
@@ -0,0 +1,212 @@
1
+ import { type DaemonEvent, type DaemonEventListener } from '../index';
2
+ /**
3
+ * The audit log is the ONLY channel a separate, short-lived CLI invocation
4
+ * (`status`/`tasks`/`tasks --follow`) has into what an already-running
5
+ * `byok-agent start` process has observed — see `byok-agent.ts`'s header
6
+ * comment for the full read-model rationale. One JSON line per
7
+ * `DaemonEvent`, oldest first, append-only.
8
+ *
9
+ * Finding P1 #3 (SECURITY): a `DaemonEvent` can carry a task's raw
10
+ * instruction-derived output — `tool_use.input`/`tool_result.output` are
11
+ * `z.unknown()` in `@byok-sdk/protocol` (a shell command, a whole file's
12
+ * contents echoed back, anything), `progress`/`needs_approval`/`completed`/
13
+ * `failed`/`cancelled` all carry free-form agent/operator text, and
14
+ * `artifact.inline` is literally base64 file bytes. Persisting all of that
15
+ * VERBATIM into a durable, append-only, potentially long-retained file —
16
+ * previously created at the default `0666 & umask` mode (NOT 0600), with
17
+ * `storeDir` only chmod'd 0700 at the moment it's first created (a
18
+ * pre-existing custom `storeDir` kept whatever mode it already had) — is
19
+ * exactly the kind of thing that turns "helpful audit trail" into "durable
20
+ * secret/credential/source leak" the moment anything else on the machine can
21
+ * read it. Fixed on two independent axes:
22
+ *
23
+ * 1. **Redaction** (`redactForAudit`/`redactAgentEvent`): only event type,
24
+ * taskId, timestamps, tool/runtime NAMES, sizes/counts, and closed-enum
25
+ * state fields are ever written to disk — every free-form text/bytes
26
+ * field is replaced with its byte SIZE. `readAuditEvents`/`followAuditLog`
27
+ * reconstruct a `DaemonEvent`-shaped value from that redacted projection
28
+ * for their callers (`tasks-view.ts`/`format.ts` stay unchanged and none
29
+ * the wiser) by substituting an unambiguous `[redacted: N bytes]`
30
+ * placeholder wherever real content used to be — so a replayed
31
+ * `tasks`/`status`/`tasks --follow` view degrades to showing sizes
32
+ * instead of content, which is the correct, honest trade-off for a
33
+ * broadly-readable-by-default durable file. The LIVE human-facing stdout
34
+ * stream (`bin/commands/start.ts`, fed directly from the in-memory
35
+ * `DaemonEvent` before it ever reaches this module) is unaffected and
36
+ * keeps full fidelity — this redaction applies ONLY to what's written to
37
+ * disk.
38
+ * 2. **Permissions**: the file is tightened to 0600 BEFORE any new content
39
+ * is written, on every single append (mirrors `util/atomic-write.ts`'s
40
+ * own defensive re-chmod, for the identical reason: `open()`'s own
41
+ * `mode` argument only governs permissions at CREATION time, so a
42
+ * pre-existing file — predating this fix, or loosened by something else
43
+ * — keeps whatever mode it already had until explicitly chmod'd).
44
+ * `storeDir` gets the same defensive re-`chmod` to 0700 on every append.
45
+ *
46
+ * Finding P1 #3b (STILL-OPEN, now fixed): the chmod used to run AFTER
47
+ * `appendFile` rather than before — for a pre-existing permissive file
48
+ * that meant the newly-appended line briefly landed in a file that was
49
+ * still world-readable, and if `appendFile` itself failed, the chmod
50
+ * (being sequenced after it) never ran at all, leaving the file exactly
51
+ * as permissive as it started. Fixed by reordering so the chmod always
52
+ * happens FIRST: there is no window where a new append lands in a
53
+ * still-permissive file, and the tightening takes effect even when the
54
+ * append that follows it fails. This closes the gap going forward; it
55
+ * does NOT retroactively scrub plaintext a pre-fix build of this code
56
+ * may have already written into an inherited permissive file — only
57
+ * that the file stops being world-readable from the first append
58
+ * onward.
59
+ *
60
+ * Finding P2/#11 (audit half): also bounded/rotated now — `appendAuditEvent`
61
+ * checks the file's size (cheap `fs.stat`) on every append and, once it
62
+ * exceeds `MAX_AUDIT_LOG_BYTES`, atomically rewrites it down to the most
63
+ * recent `AUDIT_LOG_TRIM_TARGET_LINES` lines (reusing `atomicWriteFile`, so
64
+ * a concurrent `tasks`/`tasks --follow` read never observes a torn file
65
+ * mid-rotation) — so a long-lived daemon's audit.jsonl no longer grows
66
+ * forever. `followAuditLog` no longer re-reads the entire file every poll
67
+ * either; see its own doc comment.
68
+ *
69
+ * Finding P2 (new, round 1, now fixed): that trim used to be purely
70
+ * positional (the most recent N lines, full stop) — which could evict
71
+ * EVERY event a still-running task ever had if it went quiet for long
72
+ * enough, permanently erasing it from `tasks`/`status` even though it was
73
+ * never actually done. `compactPreservingLiveTasks` now preserves one
74
+ * lifecycle-anchor line for any still-open (non-terminal) task that would
75
+ * otherwise be fully evicted — see its own doc comment.
76
+ *
77
+ * Finding P2 (new, round 2, now fixed): round 1's anchor preservation had no
78
+ * cap of its own — a daemon that crashes leaving many non-terminal tasks
79
+ * behind (or one that keeps creating non-terminal tasks that never reach a
80
+ * terminal kind) accumulates one anchor per distinct non-terminal taskId
81
+ * FOREVER, defeating `MAX_AUDIT_LOG_BYTES`'s own size cap (the whole point
82
+ * of rotation) and making every append past the cap an ever-larger O(n)
83
+ * read + atomic-rewrite. `compactPreservingLiveTasks` now also bounds the
84
+ * anchors themselves to `MAX_LIVE_TASK_ANCHORS`, keeping the most
85
+ * recently-touched ones and dropping the oldest overflow with a single
86
+ * logged warning — see that constant's own doc comment.
87
+ */
88
+ export declare function auditLogPath(storeDir: string): string;
89
+ /**
90
+ * Finding P2/#11 (audit half): rotate once the file exceeds this size,
91
+ * trimming to the most recent `AUDIT_LOG_TRIM_TARGET_LINES` lines. Checked
92
+ * via a cheap `fs.stat` (O(1), not a full read) on every single append; the
93
+ * expensive read-all-lines + atomic-rewrite only actually runs once the cap
94
+ * is crossed, and trimming well under the cap (redacted lines are small —
95
+ * mostly ids/sizes/counts) means it won't immediately re-trigger on the next
96
+ * append either. Deliberately simple size/line based bounding, not a
97
+ * time-based retention policy — "keep it simple" per this finding's own
98
+ * scope note.
99
+ */
100
+ export declare const MAX_AUDIT_LOG_BYTES: number;
101
+ export declare const AUDIT_LOG_TRIM_TARGET_LINES = 5000;
102
+ /**
103
+ * Finding P2 (new, round 2): the hard cap on how many non-terminal-task
104
+ * lifecycle anchors {@link compactPreservingLiveTasks} will ever preserve in
105
+ * a single rotation — without this, a daemon that crashes leaving many
106
+ * non-terminal tasks behind (or a bug that keeps creating tasks that never
107
+ * reach a terminal kind) accumulates one anchor per distinct taskId with no
108
+ * upper bound, defeating `MAX_AUDIT_LOG_BYTES`'s own size cap and turning
109
+ * every subsequent append into an ever-larger O(n) read + atomic-rewrite.
110
+ * Mirrors `daemon/observer.ts`'s `MAX_TRACKED_TASKS`/`task-runner.ts`'s
111
+ * `MAX_TRACKED_TASK_IDS` registries: bounded to a generously-sized "a
112
+ * handful of genuinely concurrent tasks never gets close to this" cap, kept
113
+ * the most RECENTLY-touched (by last-seen line index) and dropping the
114
+ * oldest overflow with a single `console.warn` per rotation (not one per
115
+ * dropped anchor — a runaway leak should be visible without spamming the
116
+ * log). A dropped task simply reverts to the pre-round-1 behavior (falls
117
+ * out of `tasks`/`status` once its own events age out of the retained
118
+ * tail) rather than the file growing without bound.
119
+ */
120
+ export declare const MAX_LIVE_TASK_ANCHORS = 500;
121
+ /**
122
+ * Appends one `DaemonEvent` as a single redacted JSON line (finding P1 #3 —
123
+ * see this file's module doc comment) at 0600, and rotates the file if it's
124
+ * grown past the size cap (finding P2/#11). Plain `fs.open('a')` +
125
+ * `appendFile` — sufficient for an append-only log (no torn-read risk the
126
+ * way a whole-file replace has; see `util/atomic-write.ts`'s own doc comment
127
+ * for why THAT helper exists for whole-file writes instead of just being
128
+ * used here too — rotation below is the one path in this file that DOES
129
+ * replace the whole file, and does use it).
130
+ */
131
+ export declare function appendAuditEvent(storeDir: string, event: DaemonEvent): Promise<void>;
132
+ /**
133
+ * Adapts {@link appendAuditEvent} into a `DaemonEventListener` for
134
+ * `daemon.subscribe()` (used by the `start` command). `DaemonEventListener`
135
+ * is synchronous — `observer.ts`'s `emit()` never awaits a listener (see its
136
+ * own doc comment on why, including the finding #6 async-safety fix) — so a
137
+ * naive fire-and-forget `appendAuditEvent(...).catch(...)` per call would
138
+ * have a real problem: the underlying `fs.open`/`appendFile` calls could
139
+ * complete OUT OF ORDER under load, silently corrupting the log's own event
140
+ * ordering. This chains each append onto the previous one (still
141
+ * async/non-blocking from the listener's point of view) so writes always
142
+ * land in emission order, and a failed write (reported via `onError`) never
143
+ * breaks the chain for subsequent events.
144
+ */
145
+ export declare function createAuditAppender(storeDir: string, onError?: (err: unknown) => void): DaemonEventListener;
146
+ /** Full historical read (oldest first) — `[]` if the log doesn't exist yet (e.g. `start` has never run). Used by `status`/`tasks` (no `--follow`). */
147
+ export declare function readAuditEvents(storeDir: string): Promise<DaemonEvent[]>;
148
+ export interface FollowOptions {
149
+ signal: AbortSignal;
150
+ /** How often to check the file for new bytes. Default 200ms — plenty responsive for a human tailing output, without busy-looping. */
151
+ pollIntervalMs?: number;
152
+ /**
153
+ * `true` (default): start at the file's CURRENT length — a `tail -f`,
154
+ * streaming only events appended from the moment `followAuditLog` was
155
+ * called. `false`: replay the full existing log first, then keep
156
+ * streaming — mainly useful for tests that want both in one pass.
157
+ */
158
+ fromEnd?: boolean;
159
+ }
160
+ /**
161
+ * Tails `filePath` (the audit log) for newly-appended `DaemonEvent` lines
162
+ * until `options.signal` aborts. No native filesystem-event watching (no
163
+ * `fs.watch` — inconsistent across platforms, especially the rename-based
164
+ * atomic writes this package uses elsewhere for OTHER files).
165
+ *
166
+ * Finding P2/#11 (audit half): the previous implementation re-read the
167
+ * ENTIRE file from byte 0 on every single poll, re-parsing every
168
+ * already-emitted line just to find the handful of new ones at the end —
169
+ * O(file size) work every `pollIntervalMs`, forever, for as long as
170
+ * `tasks --follow` stays attached. This tracks a byte OFFSET across polls
171
+ * instead: each poll opens the file and reads only the bytes appended since
172
+ * the last read (`FileHandle.read` at a specific position), advancing
173
+ * `offset` by exactly that many bytes — only genuinely new bytes are ever
174
+ * read or decoded.
175
+ *
176
+ * UTF-8 safety: the new bytes are split on the RAW BYTE `0x0a` ('\n')
177
+ * BEFORE any UTF-8 decoding happens, not on an already-decoded string —
178
+ * `0x0A` can only ever appear as an actual newline in valid UTF-8
179
+ * (continuation bytes are always `0x80`-`0xBF`), so this never risks
180
+ * decoding a torn multi-byte character that happened to straddle a chunk
181
+ * boundary. Bytes after the last `\n` in a chunk are held in `pending`
182
+ * (as a `Buffer`, still undecoded) until a future poll's bytes complete
183
+ * that line — mirrors `completeLines`'s existing "never guess at a torn
184
+ * trailing line" contract for `readAuditEvents`, just incremental.
185
+ *
186
+ * Rotation tolerance: if `appendAuditEvent`'s own size-cap rotation (or
187
+ * anything else) replaces the file out from under an in-progress follow,
188
+ * `offset` resets to 0 and `pending` is cleared — the follow session
189
+ * survives (rather than throwing or garbling) at the cost of re-emitting
190
+ * the post-rotation file's retained lines as if they were new.
191
+ *
192
+ * Finding P2 (new): that replacement is detected by file IDENTITY
193
+ * (`dev:ino`), not by comparing sizes. The previous implementation only
194
+ * reset `offset` when the new file was SMALLER than it (`st.size <
195
+ * offset`) — sufficient for the common case (a big pre-rotation file
196
+ * trimmed down to a small one), but `rotateIfNeeded`'s replace is an atomic
197
+ * temp-file + `fs.rename` (see `atomicWriteFile`), which swaps the
198
+ * underlying INODE regardless of whether the new file happens to be
199
+ * smaller OR LARGER than the reader's current offset. A `--follow` session
200
+ * that attached while the log was still tiny (a small `offset`) can have a
201
+ * rotation land a compacted-but-still-much-bigger-than-that-tiny-offset
202
+ * file in its place — the old size-only check missed exactly that case
203
+ * (`st.size < offset` was false), so it kept reading from the stale
204
+ * `offset` INTO THE NEW FILE's bytes: skipping that file's own prefix
205
+ * entirely and very likely starting mid-JSON-line. Tracking `dev:ino`
206
+ * across polls catches ANY replacement — smaller, larger, anything — and
207
+ * resets `offset` to 0 so the new file is always re-read from its own
208
+ * start. The size-based check is kept as a secondary fallback (a
209
+ * same-identity in-place truncate, or a platform where `ino` isn't a
210
+ * reliable identity signal).
211
+ */
212
+ export declare function followAuditLog(filePath: string, onEvent: (event: DaemonEvent) => void, options: FollowOptions): Promise<void>;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};