@slock-ai/computer 0.0.13 → 0.0.15
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/dist/index.js +1492 -713
- package/dist/lib/index.d.ts +554 -0
- package/dist/lib/index.js +505 -0
- package/package.json +9 -2
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
type DaemonState = {
|
|
2
|
+
running: true;
|
|
3
|
+
pid: number;
|
|
4
|
+
} | {
|
|
5
|
+
running: false;
|
|
6
|
+
};
|
|
7
|
+
type ServerHealth = "ok" | "degraded" | "offline";
|
|
8
|
+
interface ServerStatusRow {
|
|
9
|
+
serverId: string;
|
|
10
|
+
serverSlug: string | null;
|
|
11
|
+
serverMachineId: string;
|
|
12
|
+
serverUrl: string;
|
|
13
|
+
attachedAt: string | null;
|
|
14
|
+
daemonLogPath: string;
|
|
15
|
+
daemon: DaemonState;
|
|
16
|
+
/** v6 §11 health enum surfaced in `status` table (PR-H §3.3). */
|
|
17
|
+
health: ServerHealth;
|
|
18
|
+
}
|
|
19
|
+
interface ComputerStatusReport {
|
|
20
|
+
slockHome: string;
|
|
21
|
+
loggedIn: boolean;
|
|
22
|
+
userId: string | null;
|
|
23
|
+
loginServerUrl: string | null;
|
|
24
|
+
userSessionError: string | null;
|
|
25
|
+
supervisor: DaemonState & {
|
|
26
|
+
logPath: string;
|
|
27
|
+
};
|
|
28
|
+
servers: ServerStatusRow[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface RunnerListItem {
|
|
32
|
+
agentId: string;
|
|
33
|
+
name: string;
|
|
34
|
+
status: string;
|
|
35
|
+
model: string;
|
|
36
|
+
runtime: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Per-runner lifecycle states (§3.2). One state at a time per runner.
|
|
41
|
+
*
|
|
42
|
+
* starting — spawned, not yet reporting ready
|
|
43
|
+
* running — healthy, reporting heartbeats
|
|
44
|
+
* degraded — alive but not satisfying health invariants (e.g. crash
|
|
45
|
+
* budget near breach, see §1/§2)
|
|
46
|
+
* crashed — observed exit / fatal signal
|
|
47
|
+
* stopped — intentional stop (operator-driven via `runners stop` or
|
|
48
|
+
* supervisor shutdown)
|
|
49
|
+
*/
|
|
50
|
+
declare const RUNNER_STATE_VALUES: readonly ["starting", "running", "degraded", "crashed", "stopped"];
|
|
51
|
+
type RunnerState = (typeof RUNNER_STATE_VALUES)[number];
|
|
52
|
+
declare function isRunnerState(value: unknown): value is RunnerState;
|
|
53
|
+
/**
|
|
54
|
+
* Service (supervisor) lifecycle states (§3.2). Mirrors runner shape but
|
|
55
|
+
* narrower — the service is a singleton per install root.
|
|
56
|
+
*
|
|
57
|
+
* starting — pidfile written, socket not yet listening
|
|
58
|
+
* running — socket listening, heartbeat active
|
|
59
|
+
* degraded — service alive but one or more invariants violated
|
|
60
|
+
* (e.g. SERVICE_DEGRADED §7.3 crash-budget breach)
|
|
61
|
+
* stopping — graceful shutdown in flight (SERVICE_SHUTTING_DOWN
|
|
62
|
+
* §7.3 emitted to clients)
|
|
63
|
+
* stopped — pidfile cleared, socket closed
|
|
64
|
+
*/
|
|
65
|
+
declare const SERVICE_STATE_VALUES: readonly ["starting", "running", "degraded", "stopping", "stopped"];
|
|
66
|
+
type ServiceState = (typeof SERVICE_STATE_VALUES)[number];
|
|
67
|
+
declare function isServiceState(value: unknown): value is ServiceState;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* IPC error code family — the seed that `ServiceClient.request` and
|
|
71
|
+
* `ServiceClient.events` propagate. Forward-only post v9-sign: additions
|
|
72
|
+
* are additive minor library bumps; renames / removals are major.
|
|
73
|
+
*
|
|
74
|
+
* Full §7.3 enumeration (SETUP/ATTACH/MIGRATE/UPGRADE/SERVICE families)
|
|
75
|
+
* lands in a follow-up reconcile commit that also unifies the codes
|
|
76
|
+
* currently emitted via `ComputerServiceError` across services/*.
|
|
77
|
+
*/
|
|
78
|
+
declare const IPC_ERROR_CODES: readonly ["IPC_FRAME_TOO_LARGE", "IPC_PROTOCOL_VERSION_UNSUPPORTED", "IPC_PROTOCOL_HANDSHAKE_FAILED", "IPC_HEARTBEAT_TIMEOUT", "IPC_MALFORMED_FRAME", "IPC_REQUEST_TIMEOUT", "IPC_REQUEST_CANCELED", "IPC_CLIENT_CLOSED"];
|
|
79
|
+
type IpcErrorCode = (typeof IPC_ERROR_CODES)[number];
|
|
80
|
+
/**
|
|
81
|
+
* Runtime narrow guard — exposed so consumers can pattern-match without
|
|
82
|
+
* pulling in a generic `as` cast. The CI gate (§7.4) over a final
|
|
83
|
+
* `ERROR_CODES` union will subsume this once the reconcile lands.
|
|
84
|
+
*/
|
|
85
|
+
declare function isIpcErrorCode(value: unknown): value is IpcErrorCode;
|
|
86
|
+
/**
|
|
87
|
+
* Caller-facing options for `ServiceClient.request`. Pairs active cancel
|
|
88
|
+
* (`signal` — primary G-stage unmount path) with passive deadline
|
|
89
|
+
* (`timeoutMs` — library-side safety net). On either path the client
|
|
90
|
+
* emits an IPC `cancel` frame for the in-flight request id and rejects
|
|
91
|
+
* the returned promise with `IPC_REQUEST_CANCELED` or
|
|
92
|
+
* `IPC_REQUEST_TIMEOUT` respectively (§4.4).
|
|
93
|
+
*/
|
|
94
|
+
interface RequestOptions {
|
|
95
|
+
signal?: AbortSignal;
|
|
96
|
+
timeoutMs?: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Typed RPC table — keys are kebab-case wire literals (§5.5), values are
|
|
100
|
+
* per-method `{ params, result }` pairs. The v9 sign-lock seed listed
|
|
101
|
+
* here is the §3.2 6-method floor; new methods are additive minor bumps
|
|
102
|
+
* (extending this map).
|
|
103
|
+
*
|
|
104
|
+
* State-reader results (`service-status` / `runner-status` /
|
|
105
|
+
* `list-runners`) are pinned to the lib reader return types — the
|
|
106
|
+
* D-stage §4 IPC handlers in PR-impl-2 satisfy the wire surface by
|
|
107
|
+
* delegating to those readers (mechanical `satisfies`).
|
|
108
|
+
*
|
|
109
|
+
* Mutation results (`upgrade-start` / `reset-service` / `reset-runner`)
|
|
110
|
+
* remain `unknown` here — they pin alongside the PR-impl-3 §X migration /
|
|
111
|
+
* verb pipeline that owns the corresponding handlers.
|
|
112
|
+
*/
|
|
113
|
+
interface RequestMethodMap {
|
|
114
|
+
"service-status": {
|
|
115
|
+
params: void;
|
|
116
|
+
result: ServiceStatusResult;
|
|
117
|
+
};
|
|
118
|
+
"runner-status": {
|
|
119
|
+
params: {
|
|
120
|
+
serverId: string;
|
|
121
|
+
};
|
|
122
|
+
result: RunnerStatusResult;
|
|
123
|
+
};
|
|
124
|
+
"list-runners": {
|
|
125
|
+
params: void;
|
|
126
|
+
result: ListRunnersResult;
|
|
127
|
+
};
|
|
128
|
+
"upgrade-start": {
|
|
129
|
+
params: {
|
|
130
|
+
targetVersion?: string;
|
|
131
|
+
};
|
|
132
|
+
result: UpgradeStartResult;
|
|
133
|
+
};
|
|
134
|
+
"reset-service": {
|
|
135
|
+
params: void;
|
|
136
|
+
result: ResetServiceResult;
|
|
137
|
+
};
|
|
138
|
+
"reset-runner": {
|
|
139
|
+
params: {
|
|
140
|
+
serverId: string;
|
|
141
|
+
};
|
|
142
|
+
result: ResetRunnerResult;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Server-pushed event over `ServiceClient.events`. The discriminant field
|
|
147
|
+
* is `kind` (§5.5 — established at v9.7 sign); values are past-participle
|
|
148
|
+
* kebab-case literals. The 6-kind enumeration is the v9.8 §4.4 floor.
|
|
149
|
+
*
|
|
150
|
+
* Payload shapes finalize alongside §4 IPC impl (PR-impl-2, liuliu-owned);
|
|
151
|
+
* the discriminator is locked here so consumers can write exhaustive
|
|
152
|
+
* switches today.
|
|
153
|
+
*
|
|
154
|
+
* Note: v9.8 §4.4 has an example block still showing the v9.7-pre field
|
|
155
|
+
* name `topic` + present-tense values; §5.5 is the byte-pin source of
|
|
156
|
+
* truth (`kind` + past-participle). The §4.4 example will be reconciled
|
|
157
|
+
* in a follow-up doc-clean pass.
|
|
158
|
+
*/
|
|
159
|
+
type ServiceEvent = {
|
|
160
|
+
kind: "service-state-changed";
|
|
161
|
+
payload: unknown;
|
|
162
|
+
} | {
|
|
163
|
+
kind: "runner-state-changed";
|
|
164
|
+
payload: unknown;
|
|
165
|
+
} | {
|
|
166
|
+
kind: "runner-attached";
|
|
167
|
+
payload: unknown;
|
|
168
|
+
} | {
|
|
169
|
+
kind: "runner-detached";
|
|
170
|
+
payload: unknown;
|
|
171
|
+
} | {
|
|
172
|
+
kind: "upgrade-log-appended";
|
|
173
|
+
payload: unknown;
|
|
174
|
+
} | {
|
|
175
|
+
kind: "heartbeat";
|
|
176
|
+
payload: unknown;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Library-side error envelope thrown by `ServiceClient`. `code` is a
|
|
180
|
+
* member of the §7 closed-set; `cause` is retained in-process only and
|
|
181
|
+
* MUST NOT cross IPC / CLI fail boundaries (§7.4 redaction).
|
|
182
|
+
*/
|
|
183
|
+
declare class ServiceClientError extends Error {
|
|
184
|
+
readonly code: IpcErrorCode;
|
|
185
|
+
readonly cause?: unknown;
|
|
186
|
+
constructor(code: IpcErrorCode, message: string, cause?: unknown);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Typed IPC client. The implementation in PR-impl-2 §4 satisfies this
|
|
190
|
+
* interface; this commit only locks the surface.
|
|
191
|
+
*
|
|
192
|
+
* Termination semantics (§4.7):
|
|
193
|
+
* - `events` completes naturally (`Symbol.asyncIterator` return) on any
|
|
194
|
+
* socket-close path (graceful local/remote, transient, TCP RST). The
|
|
195
|
+
* `for-await` loop exits without throwing — idiomatic reconnect is a
|
|
196
|
+
* plain outer `while (!stopped) { … }` wrap, no try/catch.
|
|
197
|
+
* - `events` THROWS `ServiceClientError` only on protocol-level
|
|
198
|
+
* failures: handshake mismatch, frame parse, frame > 1 MiB.
|
|
199
|
+
* The disjoint split lets consumers distinguish "retry" (iterator end)
|
|
200
|
+
* from "give up" (throw) without runtime classification.
|
|
201
|
+
*/
|
|
202
|
+
interface ServiceClient {
|
|
203
|
+
events: AsyncIterable<ServiceEvent>;
|
|
204
|
+
request<M extends keyof RequestMethodMap>(method: M, params: RequestMethodMap[M]["params"], options?: RequestOptions): Promise<RequestMethodMap[M]["result"]>;
|
|
205
|
+
close(): Promise<void>;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Connection options. `protocolVersion` lets a consumer opt-in to a
|
|
209
|
+
* specific handshake version; the service negotiates highest mutually
|
|
210
|
+
* supported (§4.3) and may reject with `IPC_PROTOCOL_VERSION_UNSUPPORTED`.
|
|
211
|
+
*/
|
|
212
|
+
interface ConnectServiceOptions {
|
|
213
|
+
protocolVersion?: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Open a typed IPC connection to the Computer service. Throws
|
|
217
|
+
* `ServiceClientError` on handshake failure. Concrete implementation
|
|
218
|
+
* lands in PR-impl-2 §4 (liuliu-owned); the type is locked here so
|
|
219
|
+
* `apps/slock-computer-app` and other library consumers can type-check
|
|
220
|
+
* call sites before the transport ships.
|
|
221
|
+
*/
|
|
222
|
+
type ConnectService = (installRoot: string, options?: ConnectServiceOptions) => Promise<ServiceClient>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Canonical lib-side name for the `service-status` reader result, aligned
|
|
226
|
+
* with the wire method literal. Identical shape to `ComputerStatusReport`
|
|
227
|
+
* (re-exported above for callers that prefer the historical internal
|
|
228
|
+
* name).
|
|
229
|
+
*
|
|
230
|
+
* SECRET-FREE INVARIANT: the report carries server attachment IDENTITY
|
|
231
|
+
* and pid/liveness derived from filesystem state but NEVER the user
|
|
232
|
+
* access token or any `sk_computer_*` credential (status.ts §3.3.1
|
|
233
|
+
* redline, enforced by `status.test.ts` token-leak guards).
|
|
234
|
+
*/
|
|
235
|
+
type ServiceStatusResult = ComputerStatusReport;
|
|
236
|
+
/**
|
|
237
|
+
* Per-server block in `ListRunnersResult.servers[]`. The discriminator
|
|
238
|
+
* preserves the underlying `RunnersClient.list()` outcome so a single
|
|
239
|
+
* server's `unauthorized` / `error` does NOT mask another server's data.
|
|
240
|
+
*/
|
|
241
|
+
type RunnerListPerServer = {
|
|
242
|
+
serverId: string;
|
|
243
|
+
serverSlug: string | null;
|
|
244
|
+
status: "ok";
|
|
245
|
+
whitelist: string[];
|
|
246
|
+
runners: RunnerListItem[];
|
|
247
|
+
} | {
|
|
248
|
+
serverId: string;
|
|
249
|
+
serverSlug: string | null;
|
|
250
|
+
status: "unauthorized";
|
|
251
|
+
} | {
|
|
252
|
+
serverId: string;
|
|
253
|
+
serverSlug: string | null;
|
|
254
|
+
status: "error";
|
|
255
|
+
code: string;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Aggregate result of `listRunners(installRoot, opts?)`. When `opts.serverId`
|
|
259
|
+
* is omitted, `servers[]` contains one block per attached server (zero
|
|
260
|
+
* blocks if the Computer has no attachments). When `opts.serverId` is set,
|
|
261
|
+
* `servers[]` contains exactly that server's block, or the call throws
|
|
262
|
+
* `StateReaderError("NOT_ATTACHED")` if the id is not attached.
|
|
263
|
+
*/
|
|
264
|
+
interface ListRunnersResult {
|
|
265
|
+
servers: RunnerListPerServer[];
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Result of `readRunnerStatus(installRoot, serverId)` — per-server state
|
|
269
|
+
* row plus the runners running on it. Unauthorized / error discriminate
|
|
270
|
+
* the runners-API outcome while still surfacing the `server` row so the
|
|
271
|
+
* caller has the daemon state context.
|
|
272
|
+
*/
|
|
273
|
+
type RunnerStatusResult = {
|
|
274
|
+
status: "ok";
|
|
275
|
+
server: ServerStatusRow;
|
|
276
|
+
whitelist: string[];
|
|
277
|
+
runners: RunnerListItem[];
|
|
278
|
+
} | {
|
|
279
|
+
status: "unauthorized";
|
|
280
|
+
server: ServerStatusRow;
|
|
281
|
+
} | {
|
|
282
|
+
status: "error";
|
|
283
|
+
server: ServerStatusRow;
|
|
284
|
+
code: string;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* State-reader error closed set. Library boundary errors only — does NOT
|
|
288
|
+
* include the CLI ambient-rule errors (`NO_ATTACHMENT` / `AMBIGUOUS_SERVER`)
|
|
289
|
+
* which are v4 §6 ergonomics handled by the CLI wrapper, not the lib
|
|
290
|
+
* primitive.
|
|
291
|
+
*/
|
|
292
|
+
declare const STATE_READER_ERROR_CODES: readonly ["NOT_ATTACHED", "INVALID_ATTACHMENT"];
|
|
293
|
+
type StateReaderErrorCode = (typeof STATE_READER_ERROR_CODES)[number];
|
|
294
|
+
/**
|
|
295
|
+
* Library-side error envelope thrown by state-readers. Lib readers
|
|
296
|
+
* NEVER call `fail()` / `process.exit` — CLI wrappers translate this to
|
|
297
|
+
* `fail(code, message)` at the boundary; D-stage IPC handlers map it to
|
|
298
|
+
* an IPC error frame.
|
|
299
|
+
*/
|
|
300
|
+
declare class StateReaderError extends Error {
|
|
301
|
+
readonly code: StateReaderErrorCode;
|
|
302
|
+
constructor(code: StateReaderErrorCode, message: string);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Result of `reset-service` (`slock-computer reset --service`). Clears
|
|
307
|
+
* the service-level `crashHistory` and transitions service state
|
|
308
|
+
* `degraded → running`. MUST NOT kill runners (§1.3).
|
|
309
|
+
*
|
|
310
|
+
* `previousState` reports the state observed prior to the reset write —
|
|
311
|
+
* for callers that want to log/surface "was already running" vs
|
|
312
|
+
* "recovered from degraded". `clearedCrashCount` counts the entries
|
|
313
|
+
* removed from `service.state.json::crashHistory`.
|
|
314
|
+
*/
|
|
315
|
+
interface ResetServiceResult {
|
|
316
|
+
status: "ok";
|
|
317
|
+
previousState: ServiceState;
|
|
318
|
+
clearedCrashCount: number;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Result of `reset-runner` (`slock-computer reset --runner <slug>`).
|
|
322
|
+
* Clears the per-runner `crashHistory` and transitions runner state
|
|
323
|
+
* `degraded → running`. MUST NOT respawn or kill the runner process
|
|
324
|
+
* (§2.4).
|
|
325
|
+
*
|
|
326
|
+
* `status: "not-found"` is returned when the resolved `serverId` does
|
|
327
|
+
* not correspond to an attached server (the CLI ambient `--server` slug
|
|
328
|
+
* resolver returns its own error before the handler is invoked; this
|
|
329
|
+
* branch defends the IPC path where the caller passes a serverId
|
|
330
|
+
* directly).
|
|
331
|
+
*/
|
|
332
|
+
type ResetRunnerResult = {
|
|
333
|
+
status: "ok";
|
|
334
|
+
serverId: string;
|
|
335
|
+
previousState: RunnerState;
|
|
336
|
+
clearedCrashCount: number;
|
|
337
|
+
} | {
|
|
338
|
+
status: "not-found";
|
|
339
|
+
serverId: string;
|
|
340
|
+
};
|
|
341
|
+
/**
|
|
342
|
+
* Result of `upgrade-start` (§12 phase pipeline trigger). The same
|
|
343
|
+
* pipeline is funneled by manual CLI / auto scheduler / IPC; this
|
|
344
|
+
* result describes the trigger outcome only — the per-phase events
|
|
345
|
+
* stream over `ServiceEvent` (`upgrade-log-appended` kind).
|
|
346
|
+
*
|
|
347
|
+
* `status: "already-running"` is returned when an upgrade is already
|
|
348
|
+
* in flight; `upgradeId` then refers to the in-flight upgrade so the
|
|
349
|
+
* caller can attach to its event stream instead of starting a new one.
|
|
350
|
+
*/
|
|
351
|
+
interface UpgradeStartResult {
|
|
352
|
+
status: "started" | "already-running";
|
|
353
|
+
upgradeId: string;
|
|
354
|
+
targetVersion: string;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Identity record for a legacy `@slock-ai/daemon` machine candidate that
|
|
358
|
+
* `detectLegacyMigration` discovered locally on this Computer install
|
|
359
|
+
* root. The shape is identity-only — it never carries credentials.
|
|
360
|
+
*
|
|
361
|
+
* `machineId` is the legacy daemon's lock-id (`machine-<sha256(apiKey)
|
|
362
|
+
* [0..15]>`). It is stable across legacy daemon restarts but changes if
|
|
363
|
+
* the user rotates the api key. The id is what the user would see in
|
|
364
|
+
* `<installRoot>/machines/` directory listings, and is what
|
|
365
|
+
* `AdoptLegacyService.migrate()` uses to find the lock owner file
|
|
366
|
+
* during migration.
|
|
367
|
+
*
|
|
368
|
+
* `machineName` is the host display name observed in the legacy
|
|
369
|
+
* daemon's `daemon.lock/owner.json` (typically `os.hostname()` at
|
|
370
|
+
* legacy daemon spawn time). Best-effort, may be undefined for stale
|
|
371
|
+
* lock files.
|
|
372
|
+
*
|
|
373
|
+
* `serverUrl` is the API server the legacy daemon was attached to.
|
|
374
|
+
* Used by the migrate prompt to surface "you are about to migrate
|
|
375
|
+
* machine X attached to <server>" before the user accepts.
|
|
376
|
+
*/
|
|
377
|
+
interface LegacyMachineCandidate {
|
|
378
|
+
machineId: string;
|
|
379
|
+
machineName?: string;
|
|
380
|
+
serverUrl?: string;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* §X migration detection result — the discriminated union returned by
|
|
384
|
+
* `detectLegacyMigration(installRoot, loggedInUserId)`.
|
|
385
|
+
*
|
|
386
|
+
* Per RFC §X.2:
|
|
387
|
+
* - `match`: exactly one local legacy daemon machine. The setup flow
|
|
388
|
+
* SHOULD prompt the user to migrate before falling back to fresh
|
|
389
|
+
* setup. Migration itself is delegated to `AdoptLegacyService.
|
|
390
|
+
* migrate()` (preserved as internal helper per §X.6 — 11 typed
|
|
391
|
+
* errors / 4 events / Result discriminator byte-identical).
|
|
392
|
+
* - `no-match`: zero local legacy daemon machines. Fresh setup
|
|
393
|
+
* proceeds without any migrate prompt.
|
|
394
|
+
* - `ambiguous`: two or more local legacy daemon machines. Per §X.2
|
|
395
|
+
* "no auto-listed-pick fallback" — fresh setup proceeds without
|
|
396
|
+
* any migrate prompt; the user is expected to start the legacy
|
|
397
|
+
* daemon they want migrated first to disambiguate.
|
|
398
|
+
*
|
|
399
|
+
* Fingerprint discipline (best-effort non-authoritative — Hao
|
|
400
|
+
* `msg=4d170194`): the candidate set is computed from local
|
|
401
|
+
* `<installRoot>/machines/machine-*` directory state. It identifies
|
|
402
|
+
* "this host has legacy daemon installs" — it does NOT prove ownership.
|
|
403
|
+
* Final adoption still walks the existing
|
|
404
|
+
* `AdoptLegacyService.migrate()` credential validation path; a match
|
|
405
|
+
* is a prompt-gate, not a trust-gate.
|
|
406
|
+
*/
|
|
407
|
+
/**
|
|
408
|
+
* Closed set of `MigrationDetection.kind` discriminator values. Mirrors
|
|
409
|
+
* the `IPC_ERROR_CODES` / `RUNNER_STATE_VALUES` `as const` tuple pattern
|
|
410
|
+
* so consumers can pattern-match exhaustively and so a future §7.4 CI
|
|
411
|
+
* gate can verify the runtime kind against the type union without an
|
|
412
|
+
* `as` cast (state-machine-modeling discipline — correlated/mutex
|
|
413
|
+
* properties land as a closed-set state-enum).
|
|
414
|
+
*/
|
|
415
|
+
declare const MIGRATION_DETECTION_KINDS: readonly ["match", "no-match", "ambiguous"];
|
|
416
|
+
type MigrationDetectionKind = (typeof MIGRATION_DETECTION_KINDS)[number];
|
|
417
|
+
/** Runtime narrow guard for `MigrationDetection.kind`. */
|
|
418
|
+
declare function isMigrationDetectionKind(value: unknown): value is MigrationDetectionKind;
|
|
419
|
+
type MigrationDetection = {
|
|
420
|
+
kind: "match";
|
|
421
|
+
machineId: string;
|
|
422
|
+
machineName?: string;
|
|
423
|
+
serverUrl?: string;
|
|
424
|
+
} | {
|
|
425
|
+
kind: "no-match";
|
|
426
|
+
} | {
|
|
427
|
+
kind: "ambiguous";
|
|
428
|
+
candidates: LegacyMachineCandidate[];
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Detect whether `installRoot` already contains legacy
|
|
433
|
+
* `@slock-ai/daemon` machine state that could be migrated to a
|
|
434
|
+
* Computer attachment under the logged-in user.
|
|
435
|
+
*
|
|
436
|
+
* Returns a discriminated `MigrationDetection`:
|
|
437
|
+
* - `match` (exactly one candidate) — caller SHOULD prompt the user
|
|
438
|
+
* to migrate before fresh setup (§X.2 step 3).
|
|
439
|
+
* - `no-match` (zero candidates) — caller proceeds with fresh
|
|
440
|
+
* setup, NO prompt (§X.2 step 5).
|
|
441
|
+
* - `ambiguous` (two-or-more candidates) — caller proceeds with
|
|
442
|
+
* fresh setup, NO prompt (§X.2 step 5; "no auto-listed-pick
|
|
443
|
+
* fallback").
|
|
444
|
+
*
|
|
445
|
+
* Lib-pure: no env reads, no terminal IO, no process.exit. Filesystem
|
|
446
|
+
* errors are swallowed and treated as "no evidence" (per the doc-
|
|
447
|
+
* block above).
|
|
448
|
+
*
|
|
449
|
+
* @param installRoot The Slock home (`SLOCK_HOME`) to scan. Resolved
|
|
450
|
+
* by the CLI wrapper via `resolveSlockHome`.
|
|
451
|
+
* @param loggedInUserId Reserved for the server-roster intersection
|
|
452
|
+
* (forward-compat per §X.2 step 1). v0 does not consume this value;
|
|
453
|
+
* accept the typed argument so consumers of this function can
|
|
454
|
+
* already pass it.
|
|
455
|
+
*/
|
|
456
|
+
declare function detectLegacyMigration(installRoot: string, loggedInUserId: string): Promise<MigrationDetection>;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Read the Computer-level aggregate status from `installRoot`. Identical
|
|
460
|
+
* shape to `buildStatusReport(installRoot)` — exposed under the
|
|
461
|
+
* wire-aligned name `readServiceStatus` for IPC `service-status` parity.
|
|
462
|
+
*/
|
|
463
|
+
declare function readServiceStatus(installRoot: string): Promise<ServiceStatusResult>;
|
|
464
|
+
/**
|
|
465
|
+
* Read a single attached server's daemon state row plus the runners
|
|
466
|
+
* currently running on it. Throws `StateReaderError("NOT_ATTACHED")` if
|
|
467
|
+
* `serverId` is not in the attached set, or `"INVALID_ATTACHMENT"` if
|
|
468
|
+
* the attachment.json under that id is unreadable.
|
|
469
|
+
*/
|
|
470
|
+
declare function readRunnerStatus(installRoot: string, serverId: string): Promise<RunnerStatusResult>;
|
|
471
|
+
/**
|
|
472
|
+
* List runners across attached servers. With no `serverId` filter,
|
|
473
|
+
* returns one block per attached server (zero if no attachments). With
|
|
474
|
+
* a `serverId` filter, returns exactly that server's block or throws
|
|
475
|
+
* `StateReaderError("NOT_ATTACHED")`.
|
|
476
|
+
*
|
|
477
|
+
* Per-server discriminator preserves `unauthorized` / `error` outcomes
|
|
478
|
+
* so consumers can pattern-match without losing context.
|
|
479
|
+
*/
|
|
480
|
+
declare function listRunners(installRoot: string, opts?: {
|
|
481
|
+
serverId?: string;
|
|
482
|
+
}): Promise<ListRunnersResult>;
|
|
483
|
+
|
|
484
|
+
type UpgradeTrigger = "cli" | "web" | "tray";
|
|
485
|
+
/**
|
|
486
|
+
* v8.3.3 §4.4 (6) closed-set, verbatim 7 values. Adding / renaming /
|
|
487
|
+
* deleting any value requires an RFC bump (locked discipline). This
|
|
488
|
+
* constant array is the runtime source of truth for `assertUpgradeLogEntry`
|
|
489
|
+
* membership check; the type alias below mirrors it.
|
|
490
|
+
*/
|
|
491
|
+
declare const UPGRADE_ERROR_CODES: readonly ["UPGRADE_DEPS_CHANGED", "UPGRADE_NETWORK_FAILED", "UPGRADE_INTEGRITY_FAILED", "UPGRADE_SWAP_FAILED", "UPGRADE_RESTART_FAILED", "UPGRADE_NO_TARGET", "UPGRADE_ALREADY_RUNNING"];
|
|
492
|
+
type UpgradeErrorCode = (typeof UPGRADE_ERROR_CODES)[number];
|
|
493
|
+
interface UpgradeBundle {
|
|
494
|
+
computerVersion: string;
|
|
495
|
+
/**
|
|
496
|
+
* Forensic-only per v8.3.2 cleanup. May be omitted; consumers MUST
|
|
497
|
+
* NOT treat presence/absence as a user-facing contract. The bundled
|
|
498
|
+
* daemon version plumbing (read from Computer package
|
|
499
|
+
* `dependencies["@slock-ai/daemon"]`) lands as a follow-up; until
|
|
500
|
+
* then writers emit just `computerVersion` and downstream readers
|
|
501
|
+
* default to "unknown" when the field is absent.
|
|
502
|
+
*/
|
|
503
|
+
bundledDaemonVersion?: string;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* v8.3.3 audit-log entry shape, discriminated on `outcome`. Splitting
|
|
507
|
+
* the union means:
|
|
508
|
+
* - `outcome: "ok"` forbids `errorCode` at the type level
|
|
509
|
+
* (`errorCode?: never`)
|
|
510
|
+
* - `outcome: "err"` requires `errorCode: UpgradeErrorCode` (closed-set
|
|
511
|
+
* 7-value membership enforced by TS)
|
|
512
|
+
*
|
|
513
|
+
* Together with the runtime `assertUpgradeLogEntry` guard below, this
|
|
514
|
+
* eliminates the prior "caller-discipline only" silent contract drift
|
|
515
|
+
* (Hao caveat msg=308d99db — `errorCode?: string` allowed any string +
|
|
516
|
+
* silent omission on `err` paths).
|
|
517
|
+
*/
|
|
518
|
+
type UpgradeLogEntry = UpgradeLogEntryOk | UpgradeLogEntryErr;
|
|
519
|
+
interface UpgradeLogEntryOk {
|
|
520
|
+
/** ISO timestamp via formatUpgradeLogTimestamp(); caller may omit to auto-fill. */
|
|
521
|
+
at?: string;
|
|
522
|
+
fromBundle: UpgradeBundle;
|
|
523
|
+
toBundle: UpgradeBundle;
|
|
524
|
+
channel: string;
|
|
525
|
+
trigger: UpgradeTrigger;
|
|
526
|
+
outcome: "ok";
|
|
527
|
+
/** Forbidden on ok entries — discriminated-union enforcement. */
|
|
528
|
+
errorCode?: never;
|
|
529
|
+
}
|
|
530
|
+
interface UpgradeLogEntryErr {
|
|
531
|
+
at?: string;
|
|
532
|
+
fromBundle: UpgradeBundle;
|
|
533
|
+
toBundle: UpgradeBundle;
|
|
534
|
+
channel: string;
|
|
535
|
+
trigger: UpgradeTrigger;
|
|
536
|
+
outcome: "err";
|
|
537
|
+
/** Required iff outcome === "err"; one of the v8.3.3 closed 7-value set. */
|
|
538
|
+
errorCode: UpgradeErrorCode;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Runtime assertion: `entry` is a valid `UpgradeLogEntry`. Throws
|
|
542
|
+
* `TypeError` with an actionable message on contract violation. Defense
|
|
543
|
+
* in depth against JS-interop callers, JSON-deserialized payloads, and
|
|
544
|
+
* future type-system escapes.
|
|
545
|
+
*
|
|
546
|
+
* Validations:
|
|
547
|
+
* - `outcome === "ok"` AND `errorCode` is set → reject
|
|
548
|
+
* - `outcome === "err"` AND `errorCode` is missing → reject
|
|
549
|
+
* - `outcome === "err"` AND `errorCode` not in 7-value closed set → reject
|
|
550
|
+
* - Both bundle objects must have a non-empty `computerVersion` string
|
|
551
|
+
*/
|
|
552
|
+
declare function assertUpgradeLogEntry(entry: UpgradeLogEntry): void;
|
|
553
|
+
|
|
554
|
+
export { type ComputerStatusReport, type ConnectService, type ConnectServiceOptions, type DaemonState, IPC_ERROR_CODES, type IpcErrorCode, type LegacyMachineCandidate, type ListRunnersResult, MIGRATION_DETECTION_KINDS, type MigrationDetection, type MigrationDetectionKind, RUNNER_STATE_VALUES, type RequestMethodMap, type RequestOptions, type ResetRunnerResult, type ResetServiceResult, type RunnerListItem as RunnerInfo, type RunnerListPerServer, type RunnerState, type RunnerStatusResult, SERVICE_STATE_VALUES, STATE_READER_ERROR_CODES, type ServerHealth, type ServerStatusRow, type ServiceClient, ServiceClientError, type ServiceEvent, type ServiceState, type ServiceStatusResult, StateReaderError, type StateReaderErrorCode, UPGRADE_ERROR_CODES, type UpgradeBundle, type UpgradeErrorCode, type UpgradeLogEntry, type UpgradeLogEntryErr, type UpgradeLogEntryOk, type UpgradeStartResult, type UpgradeTrigger, assertUpgradeLogEntry, detectLegacyMigration, isIpcErrorCode, isMigrationDetectionKind, isRunnerState, isServiceState, listRunners, readRunnerStatus, readServiceStatus };
|