@slock-ai/computer 0.0.37 → 0.0.52
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/README.md +9 -0
- package/index.js +3 -0
- package/lib.js +2 -0
- package/package.json +5 -35
- package/dist/index.js +0 -34075
- package/dist/lib/index.d.ts +0 -679
- package/dist/lib/index.js +0 -873
package/dist/lib/index.d.ts
DELETED
|
@@ -1,679 +0,0 @@
|
|
|
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
|
-
serverRunnerLogPath: 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
|
-
service: DaemonState & {
|
|
26
|
-
logPath: string;
|
|
27
|
-
};
|
|
28
|
-
servers: ServerStatusRow[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface LegacyMachineRosterEntry {
|
|
32
|
-
daemonId: string;
|
|
33
|
-
apiKeyFingerprint: string;
|
|
34
|
-
machineName: string;
|
|
35
|
-
hostname: string | null;
|
|
36
|
-
lastSeenAt: string | null;
|
|
37
|
-
}
|
|
38
|
-
type LegacyMachineRosterResult = {
|
|
39
|
-
status: "success";
|
|
40
|
-
entries: LegacyMachineRosterEntry[];
|
|
41
|
-
} | {
|
|
42
|
-
status: "auth_required";
|
|
43
|
-
} | {
|
|
44
|
-
status: "not_authorized";
|
|
45
|
-
} | {
|
|
46
|
-
status: "disabled";
|
|
47
|
-
} | {
|
|
48
|
-
status: "error";
|
|
49
|
-
code: string;
|
|
50
|
-
};
|
|
51
|
-
/** A server the user belongs to. `role` mirrors the server-side
|
|
52
|
-
* `server_members.role` enum (`owner | admin | member`). */
|
|
53
|
-
interface UserServerEntry {
|
|
54
|
-
id: string;
|
|
55
|
-
name: string;
|
|
56
|
-
slug: string;
|
|
57
|
-
role: "owner" | "admin" | "member";
|
|
58
|
-
}
|
|
59
|
-
type UserServersResult = {
|
|
60
|
-
status: "success";
|
|
61
|
-
servers: UserServerEntry[];
|
|
62
|
-
} | {
|
|
63
|
-
status: "auth_required";
|
|
64
|
-
} | {
|
|
65
|
-
status: "error";
|
|
66
|
-
code: string;
|
|
67
|
-
};
|
|
68
|
-
declare class ServersClient {
|
|
69
|
-
private readonly baseUrl;
|
|
70
|
-
private readonly accessToken;
|
|
71
|
-
constructor(baseUrl: string, accessToken: string);
|
|
72
|
-
private url;
|
|
73
|
-
list(): Promise<UserServersResult>;
|
|
74
|
-
}
|
|
75
|
-
declare class LegacyMachinesClient {
|
|
76
|
-
private readonly baseUrl;
|
|
77
|
-
private readonly accessToken;
|
|
78
|
-
constructor(baseUrl: string, accessToken: string);
|
|
79
|
-
private url;
|
|
80
|
-
list(serverSlug: string): Promise<LegacyMachineRosterResult>;
|
|
81
|
-
}
|
|
82
|
-
interface RunnerListItem {
|
|
83
|
-
agentId: string;
|
|
84
|
-
name: string;
|
|
85
|
-
status: string;
|
|
86
|
-
model: string;
|
|
87
|
-
runtime: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Per-runner lifecycle states (§3.2). One state at a time per runner.
|
|
92
|
-
*
|
|
93
|
-
* starting — spawned, not yet reporting ready
|
|
94
|
-
* running — healthy, reporting heartbeats
|
|
95
|
-
* degraded — alive but not satisfying health invariants (e.g. crash
|
|
96
|
-
* budget near breach, see §1/§2)
|
|
97
|
-
* crashed — observed exit / fatal signal
|
|
98
|
-
* stopped — intentional stop (operator-driven via `runners stop` or
|
|
99
|
-
* service shutdown)
|
|
100
|
-
*/
|
|
101
|
-
declare const RUNNER_STATE_VALUES: readonly ["starting", "running", "degraded", "crashed", "stopped"];
|
|
102
|
-
type RunnerState = (typeof RUNNER_STATE_VALUES)[number];
|
|
103
|
-
declare function isRunnerState(value: unknown): value is RunnerState;
|
|
104
|
-
/**
|
|
105
|
-
* Service (service) lifecycle states (§3.2). Mirrors runner shape but
|
|
106
|
-
* narrower — the service is a singleton per install root.
|
|
107
|
-
*
|
|
108
|
-
* starting — pidfile written, socket not yet listening
|
|
109
|
-
* running — socket listening, heartbeat active
|
|
110
|
-
* degraded — service alive but one or more invariants violated
|
|
111
|
-
* (e.g. SERVICE_DEGRADED §7.3 crash-budget breach)
|
|
112
|
-
* stopping — graceful shutdown in flight (SERVICE_SHUTTING_DOWN
|
|
113
|
-
* §7.3 emitted to clients)
|
|
114
|
-
* stopped — pidfile cleared, socket closed
|
|
115
|
-
*/
|
|
116
|
-
declare const SERVICE_STATE_VALUES: readonly ["starting", "running", "degraded", "stopping", "stopped"];
|
|
117
|
-
type ServiceState = (typeof SERVICE_STATE_VALUES)[number];
|
|
118
|
-
declare function isServiceState(value: unknown): value is ServiceState;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* IPC error code family — the seed that `ServiceClient.request` and
|
|
122
|
-
* `ServiceClient.events` propagate. Forward-only post v9-sign: additions
|
|
123
|
-
* are additive minor library bumps; renames / removals are major.
|
|
124
|
-
*
|
|
125
|
-
* Full §7.3 enumeration (SETUP/ATTACH/MIGRATE/UPGRADE/SERVICE families)
|
|
126
|
-
* lands in a follow-up reconcile commit that also unifies the codes
|
|
127
|
-
* currently emitted via `ComputerServiceError` across services/*.
|
|
128
|
-
*/
|
|
129
|
-
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"];
|
|
130
|
-
type IpcErrorCode = (typeof IPC_ERROR_CODES)[number];
|
|
131
|
-
/**
|
|
132
|
-
* Runtime narrow guard — exposed so consumers can pattern-match without
|
|
133
|
-
* pulling in a generic `as` cast. The CI gate (§7.4) over a final
|
|
134
|
-
* `ERROR_CODES` union will subsume this once the reconcile lands.
|
|
135
|
-
*/
|
|
136
|
-
declare function isIpcErrorCode(value: unknown): value is IpcErrorCode;
|
|
137
|
-
/**
|
|
138
|
-
* Caller-facing options for `ServiceClient.request`. Pairs active cancel
|
|
139
|
-
* (`signal` — primary G-stage unmount path) with passive deadline
|
|
140
|
-
* (`timeoutMs` — library-side safety net). On either path the client
|
|
141
|
-
* emits an IPC `cancel` frame for the in-flight request id and rejects
|
|
142
|
-
* the returned promise with `IPC_REQUEST_CANCELED` or
|
|
143
|
-
* `IPC_REQUEST_TIMEOUT` respectively (§4.4).
|
|
144
|
-
*/
|
|
145
|
-
interface RequestOptions {
|
|
146
|
-
signal?: AbortSignal;
|
|
147
|
-
timeoutMs?: number;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Typed RPC table — keys are kebab-case wire literals (§5.5), values are
|
|
151
|
-
* per-method `{ params, result }` pairs. The v9 sign-lock seed listed
|
|
152
|
-
* here is the §3.2 6-method floor; new methods are additive minor bumps
|
|
153
|
-
* (extending this map).
|
|
154
|
-
*
|
|
155
|
-
* State-reader results (`service-status` / `runner-status` /
|
|
156
|
-
* `list-runners`) are pinned to the lib reader return types — the
|
|
157
|
-
* D-stage §4 IPC handlers in PR-impl-2 satisfy the wire surface by
|
|
158
|
-
* delegating to those readers (mechanical `satisfies`).
|
|
159
|
-
*
|
|
160
|
-
* Mutation results (`upgrade-start` / `reset-service` / `reset-runner`)
|
|
161
|
-
* remain `unknown` here — they pin alongside the PR-impl-3 §X migration /
|
|
162
|
-
* verb pipeline that owns the corresponding handlers.
|
|
163
|
-
*/
|
|
164
|
-
interface RequestMethodMap {
|
|
165
|
-
"service-status": {
|
|
166
|
-
params: void;
|
|
167
|
-
result: ServiceStatusResult;
|
|
168
|
-
};
|
|
169
|
-
"runner-status": {
|
|
170
|
-
params: {
|
|
171
|
-
serverId: string;
|
|
172
|
-
};
|
|
173
|
-
result: RunnerStatusResult;
|
|
174
|
-
};
|
|
175
|
-
"list-runners": {
|
|
176
|
-
params: void;
|
|
177
|
-
result: ListRunnersResult;
|
|
178
|
-
};
|
|
179
|
-
"upgrade-start": {
|
|
180
|
-
params: {
|
|
181
|
-
targetVersion?: string;
|
|
182
|
-
};
|
|
183
|
-
result: UpgradeStartResult;
|
|
184
|
-
};
|
|
185
|
-
"reset-service": {
|
|
186
|
-
params: void;
|
|
187
|
-
result: ResetServiceResult;
|
|
188
|
-
};
|
|
189
|
-
"reset-runner": {
|
|
190
|
-
params: {
|
|
191
|
-
serverId: string;
|
|
192
|
-
};
|
|
193
|
-
result: ResetRunnerResult;
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Server-pushed event over `ServiceClient.events`. The discriminant field
|
|
198
|
-
* is `kind` (§5.5 — established at v9.7 sign); values are past-participle
|
|
199
|
-
* kebab-case literals. The 6-kind enumeration is the v9.8 §4.4 floor.
|
|
200
|
-
*
|
|
201
|
-
* Payload shapes finalize alongside §4 IPC impl (PR-impl-2, liuliu-owned);
|
|
202
|
-
* the discriminator is locked here so consumers can write exhaustive
|
|
203
|
-
* switches today.
|
|
204
|
-
*
|
|
205
|
-
* Note: v9.8 §4.4 has an example block still showing the v9.7-pre field
|
|
206
|
-
* name `topic` + present-tense values; §5.5 is the byte-pin source of
|
|
207
|
-
* truth (`kind` + past-participle). The §4.4 example will be reconciled
|
|
208
|
-
* in a follow-up doc-clean pass.
|
|
209
|
-
*/
|
|
210
|
-
type ServiceEvent = {
|
|
211
|
-
kind: "service-state-changed";
|
|
212
|
-
payload: unknown;
|
|
213
|
-
} | {
|
|
214
|
-
kind: "runner-state-changed";
|
|
215
|
-
payload: unknown;
|
|
216
|
-
} | {
|
|
217
|
-
kind: "runner-attached";
|
|
218
|
-
payload: unknown;
|
|
219
|
-
} | {
|
|
220
|
-
kind: "runner-detached";
|
|
221
|
-
payload: unknown;
|
|
222
|
-
} | {
|
|
223
|
-
kind: "upgrade-log-appended";
|
|
224
|
-
payload: unknown;
|
|
225
|
-
} | {
|
|
226
|
-
kind: "heartbeat";
|
|
227
|
-
payload: unknown;
|
|
228
|
-
};
|
|
229
|
-
/**
|
|
230
|
-
* Library-side error envelope thrown by `ServiceClient`. `code` is a
|
|
231
|
-
* member of the §7 closed-set; `cause` is retained in-process only and
|
|
232
|
-
* MUST NOT cross IPC / CLI fail boundaries (§7.4 redaction).
|
|
233
|
-
*/
|
|
234
|
-
declare class ServiceClientError extends Error {
|
|
235
|
-
readonly code: IpcErrorCode;
|
|
236
|
-
readonly cause?: unknown;
|
|
237
|
-
constructor(code: IpcErrorCode, message: string, cause?: unknown);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Typed IPC client. The implementation in PR-impl-2 §4 satisfies this
|
|
241
|
-
* interface; this commit only locks the surface.
|
|
242
|
-
*
|
|
243
|
-
* Termination semantics (§4.7):
|
|
244
|
-
* - `events` completes naturally (`Symbol.asyncIterator` return) on any
|
|
245
|
-
* socket-close path (graceful local/remote, transient, TCP RST). The
|
|
246
|
-
* `for-await` loop exits without throwing — idiomatic reconnect is a
|
|
247
|
-
* plain outer `while (!stopped) { … }` wrap, no try/catch.
|
|
248
|
-
* - `events` THROWS `ServiceClientError` only on protocol-level
|
|
249
|
-
* failures: handshake mismatch, frame parse, frame > 1 MiB.
|
|
250
|
-
* The disjoint split lets consumers distinguish "retry" (iterator end)
|
|
251
|
-
* from "give up" (throw) without runtime classification.
|
|
252
|
-
*/
|
|
253
|
-
interface ServiceClient {
|
|
254
|
-
events: AsyncIterable<ServiceEvent>;
|
|
255
|
-
request<M extends keyof RequestMethodMap>(method: M, params: RequestMethodMap[M]["params"], options?: RequestOptions): Promise<RequestMethodMap[M]["result"]>;
|
|
256
|
-
close(): Promise<void>;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Connection options. `protocolVersion` lets a consumer opt-in to a
|
|
260
|
-
* specific handshake version; the service negotiates highest mutually
|
|
261
|
-
* supported (§4.3) and may reject with `IPC_PROTOCOL_VERSION_UNSUPPORTED`.
|
|
262
|
-
*/
|
|
263
|
-
interface ConnectServiceOptions {
|
|
264
|
-
protocolVersion?: number;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Open a typed IPC connection to the Computer service. Throws
|
|
268
|
-
* `ServiceClientError` on handshake failure. Concrete implementation
|
|
269
|
-
* lands in PR-impl-2 §4 (liuliu-owned); the type is locked here so
|
|
270
|
-
* `apps/slock-computer-app` and other library consumers can type-check
|
|
271
|
-
* call sites before the transport ships.
|
|
272
|
-
*/
|
|
273
|
-
type ConnectService = (installRoot: string, options?: ConnectServiceOptions) => Promise<ServiceClient>;
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Canonical lib-side name for the `service-status` reader result, aligned
|
|
277
|
-
* with the wire method literal. Identical shape to `ComputerStatusReport`
|
|
278
|
-
* (re-exported above for callers that prefer the historical internal
|
|
279
|
-
* name).
|
|
280
|
-
*
|
|
281
|
-
* SECRET-FREE INVARIANT: the report carries server attachment IDENTITY
|
|
282
|
-
* and pid/liveness derived from filesystem state but NEVER the user
|
|
283
|
-
* access token or any `sk_computer_*` credential (status.ts §3.3.1
|
|
284
|
-
* redline, enforced by `status.test.ts` token-leak guards).
|
|
285
|
-
*/
|
|
286
|
-
type ServiceStatusResult = ComputerStatusReport;
|
|
287
|
-
/**
|
|
288
|
-
* Per-server block in `ListRunnersResult.servers[]`. The discriminator
|
|
289
|
-
* preserves the underlying `RunnersClient.list()` outcome so a single
|
|
290
|
-
* server's `unauthorized` / `error` does NOT mask another server's data.
|
|
291
|
-
*/
|
|
292
|
-
type RunnerListPerServer = {
|
|
293
|
-
serverId: string;
|
|
294
|
-
serverSlug: string | null;
|
|
295
|
-
status: "ok";
|
|
296
|
-
whitelist: string[];
|
|
297
|
-
runners: RunnerListItem[];
|
|
298
|
-
} | {
|
|
299
|
-
serverId: string;
|
|
300
|
-
serverSlug: string | null;
|
|
301
|
-
status: "unauthorized";
|
|
302
|
-
} | {
|
|
303
|
-
serverId: string;
|
|
304
|
-
serverSlug: string | null;
|
|
305
|
-
status: "error";
|
|
306
|
-
code: string;
|
|
307
|
-
};
|
|
308
|
-
/**
|
|
309
|
-
* Aggregate result of `listRunners(installRoot, opts?)`. When `opts.serverId`
|
|
310
|
-
* is omitted, `servers[]` contains one block per attached server (zero
|
|
311
|
-
* blocks if the Computer has no attachments). When `opts.serverId` is set,
|
|
312
|
-
* `servers[]` contains exactly that server's block, or the call throws
|
|
313
|
-
* `StateReaderError("NOT_ATTACHED")` if the id is not attached.
|
|
314
|
-
*/
|
|
315
|
-
interface ListRunnersResult {
|
|
316
|
-
servers: RunnerListPerServer[];
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Result of `readRunnerStatus(installRoot, serverId)` — per-server state
|
|
320
|
-
* row plus the runners running on it. Unauthorized / error discriminate
|
|
321
|
-
* the runners-API outcome while still surfacing the `server` row so the
|
|
322
|
-
* caller has the daemon state context.
|
|
323
|
-
*/
|
|
324
|
-
type RunnerStatusResult = {
|
|
325
|
-
status: "ok";
|
|
326
|
-
server: ServerStatusRow;
|
|
327
|
-
whitelist: string[];
|
|
328
|
-
runners: RunnerListItem[];
|
|
329
|
-
} | {
|
|
330
|
-
status: "unauthorized";
|
|
331
|
-
server: ServerStatusRow;
|
|
332
|
-
} | {
|
|
333
|
-
status: "error";
|
|
334
|
-
server: ServerStatusRow;
|
|
335
|
-
code: string;
|
|
336
|
-
};
|
|
337
|
-
/**
|
|
338
|
-
* State-reader error closed set. Library boundary errors only — does NOT
|
|
339
|
-
* include the CLI ambient-rule errors (`NO_ATTACHMENT` / `AMBIGUOUS_SERVER`)
|
|
340
|
-
* which are v4 §6 ergonomics handled by the CLI wrapper, not the lib
|
|
341
|
-
* primitive.
|
|
342
|
-
*/
|
|
343
|
-
declare const STATE_READER_ERROR_CODES: readonly ["NOT_ATTACHED", "INVALID_ATTACHMENT"];
|
|
344
|
-
type StateReaderErrorCode = (typeof STATE_READER_ERROR_CODES)[number];
|
|
345
|
-
/**
|
|
346
|
-
* Library-side error envelope thrown by state-readers. Lib readers
|
|
347
|
-
* NEVER call `fail()` / `process.exit` — CLI wrappers translate this to
|
|
348
|
-
* `fail(code, message)` at the boundary; D-stage IPC handlers map it to
|
|
349
|
-
* an IPC error frame.
|
|
350
|
-
*/
|
|
351
|
-
declare class StateReaderError extends Error {
|
|
352
|
-
readonly code: StateReaderErrorCode;
|
|
353
|
-
constructor(code: StateReaderErrorCode, message: string);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Result of `reset-service` (`slock-computer reset --service`). Clears
|
|
358
|
-
* the service-level `crashHistory` and transitions service state
|
|
359
|
-
* `degraded → running`. MUST NOT kill runners (§1.3).
|
|
360
|
-
*
|
|
361
|
-
* `previousState` reports the state observed prior to the reset write —
|
|
362
|
-
* for callers that want to log/surface "was already running" vs
|
|
363
|
-
* "recovered from degraded". `clearedCrashCount` counts the entries
|
|
364
|
-
* removed from `service.state.json::crashHistory`.
|
|
365
|
-
*/
|
|
366
|
-
interface ResetServiceResult {
|
|
367
|
-
status: "ok";
|
|
368
|
-
previousState: ServiceState;
|
|
369
|
-
clearedCrashCount: number;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Result of `reset-runner` (`slock-computer reset --runner <slug>`).
|
|
373
|
-
* Clears the per-runner `crashHistory` and transitions runner state
|
|
374
|
-
* `degraded → running`. MUST NOT respawn or kill the runner process
|
|
375
|
-
* (§2.4).
|
|
376
|
-
*
|
|
377
|
-
* `status: "not-found"` is returned when the resolved `serverId` does
|
|
378
|
-
* not correspond to an attached server (the CLI ambient `--server` slug
|
|
379
|
-
* resolver returns its own error before the handler is invoked; this
|
|
380
|
-
* branch defends the IPC path where the caller passes a serverId
|
|
381
|
-
* directly).
|
|
382
|
-
*/
|
|
383
|
-
type ResetRunnerResult = {
|
|
384
|
-
status: "ok";
|
|
385
|
-
serverId: string;
|
|
386
|
-
previousState: RunnerState;
|
|
387
|
-
clearedCrashCount: number;
|
|
388
|
-
} | {
|
|
389
|
-
status: "not-found";
|
|
390
|
-
serverId: string;
|
|
391
|
-
};
|
|
392
|
-
/**
|
|
393
|
-
* Result of `upgrade-start` (§12 phase pipeline trigger). The same
|
|
394
|
-
* pipeline is funneled by manual CLI / auto scheduler / IPC; this
|
|
395
|
-
* result describes the trigger outcome only — the per-phase events
|
|
396
|
-
* stream over `ServiceEvent` (`upgrade-log-appended` kind).
|
|
397
|
-
*
|
|
398
|
-
* `status: "already-running"` is returned when an upgrade is already
|
|
399
|
-
* in flight; `upgradeId` then refers to the in-flight upgrade so the
|
|
400
|
-
* caller can attach to its event stream instead of starting a new one.
|
|
401
|
-
*/
|
|
402
|
-
interface UpgradeStartResult {
|
|
403
|
-
status: "started" | "already-running";
|
|
404
|
-
upgradeId: string;
|
|
405
|
-
targetVersion: string;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Identity record for a legacy `@slock-ai/daemon` machine candidate
|
|
409
|
-
* surfaced by `detectLegacyMigration`. Each candidate is the result of
|
|
410
|
-
* intersecting (a) local `<installRoot>/machines/machine-<fp>/owner.json`
|
|
411
|
-
* evidence with (b) the server's `GET /api/computer/legacy-machines`
|
|
412
|
-
* roster on `apiKeyFingerprint` for the logged-in user + target server.
|
|
413
|
-
* The shape is identity-only — it never carries credentials.
|
|
414
|
-
*
|
|
415
|
-
* `apiKeyFingerprint` is the v9.9 intersection key —
|
|
416
|
-
* `sha256(apiKey).slice(0,16)`. The same value is computed locally by
|
|
417
|
-
* the legacy daemon (`packages/daemon/src/machineLock.ts`) and stored
|
|
418
|
-
* server-side on `daemons.api_key_fingerprint` (handshake-backfilled).
|
|
419
|
-
* Stable as long as the legacy api key is unchanged.
|
|
420
|
-
*
|
|
421
|
-
* `daemonId` is the server's `daemons.id` UUID (display + post-migration
|
|
422
|
-
* identity). It is NOT a join key — fingerprint is. Cody redline
|
|
423
|
-
* msg=ec68c27f: server daemonId / machineName are display-only.
|
|
424
|
-
*
|
|
425
|
-
* `localPath` is the absolute path of the local owner.json that
|
|
426
|
-
* supplied the fingerprint side of the intersection. Surfaced so the
|
|
427
|
-
* picker can show the operator which on-disk legacy install they are
|
|
428
|
-
* about to adopt, and so the manual `--migrate-from` flag's three-gate
|
|
429
|
-
* validator (path-readable / owner.json-parseable / fingerprint-in-roster)
|
|
430
|
-
* can return a candidate that points at the same path the operator
|
|
431
|
-
* passed.
|
|
432
|
-
*
|
|
433
|
-
* `machineName` / `hostname` / `lastSeenAt` come from the server roster
|
|
434
|
-
* row — display-only, used for picker presentation. May be empty for
|
|
435
|
-
* stale rows; the picker tolerates blanks.
|
|
436
|
-
*/
|
|
437
|
-
interface LegacyMachineCandidate {
|
|
438
|
-
apiKeyFingerprint: string;
|
|
439
|
-
daemonId: string;
|
|
440
|
-
localPath: string;
|
|
441
|
-
machineName: string;
|
|
442
|
-
hostname?: string;
|
|
443
|
-
lastSeenAt?: string;
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* §X.2 migration detection result — the discriminated union returned by
|
|
447
|
-
* `detectLegacyMigration(installRoot, target)`.
|
|
448
|
-
*
|
|
449
|
-
* Per RFC v9.9 §X.2:
|
|
450
|
-
* - `candidates`: zero-or-more legacy daemons survived the
|
|
451
|
-
* local-owner-json ∩ server-roster intersection. Empty intersection
|
|
452
|
-
* is a valid `candidates` result with `candidates.length === 0` —
|
|
453
|
-
* the setup flow's fresh-attach trigger #1 (§X.4). One-or-more
|
|
454
|
-
* candidates → setup picker prompts the operator (TTY) or falls
|
|
455
|
-
* through to fresh attach (non-TTY).
|
|
456
|
-
* - `server-unavailable`: roster fetch failed (network / auth /
|
|
457
|
-
* server gate off / 5xx). Setup falls through to fresh attach
|
|
458
|
-
* under the documented "best-effort detection" discipline — a
|
|
459
|
-
* transiently unreachable server must NOT block fresh setup.
|
|
460
|
-
*
|
|
461
|
-
* Fingerprint discipline (Cody msg=ec68c27f redline):
|
|
462
|
-
* - `apiKeyFingerprint` is treated as a sensitive identity. It is
|
|
463
|
-
* scoped to this caller's authenticated request and never logged.
|
|
464
|
-
* - The intersection is setup's adoption identity after user login:
|
|
465
|
-
* a candidate proves the server has a roster row keyed by the same
|
|
466
|
-
* fingerprint that appears in the local owner.json. The server
|
|
467
|
-
* still enforces ownership by requiring the session user to own the
|
|
468
|
-
* selected machine row; fingerprint is only the selector.
|
|
469
|
-
*/
|
|
470
|
-
/**
|
|
471
|
-
* Closed set of `MigrationDetection.kind` discriminator values. Mirrors
|
|
472
|
-
* the `IPC_ERROR_CODES` / `RUNNER_STATE_VALUES` `as const` tuple pattern
|
|
473
|
-
* so consumers can pattern-match exhaustively and so a future §7.4 CI
|
|
474
|
-
* gate can verify the runtime kind against the type union without an
|
|
475
|
-
* `as` cast (state-machine-modeling discipline — correlated/mutex
|
|
476
|
-
* properties land as a closed-set state-enum).
|
|
477
|
-
*/
|
|
478
|
-
declare const MIGRATION_DETECTION_KINDS: readonly ["candidates", "server-unavailable"];
|
|
479
|
-
type MigrationDetectionKind = (typeof MIGRATION_DETECTION_KINDS)[number];
|
|
480
|
-
/** Runtime narrow guard for `MigrationDetection.kind`. */
|
|
481
|
-
declare function isMigrationDetectionKind(value: unknown): value is MigrationDetectionKind;
|
|
482
|
-
type MigrationDetection = {
|
|
483
|
-
kind: "candidates";
|
|
484
|
-
candidates: LegacyMachineCandidate[];
|
|
485
|
-
} | {
|
|
486
|
-
kind: "server-unavailable";
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Roster fetcher contract — `LegacyMachinesClient.list(serverSlug)`
|
|
491
|
-
* satisfies this structurally. Defining it as an interface here lets
|
|
492
|
-
* tests stub the network call without instantiating undici.
|
|
493
|
-
*/
|
|
494
|
-
interface LegacyMachineRosterClient {
|
|
495
|
-
list(serverSlug: string): Promise<LegacyMachineRosterResult>;
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Detect whether the logged-in user has a legacy `@slock-ai/daemon`
|
|
499
|
-
* machine on the target server that matches a local install on this
|
|
500
|
-
* host (intersection on `apiKeyFingerprint`).
|
|
501
|
-
*
|
|
502
|
-
* Returns:
|
|
503
|
-
* - `{ kind: "candidates"; candidates: LegacyMachineCandidate[] }` —
|
|
504
|
-
* zero-or-more mutually-known legacy daemons. Empty list is the
|
|
505
|
-
* §X.4 fresh-attach trigger #1; non-empty drives the picker.
|
|
506
|
-
* - `{ kind: "server-unavailable" }` — roster fetch failed
|
|
507
|
-
* transiently. Caller falls through to fresh attach.
|
|
508
|
-
*
|
|
509
|
-
* Lib-pure: no env reads, no terminal IO, no `process.exit`. Local
|
|
510
|
-
* filesystem errors are swallowed; only the roster-fetch outcome can
|
|
511
|
-
* change the discriminator.
|
|
512
|
-
*/
|
|
513
|
-
declare function detectLegacyMigration(installRoot: string, serverSlug: string, client: LegacyMachineRosterClient): Promise<MigrationDetection>;
|
|
514
|
-
/**
|
|
515
|
-
* Manual `--migrate-from <path>` validator — the three-gate chain that
|
|
516
|
-
* RFC v9.9 §X.4 specifies for the manual escape hatch when the picker's
|
|
517
|
-
* intersection set is non-empty but the operator wants to migrate a
|
|
518
|
-
* different on-disk install.
|
|
519
|
-
*
|
|
520
|
-
* Gates (executed in order, hard-fail on first failure):
|
|
521
|
-
* 1. `MIGRATE_FROM_NOT_FOUND` — `path` does not exist or is not a
|
|
522
|
-
* readable directory / file. Accepts either the `machines/
|
|
523
|
-
* machine-<fp>/` directory or its `daemon.lock/owner.json`
|
|
524
|
-
* directly; the latter is what doctor / forensic flows surface.
|
|
525
|
-
* 2. `MIGRATE_FROM_INVALID` — owner.json is unreadable, malformed,
|
|
526
|
-
* or missing a 16-hex-char `apiKeyFingerprint` field.
|
|
527
|
-
* 3. `MIGRATE_FROM_NOT_OWNED` — owner.json's fingerprint is not in
|
|
528
|
-
* the supplied roster (the logged-in user does not have a
|
|
529
|
-
* legacy daemon row with this fingerprint on the target server,
|
|
530
|
-
* or the row is already migrated / NULL-fingerprinted).
|
|
531
|
-
*
|
|
532
|
-
* On success returns the same `LegacyMachineCandidate` shape the
|
|
533
|
-
* picker emits, so the setup driver's adoption call is the same code
|
|
534
|
-
* path on both branches.
|
|
535
|
-
*/
|
|
536
|
-
type ManualPathValidation = {
|
|
537
|
-
ok: true;
|
|
538
|
-
candidate: LegacyMachineCandidate;
|
|
539
|
-
} | {
|
|
540
|
-
ok: false;
|
|
541
|
-
code: "MIGRATE_FROM_NOT_FOUND" | "MIGRATE_FROM_INVALID" | "MIGRATE_FROM_NOT_OWNED";
|
|
542
|
-
};
|
|
543
|
-
declare function validateManualMigratePath(inputPath: string, roster: LegacyMachineRosterEntry[]): Promise<ManualPathValidation>;
|
|
544
|
-
|
|
545
|
-
type PickerSelection = {
|
|
546
|
-
kind: "candidate";
|
|
547
|
-
index: number;
|
|
548
|
-
} | {
|
|
549
|
-
kind: "fresh";
|
|
550
|
-
} | {
|
|
551
|
-
kind: "manual";
|
|
552
|
-
path: string;
|
|
553
|
-
};
|
|
554
|
-
/**
|
|
555
|
-
* Closed-set enumeration of the §X.4 fresh-attach triggers. Used in
|
|
556
|
-
* setup info() lines so QA can grep for "fresh trigger: <name>" and
|
|
557
|
-
* test mocks can assert that ONLY these inputs route to fresh attach
|
|
558
|
-
* (Cody NIT `msg=9102a817`). The picker function itself only emits
|
|
559
|
-
* `explicit-zero` / `eof`; the other three are decided upstream in
|
|
560
|
-
* `runSetup` before the picker ever fires.
|
|
561
|
-
*/
|
|
562
|
-
declare const MIGRATION_FRESH_TRIGGERS: readonly ["empty-intersection", "explicit-zero", "eof", "non-tty", "server-unavailable"];
|
|
563
|
-
type MigrationFreshTrigger = (typeof MIGRATION_FRESH_TRIGGERS)[number];
|
|
564
|
-
/**
|
|
565
|
-
* Pure picker grammar — extracted so it can be exercised by unit tests
|
|
566
|
-
* without stubbing global stdin/stdout. RFC v9.9 §X.4 (post-Jianwei FAIL
|
|
567
|
-
* `msg=ecc2e57e` / Cody NIT `msg=9102a817`):
|
|
568
|
-
* - `1`..`N` / `<Enter>` → adopt that candidate (1-indexed; Enter picks
|
|
569
|
-
* the first candidate, since the operator already saw the
|
|
570
|
-
* intersection list).
|
|
571
|
-
* - `0` → fresh attach (Trigger #2: explicit-zero).
|
|
572
|
-
* - EOF (Ctrl-D / closed stdin) → fresh attach (Trigger #2: eof).
|
|
573
|
-
* - `m` / `M` → emits `manual` and prompts for path on the next line.
|
|
574
|
-
* - Anything else → reprompt. The picker NEVER silently falls through
|
|
575
|
-
* to fresh attach on unrecognized input — that was the B1/B4 contract
|
|
576
|
-
* drift that QA rejected.
|
|
577
|
-
*/
|
|
578
|
-
declare function pickMigrationCandidateFromInput(candidates: LegacyMachineCandidate[], read: () => Promise<{
|
|
579
|
-
line: string;
|
|
580
|
-
eof: boolean;
|
|
581
|
-
}>, write: (s: string) => void): Promise<PickerSelection>;
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Read the Computer-level aggregate status from `installRoot`. Identical
|
|
585
|
-
* shape to `buildStatusReport(installRoot)` — exposed under the
|
|
586
|
-
* wire-aligned name `readServiceStatus` for IPC `service-status` parity.
|
|
587
|
-
*/
|
|
588
|
-
declare function readServiceStatus(installRoot: string): Promise<ServiceStatusResult>;
|
|
589
|
-
/**
|
|
590
|
-
* Read a single attached server's daemon state row plus the runners
|
|
591
|
-
* currently running on it. Throws `StateReaderError("NOT_ATTACHED")` if
|
|
592
|
-
* `serverId` is not in the attached set, or `"INVALID_ATTACHMENT"` if
|
|
593
|
-
* the runner.state.json under that id is unreadable.
|
|
594
|
-
*/
|
|
595
|
-
declare function readRunnerStatus(installRoot: string, serverId: string): Promise<RunnerStatusResult>;
|
|
596
|
-
/**
|
|
597
|
-
* List runners across attached servers. With no `serverId` filter,
|
|
598
|
-
* returns one block per attached server (zero if no attachments). With
|
|
599
|
-
* a `serverId` filter, returns exactly that server's block or throws
|
|
600
|
-
* `StateReaderError("NOT_ATTACHED")`.
|
|
601
|
-
*
|
|
602
|
-
* Per-server discriminator preserves `unauthorized` / `error` outcomes
|
|
603
|
-
* so consumers can pattern-match without losing context.
|
|
604
|
-
*/
|
|
605
|
-
declare function listRunners(installRoot: string, opts?: {
|
|
606
|
-
serverId?: string;
|
|
607
|
-
}): Promise<ListRunnersResult>;
|
|
608
|
-
|
|
609
|
-
type UpgradeTrigger = "cli" | "web" | "tray";
|
|
610
|
-
/**
|
|
611
|
-
* v8.3.3 §4.4 (6) closed-set, verbatim 7 values. Adding / renaming /
|
|
612
|
-
* deleting any value requires an RFC bump (locked discipline). This
|
|
613
|
-
* constant array is the runtime source of truth for `assertUpgradeLogEntry`
|
|
614
|
-
* membership check; the type alias below mirrors it.
|
|
615
|
-
*/
|
|
616
|
-
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"];
|
|
617
|
-
type UpgradeErrorCode = (typeof UPGRADE_ERROR_CODES)[number];
|
|
618
|
-
interface UpgradeBundle {
|
|
619
|
-
computerVersion: string;
|
|
620
|
-
/**
|
|
621
|
-
* Forensic-only per v8.3.2 cleanup. May be omitted; consumers MUST
|
|
622
|
-
* NOT treat presence/absence as a user-facing contract. The bundled
|
|
623
|
-
* daemon version plumbing (read from Computer package
|
|
624
|
-
* `dependencies["@slock-ai/daemon"]`) lands as a follow-up; until
|
|
625
|
-
* then writers emit just `computerVersion` and downstream readers
|
|
626
|
-
* default to "unknown" when the field is absent.
|
|
627
|
-
*/
|
|
628
|
-
bundledDaemonVersion?: string;
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* v8.3.3 audit-log entry shape, discriminated on `outcome`. Splitting
|
|
632
|
-
* the union means:
|
|
633
|
-
* - `outcome: "ok"` forbids `errorCode` at the type level
|
|
634
|
-
* (`errorCode?: never`)
|
|
635
|
-
* - `outcome: "err"` requires `errorCode: UpgradeErrorCode` (closed-set
|
|
636
|
-
* 7-value membership enforced by TS)
|
|
637
|
-
*
|
|
638
|
-
* Together with the runtime `assertUpgradeLogEntry` guard below, this
|
|
639
|
-
* eliminates the prior "caller-discipline only" silent contract drift
|
|
640
|
-
* (Hao caveat msg=308d99db — `errorCode?: string` allowed any string +
|
|
641
|
-
* silent omission on `err` paths).
|
|
642
|
-
*/
|
|
643
|
-
type UpgradeLogEntry = UpgradeLogEntryOk | UpgradeLogEntryErr;
|
|
644
|
-
interface UpgradeLogEntryOk {
|
|
645
|
-
/** ISO timestamp via formatUpgradeLogTimestamp(); caller may omit to auto-fill. */
|
|
646
|
-
at?: string;
|
|
647
|
-
fromBundle: UpgradeBundle;
|
|
648
|
-
toBundle: UpgradeBundle;
|
|
649
|
-
channel: string;
|
|
650
|
-
trigger: UpgradeTrigger;
|
|
651
|
-
outcome: "ok";
|
|
652
|
-
/** Forbidden on ok entries — discriminated-union enforcement. */
|
|
653
|
-
errorCode?: never;
|
|
654
|
-
}
|
|
655
|
-
interface UpgradeLogEntryErr {
|
|
656
|
-
at?: string;
|
|
657
|
-
fromBundle: UpgradeBundle;
|
|
658
|
-
toBundle: UpgradeBundle;
|
|
659
|
-
channel: string;
|
|
660
|
-
trigger: UpgradeTrigger;
|
|
661
|
-
outcome: "err";
|
|
662
|
-
/** Required iff outcome === "err"; one of the v8.3.3 closed 7-value set. */
|
|
663
|
-
errorCode: UpgradeErrorCode;
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Runtime assertion: `entry` is a valid `UpgradeLogEntry`. Throws
|
|
667
|
-
* `TypeError` with an actionable message on contract violation. Defense
|
|
668
|
-
* in depth against JS-interop callers, JSON-deserialized payloads, and
|
|
669
|
-
* future type-system escapes.
|
|
670
|
-
*
|
|
671
|
-
* Validations:
|
|
672
|
-
* - `outcome === "ok"` AND `errorCode` is set → reject
|
|
673
|
-
* - `outcome === "err"` AND `errorCode` is missing → reject
|
|
674
|
-
* - `outcome === "err"` AND `errorCode` not in 7-value closed set → reject
|
|
675
|
-
* - Both bundle objects must have a non-empty `computerVersion` string
|
|
676
|
-
*/
|
|
677
|
-
declare function assertUpgradeLogEntry(entry: UpgradeLogEntry): void;
|
|
678
|
-
|
|
679
|
-
export { type ComputerStatusReport, type ConnectService, type ConnectServiceOptions, type DaemonState, IPC_ERROR_CODES, type IpcErrorCode, type LegacyMachineCandidate, type LegacyMachineRosterClient, type LegacyMachineRosterEntry, type LegacyMachineRosterResult, LegacyMachinesClient, type ListRunnersResult, MIGRATION_DETECTION_KINDS, MIGRATION_FRESH_TRIGGERS, type ManualPathValidation, type MigrationDetection, type MigrationDetectionKind, type MigrationFreshTrigger, type PickerSelection, 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, ServersClient, 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, type UserServerEntry, type UserServersResult, assertUpgradeLogEntry, detectLegacyMigration, isIpcErrorCode, isMigrationDetectionKind, isRunnerState, isServiceState, listRunners, pickMigrationCandidateFromInput, readRunnerStatus, readServiceStatus, validateManualMigratePath };
|