@capybara-hq/kernel 0.1.0-rc.12 → 0.1.0-rc.14
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/admin/admin.schemas.d.ts +13 -0
- package/dist/agents/agent-lookup.d.ts +5 -0
- package/dist/agents/agent-lookup.js +15 -0
- package/dist/background/background.module.js +2 -2
- package/dist/background/launch-lock.d.ts +1 -1
- package/dist/background/launch-lock.js +1 -1
- package/dist/background/poll.job.d.ts +2 -2
- package/dist/background/poll.job.js +10 -10
- package/dist/background/stop.d.ts +1 -1
- package/dist/background/stop.js +3 -3
- package/dist/background/store.d.ts +5 -5
- package/dist/background/store.js +5 -5
- package/dist/background/tasks.service.d.ts +6 -6
- package/dist/background/tasks.service.js +13 -13
- package/dist/catalog/admin-catalog.controller.d.ts +13 -2
- package/dist/catalog/admin-catalog.controller.js +24 -3
- package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
- package/dist/catalog/admin-catalog.schemas.js +7 -0
- package/dist/catalog/catalog-content.d.ts +9 -1
- package/dist/catalog/catalog-content.js +2 -1
- package/dist/catalog/catalog-files.d.ts +3 -2
- package/dist/catalog/catalog-files.js +8 -5
- package/dist/catalog/component-scope.service.d.ts +14 -1
- package/dist/catalog/component-scope.service.js +12 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.controller.d.ts +5 -4
- package/dist/catalog/org-catalog.controller.js +5 -4
- package/dist/catalog/org-catalog.schemas.d.ts +18 -2
- package/dist/catalog/org-catalog.schemas.js +16 -2
- package/dist/catalog/org-catalog.service.js +8 -2
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-package.js +37 -9
- package/dist/catalog/plugin-upgrade.d.ts +15 -1
- package/dist/catalog/plugin-upgrade.js +18 -5
- package/dist/channels/channels.module.js +2 -2
- package/dist/channels/connections.controller.d.ts +1 -1
- package/dist/channels/connections.controller.js +1 -1
- package/dist/channels/connections.service.d.ts +22 -5
- package/dist/channels/connections.service.js +56 -10
- package/dist/channels/inbound.d.ts +8 -8
- package/dist/channels/inbound.js +31 -31
- package/dist/channels/phrases.d.ts +27 -27
- package/dist/channels/phrases.js +27 -27
- package/dist/channels/webhook.d.ts +21 -1
- package/dist/channels/webhook.js +39 -4
- package/dist/config/docs.js +1 -1
- package/dist/config/schema.d.ts +2 -2
- package/dist/config/schema.js +21 -21
- package/dist/conversations/conversations.controller.d.ts +11 -11
- package/dist/conversations/conversations.controller.js +15 -15
- package/dist/conversations/conversations.module.d.ts +1 -1
- package/dist/conversations/conversations.module.js +7 -7
- package/dist/conversations/conversations.schemas.js +1 -1
- package/dist/conversations/conversations.service.d.ts +14 -14
- package/dist/conversations/conversations.service.js +19 -19
- package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
- package/dist/conversations/{environment-status.js → device-status.js} +18 -18
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +1 -1
- package/dist/conversations/events.d.ts +1 -1
- package/dist/conversations/events.js +2 -2
- package/dist/ctx/create-ctx.d.ts +26 -3
- package/dist/ctx/create-ctx.js +134 -26
- package/dist/db/kernel-schema.d.ts +37 -25
- package/dist/{environments → devices}/access.d.ts +19 -19
- package/dist/{environments → devices}/access.js +28 -28
- package/dist/devices/conversation-devices.d.ts +75 -0
- package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
- package/dist/devices/devices.controller.d.ts +36 -0
- package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
- package/dist/devices/devices.module.d.ts +5 -13
- package/dist/devices/devices.module.js +16 -29
- package/dist/devices/devices.schemas.d.ts +3 -0
- package/dist/devices/devices.schemas.js +14 -0
- package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
- package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
- package/dist/devices/dto.d.ts +13 -54
- package/dist/devices/dto.js +7 -39
- package/dist/devices/label.d.ts +11 -0
- package/dist/{environments → devices}/label.js +7 -7
- package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
- package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
- package/dist/errors.d.ts +29 -25
- package/dist/errors.js +29 -25
- package/dist/evals/recorded-tools.d.ts +2 -2
- package/dist/evals/recorded-tools.js +6 -6
- package/dist/extensions/admin-ops.js +1 -0
- package/dist/extensions/admin-view.js +2 -0
- package/dist/extensions/effective-config.d.ts +6 -0
- package/dist/extensions/effective-config.js +18 -7
- package/dist/extensions/guide-places.d.ts +66 -0
- package/dist/extensions/guide-places.js +152 -0
- package/dist/extensions/host.d.ts +13 -2
- package/dist/extensions/host.js +27 -6
- package/dist/extensions/instance.d.ts +17 -2
- package/dist/extensions/instance.js +21 -0
- package/dist/extensions/ledger.d.ts +1 -1
- package/dist/extensions/permission-set.d.ts +16 -2
- package/dist/extensions/permission-set.js +17 -3
- package/dist/extensions/permission-view.d.ts +10 -1
- package/dist/extensions/plugin-guides.d.ts +207 -54
- package/dist/extensions/plugin-guides.js +436 -165
- package/dist/files/previewers.d.ts +1 -1
- package/dist/files/previewers.js +1 -1
- package/dist/http/api.module.js +16 -6
- package/dist/http/gateway-routes.d.ts +1 -1
- package/dist/http/gateway-routes.js +1 -1
- package/dist/identity/settings.d.ts +32 -0
- package/dist/identity/settings.js +26 -2
- package/dist/infra/config/kernel-config.service.js +4 -4
- package/dist/infra/lock/lock.service.d.ts +8 -0
- package/dist/infra/lock/lock.service.js +27 -0
- package/dist/infra/throttler/named-throttlers.d.ts +4 -1
- package/dist/infra/throttler/named-throttlers.js +16 -1
- package/dist/infra/throttler/throttler.guard.js +1 -1
- package/dist/kernel.module.js +3 -3
- package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
- package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
- package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
- package/dist/{devices → local-devices}/device-auth.js +51 -53
- package/dist/local-devices/device-auth.module.d.ts +7 -0
- package/dist/{devices → local-devices}/device-auth.module.js +5 -5
- package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
- package/dist/{devices → local-devices}/device-routes.js +2 -2
- package/dist/local-devices/dto.d.ts +66 -0
- package/dist/local-devices/dto.js +44 -0
- package/dist/{devices → local-devices}/keys.d.ts +11 -11
- package/dist/{devices → local-devices}/keys.js +11 -11
- package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
- package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
- package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
- package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
- package/dist/local-devices/local-devices.module.d.ts +16 -0
- package/dist/local-devices/local-devices.module.js +44 -0
- package/dist/{devices → local-devices}/machines.d.ts +26 -26
- package/dist/{devices → local-devices}/machines.js +71 -71
- package/dist/{devices → local-devices}/relay.d.ts +15 -15
- package/dist/{devices → local-devices}/relay.js +55 -55
- package/dist/{devices → local-devices}/targets.d.ts +8 -8
- package/dist/{devices → local-devices}/targets.js +14 -14
- package/dist/orgs/offboarding.d.ts +6 -6
- package/dist/orgs/offboarding.js +12 -12
- package/dist/orgs/settings.service.d.ts +28 -0
- package/dist/orgs/settings.service.js +20 -1
- package/dist/overrides.d.ts +3 -1
- package/dist/overrides.js +2 -0
- package/dist/platform/exclusions.js +48 -8
- package/dist/platform/operation.js +10 -2
- package/dist/platform/ops/admin.ops.js +8 -2
- package/dist/platform/ops/connectors.ops.js +2 -0
- package/dist/platform/ops/conversations.ops.d.ts +3 -3
- package/dist/platform/ops/conversations.ops.js +9 -9
- package/dist/platform/ops/devices.ops.d.ts +26 -0
- package/dist/platform/ops/devices.ops.js +311 -0
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/setup.ops.d.ts +24 -0
- package/dist/platform/ops/setup.ops.js +212 -0
- package/dist/platform/ops/ui.ops.d.ts +8 -0
- package/dist/platform/ops/ui.ops.js +41 -0
- package/dist/platform/platform-ops.module.d.ts +4 -2
- package/dist/platform/platform-ops.module.js +13 -5
- package/dist/platform/route-schemas.js +11 -3
- package/dist/roles/gateway-role.d.ts +3 -3
- package/dist/roles/gateway-role.js +4 -4
- package/dist/roles/gateway.module.d.ts +1 -1
- package/dist/roles/gateway.module.js +5 -5
- package/dist/roles/worker.module.js +1 -1
- package/dist/runtime/context/build.d.ts +1 -1
- package/dist/runtime/context/build.js +6 -6
- package/dist/runtime/context/compaction.js +1 -1
- package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
- package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
- package/dist/runtime/maintenance.d.ts +1 -1
- package/dist/sandbox/jobs.d.ts +13 -13
- package/dist/sandbox/jobs.js +20 -20
- package/dist/sandbox/leases.d.ts +9 -9
- package/dist/sandbox/leases.js +14 -14
- package/dist/sandbox/providers.d.ts +8 -8
- package/dist/sandbox/providers.js +8 -8
- package/dist/sandbox/sandbox.module.d.ts +1 -1
- package/dist/sandbox/sandbox.module.js +1 -1
- package/dist/sandbox/sandbox.service.d.ts +76 -76
- package/dist/sandbox/sandbox.service.js +230 -230
- package/dist/setup/kernel-checks.d.ts +91 -0
- package/dist/setup/kernel-checks.js +295 -0
- package/dist/setup/oauth-token-probe.d.ts +47 -0
- package/dist/setup/oauth-token-probe.js +108 -0
- package/dist/setup/redact-check.d.ts +18 -0
- package/dist/setup/redact-check.js +46 -0
- package/dist/setup/setup-check-registry.d.ts +86 -0
- package/dist/setup/setup-check-registry.js +75 -0
- package/dist/setup/setup-checks.controller.d.ts +28 -0
- package/dist/setup/setup-checks.controller.js +78 -0
- package/dist/setup/setup-checks.schemas.d.ts +38 -0
- package/dist/setup/setup-checks.schemas.js +43 -0
- package/dist/setup/setup-checks.service.d.ts +144 -0
- package/dist/setup/setup-checks.service.js +683 -0
- package/dist/setup/setup-guides.controller.d.ts +18 -0
- package/dist/setup/setup-guides.controller.js +48 -0
- package/dist/setup/setup-guides.schemas.d.ts +10 -0
- package/dist/setup/setup-guides.schemas.js +6 -0
- package/dist/setup/setup-guides.service.d.ts +126 -0
- package/dist/setup/setup-guides.service.js +315 -0
- package/dist/setup/setup.module.d.ts +8 -0
- package/dist/setup/setup.module.js +31 -0
- package/dist/tools/builtins/builtin-tools.d.ts +2 -2
- package/dist/tools/builtins/builtin-tools.js +5 -5
- package/dist/tools/builtins/platform.js +1 -1
- package/dist/tools/builtins/sandbox/bash.js +6 -6
- package/dist/tools/builtins/sandbox/common.d.ts +25 -25
- package/dist/tools/builtins/sandbox/common.js +51 -51
- package/dist/tools/builtins/sandbox/edit.js +2 -2
- package/dist/tools/builtins/sandbox/index.d.ts +1 -1
- package/dist/tools/builtins/sandbox/index.js +1 -1
- package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
- package/dist/tools/builtins/sandbox/task-output.js +10 -10
- package/dist/tools/builtins/sandbox/task-stop.js +2 -2
- package/dist/tools/builtins/sandbox/write.js +2 -2
- package/dist/tools/builtins/skill-load.d.ts +1 -1
- package/dist/tools/builtins/skill-load.js +2 -2
- package/dist/tools/connectors.controller.d.ts +28 -11
- package/dist/tools/connectors.controller.js +49 -14
- package/dist/tools/connectors.schemas.d.ts +4 -0
- package/dist/tools/connectors.schemas.js +4 -0
- package/dist/tools/connectors.service.d.ts +30 -2
- package/dist/tools/connectors.service.js +98 -3
- package/dist/tools/credentials.d.ts +20 -0
- package/dist/tools/credentials.js +90 -1
- package/dist/tools/oauth/tokens.d.ts +20 -0
- package/dist/tools/oauth/tokens.js +49 -24
- package/dist/tools/pipeline.js +4 -3
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/tools.module.js +3 -3
- package/dist/ui/admin-ui.controller.d.ts +56 -0
- package/dist/ui/admin-ui.controller.js +192 -0
- package/dist/ui/arrangement.d.ts +74 -0
- package/dist/ui/arrangement.js +98 -0
- package/dist/ui/embed-token.d.ts +2 -2
- package/dist/ui/embed-token.js +30 -5
- package/dist/ui/registry.d.ts +29 -14
- package/dist/ui/registry.js +31 -10
- package/dist/ui/slots.controller.d.ts +11 -0
- package/dist/ui/slots.controller.js +25 -0
- package/dist/ui/ui-stream.d.ts +20 -3
- package/dist/ui/ui-stream.js +25 -7
- package/dist/ui/ui.schemas.d.ts +66 -0
- package/dist/ui/ui.schemas.js +65 -0
- package/dist/ui/views.controller.d.ts +35 -49
- package/dist/ui/views.controller.js +48 -70
- package/dist/ui/views.d.ts +84 -18
- package/dist/ui/views.js +201 -38
- package/package.json +3 -3
- package/src/migrations/0105_devices_rename.sql +136 -0
- package/src/migrations/0106_setup_guides_v2.sql +10 -0
- package/dist/devices/device-auth.module.d.ts +0 -7
- package/dist/environments/conversation-environments.d.ts +0 -75
- package/dist/environments/dto.d.ts +0 -25
- package/dist/environments/dto.js +0 -12
- package/dist/environments/environments.controller.d.ts +0 -36
- package/dist/environments/environments.module.d.ts +0 -8
- package/dist/environments/environments.module.js +0 -31
- package/dist/environments/environments.schemas.d.ts +0 -3
- package/dist/environments/environments.schemas.js +0 -14
- package/dist/environments/label.d.ts +0 -11
- package/dist/platform/ops/environments.ops.d.ts +0 -26
- package/dist/platform/ops/environments.ops.js +0 -311
|
@@ -2,17 +2,17 @@ import type { DeviceHello, DeviceStopArgs, ExtensionContext } from "@capybara-hq
|
|
|
2
2
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
3
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
4
4
|
import type { KernelLogger } from "../log.js";
|
|
5
|
-
import {
|
|
5
|
+
import { LocalDeviceRelay } from "./relay.js";
|
|
6
6
|
/** The machine credential's prefix (`cap_dev_` plus 32 random bytes, base64url). */
|
|
7
7
|
export declare const MACHINE_CREDENTIAL_PREFIX = "cap_dev_";
|
|
8
8
|
/** What a machine credential stands for. */
|
|
9
9
|
export interface MachineIdentity {
|
|
10
10
|
machineId: string;
|
|
11
11
|
orgId: string;
|
|
12
|
-
|
|
12
|
+
deviceId: string;
|
|
13
13
|
accountId: string;
|
|
14
14
|
}
|
|
15
|
-
/** The machine connected to
|
|
15
|
+
/** The machine connected to a device (its unrevoked row). */
|
|
16
16
|
export interface ActiveMachine {
|
|
17
17
|
id: string;
|
|
18
18
|
orgId: string;
|
|
@@ -26,12 +26,12 @@ export interface ActiveMachine {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Connected machines and their credentials (environments spec §13.2). One active machine per
|
|
29
|
-
* device
|
|
29
|
+
* local device: issuing a credential revokes the device's previous machine. A
|
|
30
30
|
* credential is `cap_dev_` plus 32 random bytes; only its sha256 is stored. It is accepted only
|
|
31
31
|
* by the device connection route ({@link authenticate}), never by `/api`: it is no `Principal`.
|
|
32
32
|
*
|
|
33
33
|
* A revoked machine is told so after the commit: `stop { reason }` relayed while a replica holds
|
|
34
|
-
* its connection (fire-and-forget, ruling R14) and `device.revoked.<
|
|
34
|
+
* its connection (fire-and-forget, ruling R14) and `device.revoked.<deviceId>` published with
|
|
35
35
|
* `{ machineId }`, so the holder closes that machine's connection.
|
|
36
36
|
*/
|
|
37
37
|
export declare class MachinesService {
|
|
@@ -40,15 +40,15 @@ export declare class MachinesService {
|
|
|
40
40
|
private readonly relay;
|
|
41
41
|
private readonly bus;
|
|
42
42
|
private readonly log;
|
|
43
|
-
constructor(db: KernelDb, txHost: TransactionHost, relay: Pick<
|
|
43
|
+
constructor(db: KernelDb, txHost: TransactionHost, relay: Pick<LocalDeviceRelay, "holderLive" | "send">, bus: ExtensionContext["bus"], log: Pick<KernelLogger, "warn">);
|
|
44
44
|
/**
|
|
45
|
-
* A new machine for the
|
|
46
|
-
*
|
|
47
|
-
* which has already checked the
|
|
45
|
+
* A new machine for the device and its credential (returned once). Revokes the
|
|
46
|
+
* device's active machine first. Joins the caller's transaction (the device confirmation),
|
|
47
|
+
* which has already checked the device is the person's own live local device.
|
|
48
48
|
*/
|
|
49
49
|
issue(input: {
|
|
50
50
|
orgId: string;
|
|
51
|
-
|
|
51
|
+
deviceId: string;
|
|
52
52
|
accountId: string;
|
|
53
53
|
name: string;
|
|
54
54
|
os: string;
|
|
@@ -59,29 +59,29 @@ export declare class MachinesService {
|
|
|
59
59
|
credential: string;
|
|
60
60
|
}>;
|
|
61
61
|
/**
|
|
62
|
-
* Ruling R23: the new machine may serve the
|
|
62
|
+
* Ruling R23: the new machine may serve the device from another directory or mode (other
|
|
63
63
|
* roots), and a login recreates a directory that was lost (ruling R24). So the instance row
|
|
64
64
|
* forgets its instance (status `none`, no handle) and its files count as there again; the next
|
|
65
|
-
* use creates a fresh instance on the machine (with its roots: a new `
|
|
65
|
+
* use creates a fresh instance on the machine (with its roots: a new `device_paths` event
|
|
66
66
|
* follows). The fence moves on, so a lifecycle holder of the old instance fails its next write.
|
|
67
67
|
* The reset count (`fs_resets`) moves on when the files the next instance sees are not the old
|
|
68
68
|
* ones, so conversations that used the old files are told their files were reset, not that they
|
|
69
69
|
* were kept: lost files come back as an empty directory, and another machine (`moved`: name, OS
|
|
70
|
-
* or arch differ from the
|
|
70
|
+
* or arch differ from the device's previous machine) has other files. Another directory on
|
|
71
71
|
* the same machine is known only from the new machine's first hello ({@link recordHello}).
|
|
72
72
|
*/
|
|
73
73
|
private resetInstance;
|
|
74
74
|
/**
|
|
75
|
-
* Who a machine credential stands for, or null: the machine is not revoked, its
|
|
75
|
+
* Who a machine credential stands for, or null: the machine is not revoked, its device is
|
|
76
76
|
* live, the org is not deleted (ruling R12), and the person is an active account still a member
|
|
77
77
|
* of the org. Read live on every call (a connection re-checks it as it sees fit).
|
|
78
78
|
*/
|
|
79
79
|
authenticate(credential: string): Promise<MachineIdentity | null>;
|
|
80
80
|
/**
|
|
81
81
|
* Stores what the program reported in its hello frame and marks the machine seen. On the
|
|
82
|
-
* machine's first hello, when it is the same machine as the
|
|
82
|
+
* machine's first hello, when it is the same machine as the device's previous one (else
|
|
83
83
|
* {@link issue} already counted the reset) but serves another directory or mode (other roots,
|
|
84
|
-
* ruling R23), the
|
|
84
|
+
* ruling R23), the device's files are other files: `fs_resets` moves on (maintainer
|
|
85
85
|
* decision 2026-11-08), so conversations get the files-reset notice. A previous machine that
|
|
86
86
|
* never said hello has no known roots: nothing to compare.
|
|
87
87
|
*/
|
|
@@ -89,26 +89,26 @@ export declare class MachinesService {
|
|
|
89
89
|
/** Marks the machine seen (a ping); writes at most once every 60 seconds. */
|
|
90
90
|
touch(machineId: string): Promise<void>;
|
|
91
91
|
/**
|
|
92
|
-
* Disconnects the machine of one of the person's own live
|
|
93
|
-
* /orgs/:orgId/
|
|
94
|
-
* `
|
|
95
|
-
* `
|
|
92
|
+
* Disconnects the machine of one of the person's own live local devices (`DELETE
|
|
93
|
+
* /orgs/:orgId/devices/:id/machine`): revokes it and audits
|
|
94
|
+
* `device.machine_revoked`. Anyone else's device, a cloud one or a missing one →
|
|
95
|
+
* `device.not_found`; no machine connected is a no-op.
|
|
96
96
|
*/
|
|
97
|
-
revoke(orgId: string, accountId: string,
|
|
97
|
+
revoke(orgId: string, accountId: string, deviceId: string, actorTokenId?: string): Promise<void>;
|
|
98
98
|
/**
|
|
99
|
-
* Revokes the
|
|
99
|
+
* Revokes the device's active machine, if any, in the caller's transaction, and after the
|
|
100
100
|
* commit tells it to stop for `reason` (none: only its connection is closed) and closes its
|
|
101
101
|
* connection. Returns the revoked machine's id. Used by {@link issue} (replaced: no stop),
|
|
102
|
-
* {@link revoke} and the
|
|
102
|
+
* {@link revoke} and the device's deletion (`deleted`).
|
|
103
103
|
*/
|
|
104
|
-
revokeActive(
|
|
104
|
+
revokeActive(deviceId: string, reason: DeviceStopArgs["reason"] | null): Promise<string | null>;
|
|
105
105
|
/**
|
|
106
106
|
* Revokes one machine by id (a sign-in whose credential never reached the program), with the
|
|
107
107
|
* same post-commit disconnect as a replacement (no stop frame). True when it was active.
|
|
108
108
|
*/
|
|
109
109
|
revokeMachine(machineId: string): Promise<boolean>;
|
|
110
|
-
/** The
|
|
111
|
-
activeFor(
|
|
110
|
+
/** The device's active machine, or null. */
|
|
111
|
+
activeFor(deviceId: string): Promise<ActiveMachine | null>;
|
|
112
112
|
/**
|
|
113
113
|
* After a revocation commits: `stop` is queued only while a replica holds the connection (an
|
|
114
114
|
* offline machine must not find it on the list later), then the revocation is published.
|
|
@@ -17,10 +17,10 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
17
17
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
18
18
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
19
19
|
import { KERNEL_BUS, KERNEL_LOGGER } from "../infra/tokens.js";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import { LOCAL_PROVIDER } from "../sandbox/providers.js";
|
|
21
|
+
import { deviceNotFound } from "../sandbox/sandbox.service.js";
|
|
22
22
|
import { deviceRevokedTopic } from "./keys.js";
|
|
23
|
-
import {
|
|
23
|
+
import { LocalDeviceRelay } from "./relay.js";
|
|
24
24
|
/** The machine credential's prefix (`cap_dev_` plus 32 random bytes, base64url). */
|
|
25
25
|
export const MACHINE_CREDENTIAL_PREFIX = "cap_dev_";
|
|
26
26
|
const CREDENTIAL_RE = /^cap_dev_[A-Za-z0-9_-]{43}$/;
|
|
@@ -28,12 +28,12 @@ const CREDENTIAL_RE = /^cap_dev_[A-Za-z0-9_-]{43}$/;
|
|
|
28
28
|
const STOP_DEADLINE_MS = 30_000;
|
|
29
29
|
/**
|
|
30
30
|
* Connected machines and their credentials (environments spec §13.2). One active machine per
|
|
31
|
-
* device
|
|
31
|
+
* local device: issuing a credential revokes the device's previous machine. A
|
|
32
32
|
* credential is `cap_dev_` plus 32 random bytes; only its sha256 is stored. It is accepted only
|
|
33
33
|
* by the device connection route ({@link authenticate}), never by `/api`: it is no `Principal`.
|
|
34
34
|
*
|
|
35
35
|
* A revoked machine is told so after the commit: `stop { reason }` relayed while a replica holds
|
|
36
|
-
* its connection (fire-and-forget, ruling R14) and `device.revoked.<
|
|
36
|
+
* its connection (fire-and-forget, ruling R14) and `device.revoked.<deviceId>` published with
|
|
37
37
|
* `{ machineId }`, so the holder closes that machine's connection.
|
|
38
38
|
*/
|
|
39
39
|
let MachinesService = class MachinesService {
|
|
@@ -50,16 +50,16 @@ let MachinesService = class MachinesService {
|
|
|
50
50
|
this.log = log;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* A new machine for the
|
|
54
|
-
*
|
|
55
|
-
* which has already checked the
|
|
53
|
+
* A new machine for the device and its credential (returned once). Revokes the
|
|
54
|
+
* device's active machine first. Joins the caller's transaction (the device confirmation),
|
|
55
|
+
* which has already checked the device is the person's own live local device.
|
|
56
56
|
*/
|
|
57
57
|
async issue(input) {
|
|
58
|
-
// the machine the
|
|
58
|
+
// the machine the device was last served from (revoked or not)
|
|
59
59
|
const prev = await this.db
|
|
60
|
-
.selectFrom("
|
|
60
|
+
.selectFrom("device_machines")
|
|
61
61
|
.select(["name", "os", "arch"])
|
|
62
|
-
.where("
|
|
62
|
+
.where("device_id", "=", input.deviceId)
|
|
63
63
|
.orderBy("created_at", "desc")
|
|
64
64
|
.orderBy("id", "desc")
|
|
65
65
|
.limit(1)
|
|
@@ -67,14 +67,14 @@ let MachinesService = class MachinesService {
|
|
|
67
67
|
// another machine has other files (maintainer decision 2026-11-08)
|
|
68
68
|
const moved = prev !== undefined && !sameMachine(prev, input);
|
|
69
69
|
// no stop frame: it would wait on the list and could reach the replacement machine instead
|
|
70
|
-
await this.revokeActive(input.
|
|
71
|
-
await this.resetInstance(input.
|
|
70
|
+
await this.revokeActive(input.deviceId, null);
|
|
71
|
+
await this.resetInstance(input.deviceId, moved);
|
|
72
72
|
const credential = MACHINE_CREDENTIAL_PREFIX + randomBytes(32).toString("base64url");
|
|
73
73
|
const row = await this.db
|
|
74
|
-
.insertInto("
|
|
74
|
+
.insertInto("device_machines")
|
|
75
75
|
.values({
|
|
76
76
|
org_id: input.orgId,
|
|
77
|
-
|
|
77
|
+
device_id: input.deviceId,
|
|
78
78
|
account_id: input.accountId,
|
|
79
79
|
credential_hash: sha256Hex(credential),
|
|
80
80
|
name: input.name,
|
|
@@ -87,20 +87,20 @@ let MachinesService = class MachinesService {
|
|
|
87
87
|
return { machineId: row.id, credential };
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Ruling R23: the new machine may serve the
|
|
90
|
+
* Ruling R23: the new machine may serve the device from another directory or mode (other
|
|
91
91
|
* roots), and a login recreates a directory that was lost (ruling R24). So the instance row
|
|
92
92
|
* forgets its instance (status `none`, no handle) and its files count as there again; the next
|
|
93
|
-
* use creates a fresh instance on the machine (with its roots: a new `
|
|
93
|
+
* use creates a fresh instance on the machine (with its roots: a new `device_paths` event
|
|
94
94
|
* follows). The fence moves on, so a lifecycle holder of the old instance fails its next write.
|
|
95
95
|
* The reset count (`fs_resets`) moves on when the files the next instance sees are not the old
|
|
96
96
|
* ones, so conversations that used the old files are told their files were reset, not that they
|
|
97
97
|
* were kept: lost files come back as an empty directory, and another machine (`moved`: name, OS
|
|
98
|
-
* or arch differ from the
|
|
98
|
+
* or arch differ from the device's previous machine) has other files. Another directory on
|
|
99
99
|
* the same machine is known only from the new machine's first hello ({@link recordHello}).
|
|
100
100
|
*/
|
|
101
|
-
async resetInstance(
|
|
101
|
+
async resetInstance(deviceId, moved) {
|
|
102
102
|
await this.db
|
|
103
|
-
.updateTable("
|
|
103
|
+
.updateTable("device_instances")
|
|
104
104
|
.set((eb) => ({
|
|
105
105
|
status: "none",
|
|
106
106
|
handle: null,
|
|
@@ -113,11 +113,11 @@ let MachinesService = class MachinesService {
|
|
|
113
113
|
? sql `fs_resets + 1`
|
|
114
114
|
: sql `CASE WHEN fs_status = 'lost' THEN fs_resets + 1 ELSE fs_resets END`,
|
|
115
115
|
}))
|
|
116
|
-
.where("
|
|
116
|
+
.where("device_id", "=", deviceId)
|
|
117
117
|
.execute();
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
* Who a machine credential stands for, or null: the machine is not revoked, its
|
|
120
|
+
* Who a machine credential stands for, or null: the machine is not revoked, its device is
|
|
121
121
|
* live, the org is not deleted (ruling R12), and the person is an active account still a member
|
|
122
122
|
* of the org. Read live on every call (a connection re-checks it as it sees fit).
|
|
123
123
|
*/
|
|
@@ -125,17 +125,17 @@ let MachinesService = class MachinesService {
|
|
|
125
125
|
if (!CREDENTIAL_RE.test(credential))
|
|
126
126
|
return null;
|
|
127
127
|
const row = await this.db
|
|
128
|
-
.selectFrom("
|
|
129
|
-
.innerJoin("
|
|
128
|
+
.selectFrom("device_machines as m")
|
|
129
|
+
.innerJoin("devices as e", "e.id", "m.device_id")
|
|
130
130
|
.innerJoin("orgs as o", "o.id", "m.org_id")
|
|
131
131
|
.innerJoin("memberships as ms", (j) => j.onRef("ms.org_id", "=", "m.org_id").onRef("ms.account_id", "=", "m.account_id"))
|
|
132
132
|
.innerJoin("accounts as a", "a.id", "m.account_id")
|
|
133
|
-
.select(["m.id", "m.org_id", "m.
|
|
133
|
+
.select(["m.id", "m.org_id", "m.device_id", "m.account_id"])
|
|
134
134
|
.where("m.credential_hash", "=", sha256Hex(credential))
|
|
135
135
|
.where("m.revoked_at", "is", null)
|
|
136
136
|
.whereRef("e.org_id", "=", "m.org_id")
|
|
137
137
|
.where("e.deleted_at", "is", null)
|
|
138
|
-
.where("e.source", "=", "
|
|
138
|
+
.where("e.source", "=", "local")
|
|
139
139
|
.where("o.deleted_at", "is", null)
|
|
140
140
|
.where("a.status", "=", "active")
|
|
141
141
|
.executeTakeFirst();
|
|
@@ -143,23 +143,23 @@ let MachinesService = class MachinesService {
|
|
|
143
143
|
? {
|
|
144
144
|
machineId: row.id,
|
|
145
145
|
orgId: row.org_id,
|
|
146
|
-
|
|
146
|
+
deviceId: row.device_id,
|
|
147
147
|
accountId: row.account_id,
|
|
148
148
|
}
|
|
149
149
|
: null;
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
152
|
* Stores what the program reported in its hello frame and marks the machine seen. On the
|
|
153
|
-
* machine's first hello, when it is the same machine as the
|
|
153
|
+
* machine's first hello, when it is the same machine as the device's previous one (else
|
|
154
154
|
* {@link issue} already counted the reset) but serves another directory or mode (other roots,
|
|
155
|
-
* ruling R23), the
|
|
155
|
+
* ruling R23), the device's files are other files: `fs_resets` moves on (maintainer
|
|
156
156
|
* decision 2026-11-08), so conversations get the files-reset notice. A previous machine that
|
|
157
157
|
* never said hello has no known roots: nothing to compare.
|
|
158
158
|
*/
|
|
159
159
|
async recordHello(machineId, hello) {
|
|
160
160
|
const self = await this.db
|
|
161
|
-
.selectFrom("
|
|
162
|
-
.select(["
|
|
161
|
+
.selectFrom("device_machines")
|
|
162
|
+
.select(["device_id", "name", "os", "arch", "roots", "created_at"])
|
|
163
163
|
.where("id", "=", machineId)
|
|
164
164
|
.where("revoked_at", "is", null)
|
|
165
165
|
.forUpdate()
|
|
@@ -168,10 +168,10 @@ let MachinesService = class MachinesService {
|
|
|
168
168
|
return;
|
|
169
169
|
if (self.roots === null) {
|
|
170
170
|
const prev = await this.db
|
|
171
|
-
.selectFrom("
|
|
171
|
+
.selectFrom("device_machines")
|
|
172
172
|
.select(["name", "os", "arch", "mode"])
|
|
173
173
|
.select(sql `roots = ${JSON.stringify(hello.roots)}::jsonb`.as("same_roots"))
|
|
174
|
-
.where("
|
|
174
|
+
.where("device_id", "=", self.device_id)
|
|
175
175
|
.where("id", "!=", machineId)
|
|
176
176
|
.where("created_at", "<=", self.created_at)
|
|
177
177
|
.where("roots", "is not", null)
|
|
@@ -183,14 +183,14 @@ let MachinesService = class MachinesService {
|
|
|
183
183
|
sameMachine(prev, self) &&
|
|
184
184
|
(!prev.same_roots || prev.mode !== hello.mode)) {
|
|
185
185
|
await this.db
|
|
186
|
-
.updateTable("
|
|
186
|
+
.updateTable("device_instances")
|
|
187
187
|
.set({ fs_resets: sql `fs_resets + 1` })
|
|
188
|
-
.where("
|
|
188
|
+
.where("device_id", "=", self.device_id)
|
|
189
189
|
.execute();
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
await this.db
|
|
193
|
-
.updateTable("
|
|
193
|
+
.updateTable("device_machines")
|
|
194
194
|
.set({
|
|
195
195
|
program_version: hello.programVersion,
|
|
196
196
|
os: hello.os,
|
|
@@ -207,7 +207,7 @@ let MachinesService = class MachinesService {
|
|
|
207
207
|
/** Marks the machine seen (a ping); writes at most once every 60 seconds. */
|
|
208
208
|
async touch(machineId) {
|
|
209
209
|
await this.db
|
|
210
|
-
.updateTable("
|
|
210
|
+
.updateTable("device_machines")
|
|
211
211
|
.set({ last_seen_at: sql `now()` })
|
|
212
212
|
.where("id", "=", machineId)
|
|
213
213
|
.where("revoked_at", "is", null)
|
|
@@ -218,56 +218,56 @@ let MachinesService = class MachinesService {
|
|
|
218
218
|
.execute();
|
|
219
219
|
}
|
|
220
220
|
/**
|
|
221
|
-
* Disconnects the machine of one of the person's own live
|
|
222
|
-
* /orgs/:orgId/
|
|
223
|
-
* `
|
|
224
|
-
* `
|
|
221
|
+
* Disconnects the machine of one of the person's own live local devices (`DELETE
|
|
222
|
+
* /orgs/:orgId/devices/:id/machine`): revokes it and audits
|
|
223
|
+
* `device.machine_revoked`. Anyone else's device, a cloud one or a missing one →
|
|
224
|
+
* `device.not_found`; no machine connected is a no-op.
|
|
225
225
|
*/
|
|
226
|
-
async revoke(orgId, accountId,
|
|
226
|
+
async revoke(orgId, accountId, deviceId, actorTokenId) {
|
|
227
227
|
const env = await this.db
|
|
228
|
-
.selectFrom("
|
|
228
|
+
.selectFrom("devices")
|
|
229
229
|
.select("id")
|
|
230
|
-
.where("id", "=",
|
|
230
|
+
.where("id", "=", deviceId)
|
|
231
231
|
.where("org_id", "=", orgId)
|
|
232
232
|
.where("owner_kind", "=", "personal")
|
|
233
233
|
.where("owner_account_id", "=", accountId)
|
|
234
|
-
.where("source", "=", "
|
|
235
|
-
.where("provider", "=",
|
|
234
|
+
.where("source", "=", "local")
|
|
235
|
+
.where("provider", "=", LOCAL_PROVIDER)
|
|
236
236
|
.where("deleted_at", "is", null)
|
|
237
237
|
.forNoKeyUpdate()
|
|
238
238
|
.executeTakeFirst();
|
|
239
239
|
if (!env)
|
|
240
|
-
throw
|
|
241
|
-
const machineId = await this.revokeActive(
|
|
240
|
+
throw deviceNotFound();
|
|
241
|
+
const machineId = await this.revokeActive(deviceId, "revoked");
|
|
242
242
|
if (machineId === null)
|
|
243
243
|
return;
|
|
244
244
|
await audit(this.db.db, {
|
|
245
245
|
actorAccountId: accountId,
|
|
246
246
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
247
247
|
orgId,
|
|
248
|
-
action: "
|
|
249
|
-
target:
|
|
248
|
+
action: "device.machine_revoked",
|
|
249
|
+
target: deviceId,
|
|
250
250
|
detail: { machineId },
|
|
251
251
|
});
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
|
-
* Revokes the
|
|
254
|
+
* Revokes the device's active machine, if any, in the caller's transaction, and after the
|
|
255
255
|
* commit tells it to stop for `reason` (none: only its connection is closed) and closes its
|
|
256
256
|
* connection. Returns the revoked machine's id. Used by {@link issue} (replaced: no stop),
|
|
257
|
-
* {@link revoke} and the
|
|
257
|
+
* {@link revoke} and the device's deletion (`deleted`).
|
|
258
258
|
*/
|
|
259
|
-
async revokeActive(
|
|
259
|
+
async revokeActive(deviceId, reason) {
|
|
260
260
|
const row = await this.db
|
|
261
|
-
.updateTable("
|
|
261
|
+
.updateTable("device_machines")
|
|
262
262
|
.set({ revoked_at: sql `now()` })
|
|
263
|
-
.where("
|
|
263
|
+
.where("device_id", "=", deviceId)
|
|
264
264
|
.where("revoked_at", "is", null)
|
|
265
265
|
.returning("id")
|
|
266
266
|
.executeTakeFirst();
|
|
267
267
|
if (!row)
|
|
268
268
|
return null;
|
|
269
269
|
const machineId = row.id;
|
|
270
|
-
afterCommit(() => this.disconnect(
|
|
270
|
+
afterCommit(() => this.disconnect(deviceId, machineId, reason));
|
|
271
271
|
return machineId;
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
@@ -276,22 +276,22 @@ let MachinesService = class MachinesService {
|
|
|
276
276
|
*/
|
|
277
277
|
async revokeMachine(machineId) {
|
|
278
278
|
const row = await this.db
|
|
279
|
-
.updateTable("
|
|
279
|
+
.updateTable("device_machines")
|
|
280
280
|
.set({ revoked_at: sql `now()` })
|
|
281
281
|
.where("id", "=", machineId)
|
|
282
282
|
.where("revoked_at", "is", null)
|
|
283
|
-
.returning("
|
|
283
|
+
.returning("device_id")
|
|
284
284
|
.executeTakeFirst();
|
|
285
285
|
if (!row)
|
|
286
286
|
return false;
|
|
287
|
-
const
|
|
288
|
-
afterCommit(() => this.disconnect(
|
|
287
|
+
const deviceId = row.device_id;
|
|
288
|
+
afterCommit(() => this.disconnect(deviceId, machineId, null));
|
|
289
289
|
return true;
|
|
290
290
|
}
|
|
291
|
-
/** The
|
|
292
|
-
async activeFor(
|
|
291
|
+
/** The device's active machine, or null. */
|
|
292
|
+
async activeFor(deviceId) {
|
|
293
293
|
const r = await this.db
|
|
294
|
-
.selectFrom("
|
|
294
|
+
.selectFrom("device_machines")
|
|
295
295
|
.select([
|
|
296
296
|
"id",
|
|
297
297
|
"org_id",
|
|
@@ -303,7 +303,7 @@ let MachinesService = class MachinesService {
|
|
|
303
303
|
"last_seen_at",
|
|
304
304
|
"created_at",
|
|
305
305
|
])
|
|
306
|
-
.where("
|
|
306
|
+
.where("device_id", "=", deviceId)
|
|
307
307
|
.where("revoked_at", "is", null)
|
|
308
308
|
.executeTakeFirst();
|
|
309
309
|
return r
|
|
@@ -325,20 +325,20 @@ let MachinesService = class MachinesService {
|
|
|
325
325
|
* offline machine must not find it on the list later), then the revocation is published.
|
|
326
326
|
* Failures are logged; the revocation itself stands.
|
|
327
327
|
*/
|
|
328
|
-
async disconnect(
|
|
328
|
+
async disconnect(deviceId, machineId, reason) {
|
|
329
329
|
try {
|
|
330
|
-
if (reason !== null && (await this.relay.holderLive(
|
|
331
|
-
await this.relay.send(
|
|
330
|
+
if (reason !== null && (await this.relay.holderLive(deviceId))) {
|
|
331
|
+
await this.relay.send(deviceId, "stop", { reason }, STOP_DEADLINE_MS);
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
334
|
catch (err) {
|
|
335
|
-
this.log.warn("could not tell a revoked machine to stop", {
|
|
335
|
+
this.log.warn("could not tell a revoked machine to stop", { deviceId, machineId, err });
|
|
336
336
|
}
|
|
337
337
|
try {
|
|
338
|
-
await this.bus.publish(deviceRevokedTopic(
|
|
338
|
+
await this.bus.publish(deviceRevokedTopic(deviceId), { machineId });
|
|
339
339
|
}
|
|
340
340
|
catch (err) {
|
|
341
|
-
this.log.warn("could not publish a machine revocation", {
|
|
341
|
+
this.log.warn("could not publish a machine revocation", { deviceId, machineId, err });
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
};
|
|
@@ -355,7 +355,7 @@ MachinesService = __decorate([
|
|
|
355
355
|
Injectable(),
|
|
356
356
|
__param(0, Inject(KernelDb)),
|
|
357
357
|
__param(1, Inject(TransactionHost)),
|
|
358
|
-
__param(2, Inject(
|
|
358
|
+
__param(2, Inject(LocalDeviceRelay)),
|
|
359
359
|
__param(3, Inject(KERNEL_BUS)),
|
|
360
360
|
__param(4, Inject(KERNEL_LOGGER))
|
|
361
361
|
], MachinesService);
|
|
@@ -5,10 +5,10 @@ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
|
5
5
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
6
6
|
import type { KernelLogger } from "../log.js";
|
|
7
7
|
import { DeviceTargets } from "./targets.js";
|
|
8
|
-
/** The
|
|
9
|
-
export declare const
|
|
8
|
+
/** The device's connected machine is not connected (no replica holds its lease). */
|
|
9
|
+
export declare const deviceOffline: () => ApiError;
|
|
10
10
|
/**
|
|
11
|
-
* The
|
|
11
|
+
* The device's machine was disconnected (revoked, signed out): 403 `device.forbidden`
|
|
12
12
|
* with `details: [{ reason: "machine_revoked" }]`, so the tools tell the model what to ask for.
|
|
13
13
|
*/
|
|
14
14
|
export declare const machineRevoked: () => ApiError;
|
|
@@ -31,8 +31,8 @@ export interface DeviceCallOptions {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* The caller side of the device relay (environments spec §13.4). A request goes onto the
|
|
34
|
-
*
|
|
35
|
-
* `device.req.<
|
|
34
|
+
* device's Redis list (`capk:device-req:<deviceId>`) and is announced on
|
|
35
|
+
* `device.req.<deviceId>`; whichever gateway replica holds the machine's lease drains the
|
|
36
36
|
* list and forwards the frames to the machine.
|
|
37
37
|
*
|
|
38
38
|
* **The holder's half of the wire** (Task 6 follows it): items on the list are `KernelReq` or
|
|
@@ -45,13 +45,13 @@ export interface DeviceCallOptions {
|
|
|
45
45
|
* The caller subscribes to its result topic before pushing and reads the result key once after,
|
|
46
46
|
* and once more before giving up (the run-abort pattern; a publish lost while the subscriber
|
|
47
47
|
* reconnects is still found). It waits within its own call for at most `deadlineMs` plus
|
|
48
|
-
* `
|
|
48
|
+
* `devices.local.reconnectGraceSeconds`; when the lease disappears meanwhile (checked every
|
|
49
49
|
* {@link HOLDER_POLL_MS}) the wait ends at most one grace later. A result produced while the
|
|
50
50
|
* machine was disconnected arrives after it reconnects. Giving up (grace, abort, a bad result)
|
|
51
51
|
* removes the request from the list, or queues a `cancel` when the machine already took it
|
|
52
52
|
* (ruling R15). Nothing is kept in process memory beyond the call.
|
|
53
53
|
*/
|
|
54
|
-
export declare class
|
|
54
|
+
export declare class LocalDeviceRelay {
|
|
55
55
|
private readonly redis;
|
|
56
56
|
private readonly bus;
|
|
57
57
|
private readonly locks;
|
|
@@ -60,27 +60,27 @@ export declare class DeviceRelay {
|
|
|
60
60
|
private readonly targets;
|
|
61
61
|
constructor(redis: Redis, bus: ExtensionContext["bus"], locks: Pick<LockService, "holderOf">, config: Pick<KernelConfigService, "get">, log: Pick<KernelLogger, "warn">, targets: Pick<DeviceTargets, "state">);
|
|
62
62
|
/** Whether a gateway replica holds the machine's connection now. */
|
|
63
|
-
holderLive(
|
|
63
|
+
holderLive(deviceId: string): Promise<boolean>;
|
|
64
64
|
/**
|
|
65
65
|
* Queues `op` for the machine without waiting for an answer (ruling R14: `stop`). The caller
|
|
66
66
|
* checks {@link holderLive} first; the frame waits on the list for the grace like any request.
|
|
67
67
|
*/
|
|
68
|
-
send(
|
|
68
|
+
send(deviceId: string, op: DeviceOp, args: unknown, deadlineMs: number): Promise<void>;
|
|
69
69
|
/**
|
|
70
|
-
* Runs `op` on the
|
|
71
|
-
* `
|
|
72
|
-
* out, but `
|
|
73
|
-
* `
|
|
70
|
+
* Runs `op` on the device's machine and returns its validated result. Throws
|
|
71
|
+
* `device.offline` when no replica holds the machine at send time or when the wait ran
|
|
72
|
+
* out, but `device.forbidden` ({@link machineRevoked}) when the machine was revoked and
|
|
73
|
+
* `device.not_found` when the device was deleted (checked whenever the connection is
|
|
74
74
|
* missing, ruling R27); the machine's failures map to `SandboxGoneError` (`gone`),
|
|
75
75
|
* `FileSystemGoneError` (`fs_gone`) or a `ToolError` (`policy`, and `error` prefixed
|
|
76
76
|
* {@link ON_MACHINE_PREFIX}); an invalid answer throws a `DeviceProtocolError`. On abort the
|
|
77
77
|
* abort reason is thrown.
|
|
78
78
|
*/
|
|
79
|
-
call<Op extends DeviceOp>(
|
|
79
|
+
call<Op extends DeviceOp>(deviceId: string, op: Op, args: unknown, opts: DeviceCallOptions): Promise<DeviceOpResults[Op]>;
|
|
80
80
|
/** {@link holderLive}, or undefined when it takes longer than {@link LEASE_CHECK_TIMEOUT_MS}. */
|
|
81
81
|
private leaseCheck;
|
|
82
82
|
/**
|
|
83
|
-
* The
|
|
83
|
+
* The device's state ({@link DeviceTargets}), or undefined when it cannot be read within
|
|
84
84
|
* {@link LEASE_CHECK_TIMEOUT_MS} (a failure only logs: the caller keeps its other rules).
|
|
85
85
|
*/
|
|
86
86
|
private targetState;
|