@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
|
@@ -10,8 +10,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { sql } from "kysely";
|
|
13
|
-
import { deviceLeaseKey } from "../devices/keys.js";
|
|
14
|
-
import { MachinesService } from "../devices/machines.js";
|
|
15
13
|
import { ApiError } from "../errors.js";
|
|
16
14
|
import { ExtensionDisplayNames } from "../extensions/display-name.js";
|
|
17
15
|
import { audit } from "../identity/audit.js";
|
|
@@ -22,23 +20,25 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
|
22
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
23
21
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
24
22
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
23
|
+
import { deviceLeaseKey } from "../local-devices/keys.js";
|
|
24
|
+
import { MachinesService } from "../local-devices/machines.js";
|
|
25
25
|
import { SandboxJobs } from "../sandbox/jobs.js";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
26
|
+
import { isInternalProvider, LOCAL_PROVIDER, SandboxProviders } from "../sandbox/providers.js";
|
|
27
|
+
import { deviceNotFound, SandboxService } from "../sandbox/sandbox.service.js";
|
|
28
|
+
import { DeviceAccess, demotedProvider, deviceProvider, deviceUnavailable, inUseForDefault, } from "./access.js";
|
|
29
29
|
const isUniqueViolation = (e, constraint) => typeof e === "object" &&
|
|
30
30
|
e !== null &&
|
|
31
31
|
e.code === "23505" &&
|
|
32
32
|
e.constraint === constraint;
|
|
33
|
-
const nameTaken = () => new ApiError("
|
|
33
|
+
const nameTaken = () => new ApiError("device.name_taken", "You already have a device with this name");
|
|
34
34
|
/**
|
|
35
|
-
* Personal
|
|
35
|
+
* Personal devices (spec 2026-11-07-environments-design §4.2 items 7, 12, 13; §6; §9):
|
|
36
36
|
* cloud ones on the deployment's sandbox provider and `device` ones on a connected machine; a
|
|
37
|
-
* person's
|
|
38
|
-
* `
|
|
37
|
+
* person's devices in an org, visible and usable only by their owner (anyone else gets
|
|
38
|
+
* `device.not_found`). Deleting one soft-deletes it and destroys its instance and files in a
|
|
39
39
|
* `sandbox-destroy-for` job after the commit.
|
|
40
40
|
*/
|
|
41
|
-
let
|
|
41
|
+
let DevicesService = class DevicesService {
|
|
42
42
|
db;
|
|
43
43
|
txHost;
|
|
44
44
|
config;
|
|
@@ -66,8 +66,8 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
66
66
|
this.machines = machines;
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* The person's live
|
|
70
|
-
* current sandbox provider the default
|
|
69
|
+
* The person's live devices in the org, the default first, then by creation. With a
|
|
70
|
+
* current sandbox provider the default device is created first if missing (its first need,
|
|
71
71
|
* spec §4.2 item 7; an existing one is only read).
|
|
72
72
|
*/
|
|
73
73
|
async list(orgId, accountId) {
|
|
@@ -76,8 +76,8 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
76
76
|
const items = await this.views(orgId, accountId);
|
|
77
77
|
const last = await this.db
|
|
78
78
|
.selectFrom("conversations as c")
|
|
79
|
-
.innerJoin("
|
|
80
|
-
.select("c.
|
|
79
|
+
.innerJoin("devices as e", "e.id", "c.device_id")
|
|
80
|
+
.select("c.device_id")
|
|
81
81
|
.where("c.org_id", "=", orgId)
|
|
82
82
|
.where("c.account_id", "=", accountId)
|
|
83
83
|
.where("e.org_id", "=", orgId)
|
|
@@ -90,22 +90,22 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
90
90
|
return {
|
|
91
91
|
items,
|
|
92
92
|
defaultId: defaultId ?? items.find((v) => v.isDefault)?.id ?? null,
|
|
93
|
-
lastUsedId: last?.
|
|
93
|
+
lastUsedId: last?.device_id ?? null,
|
|
94
94
|
cloudAvailable,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
-
/** One of the person's live
|
|
97
|
+
/** One of the person's live devices; anyone else's (or a missing one) → `not_found`. */
|
|
98
98
|
async get(orgId, accountId, id) {
|
|
99
99
|
const [view] = await this.views(orgId, accountId, id);
|
|
100
100
|
if (!view)
|
|
101
|
-
throw
|
|
101
|
+
throw deviceNotFound();
|
|
102
102
|
return view;
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
|
-
* A new personal cloud
|
|
106
|
-
* use). Needs a current sandbox provider (`
|
|
107
|
-
* (case-insensitive) → `
|
|
108
|
-
* default counts) → `
|
|
105
|
+
* A new personal cloud device, ready at once (its instance and files are made on first
|
|
106
|
+
* use). Needs a current sandbox provider (`device.unavailable`); a live name of the person's
|
|
107
|
+
* (case-insensitive) → `device.name_taken`; `devices.maxPersonal` live ones (the
|
|
108
|
+
* default counts) → `device.limit_reached`. Serialized per person by their membership row.
|
|
109
109
|
*/
|
|
110
110
|
async create(orgId, accountId, input, actorTokenId, auditDetail) {
|
|
111
111
|
const id = await this.insert(orgId, accountId, input.name, actorTokenId, auditDetail);
|
|
@@ -114,44 +114,44 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
114
114
|
async insert(orgId, accountId, name, actorTokenId, auditDetail) {
|
|
115
115
|
const current = this.providers.cloud();
|
|
116
116
|
if (!current)
|
|
117
|
-
throw
|
|
117
|
+
throw deviceUnavailable(null);
|
|
118
118
|
return this.insertNamed(orgId, accountId, name, "cloud", current.plugin, actorTokenId, auditDetail ?? {});
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
121
|
-
* A new personal
|
|
121
|
+
* A new personal local device for a machine being connected (provider `@local`; the
|
|
122
122
|
* machine row is the caller's). Joins the caller's transaction (the device authorization's).
|
|
123
|
-
* Counted against `
|
|
124
|
-
* person's → `
|
|
123
|
+
* Counted against `devices.maxPersonal` (`device.limit_reached`); a live name of the
|
|
124
|
+
* person's → `device.name_taken`. Needs no cloud provider.
|
|
125
125
|
*/
|
|
126
|
-
async
|
|
127
|
-
const id = await this.insertNamed(orgId, accountId, name, "
|
|
128
|
-
source: "
|
|
126
|
+
async createLocal(orgId, accountId, name) {
|
|
127
|
+
const id = await this.insertNamed(orgId, accountId, name, "local", LOCAL_PROVIDER, undefined, {
|
|
128
|
+
source: "local",
|
|
129
129
|
});
|
|
130
130
|
return { id };
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* Inserts a named personal
|
|
134
|
-
* and name checks, and audits `
|
|
133
|
+
* Inserts a named personal device under the person's membership lock, after the limit
|
|
134
|
+
* and name checks, and audits `device.created`. Runs in the caller's transaction.
|
|
135
135
|
*/
|
|
136
136
|
async insertNamed(orgId, accountId, name, source, provider, actorTokenId, auditDetail) {
|
|
137
137
|
await this.lockMember(orgId, accountId);
|
|
138
138
|
const { n } = await this.db
|
|
139
|
-
.selectFrom("
|
|
139
|
+
.selectFrom("devices")
|
|
140
140
|
.select((eb) => eb.fn.countAll().as("n"))
|
|
141
141
|
.where("org_id", "=", orgId)
|
|
142
142
|
.where("owner_kind", "=", "personal")
|
|
143
143
|
.where("owner_account_id", "=", accountId)
|
|
144
144
|
.where("deleted_at", "is", null)
|
|
145
145
|
.executeTakeFirstOrThrow();
|
|
146
|
-
const max = this.config.get().
|
|
146
|
+
const max = this.config.get().devices.maxPersonal;
|
|
147
147
|
if (Number(n) >= max) {
|
|
148
|
-
throw new ApiError("
|
|
148
|
+
throw new ApiError("device.limit_reached", `You already have ${max} devices in this organization; delete one to create another`);
|
|
149
149
|
}
|
|
150
150
|
await this.assertNameFree(orgId, accountId, name);
|
|
151
151
|
const id = randomUUID();
|
|
152
152
|
try {
|
|
153
153
|
await this.db
|
|
154
|
-
.insertInto("
|
|
154
|
+
.insertInto("devices")
|
|
155
155
|
.values({
|
|
156
156
|
id,
|
|
157
157
|
org_id: orgId,
|
|
@@ -164,7 +164,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
164
164
|
.execute();
|
|
165
165
|
}
|
|
166
166
|
catch (e) {
|
|
167
|
-
if (isUniqueViolation(e, "
|
|
167
|
+
if (isUniqueViolation(e, "devices_name_uq"))
|
|
168
168
|
throw nameTaken();
|
|
169
169
|
throw e;
|
|
170
170
|
}
|
|
@@ -172,7 +172,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
172
172
|
actorAccountId: accountId,
|
|
173
173
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
174
174
|
orgId,
|
|
175
|
-
action: "
|
|
175
|
+
action: "device.created",
|
|
176
176
|
target: id,
|
|
177
177
|
detail: { name, ...auditDetail },
|
|
178
178
|
});
|
|
@@ -185,35 +185,35 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
185
185
|
*/
|
|
186
186
|
async checkCreate(orgId, accountId, name) {
|
|
187
187
|
if (!this.providers.cloud())
|
|
188
|
-
throw
|
|
188
|
+
throw deviceUnavailable(null);
|
|
189
189
|
const { n } = await this.db
|
|
190
|
-
.selectFrom("
|
|
190
|
+
.selectFrom("devices")
|
|
191
191
|
.select((eb) => eb.fn.countAll().as("n"))
|
|
192
192
|
.where("org_id", "=", orgId)
|
|
193
193
|
.where("owner_kind", "=", "personal")
|
|
194
194
|
.where("owner_account_id", "=", accountId)
|
|
195
195
|
.where("deleted_at", "is", null)
|
|
196
196
|
.executeTakeFirstOrThrow();
|
|
197
|
-
const max = this.config.get().
|
|
197
|
+
const max = this.config.get().devices.maxPersonal;
|
|
198
198
|
if (Number(n) >= max) {
|
|
199
|
-
throw new ApiError("
|
|
199
|
+
throw new ApiError("device.limit_reached", `You already have ${max} devices in this organization; delete one to create another`);
|
|
200
200
|
}
|
|
201
201
|
await this.assertNameFree(orgId, accountId, name);
|
|
202
202
|
}
|
|
203
|
-
/** How many of the person's conversations work in the
|
|
203
|
+
/** How many of the person's conversations work in the device (read only). */
|
|
204
204
|
async boundConversations(orgId, accountId, id) {
|
|
205
205
|
const { n } = await this.db
|
|
206
206
|
.selectFrom("conversations")
|
|
207
207
|
.select((eb) => eb.fn.countAll().as("n"))
|
|
208
208
|
.where("org_id", "=", orgId)
|
|
209
209
|
.where("account_id", "=", accountId)
|
|
210
|
-
.where("
|
|
210
|
+
.where("device_id", "=", id)
|
|
211
211
|
.executeTakeFirstOrThrow();
|
|
212
212
|
return Number(n);
|
|
213
213
|
}
|
|
214
214
|
/**
|
|
215
215
|
* The refusals {@link resetFiles} would raise (not found, its provider not current, a connected
|
|
216
|
-
*
|
|
216
|
+
* device), read only.
|
|
217
217
|
*/
|
|
218
218
|
async checkReset(orgId, accountId, id) {
|
|
219
219
|
refuseDeviceReset(await this.access.usable(orgId, accountId, id));
|
|
@@ -226,7 +226,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
229
|
-
* Renames one of the person's live
|
|
229
|
+
* Renames one of the person's live devices (the default too); see {@link create}. A
|
|
230
230
|
* renamed former default is the person's own from then on: it loses its demotion mark, so a
|
|
231
231
|
* switch back to its provider no longer restores it.
|
|
232
232
|
*/
|
|
@@ -238,19 +238,19 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
238
238
|
const before = await this.lockOwn(orgId, accountId, id);
|
|
239
239
|
if (before.name === name)
|
|
240
240
|
return;
|
|
241
|
-
// the same name in another case is this
|
|
241
|
+
// the same name in another case is this device's own: the index allows it
|
|
242
242
|
if (before.name?.toLowerCase() !== name.toLowerCase()) {
|
|
243
243
|
await this.assertNameFree(orgId, accountId, name);
|
|
244
244
|
}
|
|
245
245
|
try {
|
|
246
246
|
await this.db
|
|
247
|
-
.updateTable("
|
|
247
|
+
.updateTable("devices")
|
|
248
248
|
.set({ name, demoted_from_provider: null, updated_at: sql `now()` })
|
|
249
249
|
.where("id", "=", id)
|
|
250
250
|
.execute();
|
|
251
251
|
}
|
|
252
252
|
catch (e) {
|
|
253
|
-
if (isUniqueViolation(e, "
|
|
253
|
+
if (isUniqueViolation(e, "devices_name_uq"))
|
|
254
254
|
throw nameTaken();
|
|
255
255
|
throw e;
|
|
256
256
|
}
|
|
@@ -258,38 +258,38 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
258
258
|
actorAccountId: accountId,
|
|
259
259
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
260
260
|
orgId,
|
|
261
|
-
action: "
|
|
261
|
+
action: "device.renamed",
|
|
262
262
|
target: id,
|
|
263
263
|
detail: { from: before.name, to: name, ...auditDetail },
|
|
264
264
|
});
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
|
-
* Deletes one of the person's live
|
|
267
|
+
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and
|
|
268
268
|
* after the commit a `sandbox-destroy-for` job (the worker waits for running commands, then
|
|
269
269
|
* destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
270
|
-
* sandbox tools then fail with `
|
|
270
|
+
* sandbox tools then fail with `device.not_found`. A local device's machine is
|
|
271
271
|
* revoked in the same transaction; after the commit it is told `stop { reason: "deleted" }`
|
|
272
272
|
* (while connected) and its connection is closed.
|
|
273
273
|
*/
|
|
274
274
|
async remove(orgId, accountId, id, actorTokenId, auditDetail) {
|
|
275
275
|
const before = await this.lockOwn(orgId, accountId, id);
|
|
276
276
|
await this.db
|
|
277
|
-
.updateTable("
|
|
277
|
+
.updateTable("devices")
|
|
278
278
|
.set({ deleted_at: sql `now()`, updated_at: sql `now()` })
|
|
279
279
|
.where("id", "=", id)
|
|
280
280
|
.execute();
|
|
281
281
|
await this.db
|
|
282
|
-
.deleteFrom("
|
|
282
|
+
.deleteFrom("device_defaults")
|
|
283
283
|
.where("org_id", "=", orgId)
|
|
284
284
|
.where("account_id", "=", accountId)
|
|
285
|
-
.where("
|
|
285
|
+
.where("device_id", "=", id)
|
|
286
286
|
.execute();
|
|
287
287
|
await this.machines.revokeActive(id, "deleted");
|
|
288
288
|
await audit(this.db.db, {
|
|
289
289
|
actorAccountId: accountId,
|
|
290
290
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
291
291
|
orgId,
|
|
292
|
-
action: "
|
|
292
|
+
action: "device.deleted",
|
|
293
293
|
target: id,
|
|
294
294
|
detail: { name: before.name, isDefault: before.is_default, ...auditDetail },
|
|
295
295
|
});
|
|
@@ -298,25 +298,25 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
298
298
|
await this.sandboxJobs.destroy([id]);
|
|
299
299
|
}
|
|
300
300
|
catch (err) {
|
|
301
|
-
// the hourly destruction sweep destroys deleted
|
|
302
|
-
this.log.warn("cannot schedule
|
|
301
|
+
// the hourly destruction sweep destroys deleted devices too
|
|
302
|
+
this.log.warn("cannot schedule device destruction", { deviceId: id, err });
|
|
303
303
|
}
|
|
304
304
|
});
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
|
-
* Disconnects the machine of one of the person's own
|
|
307
|
+
* Disconnects the machine of one of the person's own local devices (it must sign in
|
|
308
308
|
* again); see {@link MachinesService.revoke}.
|
|
309
309
|
*/
|
|
310
310
|
async revokeMachine(orgId, accountId, id, actorTokenId) {
|
|
311
311
|
await this.machines.revoke(orgId, accountId, id, actorTokenId);
|
|
312
312
|
}
|
|
313
313
|
/**
|
|
314
|
-
* Starts one of the person's
|
|
315
|
-
* `
|
|
316
|
-
* `sandbox-reset-files` job (deterministic id per
|
|
314
|
+
* Starts one of the person's devices over with empty files (after
|
|
315
|
+
* `device.files_lost`): records the request (audit), then, after the commit, enqueues the
|
|
316
|
+
* `sandbox-reset-files` job (deterministic id per device), whose worker waits for running
|
|
317
317
|
* commands and destroys any instance and the file system; the next use creates new ones. Its
|
|
318
|
-
* provider must be the current one (`
|
|
319
|
-
* are its machine owner's (`
|
|
318
|
+
* provider must be the current one (`device.unavailable`); a local device's files
|
|
319
|
+
* are its machine owner's (`device.files_on_device`). An enqueue failure is thrown
|
|
320
320
|
* (the request can be repeated: the reset is idempotent).
|
|
321
321
|
*/
|
|
322
322
|
async resetFiles(orgId, accountId, id, actorTokenId, auditDetail) {
|
|
@@ -330,17 +330,17 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
330
330
|
actorAccountId: accountId,
|
|
331
331
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
332
332
|
orgId,
|
|
333
|
-
action: "
|
|
333
|
+
action: "device.files_reset",
|
|
334
334
|
target: id,
|
|
335
335
|
...(auditDetail !== undefined && { detail: auditDetail }),
|
|
336
336
|
});
|
|
337
337
|
}
|
|
338
338
|
/**
|
|
339
|
-
* The person's default
|
|
339
|
+
* The person's default device in the org, created when missing (named by the UI, `name`
|
|
340
340
|
* NULL). An existing usable one is read without locking. A live default whose files or
|
|
341
341
|
* instance are kept by a provider that is not the current one (a provider switch, spec §4.2
|
|
342
342
|
* item 13) is stranded: it is demoted (marked with the provider it was demoted from; the UI
|
|
343
|
-
* names it
|
|
343
|
+
* names it 「个人设备(<provider display name>)」) and stays listed as unavailable and deletable.
|
|
344
344
|
* Its replacement is the person's former default demoted from the current provider when there
|
|
345
345
|
* is one (switching back restores it, with its files), else a fresh default. A live default
|
|
346
346
|
* not in use yet (no healthy files, no instance, none being created: {@link inUseForDefault})
|
|
@@ -349,7 +349,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
349
349
|
* belongs to the provider holding its resources once it holds any, else to the one it was
|
|
350
350
|
* demoted from ({@link demotedProvider}), so one swapped out empty and used afterwards is
|
|
351
351
|
* labelled and restored by where its files are.
|
|
352
|
-
* All of it in one transaction: switching back and forth never adds
|
|
352
|
+
* All of it in one transaction: switching back and forth never adds devices.
|
|
353
353
|
*
|
|
354
354
|
* Replacing one first takes the person's membership row (`FOR SHARE`, after the org row), so an
|
|
355
355
|
* offboarding that removes them cannot leave a live default behind; not a member (any more) →
|
|
@@ -357,8 +357,8 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
357
357
|
* defaults of the current provider are locked (`FOR NO KEY UPDATE`) and the live default re-read
|
|
358
358
|
* after them, the partial unique index on the live default makes a racing insert do nothing, and
|
|
359
359
|
* the select that follows reads the winner. Needs a current sandbox provider
|
|
360
|
-
* (`
|
|
361
|
-
* `
|
|
360
|
+
* (`device.unavailable`). It is the person's baseline, so it is created even at
|
|
361
|
+
* `devices.maxPersonal`.
|
|
362
362
|
*/
|
|
363
363
|
async ensureDefault(orgId, accountId) {
|
|
364
364
|
const live = await this.defaultOf(orgId, accountId);
|
|
@@ -375,7 +375,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
375
375
|
async insertDefault(orgId, accountId) {
|
|
376
376
|
const current = this.providers.cloud();
|
|
377
377
|
if (!current)
|
|
378
|
-
throw
|
|
378
|
+
throw deviceUnavailable(null);
|
|
379
379
|
await this.lockMember(orgId, accountId, "share");
|
|
380
380
|
const locked = await this.defaultOf(orgId, accountId, true);
|
|
381
381
|
const former = await this.formerDefaults(orgId, accountId, current.plugin, true);
|
|
@@ -391,20 +391,20 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
391
391
|
const restore = withFiles ?? (live === undefined || stranded ? former[0] : undefined);
|
|
392
392
|
if (live) {
|
|
393
393
|
const from = stranded
|
|
394
|
-
? (
|
|
394
|
+
? (deviceProvider(live, current.plugin) ?? "unknown")
|
|
395
395
|
: (live.provider ?? current.plugin);
|
|
396
396
|
await this.demote(live.id, from);
|
|
397
397
|
}
|
|
398
398
|
if (restore) {
|
|
399
399
|
await this.db
|
|
400
|
-
.updateTable("
|
|
400
|
+
.updateTable("devices")
|
|
401
401
|
.set({ is_default: true, demoted_from_provider: null, updated_at: sql `now()` })
|
|
402
402
|
.where("id", "=", restore.id)
|
|
403
403
|
.execute();
|
|
404
404
|
return restore.id;
|
|
405
405
|
}
|
|
406
406
|
await this.db
|
|
407
|
-
.insertInto("
|
|
407
|
+
.insertInto("devices")
|
|
408
408
|
.values({
|
|
409
409
|
id: randomUUID(),
|
|
410
410
|
org_id: orgId,
|
|
@@ -422,17 +422,17 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
422
422
|
.execute();
|
|
423
423
|
const row = await this.defaultOf(orgId, accountId);
|
|
424
424
|
if (row === undefined)
|
|
425
|
-
throw new Error("the default
|
|
425
|
+
throw new Error("the default device was not created");
|
|
426
426
|
return row.id;
|
|
427
427
|
}
|
|
428
428
|
/**
|
|
429
|
-
* Turns the live default into an ordinary
|
|
429
|
+
* Turns the live default into an ordinary device marked as demoted from `provider`; a name
|
|
430
430
|
* the person gave it stays. Runs under the person's membership lock and the default row's lock,
|
|
431
431
|
* before any other row becomes the default (the partial unique index).
|
|
432
432
|
*/
|
|
433
433
|
async demote(id, provider) {
|
|
434
434
|
await this.db
|
|
435
|
-
.updateTable("
|
|
435
|
+
.updateTable("devices")
|
|
436
436
|
.set({ is_default: false, demoted_from_provider: provider, updated_at: sql `now()` })
|
|
437
437
|
.where("id", "=", id)
|
|
438
438
|
.execute();
|
|
@@ -446,8 +446,8 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
446
446
|
*/
|
|
447
447
|
async formerDefaults(orgId, accountId, provider, lock = false) {
|
|
448
448
|
const rows = await this.db
|
|
449
|
-
.selectFrom("
|
|
450
|
-
.leftJoin("
|
|
449
|
+
.selectFrom("devices as e")
|
|
450
|
+
.leftJoin("device_instances as i", "i.device_id", "e.id")
|
|
451
451
|
.select([
|
|
452
452
|
"e.id",
|
|
453
453
|
"e.provider",
|
|
@@ -491,13 +491,13 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
491
491
|
.map(({ r, holdsFiles }) => ({ id: r.id, holdsFiles }));
|
|
492
492
|
}
|
|
493
493
|
/**
|
|
494
|
-
* The person's IM default (what `/
|
|
494
|
+
* The person's IM default (what `/device` set): the device their next new IM conversation
|
|
495
495
|
* uses. Null when none was set, or the chosen one is deleted (the row is cleared with it).
|
|
496
496
|
*/
|
|
497
497
|
async imDefault(orgId, accountId) {
|
|
498
498
|
const row = await this.db
|
|
499
|
-
.selectFrom("
|
|
500
|
-
.innerJoin("
|
|
499
|
+
.selectFrom("device_defaults as d")
|
|
500
|
+
.innerJoin("devices as e", "e.id", "d.device_id")
|
|
501
501
|
.select("e.id")
|
|
502
502
|
.where("d.org_id", "=", orgId)
|
|
503
503
|
.where("d.account_id", "=", accountId)
|
|
@@ -509,44 +509,44 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
509
509
|
return row?.id ?? null;
|
|
510
510
|
}
|
|
511
511
|
/**
|
|
512
|
-
* Sets the person's IM default. Only one of their own live
|
|
513
|
-
* (`
|
|
514
|
-
* statement that re-checks the
|
|
512
|
+
* Sets the person's IM default. Only one of their own live devices in the org, usable now
|
|
513
|
+
* (`device.not_found` / `device.unavailable` otherwise); the row is written by one
|
|
514
|
+
* statement that re-checks the device is live. There is no row lock, so a delete racing
|
|
515
515
|
* past that check can leave a stale row; reads ignore it ({@link imDefault} joins on
|
|
516
516
|
* `deleted_at IS NULL`).
|
|
517
517
|
*/
|
|
518
|
-
async setImDefault(orgId, accountId,
|
|
519
|
-
await this.access.usable(orgId, accountId,
|
|
518
|
+
async setImDefault(orgId, accountId, deviceId) {
|
|
519
|
+
await this.access.usable(orgId, accountId, deviceId);
|
|
520
520
|
const written = await this.db
|
|
521
|
-
.insertInto("
|
|
522
|
-
.columns(["org_id", "account_id", "
|
|
521
|
+
.insertInto("device_defaults")
|
|
522
|
+
.columns(["org_id", "account_id", "device_id"])
|
|
523
523
|
.expression(this.db
|
|
524
|
-
.selectFrom("
|
|
524
|
+
.selectFrom("devices")
|
|
525
525
|
.select([
|
|
526
526
|
sql `${orgId}::text`.as("org_id"),
|
|
527
527
|
sql `${accountId}::text`.as("account_id"),
|
|
528
528
|
"id",
|
|
529
529
|
])
|
|
530
|
-
.where("id", "=",
|
|
530
|
+
.where("id", "=", deviceId)
|
|
531
531
|
.where("org_id", "=", orgId)
|
|
532
532
|
.where("owner_kind", "=", "personal")
|
|
533
533
|
.where("owner_account_id", "=", accountId)
|
|
534
534
|
.where("deleted_at", "is", null))
|
|
535
535
|
.onConflict((oc) => oc
|
|
536
536
|
.columns(["org_id", "account_id"])
|
|
537
|
-
.doUpdateSet({
|
|
537
|
+
.doUpdateSet({ device_id: deviceId, updated_at: new Date() }))
|
|
538
538
|
.executeTakeFirst();
|
|
539
539
|
if (Number(written.numInsertedOrUpdatedRows ?? 0) === 0)
|
|
540
|
-
throw
|
|
540
|
+
throw deviceNotFound();
|
|
541
541
|
}
|
|
542
542
|
/**
|
|
543
|
-
* The person's live default
|
|
543
|
+
* The person's live default device in the org, if any, with what decides its provider;
|
|
544
544
|
* `lock` takes its row (`FOR NO KEY UPDATE`; a racing demotion makes it read none).
|
|
545
545
|
*/
|
|
546
546
|
async defaultOf(orgId, accountId, lock = false) {
|
|
547
547
|
const row = await this.db
|
|
548
|
-
.selectFrom("
|
|
549
|
-
.leftJoin("
|
|
548
|
+
.selectFrom("devices as e")
|
|
549
|
+
.leftJoin("device_instances as i", "i.device_id", "e.id")
|
|
550
550
|
.select([
|
|
551
551
|
"e.id",
|
|
552
552
|
"e.provider",
|
|
@@ -574,15 +574,15 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
574
574
|
/** Its files or instance are kept by a provider that is not the current one. */
|
|
575
575
|
stranded(f) {
|
|
576
576
|
const current = this.providers.cloud()?.plugin;
|
|
577
|
-
return current !== undefined &&
|
|
577
|
+
return current !== undefined && deviceProvider(f, current) !== current;
|
|
578
578
|
}
|
|
579
|
-
/** The person's live
|
|
579
|
+
/** The person's live devices (or the one `id`) as views. */
|
|
580
580
|
async views(orgId, accountId, id) {
|
|
581
581
|
// all of them even for one `id`: a former default's ordinal counts its siblings
|
|
582
582
|
const all = await this.db
|
|
583
|
-
.selectFrom("
|
|
584
|
-
.leftJoin("
|
|
585
|
-
.leftJoin("
|
|
583
|
+
.selectFrom("devices as e")
|
|
584
|
+
.leftJoin("device_instances as i", "i.device_id", "e.id")
|
|
585
|
+
.leftJoin("device_machines as m", (j) => j.onRef("m.device_id", "=", "e.id").on("m.revoked_at", "is", null))
|
|
586
586
|
.select([
|
|
587
587
|
"e.id",
|
|
588
588
|
"e.name",
|
|
@@ -641,12 +641,12 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
641
641
|
this.displayNames.many(demotedProviders),
|
|
642
642
|
]);
|
|
643
643
|
const current = this.providers.cloud()?.plugin;
|
|
644
|
-
// a device
|
|
644
|
+
// a local device is online while a replica holds its machine's connection lease
|
|
645
645
|
const online = new Map(await Promise.all(rows
|
|
646
|
-
.filter((r) => r.source === "
|
|
646
|
+
.filter((r) => r.source === "local")
|
|
647
647
|
.map(async (r) => [r.id, (await this.locks.holderOf(deviceLeaseKey(r.id))) !== null])));
|
|
648
648
|
return rows.map((r) => {
|
|
649
|
-
const provider =
|
|
649
|
+
const provider = deviceProvider(factsOf(r), current);
|
|
650
650
|
const live = (r.status === "running" || r.status === "suspended") && r.last_used_at;
|
|
651
651
|
const from = demoted.get(r.id) ?? null;
|
|
652
652
|
return {
|
|
@@ -666,7 +666,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
666
666
|
available: provider !== null && isInternalProvider(provider)
|
|
667
667
|
? this.providers.forName(provider) !== undefined
|
|
668
668
|
: current !== undefined && provider === current,
|
|
669
|
-
online: r.source === "
|
|
669
|
+
online: r.source === "local" ? (online.get(r.id) ?? false) : null,
|
|
670
670
|
machine: r.machine_id !== null && r.machine_name !== null && r.machine_os !== null
|
|
671
671
|
? {
|
|
672
672
|
id: r.machine_id,
|
|
@@ -677,7 +677,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
677
677
|
lastSeenAt: r.machine_last_seen_at?.toISOString() ?? null,
|
|
678
678
|
}
|
|
679
679
|
: null,
|
|
680
|
-
// a device
|
|
680
|
+
// a local device's machine decides it; cloud devices have none in phase 2
|
|
681
681
|
sessionIsolation: r.machine_session_isolation ?? false,
|
|
682
682
|
filesLost: r.fs_status === "lost",
|
|
683
683
|
instance: live
|
|
@@ -692,7 +692,7 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
692
692
|
});
|
|
693
693
|
}
|
|
694
694
|
/**
|
|
695
|
-
* Serializes the person's
|
|
695
|
+
* Serializes the person's device writes in the org: the org row (`FOR SHARE`), then their
|
|
696
696
|
* membership row (`FOR NO KEY UPDATE`, or `FOR SHARE` with `mode` share: enough to keep the
|
|
697
697
|
* membership from being removed meanwhile), the order member removal locks them in. Not a
|
|
698
698
|
* member (any more) → `org.not_found`, as the org guard answers.
|
|
@@ -717,11 +717,11 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
717
717
|
if (!m)
|
|
718
718
|
throw new ApiError("org.not_found", "Organization not found");
|
|
719
719
|
}
|
|
720
|
-
/** Locks one of the person's live
|
|
720
|
+
/** Locks one of the person's live devices (after their membership); else `not_found`. */
|
|
721
721
|
async lockOwn(orgId, accountId, id) {
|
|
722
722
|
await this.lockMember(orgId, accountId);
|
|
723
723
|
const row = await this.db
|
|
724
|
-
.selectFrom("
|
|
724
|
+
.selectFrom("devices")
|
|
725
725
|
.select(["name", "is_default"])
|
|
726
726
|
.where("id", "=", id)
|
|
727
727
|
.where("org_id", "=", orgId)
|
|
@@ -731,12 +731,12 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
731
731
|
.forNoKeyUpdate()
|
|
732
732
|
.executeTakeFirst();
|
|
733
733
|
if (!row)
|
|
734
|
-
throw
|
|
734
|
+
throw deviceNotFound();
|
|
735
735
|
return row;
|
|
736
736
|
}
|
|
737
737
|
async assertNameFree(orgId, accountId, name) {
|
|
738
738
|
const clash = await this.db
|
|
739
|
-
.selectFrom("
|
|
739
|
+
.selectFrom("devices")
|
|
740
740
|
.select("id")
|
|
741
741
|
.where("org_id", "=", orgId)
|
|
742
742
|
.where("owner_account_id", "=", accountId)
|
|
@@ -749,23 +749,23 @@ let EnvironmentsService = class EnvironmentsService {
|
|
|
749
749
|
};
|
|
750
750
|
__decorate([
|
|
751
751
|
Transactional()
|
|
752
|
-
],
|
|
752
|
+
], DevicesService.prototype, "insert", null);
|
|
753
753
|
__decorate([
|
|
754
754
|
Transactional()
|
|
755
|
-
],
|
|
755
|
+
], DevicesService.prototype, "createLocal", null);
|
|
756
756
|
__decorate([
|
|
757
757
|
Transactional()
|
|
758
|
-
],
|
|
758
|
+
], DevicesService.prototype, "renameIn", null);
|
|
759
759
|
__decorate([
|
|
760
760
|
Transactional()
|
|
761
|
-
],
|
|
761
|
+
], DevicesService.prototype, "remove", null);
|
|
762
762
|
__decorate([
|
|
763
763
|
Transactional()
|
|
764
|
-
],
|
|
764
|
+
], DevicesService.prototype, "recordReset", null);
|
|
765
765
|
__decorate([
|
|
766
766
|
Transactional()
|
|
767
|
-
],
|
|
768
|
-
|
|
767
|
+
], DevicesService.prototype, "insertDefault", null);
|
|
768
|
+
DevicesService = __decorate([
|
|
769
769
|
Injectable(),
|
|
770
770
|
__param(0, Inject(KernelDb)),
|
|
771
771
|
__param(1, Inject(TransactionHost)),
|
|
@@ -773,19 +773,19 @@ EnvironmentsService = __decorate([
|
|
|
773
773
|
__param(3, Inject(SandboxProviders)),
|
|
774
774
|
__param(4, Inject(SandboxService)),
|
|
775
775
|
__param(5, Inject(SandboxJobs)),
|
|
776
|
-
__param(6, Inject(
|
|
776
|
+
__param(6, Inject(DeviceAccess)),
|
|
777
777
|
__param(7, Inject(ExtensionDisplayNames)),
|
|
778
778
|
__param(8, Inject(KERNEL_LOGGER)),
|
|
779
779
|
__param(9, Inject(LockService)),
|
|
780
780
|
__param(10, Inject(MachinesService))
|
|
781
|
-
],
|
|
782
|
-
export {
|
|
781
|
+
], DevicesService);
|
|
782
|
+
export { DevicesService };
|
|
783
783
|
/**
|
|
784
|
-
* Ruling R24: a
|
|
784
|
+
* Ruling R24: a local device's files are in a directory on its machine, which the
|
|
785
785
|
* platform never wipes; a lost directory comes back with `capybara-device logout` and a new `login` on the machine.
|
|
786
786
|
*/
|
|
787
787
|
function refuseDeviceReset(env) {
|
|
788
|
-
if (env.provider ===
|
|
789
|
-
throw new ApiError("
|
|
788
|
+
if (env.provider === LOCAL_PROVIDER) {
|
|
789
|
+
throw new ApiError("device.files_on_device", "This device's files are in a directory on its connected machine and cannot be reset from here. To start over, run capybara-device logout, then capybara-device login into this device, on that machine.");
|
|
790
790
|
}
|
|
791
791
|
}
|