@slock-ai/computer 0.0.16-alpha.0 → 0.0.17
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 +621 -292
- package/dist/lib/index.d.ts +173 -73
- package/dist/lib/index.js +380 -108
- package/package.json +2 -2
package/dist/lib/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface ServerStatusRow {
|
|
|
11
11
|
serverMachineId: string;
|
|
12
12
|
serverUrl: string;
|
|
13
13
|
attachedAt: string | null;
|
|
14
|
-
|
|
14
|
+
serverRunnerLogPath: string;
|
|
15
15
|
daemon: DaemonState;
|
|
16
16
|
/** v6 §11 health enum surfaced in `status` table (PR-H §3.3). */
|
|
17
17
|
health: ServerHealth;
|
|
@@ -28,6 +28,33 @@ interface ComputerStatusReport {
|
|
|
28
28
|
servers: ServerStatusRow[];
|
|
29
29
|
}
|
|
30
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
|
+
declare class LegacyMachinesClient {
|
|
52
|
+
private readonly baseUrl;
|
|
53
|
+
private readonly accessToken;
|
|
54
|
+
constructor(baseUrl: string, accessToken: string);
|
|
55
|
+
private url;
|
|
56
|
+
list(serverSlug: string): Promise<LegacyMachineRosterResult>;
|
|
57
|
+
}
|
|
31
58
|
interface RunnerListItem {
|
|
32
59
|
agentId: string;
|
|
33
60
|
name: string;
|
|
@@ -354,55 +381,66 @@ interface UpgradeStartResult {
|
|
|
354
381
|
targetVersion: string;
|
|
355
382
|
}
|
|
356
383
|
/**
|
|
357
|
-
* Identity record for a legacy `@slock-ai/daemon` machine candidate
|
|
358
|
-
* `detectLegacyMigration
|
|
359
|
-
*
|
|
384
|
+
* Identity record for a legacy `@slock-ai/daemon` machine candidate
|
|
385
|
+
* surfaced by `detectLegacyMigration`. Each candidate is the result of
|
|
386
|
+
* intersecting (a) local `<installRoot>/machines/machine-<fp>/owner.json`
|
|
387
|
+
* evidence with (b) the server's `GET /api/computer/legacy-machines`
|
|
388
|
+
* roster on `apiKeyFingerprint` for the logged-in user + target server.
|
|
389
|
+
* The shape is identity-only — it never carries credentials.
|
|
390
|
+
*
|
|
391
|
+
* `apiKeyFingerprint` is the v9.9 intersection key —
|
|
392
|
+
* `sha256(apiKey).slice(0,16)`. The same value is computed locally by
|
|
393
|
+
* the legacy daemon (`packages/daemon/src/machineLock.ts`) and stored
|
|
394
|
+
* server-side on `daemons.api_key_fingerprint` (handshake-backfilled).
|
|
395
|
+
* Stable as long as the legacy api key is unchanged.
|
|
360
396
|
*
|
|
361
|
-
* `
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
* `<installRoot>/machines/` directory listings, and is what
|
|
365
|
-
* `AdoptLegacyService.migrate()` uses to find the lock owner file
|
|
366
|
-
* during migration.
|
|
397
|
+
* `daemonId` is the server's `daemons.id` UUID (display + post-migration
|
|
398
|
+
* identity). It is NOT a join key — fingerprint is. Cody redline
|
|
399
|
+
* msg=ec68c27f: server daemonId / machineName are display-only.
|
|
367
400
|
*
|
|
368
|
-
* `
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
401
|
+
* `localPath` is the absolute path of the local owner.json that
|
|
402
|
+
* supplied the fingerprint side of the intersection. Surfaced so the
|
|
403
|
+
* picker can show the operator which on-disk legacy install they are
|
|
404
|
+
* about to adopt, and so the manual `--migrate-from` flag's three-gate
|
|
405
|
+
* validator (path-readable / owner.json-parseable / fingerprint-in-roster)
|
|
406
|
+
* can return a candidate that points at the same path the operator
|
|
407
|
+
* passed.
|
|
372
408
|
*
|
|
373
|
-
* `
|
|
374
|
-
*
|
|
375
|
-
*
|
|
409
|
+
* `machineName` / `hostname` / `lastSeenAt` come from the server roster
|
|
410
|
+
* row — display-only, used for picker presentation. May be empty for
|
|
411
|
+
* stale rows; the picker tolerates blanks.
|
|
376
412
|
*/
|
|
377
413
|
interface LegacyMachineCandidate {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
414
|
+
apiKeyFingerprint: string;
|
|
415
|
+
daemonId: string;
|
|
416
|
+
localPath: string;
|
|
417
|
+
machineName: string;
|
|
418
|
+
hostname?: string;
|
|
419
|
+
lastSeenAt?: string;
|
|
381
420
|
}
|
|
382
421
|
/**
|
|
383
|
-
* §X migration detection result — the discriminated union returned by
|
|
384
|
-
* `detectLegacyMigration(installRoot,
|
|
422
|
+
* §X.2 migration detection result — the discriminated union returned by
|
|
423
|
+
* `detectLegacyMigration(installRoot, target)`.
|
|
385
424
|
*
|
|
386
|
-
* Per RFC §X.2:
|
|
387
|
-
* - `
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* "
|
|
396
|
-
*
|
|
397
|
-
* daemon they want migrated first to disambiguate.
|
|
425
|
+
* Per RFC v9.9 §X.2:
|
|
426
|
+
* - `candidates`: zero-or-more legacy daemons survived the
|
|
427
|
+
* local-owner-json ∩ server-roster intersection. Empty intersection
|
|
428
|
+
* is a valid `candidates` result with `candidates.length === 0` —
|
|
429
|
+
* the setup flow's fresh-attach trigger #1 (§X.4). One-or-more
|
|
430
|
+
* candidates → setup picker prompts the operator (TTY) or falls
|
|
431
|
+
* through to fresh attach (non-TTY).
|
|
432
|
+
* - `server-unavailable`: roster fetch failed (network / auth /
|
|
433
|
+
* server gate off / 5xx). Setup falls through to fresh attach
|
|
434
|
+
* under the documented "best-effort detection" discipline — a
|
|
435
|
+
* transiently unreachable server must NOT block fresh setup.
|
|
398
436
|
*
|
|
399
|
-
* Fingerprint discipline (
|
|
400
|
-
* `
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
437
|
+
* Fingerprint discipline (Cody msg=ec68c27f redline):
|
|
438
|
+
* - `apiKeyFingerprint` is treated as a sensitive identity. It is
|
|
439
|
+
* scoped to this caller's authenticated request and never logged.
|
|
440
|
+
* - The intersection is the trust-gate: a candidate proves the
|
|
441
|
+
* server has a roster row keyed by the same fingerprint that
|
|
442
|
+
* appears in the local owner.json. Final adoption still walks
|
|
443
|
+
* `AdoptLegacyService.migrate()` credential validation.
|
|
406
444
|
*/
|
|
407
445
|
/**
|
|
408
446
|
* Closed set of `MigrationDetection.kind` discriminator values. Mirrors
|
|
@@ -412,48 +450,110 @@ interface LegacyMachineCandidate {
|
|
|
412
450
|
* `as` cast (state-machine-modeling discipline — correlated/mutex
|
|
413
451
|
* properties land as a closed-set state-enum).
|
|
414
452
|
*/
|
|
415
|
-
declare const MIGRATION_DETECTION_KINDS: readonly ["
|
|
453
|
+
declare const MIGRATION_DETECTION_KINDS: readonly ["candidates", "server-unavailable"];
|
|
416
454
|
type MigrationDetectionKind = (typeof MIGRATION_DETECTION_KINDS)[number];
|
|
417
455
|
/** Runtime narrow guard for `MigrationDetection.kind`. */
|
|
418
456
|
declare function isMigrationDetectionKind(value: unknown): value is MigrationDetectionKind;
|
|
419
457
|
type MigrationDetection = {
|
|
420
|
-
kind: "
|
|
421
|
-
machineId: string;
|
|
422
|
-
machineName?: string;
|
|
423
|
-
serverUrl?: string;
|
|
424
|
-
} | {
|
|
425
|
-
kind: "no-match";
|
|
426
|
-
} | {
|
|
427
|
-
kind: "ambiguous";
|
|
458
|
+
kind: "candidates";
|
|
428
459
|
candidates: LegacyMachineCandidate[];
|
|
460
|
+
} | {
|
|
461
|
+
kind: "server-unavailable";
|
|
429
462
|
};
|
|
430
463
|
|
|
431
464
|
/**
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
465
|
+
* Roster fetcher contract — `LegacyMachinesClient.list(serverSlug)`
|
|
466
|
+
* satisfies this structurally. Defining it as an interface here lets
|
|
467
|
+
* tests stub the network call without instantiating undici.
|
|
468
|
+
*/
|
|
469
|
+
interface LegacyMachineRosterClient {
|
|
470
|
+
list(serverSlug: string): Promise<LegacyMachineRosterResult>;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Detect whether the logged-in user has a legacy `@slock-ai/daemon`
|
|
474
|
+
* machine on the target server that matches a local install on this
|
|
475
|
+
* host (intersection on `apiKeyFingerprint`).
|
|
476
|
+
*
|
|
477
|
+
* Returns:
|
|
478
|
+
* - `{ kind: "candidates"; candidates: LegacyMachineCandidate[] }` —
|
|
479
|
+
* zero-or-more mutually-known legacy daemons. Empty list is the
|
|
480
|
+
* §X.4 fresh-attach trigger #1; non-empty drives the picker.
|
|
481
|
+
* - `{ kind: "server-unavailable" }` — roster fetch failed
|
|
482
|
+
* transiently. Caller falls through to fresh attach.
|
|
435
483
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
*
|
|
443
|
-
*
|
|
484
|
+
* Lib-pure: no env reads, no terminal IO, no `process.exit`. Local
|
|
485
|
+
* filesystem errors are swallowed; only the roster-fetch outcome can
|
|
486
|
+
* change the discriminator.
|
|
487
|
+
*/
|
|
488
|
+
declare function detectLegacyMigration(installRoot: string, serverSlug: string, client: LegacyMachineRosterClient): Promise<MigrationDetection>;
|
|
489
|
+
/**
|
|
490
|
+
* Manual `--migrate-from <path>` validator — the three-gate chain that
|
|
491
|
+
* RFC v9.9 §X.4 specifies for the manual escape hatch when the picker's
|
|
492
|
+
* intersection set is non-empty but the operator wants to migrate a
|
|
493
|
+
* different on-disk install.
|
|
444
494
|
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
495
|
+
* Gates (executed in order, hard-fail on first failure):
|
|
496
|
+
* 1. `MIGRATE_FROM_NOT_FOUND` — `path` does not exist or is not a
|
|
497
|
+
* readable directory / file. Accepts either the `machines/
|
|
498
|
+
* machine-<fp>/` directory or its `daemon.lock/owner.json`
|
|
499
|
+
* directly; the latter is what doctor / forensic flows surface.
|
|
500
|
+
* 2. `MIGRATE_FROM_INVALID` — owner.json is unreadable, malformed,
|
|
501
|
+
* or missing a 16-hex-char `apiKeyFingerprint` field.
|
|
502
|
+
* 3. `MIGRATE_FROM_NOT_OWNED` — owner.json's fingerprint is not in
|
|
503
|
+
* the supplied roster (the logged-in user does not have a
|
|
504
|
+
* legacy daemon row with this fingerprint on the target server,
|
|
505
|
+
* or the row is already migrated / NULL-fingerprinted).
|
|
448
506
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
507
|
+
* On success returns the same `LegacyMachineCandidate` shape the
|
|
508
|
+
* picker emits, so the setup driver's adoption call is the same code
|
|
509
|
+
* path on both branches.
|
|
510
|
+
*/
|
|
511
|
+
type ManualPathValidation = {
|
|
512
|
+
ok: true;
|
|
513
|
+
candidate: LegacyMachineCandidate;
|
|
514
|
+
} | {
|
|
515
|
+
ok: false;
|
|
516
|
+
code: "MIGRATE_FROM_NOT_FOUND" | "MIGRATE_FROM_INVALID" | "MIGRATE_FROM_NOT_OWNED";
|
|
517
|
+
};
|
|
518
|
+
declare function validateManualMigratePath(inputPath: string, roster: LegacyMachineRosterEntry[]): Promise<ManualPathValidation>;
|
|
519
|
+
|
|
520
|
+
type PickerSelection = {
|
|
521
|
+
kind: "candidate";
|
|
522
|
+
index: number;
|
|
523
|
+
} | {
|
|
524
|
+
kind: "fresh";
|
|
525
|
+
} | {
|
|
526
|
+
kind: "manual";
|
|
527
|
+
path: string;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Closed-set enumeration of the §X.4 fresh-attach triggers. Used in
|
|
531
|
+
* setup info() lines so QA can grep for "fresh trigger: <name>" and
|
|
532
|
+
* test mocks can assert that ONLY these inputs route to fresh attach
|
|
533
|
+
* (Cody NIT `msg=9102a817`). The picker function itself only emits
|
|
534
|
+
* `explicit-zero` / `eof`; the other three are decided upstream in
|
|
535
|
+
* `runSetup` before the picker ever fires.
|
|
536
|
+
*/
|
|
537
|
+
declare const MIGRATION_FRESH_TRIGGERS: readonly ["empty-intersection", "explicit-zero", "eof", "non-tty", "server-unavailable"];
|
|
538
|
+
type MigrationFreshTrigger = (typeof MIGRATION_FRESH_TRIGGERS)[number];
|
|
539
|
+
/**
|
|
540
|
+
* Pure picker grammar — extracted so it can be exercised by unit tests
|
|
541
|
+
* without stubbing global stdin/stdout. RFC v9.9 §X.4 (post-Jianwei FAIL
|
|
542
|
+
* `msg=ecc2e57e` / Cody NIT `msg=9102a817`):
|
|
543
|
+
* - `1`..`N` / `<Enter>` → adopt that candidate (1-indexed; Enter picks
|
|
544
|
+
* the first candidate, since the operator already saw the
|
|
545
|
+
* intersection list).
|
|
546
|
+
* - `0` → fresh attach (Trigger #2: explicit-zero).
|
|
547
|
+
* - EOF (Ctrl-D / closed stdin) → fresh attach (Trigger #2: eof).
|
|
548
|
+
* - `m` / `M` → emits `manual` and prompts for path on the next line.
|
|
549
|
+
* - Anything else → reprompt. The picker NEVER silently falls through
|
|
550
|
+
* to fresh attach on unrecognized input — that was the B1/B4 contract
|
|
551
|
+
* drift that QA rejected.
|
|
552
|
+
*/
|
|
553
|
+
declare function pickMigrationCandidateFromInput(candidates: LegacyMachineCandidate[], read: () => Promise<{
|
|
554
|
+
line: string;
|
|
555
|
+
eof: boolean;
|
|
556
|
+
}>, write: (s: string) => void): Promise<PickerSelection>;
|
|
457
557
|
|
|
458
558
|
/**
|
|
459
559
|
* Read the Computer-level aggregate status from `installRoot`. Identical
|
|
@@ -551,4 +651,4 @@ interface UpgradeLogEntryErr {
|
|
|
551
651
|
*/
|
|
552
652
|
declare function assertUpgradeLogEntry(entry: UpgradeLogEntry): void;
|
|
553
653
|
|
|
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 };
|
|
654
|
+
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, 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, pickMigrationCandidateFromInput, readRunnerStatus, readServiceStatus, validateManualMigratePath };
|