@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,299 @@
1
+ import type { TaskState } from '@byok-sdk/protocol';
2
+ import type { ApprovalDecision, PendingApproval } from './approvals';
3
+ import type { StorageCategory } from './journal/journal';
4
+ import type { StoragePressureState } from './journal/storage-policy';
5
+ import type { OperationalHealthSnapshot } from './operational-health';
6
+ /**
7
+ * M4 Phase 2: shared local-IPC contract between the daemon's control server
8
+ * (`control-server.ts`) and the CLI's control client (`bin/control-client.ts`)
9
+ * — frame shapes, endpoint path/pipe-name derivation, and the HMAC handshake
10
+ * math. Both sides import from here so the two can never independently drift
11
+ * (e.g. a mismatched HMAC label string, or a socket path computed two
12
+ * slightly different ways).
13
+ *
14
+ * Transport: NDJSON (one JSON object per line) over a Unix domain socket
15
+ * (darwin/linux) or a Windows named pipe — both addressed by the same
16
+ * path-like string via Node's `net` module, so neither `control-server.ts`
17
+ * nor `bin/control-client.ts` needs to special-case the transport itself,
18
+ * only the path/pipe-name derivation below.
19
+ */
20
+ export declare const CONTROL_PROTOCOL_VERSION = 1;
21
+ /** Handshake must complete within this long, on both sides — see each side's own timer. */
22
+ export declare const HANDSHAKE_TIMEOUT_MS = 3000;
23
+ /**
24
+ * The Unix domain socket path for a daemon rooted at `storeDir`. Prefers
25
+ * `<storeDir>/control.sock` (keeps every one of this daemon's local state
26
+ * files under one directory, which is already created+chmod'd 0700 by the
27
+ * time this matters — see `control-server.ts`'s `startControlServer`);
28
+ * falls back, whenever the natural path would risk exceeding {@link
29
+ * UNIX_SOCKET_PATH_SOFT_LIMIT}, to a short, deterministic path nested under
30
+ * a PER-DAEMON PRIVATE subdirectory of `os.tmpdir()` — derived from a hash
31
+ * of `storeDir` alone, so both the daemon and any CLI invocation pointed at
32
+ * the same `storeDir` independently compute the identical fallback path.
33
+ *
34
+ * Nested one level deep (rather than a bare `<hash>.sock` file directly in
35
+ * the shared, world-traversable `os.tmpdir()`) specifically so
36
+ * `control-server.ts`'s `bindControlEndpoint` can create+chmod that
37
+ * subdirectory 0700 BEFORE ever binding inside it — the directory's own
38
+ * mode gates traversal into it regardless of the socket file's own
39
+ * (briefly default-permissioned, until the post-bind `chmod`) mode, closing
40
+ * what would otherwise be a real window for another user on the same
41
+ * machine to reach a socket living directly in a shared tmpdir.
42
+ */
43
+ export declare function controlSocketPath(storeDir: string): string;
44
+ /**
45
+ * The Windows named pipe name for a daemon identified by `productId` +
46
+ * (`path.resolve`-normalized) `storeDir`. Named pipes have no filesystem
47
+ * path (no stale-file cleanup concern the way Unix sockets have — see
48
+ * `control-server.ts`), but DO share one flat namespace across the whole
49
+ * machine, so the name must be scoped to this exact daemon instance: two
50
+ * different products, or two different store directories (e.g. two agents
51
+ * of the same product — see `templates/service/README.md`'s "running
52
+ * multiple agents" section), must never collide. `storeDir` is resolved
53
+ * before hashing so a trivial path-form difference (trailing slash, etc.)
54
+ * between the two sides can't split the name.
55
+ *
56
+ * NOT keyed by the OS user: a WinSW-installed service runs the daemon under
57
+ * the Windows service account (e.g. `SYSTEM`) while the operator CLI runs
58
+ * as the interactive user, so both sides must derive the identical name
59
+ * from the same `storeDir` alone. Impostor servers are defeated by the
60
+ * mutual HMAC handshake below, not by pipe-name secrecy — keying by user
61
+ * was security theater that broke the service-account topology.
62
+ */
63
+ export declare function controlPipeName(productId: string, storeDir: string): string;
64
+ /**
65
+ * Dispatches to {@link controlPipeName} on `win32`, {@link controlSocketPath}
66
+ * everywhere else. `platform` defaults to `process.platform`; overridable
67
+ * for tests exercising a specific platform's branch on any host (mirrors
68
+ * `lifecycle/create-service-lifecycle.ts`'s identical `platform` override —
69
+ * the REAL win32 named-pipe semantics can only be proven on actual Windows,
70
+ * which CI's `ipc-smoke` job does; this override just makes the PATH-CHOICE
71
+ * logic itself testable everywhere).
72
+ */
73
+ export declare function controlEndpointPath(productId: string, storeDir: string, platform?: NodeJS.Platform): string;
74
+ /** Where the daemon writes its per-session control-auth token (see the handshake section below). Always a real file, even on Windows (pipes have no path of their own to piggyback secrets on). */
75
+ export declare function controlTokenPath(storeDir: string): string;
76
+ export declare function randomNonceHex(): string;
77
+ /** What the server proves to the client: it holds `token`, bound to the client's own nonce so a captured proof can't be replayed against a different handshake. */
78
+ export declare function computeServerProof(token: string, clientNonce: string): string;
79
+ /** What the client proves to the server, symmetrically, bound to the server's nonce. */
80
+ export declare function computeClientAuth(token: string, serverNonce: string): string;
81
+ /** Constant-time hex-string comparison (`crypto.timingSafeEqual` requires equal-length buffers; a length mismatch is itself a safe, immediate "not equal" — no early-exit on content). */
82
+ export declare function timingSafeEqualHex(a: string, b: string): boolean;
83
+ export interface ClientHello {
84
+ v: 1;
85
+ hello: 'client';
86
+ nonce: string;
87
+ }
88
+ export interface ServerHello {
89
+ v: 1;
90
+ hello: 'server';
91
+ proof: string;
92
+ nonce: string;
93
+ }
94
+ export interface ClientAuth {
95
+ v: 1;
96
+ auth: string;
97
+ }
98
+ export interface ServerReady {
99
+ v: 1;
100
+ ready: true;
101
+ }
102
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
103
+ export declare function parseClientHello(value: unknown): ClientHello | undefined;
104
+ export declare function parseServerHello(value: unknown): ServerHello | undefined;
105
+ export declare function parseClientAuth(value: unknown): ClientAuth | undefined;
106
+ export declare function parseServerReady(value: unknown): ServerReady | undefined;
107
+ export interface RawControlRequest {
108
+ /** Not narrowed to `1` here on purpose — an unexpected value is a `bad_version` RESPONSE, not a parse failure; see `control-server.ts`. */
109
+ v: unknown;
110
+ id: string;
111
+ method: string;
112
+ params?: unknown;
113
+ }
114
+ /** Loose shape check for an incoming request line: only `id`/`method` need to be well-formed for the server to be able to respond at all (including a `bad_version`/`unknown_method` response) — `v` is deliberately passed through unvalidated. */
115
+ export declare function parseRawControlRequest(value: unknown): RawControlRequest | undefined;
116
+ export interface ControlErrorShape {
117
+ code: string;
118
+ message: string;
119
+ }
120
+ export interface ControlResponseOk {
121
+ v: 1;
122
+ id: string;
123
+ ok: true;
124
+ result?: unknown;
125
+ /** Present (and `true`) only on the final frame of a streaming method — see `control-server.ts`'s dispatch. */
126
+ done?: true;
127
+ }
128
+ export interface ControlResponseErr {
129
+ v: 1;
130
+ id: string;
131
+ ok: false;
132
+ error: ControlErrorShape;
133
+ }
134
+ export type ControlResponse = ControlResponseOk | ControlResponseErr;
135
+ export interface ControlEventFrame {
136
+ v: 1;
137
+ id: string;
138
+ event: unknown;
139
+ }
140
+ export declare function encodeFrame(frame: unknown): string;
141
+ /** Thrown by a method handler to control the wire error `{code, message}` a caller sees — anything else thrown surfaces as a generic `internal_error`. See `control-server.ts`'s dispatch and `bin/control-client.ts`'s `request()` (which re-throws this same class on the client side). */
142
+ export declare class ControlError extends Error {
143
+ readonly code: string;
144
+ constructor(code: string, message: string);
145
+ }
146
+ /**
147
+ * Bound on a single NDJSON line's byte length. Every real frame this
148
+ * protocol ever sends (handshake frames, requests/responses/events) is well
149
+ * under this — it exists purely as a defensive cap against a misbehaving or
150
+ * hostile peer streaming an unterminated line forever to grow `pending`
151
+ * without bound. Exceeding it is a fail-closed condition: {@link
152
+ * NdjsonLineReader.push} throws, and every caller (`control-server.ts`,
153
+ * `bin/control-client.ts`) destroys the connection on that throw.
154
+ */
155
+ export declare const MAX_LINE_BYTES: number;
156
+ /**
157
+ * Buffers raw socket bytes and yields complete lines. Splits on the raw byte
158
+ * `0x0a` BEFORE any UTF-8 decoding (mirrors `bin/audit-log.ts`'s
159
+ * `followAuditLog`) — `0x0A` can only ever appear as an actual newline in
160
+ * valid UTF-8, so this never risks decoding a multi-byte character that
161
+ * happened to straddle a chunk boundary.
162
+ */
163
+ export declare class NdjsonLineReader {
164
+ private pending;
165
+ /** @throws if the still-unterminated remainder exceeds {@link MAX_LINE_BYTES} — see that constant's own doc comment. */
166
+ push(chunk: Buffer): string[];
167
+ }
168
+ export interface ControlActiveTask {
169
+ taskId: string;
170
+ state: TaskState;
171
+ }
172
+ /**
173
+ * M4 Phase 4 (part B.3, observability): a cheap per-active-task queue-depth
174
+ * watermark for the `status` result. The IDEAL metric here would be each
175
+ * runtime adapter's own event-queue depth (`util/async-queue.ts`'s
176
+ * `AsyncQueue`) — but that queue lives inside each adapter's concrete
177
+ * `Session` implementation, and `Session.events` (`types.ts`) is typed only
178
+ * as a plain `AsyncIterable<AgentEvent>`, which has no queryable backlog
179
+ * size; reaching it would mean adding a new method to the `Session`
180
+ * interface AND implementing it in all three bundled adapters
181
+ * (pi/claude/codex), which is out of scope for this pass. This instead
182
+ * reflects two things `TaskRunner` already cheaply knows about the SAME
183
+ * task without any new plumbing: how much progress is buffered locally
184
+ * (not yet flushed as a `task.progress` batch), and how many out-of-band
185
+ * approval requests are currently in flight for it. See
186
+ * `task-runner.ts`'s `getQueueWatermarks` for how each field is computed.
187
+ */
188
+ export interface TaskQueueWatermark {
189
+ taskId: string;
190
+ /** Events buffered in this task's `ProgressBatcher`, not yet flushed as a `task.progress` batch. */
191
+ progressBatcherPending: number;
192
+ /** Approval requests currently in flight for this task: 1 if one is actively dispatched (registered + `task.await_approval` sent) plus however many more are queued behind it (M4 Phase 4 fold-in — see `TaskRunner.requestApproval`). */
193
+ pendingApprovals: number;
194
+ }
195
+ /**
196
+ * S3b (L-003): local storage usage and pressure, as the `status` method
197
+ * reports them (architecture §12.7.2.1).
198
+ *
199
+ * Named `storage*` throughout, NOT `watermark*`: {@link TaskQueueWatermark}
200
+ * above is a per-task progress-buffer depth and has nothing to do with disk.
201
+ * Two unrelated concepts sharing a name on one status result is how an
202
+ * operator reads the wrong number during an incident.
203
+ *
204
+ * Present only when a daemon actually runs a storage policy
205
+ * (`DaemonConfig.hostedJournal.storagePolicy`). Absent means "not measured",
206
+ * which is a different statement from "measured, and fine" — so it is an
207
+ * absent field rather than a zeroed one.
208
+ */
209
+ export interface ControlStorageStatus {
210
+ /** §12.7.2.1's four states. `hard-pressure` declines new offers; `emergency` refuses to ack at all. */
211
+ pressureState: StoragePressureState;
212
+ /** `maxStoreBytes` — the budget `usedBytes` is measured against. */
213
+ budgetBytes: number;
214
+ /** Total across every category below, as of `measuredAt`. */
215
+ usedBytes: number;
216
+ /** Bytes available to this daemon on the store's filesystem — the free-space axis of the watermark, independent of the budget. */
217
+ freeBytes: number;
218
+ measuredAt: string;
219
+ /** The five §12.7.2.1 categories, always reported separately — a single total cannot drive a category-scoped cleanup order or a category-scoped never-delete list. */
220
+ categories: ControlStorageCategoryUsage[];
221
+ /** The most recent bounded WAL checkpoint + incremental vacuum, if one has run in this daemon's lifetime. */
222
+ lastCompaction?: ControlStorageCompaction;
223
+ }
224
+ export interface ControlStorageCategoryUsage {
225
+ category: StorageCategory;
226
+ bytes: number;
227
+ /** `true` when this is a host-reported or sampled figure rather than one measured off the filesystem. */
228
+ approximate: boolean;
229
+ }
230
+ export interface ControlStorageCompaction {
231
+ checkpointed: boolean;
232
+ walFramesRemaining: number;
233
+ pagesVacuumed: number;
234
+ durationMs: number;
235
+ at: string;
236
+ }
237
+ /** Result shape for the `status` method — see `create-daemon.ts`'s control-method wiring for how each field is sourced, and `bin/format.ts`'s `formatLiveStatusLines` for how the CLI renders it. */
238
+ export interface ControlStatusResult {
239
+ pid: number;
240
+ uptimeMs: number;
241
+ paired: boolean;
242
+ deviceId?: string;
243
+ /** The connection state machine's own current value (`ws-transport.ts`'s `ConnectionState`) — e.g. `'open'`, `'degraded'` (long-poll fallback), `'revoked'`, `'closed'`, `'connecting'`. */
244
+ transport: string;
245
+ activeTasks: ControlActiveTask[];
246
+ runtimeIds: string[];
247
+ /** M4 Phase 4 (part B.3): per-active-task queue watermarks — see {@link TaskQueueWatermark}. */
248
+ queueWatermarks: TaskQueueWatermark[];
249
+ /**
250
+ * Finding F4 (cross-model adversarial review): the actual pending
251
+ * approvals currently dispatched — the SAME entries `approvals.list`
252
+ * returns (`ApprovalRegistry.list()`), surfaced here too so a single
253
+ * `status` call can show an operator every `approvalId` they'd need to
254
+ * `approve`/`reject`, without a second control-socket round trip. This is
255
+ * `approvalsPending`'s own source list (`approvalsPending ===
256
+ * approvals.length`, always).
257
+ */
258
+ approvals: PendingApproval[];
259
+ /** M4 Phase 4 (part B.3): total approvals currently DISPATCHED (registered) across the whole daemon — the same count `approvals.list` returns, surfaced here too for a one-call status view. */
260
+ approvalsPending: number;
261
+ /** S3b (L-003): local storage usage + pressure — see {@link ControlStorageStatus}. Absent unless a storage policy is configured. */
262
+ storage?: ControlStorageStatus;
263
+ /** Local lifecycle/retry budget. This is not the transport state above. */
264
+ operationalHealth: OperationalHealthSnapshot;
265
+ }
266
+ export interface ApprovalsListResult {
267
+ approvals: PendingApproval[];
268
+ }
269
+ export type { ApprovalDecision, PendingApproval } from './approvals';
270
+ export interface ApprovalsResolveParams {
271
+ approvalId: string;
272
+ decision: ApprovalDecision;
273
+ reason?: string;
274
+ }
275
+ export declare function parseApprovalsResolveParams(value: unknown): ApprovalsResolveParams | undefined;
276
+ /**
277
+ * M4 Phase 3: the control method `byok-approval-mcp` (`bin/byok-approval-mcp.ts`)
278
+ * calls FROM a claude-spawned MCP-server child process — a genuinely
279
+ * different OS process from the daemon, reachable only over this same
280
+ * control socket (see `../types.ts`'s `ApprovalChannel` doc comment for the
281
+ * full why). `taskId` correlates the request to an active task;
282
+ * `summary` is a short, human-readable description of the gated action
283
+ * (carried verbatim into the wire `task.await_approval.summary`).
284
+ */
285
+ export interface ApprovalsRequestParams {
286
+ taskId: string;
287
+ summary: string;
288
+ }
289
+ export declare function parseApprovalsRequestParams(value: unknown): ApprovalsRequestParams | undefined;
290
+ /** Result of `approvals.request` — the outcome `byok-approval-mcp` translates into its own MCP `allow`/`deny` answer. */
291
+ export interface ApprovalsRequestResult {
292
+ approved: boolean;
293
+ reason?: string;
294
+ }
295
+ export type ShutdownReason = 'unpair' | 'operator';
296
+ export interface ShutdownParams {
297
+ reason?: ShutdownReason;
298
+ }
299
+ export declare function parseShutdownParams(value: unknown): ShutdownParams;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * M4 Phase 2: the daemon-side local control socket — Unix domain socket
3
+ * (darwin/linux) or Windows named pipe, mutually authenticated by an
4
+ * HMAC handshake over a token that never crosses the wire (see
5
+ * `control-protocol.ts`), speaking a small NDJSON request/response(/event)
6
+ * RPC protocol. Transport-only and daemon-agnostic: `create-daemon.ts`
7
+ * supplies the actual method handlers (`status`, `tasks.subscribe`,
8
+ * `approvals.*`, `shutdown`) as a plain registry — this module knows
9
+ * nothing about `Daemon`/`TaskRunner` internals, which keeps it testable
10
+ * (and reusable) on its own.
11
+ */
12
+ export declare class AnotherControlServerRunningError extends Error {
13
+ constructor(endpoint: string);
14
+ }
15
+ /** Default cap on simultaneous pre-handshake ("half-open") connections — see `startControlServer`'s own comment on why. */
16
+ export declare const MAX_HALF_OPEN_CONNECTIONS = 8;
17
+ export interface ControlMethodContext {
18
+ /** Emits one `event` frame for a streaming method's current request. */
19
+ emit: (event: unknown) => void;
20
+ /** Aborts once the client disconnects — a streaming handler must stop producing events and release whatever it subscribed to. */
21
+ signal: AbortSignal;
22
+ }
23
+ export type UnaryMethod = (params: unknown) => Promise<unknown> | unknown;
24
+ export type StreamMethod = (params: unknown, ctx: ControlMethodContext) => Promise<void>;
25
+ export interface ControlMethods {
26
+ unary: Record<string, UnaryMethod>;
27
+ stream: Record<string, StreamMethod>;
28
+ }
29
+ export interface ControlServerOptions {
30
+ storeDir: string;
31
+ productId: string;
32
+ methods: ControlMethods;
33
+ /** Test-only override — default `HANDSHAKE_TIMEOUT_MS` (3000ms). */
34
+ handshakeTimeoutMs?: number;
35
+ /** Test-only override — default `MAX_HALF_OPEN_CONNECTIONS` (8). */
36
+ maxHalfOpenConnections?: number;
37
+ }
38
+ export interface ControlServerHandle {
39
+ /** The bound Unix socket path or Windows pipe name. */
40
+ endpoint: string;
41
+ /** Stops accepting new connections, destroys every open one, and removes the socket/token files (a Windows pipe leaves no file to remove). Idempotent. */
42
+ close(): Promise<void>;
43
+ }
44
+ /**
45
+ * Binds the platform endpoint (Unix socket or Windows pipe — see
46
+ * `control-protocol.ts`) FIRST, and only once that succeeds generates and
47
+ * persists a fresh per-session token to `<storeDir>/control.token` (0600).
48
+ * This ordering matters: if a bind fails because ANOTHER daemon's control
49
+ * server already owns this endpoint (see
50
+ * {@link AnotherControlServerRunningError}), that other daemon's own token
51
+ * file must be left completely untouched — writing (and, on failure,
52
+ * deleting) a token before the bind attempt would corrupt or erase the
53
+ * still-running daemon's valid token out from under it.
54
+ *
55
+ * Throws {@link AnotherControlServerRunningError} if a live listener already
56
+ * owns this endpoint (fatal — the caller, `create-daemon.ts`, treats this as
57
+ * a hard startup failure); any OTHER bind failure propagates as a plain
58
+ * `Error` so the caller can decide to degrade instead (the control socket
59
+ * must never be allowed to brick the rest of the daemon).
60
+ */
61
+ export declare function startControlServer(opts: ControlServerOptions): Promise<ControlServerHandle>;