@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.js
DELETED
|
@@ -1,873 +0,0 @@
|
|
|
1
|
-
// src/lib/types.ts
|
|
2
|
-
var IPC_ERROR_CODES = [
|
|
3
|
-
"IPC_FRAME_TOO_LARGE",
|
|
4
|
-
"IPC_PROTOCOL_VERSION_UNSUPPORTED",
|
|
5
|
-
"IPC_PROTOCOL_HANDSHAKE_FAILED",
|
|
6
|
-
"IPC_HEARTBEAT_TIMEOUT",
|
|
7
|
-
"IPC_MALFORMED_FRAME",
|
|
8
|
-
"IPC_REQUEST_TIMEOUT",
|
|
9
|
-
"IPC_REQUEST_CANCELED",
|
|
10
|
-
"IPC_CLIENT_CLOSED"
|
|
11
|
-
];
|
|
12
|
-
function isIpcErrorCode(value) {
|
|
13
|
-
return typeof value === "string" && IPC_ERROR_CODES.includes(value);
|
|
14
|
-
}
|
|
15
|
-
var ServiceClientError = class extends Error {
|
|
16
|
-
code;
|
|
17
|
-
cause;
|
|
18
|
-
constructor(code, message, cause) {
|
|
19
|
-
super(message);
|
|
20
|
-
this.name = "ServiceClientError";
|
|
21
|
-
this.code = code;
|
|
22
|
-
if (cause !== void 0) this.cause = cause;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
var STATE_READER_ERROR_CODES = ["NOT_ATTACHED", "INVALID_ATTACHMENT"];
|
|
26
|
-
var StateReaderError = class extends Error {
|
|
27
|
-
code;
|
|
28
|
-
constructor(code, message) {
|
|
29
|
-
super(message);
|
|
30
|
-
this.name = "StateReaderError";
|
|
31
|
-
this.code = code;
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
var MIGRATION_DETECTION_KINDS = ["candidates", "server-unavailable"];
|
|
35
|
-
function isMigrationDetectionKind(value) {
|
|
36
|
-
return typeof value === "string" && MIGRATION_DETECTION_KINDS.includes(value);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// src/lib/migration.ts
|
|
40
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
41
|
-
import { join, resolve } from "path";
|
|
42
|
-
var MACHINE_DIR_PREFIX = "machine-";
|
|
43
|
-
var FINGERPRINT_HEX_RE = /^[0-9a-f]{16}$/;
|
|
44
|
-
async function readLocalOwners(installRoot) {
|
|
45
|
-
const machinesDir = join(installRoot, "machines");
|
|
46
|
-
let entries;
|
|
47
|
-
try {
|
|
48
|
-
entries = await readdir(machinesDir);
|
|
49
|
-
} catch {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
const owners = [];
|
|
53
|
-
for (const name of entries) {
|
|
54
|
-
if (!name.startsWith(MACHINE_DIR_PREFIX)) continue;
|
|
55
|
-
const ownerPath = join(machinesDir, name, "daemon.lock", "owner.json");
|
|
56
|
-
let raw;
|
|
57
|
-
try {
|
|
58
|
-
raw = await readFile(ownerPath, "utf8");
|
|
59
|
-
} catch (err) {
|
|
60
|
-
if (err?.code === "ENOENT") {
|
|
61
|
-
const fromDir = name.slice(MACHINE_DIR_PREFIX.length);
|
|
62
|
-
if (FINGERPRINT_HEX_RE.test(fromDir)) {
|
|
63
|
-
owners.push({ apiKeyFingerprint: fromDir, localPath: join(machinesDir, name) });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
let parsed;
|
|
69
|
-
try {
|
|
70
|
-
parsed = JSON.parse(raw);
|
|
71
|
-
} catch {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const fp = parsed.apiKeyFingerprint;
|
|
75
|
-
if (typeof fp !== "string" || !FINGERPRINT_HEX_RE.test(fp)) continue;
|
|
76
|
-
owners.push({ apiKeyFingerprint: fp, localPath: ownerPath });
|
|
77
|
-
}
|
|
78
|
-
return owners;
|
|
79
|
-
}
|
|
80
|
-
function indexLocalByFingerprint(owners) {
|
|
81
|
-
const map = /* @__PURE__ */ new Map();
|
|
82
|
-
for (const owner of owners) {
|
|
83
|
-
if (!map.has(owner.apiKeyFingerprint)) {
|
|
84
|
-
map.set(owner.apiKeyFingerprint, owner);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return map;
|
|
88
|
-
}
|
|
89
|
-
function intersect(localByFp, roster) {
|
|
90
|
-
const out = [];
|
|
91
|
-
for (const entry of roster) {
|
|
92
|
-
const local = localByFp.get(entry.apiKeyFingerprint);
|
|
93
|
-
if (!local) continue;
|
|
94
|
-
out.push({
|
|
95
|
-
apiKeyFingerprint: entry.apiKeyFingerprint,
|
|
96
|
-
daemonId: entry.daemonId,
|
|
97
|
-
localPath: local.localPath,
|
|
98
|
-
machineName: entry.machineName,
|
|
99
|
-
...entry.hostname ? { hostname: entry.hostname } : {},
|
|
100
|
-
...entry.lastSeenAt ? { lastSeenAt: entry.lastSeenAt } : {}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
out.sort((a, b) => {
|
|
104
|
-
const aSeen = a.lastSeenAt ?? "";
|
|
105
|
-
const bSeen = b.lastSeenAt ?? "";
|
|
106
|
-
if (aSeen !== bSeen) {
|
|
107
|
-
if (aSeen === "") return 1;
|
|
108
|
-
if (bSeen === "") return -1;
|
|
109
|
-
return aSeen < bSeen ? 1 : -1;
|
|
110
|
-
}
|
|
111
|
-
return a.apiKeyFingerprint < b.apiKeyFingerprint ? -1 : a.apiKeyFingerprint > b.apiKeyFingerprint ? 1 : 0;
|
|
112
|
-
});
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
async function detectLegacyMigration(installRoot, serverSlug, client) {
|
|
116
|
-
const localOwners = await readLocalOwners(installRoot);
|
|
117
|
-
if (localOwners.length === 0) {
|
|
118
|
-
return { kind: "candidates", candidates: [] };
|
|
119
|
-
}
|
|
120
|
-
const result = await client.list(serverSlug);
|
|
121
|
-
if (result.status !== "success") {
|
|
122
|
-
return { kind: "server-unavailable" };
|
|
123
|
-
}
|
|
124
|
-
const localByFp = indexLocalByFingerprint(localOwners);
|
|
125
|
-
return { kind: "candidates", candidates: intersect(localByFp, result.entries) };
|
|
126
|
-
}
|
|
127
|
-
async function validateManualMigratePath(inputPath, roster) {
|
|
128
|
-
const absInput = resolve(inputPath);
|
|
129
|
-
let stats;
|
|
130
|
-
try {
|
|
131
|
-
stats = await stat(absInput);
|
|
132
|
-
} catch {
|
|
133
|
-
return { ok: false, code: "MIGRATE_FROM_NOT_FOUND" };
|
|
134
|
-
}
|
|
135
|
-
const ownerPath = stats.isDirectory() ? join(absInput, "daemon.lock", "owner.json") : absInput;
|
|
136
|
-
const candidateOwnerPath = await firstReadableOwner([
|
|
137
|
-
ownerPath,
|
|
138
|
-
join(absInput, "owner.json"),
|
|
139
|
-
absInput
|
|
140
|
-
]);
|
|
141
|
-
if (!candidateOwnerPath) {
|
|
142
|
-
return { ok: false, code: "MIGRATE_FROM_INVALID" };
|
|
143
|
-
}
|
|
144
|
-
let raw;
|
|
145
|
-
try {
|
|
146
|
-
raw = await readFile(candidateOwnerPath, "utf8");
|
|
147
|
-
} catch {
|
|
148
|
-
return { ok: false, code: "MIGRATE_FROM_INVALID" };
|
|
149
|
-
}
|
|
150
|
-
let parsed;
|
|
151
|
-
try {
|
|
152
|
-
parsed = JSON.parse(raw);
|
|
153
|
-
} catch {
|
|
154
|
-
return { ok: false, code: "MIGRATE_FROM_INVALID" };
|
|
155
|
-
}
|
|
156
|
-
const fp = parsed.apiKeyFingerprint;
|
|
157
|
-
if (typeof fp !== "string" || !FINGERPRINT_HEX_RE.test(fp)) {
|
|
158
|
-
return { ok: false, code: "MIGRATE_FROM_INVALID" };
|
|
159
|
-
}
|
|
160
|
-
const match = roster.find((r) => r.apiKeyFingerprint === fp);
|
|
161
|
-
if (!match) {
|
|
162
|
-
return { ok: false, code: "MIGRATE_FROM_NOT_OWNED" };
|
|
163
|
-
}
|
|
164
|
-
return {
|
|
165
|
-
ok: true,
|
|
166
|
-
candidate: {
|
|
167
|
-
apiKeyFingerprint: match.apiKeyFingerprint,
|
|
168
|
-
daemonId: match.daemonId,
|
|
169
|
-
localPath: candidateOwnerPath,
|
|
170
|
-
machineName: match.machineName,
|
|
171
|
-
...match.hostname ? { hostname: match.hostname } : {},
|
|
172
|
-
...match.lastSeenAt ? { lastSeenAt: match.lastSeenAt } : {}
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
async function firstReadableOwner(paths) {
|
|
177
|
-
const seen = /* @__PURE__ */ new Set();
|
|
178
|
-
for (const p of paths) {
|
|
179
|
-
if (seen.has(p)) continue;
|
|
180
|
-
seen.add(p);
|
|
181
|
-
try {
|
|
182
|
-
const st = await stat(p);
|
|
183
|
-
if (st.isFile()) return p;
|
|
184
|
-
} catch {
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// src/apiClient.ts
|
|
191
|
-
import { fetch as fetch2 } from "undici";
|
|
192
|
-
var ServersClient = class {
|
|
193
|
-
constructor(baseUrl, accessToken) {
|
|
194
|
-
this.baseUrl = baseUrl;
|
|
195
|
-
this.accessToken = accessToken;
|
|
196
|
-
}
|
|
197
|
-
url(p) {
|
|
198
|
-
return new URL(p, this.baseUrl).toString();
|
|
199
|
-
}
|
|
200
|
-
async list() {
|
|
201
|
-
let res;
|
|
202
|
-
try {
|
|
203
|
-
res = await fetch2(this.url("/api/servers/"), {
|
|
204
|
-
method: "GET",
|
|
205
|
-
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
206
|
-
});
|
|
207
|
-
} catch {
|
|
208
|
-
return { status: "error", code: "request_failed" };
|
|
209
|
-
}
|
|
210
|
-
if (res.status === 401) return { status: "auth_required" };
|
|
211
|
-
const body = await res.json().catch(() => null);
|
|
212
|
-
if (res.status === 200 && Array.isArray(body)) {
|
|
213
|
-
const servers = body.map((raw) => {
|
|
214
|
-
if (!raw || typeof raw !== "object") return null;
|
|
215
|
-
const e = raw;
|
|
216
|
-
if (typeof e.id !== "string" || typeof e.slug !== "string" || typeof e.role !== "string") {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
if (e.role !== "owner" && e.role !== "admin" && e.role !== "member") {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
id: e.id,
|
|
224
|
-
name: typeof e.name === "string" ? e.name : e.slug,
|
|
225
|
-
slug: e.slug,
|
|
226
|
-
role: e.role
|
|
227
|
-
};
|
|
228
|
-
}).filter((entry) => entry !== null);
|
|
229
|
-
return { status: "success", servers };
|
|
230
|
-
}
|
|
231
|
-
const errBody = body;
|
|
232
|
-
const code = errBody && typeof errBody.code === "string" ? errBody.code : `http_${res.status}`;
|
|
233
|
-
return { status: "error", code };
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
var LegacyMachinesClient = class {
|
|
237
|
-
constructor(baseUrl, accessToken) {
|
|
238
|
-
this.baseUrl = baseUrl;
|
|
239
|
-
this.accessToken = accessToken;
|
|
240
|
-
}
|
|
241
|
-
url(p) {
|
|
242
|
-
return new URL(p, this.baseUrl).toString();
|
|
243
|
-
}
|
|
244
|
-
async list(serverSlug) {
|
|
245
|
-
let res;
|
|
246
|
-
try {
|
|
247
|
-
res = await fetch2(
|
|
248
|
-
this.url(`/api/computer/legacy-machines?serverSlug=${encodeURIComponent(serverSlug)}`),
|
|
249
|
-
{
|
|
250
|
-
method: "GET",
|
|
251
|
-
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
|
-
} catch {
|
|
255
|
-
return { status: "error", code: "request_failed" };
|
|
256
|
-
}
|
|
257
|
-
const body = await res.json().catch(() => null);
|
|
258
|
-
if (res.status === 200 && body && Array.isArray(body.entries)) {
|
|
259
|
-
const entries = body.entries.map((raw) => {
|
|
260
|
-
if (!raw || typeof raw !== "object") return null;
|
|
261
|
-
const e = raw;
|
|
262
|
-
if (typeof e.daemonId !== "string" || typeof e.apiKeyFingerprint !== "string" || typeof e.machineName !== "string") {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
return {
|
|
266
|
-
daemonId: e.daemonId,
|
|
267
|
-
apiKeyFingerprint: e.apiKeyFingerprint,
|
|
268
|
-
machineName: e.machineName,
|
|
269
|
-
hostname: typeof e.hostname === "string" ? e.hostname : null,
|
|
270
|
-
lastSeenAt: typeof e.lastSeenAt === "string" ? e.lastSeenAt : null
|
|
271
|
-
};
|
|
272
|
-
}).filter((entry) => entry !== null);
|
|
273
|
-
return { status: "success", entries };
|
|
274
|
-
}
|
|
275
|
-
if (res.status === 401) return { status: "auth_required" };
|
|
276
|
-
if (res.status === 403) return { status: "not_authorized" };
|
|
277
|
-
if (res.status === 404) return { status: "disabled" };
|
|
278
|
-
const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
|
|
279
|
-
return { status: "error", code };
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
var RunnersClient = class {
|
|
283
|
-
constructor(baseUrl, computerApiKey) {
|
|
284
|
-
this.baseUrl = baseUrl;
|
|
285
|
-
this.computerApiKey = computerApiKey;
|
|
286
|
-
}
|
|
287
|
-
url(p) {
|
|
288
|
-
return new URL(p, this.baseUrl).toString();
|
|
289
|
-
}
|
|
290
|
-
async list() {
|
|
291
|
-
const res = await fetch2(this.url("/internal/computer/runners"), {
|
|
292
|
-
method: "GET",
|
|
293
|
-
headers: { Authorization: `Bearer ${this.computerApiKey}` }
|
|
294
|
-
});
|
|
295
|
-
const body = await res.json().catch(() => null);
|
|
296
|
-
if (res.status === 200 && body && Array.isArray(body.runners)) {
|
|
297
|
-
return {
|
|
298
|
-
status: "success",
|
|
299
|
-
whitelist: Array.isArray(body.whitelist) ? body.whitelist : [],
|
|
300
|
-
runners: body.runners
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
if (res.status === 401 || res.status === 403) return { status: "unauthorized" };
|
|
304
|
-
const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
|
|
305
|
-
return { status: "error", code };
|
|
306
|
-
}
|
|
307
|
-
async stop(agentId) {
|
|
308
|
-
const res = await fetch2(this.url(`/internal/computer/runners/${encodeURIComponent(agentId)}/stop`), {
|
|
309
|
-
method: "POST",
|
|
310
|
-
headers: { Authorization: `Bearer ${this.computerApiKey}`, "Content-Type": "application/json" },
|
|
311
|
-
body: "{}"
|
|
312
|
-
});
|
|
313
|
-
if (res.status === 200) return { status: "success" };
|
|
314
|
-
if (res.status === 404) return { status: "not_found" };
|
|
315
|
-
if (res.status === 401 || res.status === 403) return { status: "unauthorized" };
|
|
316
|
-
const body = await res.json().catch(() => null);
|
|
317
|
-
const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
|
|
318
|
-
return { status: "error", code };
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
// src/setup.ts
|
|
323
|
-
import { chmod as chmod5, mkdir as mkdir11, readFile as readFile10, rename as rename4, rm as rm3, writeFile as writeFile10 } from "fs/promises";
|
|
324
|
-
import { dirname as dirname9 } from "path";
|
|
325
|
-
import { fetch as fetch4 } from "undici";
|
|
326
|
-
|
|
327
|
-
// src/serverState.ts
|
|
328
|
-
import { readFile as readFile2, readdir as readdir2, writeFile, mkdir, unlink, access, chmod } from "fs/promises";
|
|
329
|
-
import { dirname } from "path";
|
|
330
|
-
import { constants as fsConstants } from "fs";
|
|
331
|
-
|
|
332
|
-
// src/paths.ts
|
|
333
|
-
import { createHash } from "crypto";
|
|
334
|
-
import os from "os";
|
|
335
|
-
import path from "path";
|
|
336
|
-
function computerDir(slockHome) {
|
|
337
|
-
return path.join(slockHome, "computer");
|
|
338
|
-
}
|
|
339
|
-
function userSessionPath(slockHome) {
|
|
340
|
-
return path.join(computerDir(slockHome), "user-session.json");
|
|
341
|
-
}
|
|
342
|
-
var SERVER_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
343
|
-
function isValidServerId(serverId) {
|
|
344
|
-
return SERVER_ID_RE.test(serverId);
|
|
345
|
-
}
|
|
346
|
-
function assertValidServerId(serverId) {
|
|
347
|
-
if (!isValidServerId(serverId)) {
|
|
348
|
-
throw new Error(`invalid server id: ${JSON.stringify(serverId)} (expected a UUID)`);
|
|
349
|
-
}
|
|
350
|
-
return serverId;
|
|
351
|
-
}
|
|
352
|
-
function serversDir(slockHome) {
|
|
353
|
-
return path.join(computerDir(slockHome), "servers");
|
|
354
|
-
}
|
|
355
|
-
function serverDir(slockHome, serverId) {
|
|
356
|
-
return path.join(serversDir(slockHome), assertValidServerId(serverId));
|
|
357
|
-
}
|
|
358
|
-
function serverAttachmentPath(slockHome, serverId) {
|
|
359
|
-
return path.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
360
|
-
}
|
|
361
|
-
function legacyServerAttachmentPath(slockHome, serverId) {
|
|
362
|
-
return path.join(serverDir(slockHome, serverId), "attachment.json");
|
|
363
|
-
}
|
|
364
|
-
function serverRunnerPidPath(slockHome, serverId) {
|
|
365
|
-
return path.join(serverDir(slockHome, serverId), "server-runner.pid");
|
|
366
|
-
}
|
|
367
|
-
function serverRunnerLogPath(slockHome, serverId) {
|
|
368
|
-
return path.join(serverDir(slockHome, serverId), "server-runner.log");
|
|
369
|
-
}
|
|
370
|
-
function serverHealthPath(slockHome, serverId) {
|
|
371
|
-
return path.join(serverDir(slockHome, serverId), "health.json");
|
|
372
|
-
}
|
|
373
|
-
function serviceRunDir(slockHome) {
|
|
374
|
-
return path.join(computerDir(slockHome), "run");
|
|
375
|
-
}
|
|
376
|
-
function servicePidPath(slockHome) {
|
|
377
|
-
return path.join(serviceRunDir(slockHome), "service.pid");
|
|
378
|
-
}
|
|
379
|
-
function legacyServicePidPath(slockHome) {
|
|
380
|
-
return path.join(computerDir(slockHome), "service.pid");
|
|
381
|
-
}
|
|
382
|
-
function legacySupervisorPidPath(slockHome) {
|
|
383
|
-
return path.join(computerDir(slockHome), "supervisor.pid");
|
|
384
|
-
}
|
|
385
|
-
function servicePidReadFallback(slockHome) {
|
|
386
|
-
return [
|
|
387
|
-
servicePidPath(slockHome),
|
|
388
|
-
legacyServicePidPath(slockHome),
|
|
389
|
-
legacySupervisorPidPath(slockHome)
|
|
390
|
-
];
|
|
391
|
-
}
|
|
392
|
-
function serviceLogPath(slockHome) {
|
|
393
|
-
return path.join(serviceRunDir(slockHome), "service.log");
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/serverState.ts
|
|
397
|
-
function parseAttachment(raw) {
|
|
398
|
-
try {
|
|
399
|
-
const a = JSON.parse(raw);
|
|
400
|
-
if (a.kind === "computer-attachment" && typeof a.serverId === "string" && typeof a.serverMachineId === "string" && typeof a.apiKey === "string" && a.apiKey.length > 0 && typeof a.serverUrl === "string") {
|
|
401
|
-
return {
|
|
402
|
-
kind: "computer-attachment",
|
|
403
|
-
serverId: a.serverId,
|
|
404
|
-
serverSlug: typeof a.serverSlug === "string" && a.serverSlug.length > 0 ? a.serverSlug : void 0,
|
|
405
|
-
serverMachineId: a.serverMachineId,
|
|
406
|
-
apiKey: a.apiKey,
|
|
407
|
-
serverUrl: a.serverUrl,
|
|
408
|
-
attachedAt: typeof a.attachedAt === "string" ? a.attachedAt : void 0,
|
|
409
|
-
adoptedFromLegacy: a.adoptedFromLegacy === true ? true : void 0,
|
|
410
|
-
legacyMachineId: typeof a.legacyMachineId === "string" ? a.legacyMachineId : void 0
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
} catch {
|
|
414
|
-
}
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
async function readAttachmentAt(path2) {
|
|
418
|
-
try {
|
|
419
|
-
return parseAttachment(await readFile2(path2, "utf8"));
|
|
420
|
-
} catch {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
async function readServerAttachment(slockHome, serverId) {
|
|
425
|
-
if (!isValidServerId(serverId)) return null;
|
|
426
|
-
const current = await readAttachmentAt(serverAttachmentPath(slockHome, serverId));
|
|
427
|
-
const legacy = await readAttachmentAt(legacyServerAttachmentPath(slockHome, serverId));
|
|
428
|
-
if (!current) return legacy;
|
|
429
|
-
if (!legacy) return current;
|
|
430
|
-
if (legacy.adoptedFromLegacy === true && current.adoptedFromLegacy !== true && legacy.serverMachineId !== current.serverMachineId) {
|
|
431
|
-
return legacy;
|
|
432
|
-
}
|
|
433
|
-
return current;
|
|
434
|
-
}
|
|
435
|
-
async function listAttachedServerIds(slockHome) {
|
|
436
|
-
let entries;
|
|
437
|
-
try {
|
|
438
|
-
entries = await readdir2(serversDir(slockHome));
|
|
439
|
-
} catch {
|
|
440
|
-
return [];
|
|
441
|
-
}
|
|
442
|
-
const ids = [];
|
|
443
|
-
for (const name of entries) {
|
|
444
|
-
if (!isValidServerId(name)) continue;
|
|
445
|
-
if (await readServerAttachment(slockHome, name)) ids.push(name);
|
|
446
|
-
}
|
|
447
|
-
return ids.sort();
|
|
448
|
-
}
|
|
449
|
-
async function listServerAttachments(slockHome) {
|
|
450
|
-
const ids = await listAttachedServerIds(slockHome);
|
|
451
|
-
const out = [];
|
|
452
|
-
for (const id of ids) {
|
|
453
|
-
const a = await readServerAttachment(slockHome, id);
|
|
454
|
-
if (a) out.push(a);
|
|
455
|
-
}
|
|
456
|
-
return out;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// src/services/attach.ts
|
|
460
|
-
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
461
|
-
import { dirname as dirname2 } from "path";
|
|
462
|
-
|
|
463
|
-
// src/services/login.ts
|
|
464
|
-
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
465
|
-
import { dirname as dirname3 } from "path";
|
|
466
|
-
|
|
467
|
-
// src/services/adoptLegacy.ts
|
|
468
|
-
import { chmod as chmod3, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, appendFile, stat as stat2 } from "fs/promises";
|
|
469
|
-
import { createHash as createHash2 } from "crypto";
|
|
470
|
-
import { dirname as dirname4, join as join2 } from "path";
|
|
471
|
-
import { setTimeout as delay } from "timers/promises";
|
|
472
|
-
|
|
473
|
-
// src/service.ts
|
|
474
|
-
import { spawn as spawn2 } from "child_process";
|
|
475
|
-
import { createRequire as createRequire2 } from "module";
|
|
476
|
-
|
|
477
|
-
// src/version.ts
|
|
478
|
-
import { createRequire } from "module";
|
|
479
|
-
function readComputerVersion(moduleUrl = import.meta.url) {
|
|
480
|
-
const baked = process.env.SLOCK_COMPUTER_VERSION;
|
|
481
|
-
if (typeof baked === "string" && baked.length > 0) return baked;
|
|
482
|
-
try {
|
|
483
|
-
const require2 = createRequire(moduleUrl);
|
|
484
|
-
const pkg = require2("../package.json");
|
|
485
|
-
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "0.0.0-dev";
|
|
486
|
-
} catch {
|
|
487
|
-
return "0.0.0-dev";
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
var COMPUTER_VERSION = readComputerVersion();
|
|
491
|
-
|
|
492
|
-
// src/service.ts
|
|
493
|
-
import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile9, open, rename as rename3 } from "fs/promises";
|
|
494
|
-
import { dirname as dirname8, join as joinPath } from "path";
|
|
495
|
-
import { fileURLToPath } from "url";
|
|
496
|
-
|
|
497
|
-
// src/cleanup.ts
|
|
498
|
-
import { readdir as readdir3, stat as stat3, unlink as unlink3, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
|
|
499
|
-
import { spawn } from "child_process";
|
|
500
|
-
import { join as join3 } from "path";
|
|
501
|
-
|
|
502
|
-
// src/internal/process-primitives.ts
|
|
503
|
-
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as unlink2 } from "fs/promises";
|
|
504
|
-
import { dirname as dirname5 } from "path";
|
|
505
|
-
async function readPidfileAt(pidfilePath) {
|
|
506
|
-
try {
|
|
507
|
-
const raw = (await readFile5(pidfilePath, "utf8")).trim();
|
|
508
|
-
const pid = Number.parseInt(raw, 10);
|
|
509
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
510
|
-
} catch {
|
|
511
|
-
return null;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
function isProcessAlive(pid) {
|
|
515
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
516
|
-
try {
|
|
517
|
-
process.kill(pid, 0);
|
|
518
|
-
return true;
|
|
519
|
-
} catch (err) {
|
|
520
|
-
return err.code === "EPERM";
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// src/cleanup.ts
|
|
525
|
-
var TMP_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
526
|
-
|
|
527
|
-
// src/health.ts
|
|
528
|
-
import { readFile as readFile6, writeFile as writeFile6, unlink as unlink4, mkdir as mkdir7, appendFile as appendFile2 } from "fs/promises";
|
|
529
|
-
import { dirname as dirname6 } from "path";
|
|
530
|
-
var CRASH_WINDOW_MS = 6e4;
|
|
531
|
-
var DEGRADED_THRESHOLD = 3;
|
|
532
|
-
async function readHealthFile(slockHome, serverId) {
|
|
533
|
-
if (!isValidServerId(serverId)) return { crashes: [] };
|
|
534
|
-
try {
|
|
535
|
-
const raw = await readFile6(serverHealthPath(slockHome, serverId), "utf8");
|
|
536
|
-
const parsed = JSON.parse(raw);
|
|
537
|
-
if (parsed && typeof parsed === "object" && Array.isArray(parsed.crashes)) {
|
|
538
|
-
return parsed;
|
|
539
|
-
}
|
|
540
|
-
} catch {
|
|
541
|
-
}
|
|
542
|
-
return { crashes: [] };
|
|
543
|
-
}
|
|
544
|
-
async function isDegraded(slockHome, serverId, nowMs = Date.now()) {
|
|
545
|
-
const file = await readHealthFile(slockHome, serverId);
|
|
546
|
-
if (file.fatalConfig) return true;
|
|
547
|
-
const cutoffMs = nowMs - CRASH_WINDOW_MS;
|
|
548
|
-
const recent = file.crashes.filter((c) => {
|
|
549
|
-
const t = new Date(c.at).getTime();
|
|
550
|
-
return Number.isFinite(t) && t >= cutoffMs;
|
|
551
|
-
});
|
|
552
|
-
return recent.length >= DEGRADED_THRESHOLD;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// src/internal/service-pid-fallback.ts
|
|
556
|
-
async function findLiveServicePidReadOnly(slockHome, deps = {}) {
|
|
557
|
-
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
558
|
-
const isAlive = deps.isProcessAlive ?? isProcessAlive;
|
|
559
|
-
return walkFallback(slockHome, readPidfile, isAlive, async () => void 0);
|
|
560
|
-
}
|
|
561
|
-
async function walkFallback(slockHome, readPidfile, isAlive, clearStale) {
|
|
562
|
-
const candidates = servicePidReadFallback(slockHome);
|
|
563
|
-
let firstStalePidfile = null;
|
|
564
|
-
let firstStalePid = null;
|
|
565
|
-
for (const candidate of candidates) {
|
|
566
|
-
const candidatePid = await readPidfile(candidate);
|
|
567
|
-
if (candidatePid === null) continue;
|
|
568
|
-
if (isAlive(candidatePid)) {
|
|
569
|
-
return {
|
|
570
|
-
pid: candidatePid,
|
|
571
|
-
pidfilePath: candidate,
|
|
572
|
-
firstStalePidfile,
|
|
573
|
-
firstStalePid
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
await clearStale(candidate);
|
|
577
|
-
if (firstStalePidfile === null) {
|
|
578
|
-
firstStalePidfile = candidate;
|
|
579
|
-
firstStalePid = candidatePid;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
return {
|
|
583
|
-
pid: null,
|
|
584
|
-
pidfilePath: candidates[0],
|
|
585
|
-
firstStalePidfile,
|
|
586
|
-
firstStalePid
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// src/services/detach.ts
|
|
591
|
-
import { fetch as fetch3 } from "undici";
|
|
592
|
-
|
|
593
|
-
// src/upgradeSea.ts
|
|
594
|
-
import { createHash as createHash3 } from "crypto";
|
|
595
|
-
import { chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
596
|
-
import { join as join4 } from "path";
|
|
597
|
-
|
|
598
|
-
// src/channel.ts
|
|
599
|
-
import { readFile as readFile7, writeFile as writeFile7, mkdir as mkdir8 } from "fs/promises";
|
|
600
|
-
import { dirname as dirname7 } from "path";
|
|
601
|
-
|
|
602
|
-
// src/service.ts
|
|
603
|
-
var seaRequire = createRequire2(import.meta.url);
|
|
604
|
-
|
|
605
|
-
// src/setup.ts
|
|
606
|
-
var MIGRATION_FRESH_TRIGGERS = [
|
|
607
|
-
"empty-intersection",
|
|
608
|
-
"explicit-zero",
|
|
609
|
-
"eof",
|
|
610
|
-
"non-tty",
|
|
611
|
-
"server-unavailable"
|
|
612
|
-
];
|
|
613
|
-
async function pickMigrationCandidateFromInput(candidates, read, write) {
|
|
614
|
-
write(
|
|
615
|
-
"Migration: detected legacy daemon(s) on this Computer that match the target server.\n"
|
|
616
|
-
);
|
|
617
|
-
candidates.forEach((c, i) => {
|
|
618
|
-
const host = c.hostname ? ` (${c.hostname})` : "";
|
|
619
|
-
const seen = c.lastSeenAt ? ` last seen ${c.lastSeenAt}` : "";
|
|
620
|
-
write(` ${i + 1}. ${c.machineName}${host}${seen}
|
|
621
|
-
`);
|
|
622
|
-
write(` local: ${c.localPath}
|
|
623
|
-
`);
|
|
624
|
-
});
|
|
625
|
-
write(" 0. Fresh attach (skip migration)\n");
|
|
626
|
-
write(" m. Migrate from a different on-disk path\n");
|
|
627
|
-
while (true) {
|
|
628
|
-
write("Choose [1]: ");
|
|
629
|
-
const { line, eof } = await read();
|
|
630
|
-
if (eof) return { kind: "fresh" };
|
|
631
|
-
const trimmed = line.trim();
|
|
632
|
-
const norm = trimmed.toLowerCase();
|
|
633
|
-
if (norm === "m") {
|
|
634
|
-
write("Path to legacy machine dir or owner.json: ");
|
|
635
|
-
const { line: pathLine } = await read();
|
|
636
|
-
return { kind: "manual", path: pathLine.trim() };
|
|
637
|
-
}
|
|
638
|
-
if (norm.length === 0) return { kind: "candidate", index: 0 };
|
|
639
|
-
if (norm === "0") return { kind: "fresh" };
|
|
640
|
-
const n = Number.parseInt(norm, 10);
|
|
641
|
-
if (Number.isFinite(n) && String(n) === norm && n >= 1 && n <= candidates.length) {
|
|
642
|
-
return { kind: "candidate", index: n - 1 };
|
|
643
|
-
}
|
|
644
|
-
write(
|
|
645
|
-
`Invalid selection "${trimmed}". Enter 1..${candidates.length}, 0, or m.
|
|
646
|
-
`
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/status.ts
|
|
652
|
-
import { readFile as readFile11 } from "fs/promises";
|
|
653
|
-
async function readUserSession(path2) {
|
|
654
|
-
try {
|
|
655
|
-
const parsed = JSON.parse(await readFile11(path2, "utf8"));
|
|
656
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
657
|
-
return { state: "present", session: parsed, error: null };
|
|
658
|
-
}
|
|
659
|
-
return { state: "invalid", session: null, error: "not a JSON object" };
|
|
660
|
-
} catch (err) {
|
|
661
|
-
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
662
|
-
return { state: "missing", session: null, error: null };
|
|
663
|
-
}
|
|
664
|
-
return {
|
|
665
|
-
state: "invalid",
|
|
666
|
-
session: null,
|
|
667
|
-
error: err instanceof Error ? err.message : String(err)
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
function str(v) {
|
|
672
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
673
|
-
}
|
|
674
|
-
async function pidStatus(pidfile) {
|
|
675
|
-
const pid = await readPidfileAt(pidfile);
|
|
676
|
-
return pid !== null && isProcessAlive(pid) ? { running: true, pid } : { running: false };
|
|
677
|
-
}
|
|
678
|
-
async function serviceState(slockHome) {
|
|
679
|
-
const { pid } = await findLiveServicePidReadOnly(slockHome);
|
|
680
|
-
return pid !== null ? { running: true, pid } : { running: false };
|
|
681
|
-
}
|
|
682
|
-
async function deriveHealth(slockHome, serverId, daemon) {
|
|
683
|
-
if (!daemon.running) return "offline";
|
|
684
|
-
if (await isDegraded(slockHome, serverId)) return "degraded";
|
|
685
|
-
return "ok";
|
|
686
|
-
}
|
|
687
|
-
async function buildStatusReport(installRoot) {
|
|
688
|
-
const sessionRead = await readUserSession(userSessionPath(installRoot));
|
|
689
|
-
const session = sessionRead.session;
|
|
690
|
-
const attachments = await listServerAttachments(installRoot);
|
|
691
|
-
const service = {
|
|
692
|
-
...await serviceState(installRoot),
|
|
693
|
-
logPath: serviceLogPath(installRoot)
|
|
694
|
-
};
|
|
695
|
-
const servers = [];
|
|
696
|
-
for (const a of attachments) {
|
|
697
|
-
const daemon = await pidStatus(serverRunnerPidPath(installRoot, a.serverId));
|
|
698
|
-
servers.push({
|
|
699
|
-
serverId: a.serverId,
|
|
700
|
-
serverSlug: a.serverSlug ?? null,
|
|
701
|
-
serverMachineId: a.serverMachineId,
|
|
702
|
-
serverUrl: a.serverUrl,
|
|
703
|
-
attachedAt: a.attachedAt ?? null,
|
|
704
|
-
serverRunnerLogPath: serverRunnerLogPath(installRoot, a.serverId),
|
|
705
|
-
daemon,
|
|
706
|
-
health: await deriveHealth(installRoot, a.serverId, daemon)
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
const loggedIn = session?.kind === "user-session" && typeof session.accessToken === "string" && session.accessToken.length > 0;
|
|
710
|
-
return {
|
|
711
|
-
slockHome: installRoot,
|
|
712
|
-
loggedIn,
|
|
713
|
-
userId: session ? str(session.userId) : null,
|
|
714
|
-
loginServerUrl: session ? str(session.serverUrl) : null,
|
|
715
|
-
userSessionError: sessionRead.state === "invalid" ? sessionRead.error : null,
|
|
716
|
-
service,
|
|
717
|
-
servers
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// src/lib/readers.ts
|
|
722
|
-
async function readServiceStatus(installRoot) {
|
|
723
|
-
return buildStatusReport(installRoot);
|
|
724
|
-
}
|
|
725
|
-
async function readRunnerStatus(installRoot, serverId) {
|
|
726
|
-
const report = await buildStatusReport(installRoot);
|
|
727
|
-
const server = report.servers.find((s) => s.serverId === serverId);
|
|
728
|
-
if (!server) {
|
|
729
|
-
throw new StateReaderError(
|
|
730
|
-
"NOT_ATTACHED",
|
|
731
|
-
`Server ${serverId} is not attached to this Computer.`
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
const attachment = await readServerAttachment(installRoot, serverId);
|
|
735
|
-
if (!attachment) {
|
|
736
|
-
throw new StateReaderError(
|
|
737
|
-
"INVALID_ATTACHMENT",
|
|
738
|
-
`Attachment for server ${serverId} is missing or invalid.`
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
|
-
const client = new RunnersClient(attachment.serverUrl, attachment.apiKey);
|
|
742
|
-
const result = await client.list();
|
|
743
|
-
if (result.status === "success") {
|
|
744
|
-
return { status: "ok", server, whitelist: result.whitelist, runners: result.runners };
|
|
745
|
-
}
|
|
746
|
-
if (result.status === "unauthorized") {
|
|
747
|
-
return { status: "unauthorized", server };
|
|
748
|
-
}
|
|
749
|
-
return { status: "error", server, code: result.code };
|
|
750
|
-
}
|
|
751
|
-
async function listRunners(installRoot, opts = {}) {
|
|
752
|
-
const all = await listServerAttachments(installRoot);
|
|
753
|
-
let subset = all;
|
|
754
|
-
if (opts.serverId !== void 0) {
|
|
755
|
-
const found = all.find((a) => a.serverId === opts.serverId);
|
|
756
|
-
if (!found) {
|
|
757
|
-
throw new StateReaderError(
|
|
758
|
-
"NOT_ATTACHED",
|
|
759
|
-
`Server ${opts.serverId} is not attached to this Computer.`
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
subset = [found];
|
|
763
|
-
}
|
|
764
|
-
const servers = [];
|
|
765
|
-
for (const a of subset) {
|
|
766
|
-
const client = new RunnersClient(a.serverUrl, a.apiKey);
|
|
767
|
-
const result = await client.list();
|
|
768
|
-
const idCols = { serverId: a.serverId, serverSlug: a.serverSlug ?? null };
|
|
769
|
-
if (result.status === "success") {
|
|
770
|
-
servers.push({
|
|
771
|
-
...idCols,
|
|
772
|
-
status: "ok",
|
|
773
|
-
whitelist: result.whitelist,
|
|
774
|
-
runners: result.runners
|
|
775
|
-
});
|
|
776
|
-
} else if (result.status === "unauthorized") {
|
|
777
|
-
servers.push({ ...idCols, status: "unauthorized" });
|
|
778
|
-
} else {
|
|
779
|
-
servers.push({ ...idCols, status: "error", code: result.code });
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
return { servers };
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// src/lib/state.ts
|
|
786
|
-
var RUNNER_STATE_VALUES = [
|
|
787
|
-
"starting",
|
|
788
|
-
"running",
|
|
789
|
-
"degraded",
|
|
790
|
-
"crashed",
|
|
791
|
-
"stopped"
|
|
792
|
-
];
|
|
793
|
-
function isRunnerState(value) {
|
|
794
|
-
return typeof value === "string" && RUNNER_STATE_VALUES.includes(value);
|
|
795
|
-
}
|
|
796
|
-
var SERVICE_STATE_VALUES = [
|
|
797
|
-
"starting",
|
|
798
|
-
"running",
|
|
799
|
-
"degraded",
|
|
800
|
-
"stopping",
|
|
801
|
-
"stopped"
|
|
802
|
-
];
|
|
803
|
-
function isServiceState(value) {
|
|
804
|
-
return typeof value === "string" && SERVICE_STATE_VALUES.includes(value);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// src/upgradeLog.ts
|
|
808
|
-
import { chmod as chmod6, mkdir as mkdir12, open as open2 } from "fs/promises";
|
|
809
|
-
var UPGRADE_ERROR_CODES = [
|
|
810
|
-
"UPGRADE_DEPS_CHANGED",
|
|
811
|
-
"UPGRADE_NETWORK_FAILED",
|
|
812
|
-
"UPGRADE_INTEGRITY_FAILED",
|
|
813
|
-
"UPGRADE_SWAP_FAILED",
|
|
814
|
-
"UPGRADE_RESTART_FAILED",
|
|
815
|
-
"UPGRADE_NO_TARGET",
|
|
816
|
-
"UPGRADE_ALREADY_RUNNING"
|
|
817
|
-
];
|
|
818
|
-
var UPGRADE_ERROR_CODE_SET = new Set(UPGRADE_ERROR_CODES);
|
|
819
|
-
function assertUpgradeLogEntry(entry) {
|
|
820
|
-
const e = entry;
|
|
821
|
-
if (e.outcome === "ok") {
|
|
822
|
-
if (e.errorCode !== void 0) {
|
|
823
|
-
throw new TypeError(
|
|
824
|
-
`UpgradeLogEntry contract violation: outcome="ok" must not carry errorCode (got "${e.errorCode}").`
|
|
825
|
-
);
|
|
826
|
-
}
|
|
827
|
-
} else if (e.outcome === "err") {
|
|
828
|
-
if (e.errorCode === void 0) {
|
|
829
|
-
throw new TypeError(
|
|
830
|
-
`UpgradeLogEntry contract violation: outcome="err" requires errorCode from the v8.3.3 closed-set (${UPGRADE_ERROR_CODES.join(" | ")}).`
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
if (!UPGRADE_ERROR_CODE_SET.has(e.errorCode)) {
|
|
834
|
-
throw new TypeError(
|
|
835
|
-
`UpgradeLogEntry contract violation: errorCode "${e.errorCode}" is not in the v8.3.3 closed 7-value set (${UPGRADE_ERROR_CODES.join(" | ")}).`
|
|
836
|
-
);
|
|
837
|
-
}
|
|
838
|
-
} else {
|
|
839
|
-
throw new TypeError(
|
|
840
|
-
`UpgradeLogEntry contract violation: outcome must be "ok" or "err" (got "${e.outcome}").`
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
if (typeof e.fromBundle?.computerVersion !== "string" || e.fromBundle.computerVersion.length === 0) {
|
|
844
|
-
throw new TypeError(`UpgradeLogEntry contract violation: fromBundle.computerVersion must be a non-empty string.`);
|
|
845
|
-
}
|
|
846
|
-
if (typeof e.toBundle?.computerVersion !== "string" || e.toBundle.computerVersion.length === 0) {
|
|
847
|
-
throw new TypeError(`UpgradeLogEntry contract violation: toBundle.computerVersion must be a non-empty string.`);
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
export {
|
|
851
|
-
IPC_ERROR_CODES,
|
|
852
|
-
LegacyMachinesClient,
|
|
853
|
-
MIGRATION_DETECTION_KINDS,
|
|
854
|
-
MIGRATION_FRESH_TRIGGERS,
|
|
855
|
-
RUNNER_STATE_VALUES,
|
|
856
|
-
SERVICE_STATE_VALUES,
|
|
857
|
-
STATE_READER_ERROR_CODES,
|
|
858
|
-
ServersClient,
|
|
859
|
-
ServiceClientError,
|
|
860
|
-
StateReaderError,
|
|
861
|
-
UPGRADE_ERROR_CODES,
|
|
862
|
-
assertUpgradeLogEntry,
|
|
863
|
-
detectLegacyMigration,
|
|
864
|
-
isIpcErrorCode,
|
|
865
|
-
isMigrationDetectionKind,
|
|
866
|
-
isRunnerState,
|
|
867
|
-
isServiceState,
|
|
868
|
-
listRunners,
|
|
869
|
-
pickMigrationCandidateFromInput,
|
|
870
|
-
readRunnerStatus,
|
|
871
|
-
readServiceStatus,
|
|
872
|
-
validateManualMigratePath
|
|
873
|
-
};
|