@capybara-hq/kernel 0.1.0-rc.12 → 0.1.0-rc.13
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/marketplaces.service.d.ts +1 -1
- package/dist/channels/channels.module.js +2 -2
- 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/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 +10 -1
- package/dist/ctx/create-ctx.js +86 -25
- package/dist/db/kernel-schema.d.ts +26 -23
- 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 +25 -25
- package/dist/errors.js +25 -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/host.d.ts +13 -2
- package/dist/extensions/host.js +24 -3
- package/dist/extensions/instance.d.ts +12 -1
- package/dist/extensions/instance.js +13 -0
- package/dist/extensions/permission-view.d.ts +10 -1
- package/dist/files/previewers.d.ts +1 -1
- package/dist/files/previewers.js +1 -1
- package/dist/http/api.module.js +10 -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/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/platform/exclusions.js +38 -3
- package/dist/platform/operation.js +6 -2
- package/dist/platform/ops/admin.ops.js +8 -2
- 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/ui.ops.d.ts +8 -0
- package/dist/platform/ops/ui.ops.js +41 -0
- package/dist/platform/platform-ops.module.d.ts +3 -2
- package/dist/platform/platform-ops.module.js +8 -5
- package/dist/platform/route-schemas.js +6 -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/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/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/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,7 +10,7 @@ import { ChannelsOperations } from "./ops/channels.ops.js";
|
|
|
10
10
|
import { ComponentsOperations } from "./ops/components.ops.js";
|
|
11
11
|
import { ConnectorsOperations } from "./ops/connectors.ops.js";
|
|
12
12
|
import { ConversationsOperations } from "./ops/conversations.ops.js";
|
|
13
|
-
import {
|
|
13
|
+
import { DevicesOperations } from "./ops/devices.ops.js";
|
|
14
14
|
import { EvalsOperations } from "./ops/evals.ops.js";
|
|
15
15
|
import { ExtensionsOperations } from "./ops/extensions.ops.js";
|
|
16
16
|
import { ExtensionsAdminOperations } from "./ops/extensions-admin.ops.js";
|
|
@@ -26,11 +26,12 @@ import { PreviewersOperations } from "./ops/previewers.ops.js";
|
|
|
26
26
|
import { RequestsOperations } from "./ops/requests.ops.js";
|
|
27
27
|
import { SkillsOperations } from "./ops/skills.ops.js";
|
|
28
28
|
import { ThemesOperations } from "./ops/themes.ops.js";
|
|
29
|
+
import { UiOperations } from "./ops/ui.ops.js";
|
|
29
30
|
/**
|
|
30
31
|
* One provider per domain, each taking `PlatformOperations` as its first constructor argument:
|
|
31
32
|
* the parity test in `tools/checks` builds the operations from it without a Nest app.
|
|
32
33
|
*/
|
|
33
|
-
export declare const PLATFORM_OPERATION_PROVIDERS: readonly [typeof PlatformOperationsProvider, typeof SkillsOperations, typeof AgentsOperations, typeof AgentTemplatesOperations, typeof PluginsOperations, typeof MarketplacesOperations, typeof RequestsOperations, typeof ConnectorsOperations, typeof ExtensionsOperations, typeof ConversationsOperations, typeof ComponentsOperations, typeof AccountsOperations, typeof OrgsOperations, typeof AdminOperations, typeof CatalogAdminOperations, typeof OrgPluginsOperations, typeof ExtensionsAdminOperations, typeof ModelsOperations, typeof InboxOperations, typeof ApprovalsOperations, typeof ChannelsOperations, typeof OrgSettingsOperations, typeof EvalsOperations, typeof BackgroundOperations, typeof PreviewersOperations, typeof
|
|
34
|
+
export declare const PLATFORM_OPERATION_PROVIDERS: readonly [typeof PlatformOperationsProvider, typeof SkillsOperations, typeof AgentsOperations, typeof AgentTemplatesOperations, typeof PluginsOperations, typeof MarketplacesOperations, typeof RequestsOperations, typeof ConnectorsOperations, typeof ExtensionsOperations, typeof ConversationsOperations, typeof ComponentsOperations, typeof AccountsOperations, typeof OrgsOperations, typeof AdminOperations, typeof CatalogAdminOperations, typeof OrgPluginsOperations, typeof ExtensionsAdminOperations, typeof ModelsOperations, typeof InboxOperations, typeof ApprovalsOperations, typeof ChannelsOperations, typeof OrgSettingsOperations, typeof EvalsOperations, typeof BackgroundOperations, typeof PreviewersOperations, typeof DevicesOperations, typeof AgentSkillsOperations, typeof ThemesOperations, typeof UiOperations];
|
|
34
35
|
/**
|
|
35
36
|
* The `Platform` tool's operations (ruling P2): one provider per domain, each registering its
|
|
36
37
|
* operations into `PlatformOperations` from `onModuleInit`. It imports the domain modules, which
|
|
@@ -10,7 +10,7 @@ import { BackgroundModule } from "../background/background.module.js";
|
|
|
10
10
|
import { CatalogModule } from "../catalog/catalog.module.js";
|
|
11
11
|
import { ChannelsModule } from "../channels/channels.module.js";
|
|
12
12
|
import { ConversationsModule } from "../conversations/conversations.module.js";
|
|
13
|
-
import {
|
|
13
|
+
import { DevicesModule } from "../devices/devices.module.js";
|
|
14
14
|
import { EvalsModule } from "../evals/evals.module.js";
|
|
15
15
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
16
16
|
import { FilesModule } from "../files/files.module.js";
|
|
@@ -34,7 +34,7 @@ import { ChannelsOperations } from "./ops/channels.ops.js";
|
|
|
34
34
|
import { ComponentsOperations } from "./ops/components.ops.js";
|
|
35
35
|
import { ConnectorsOperations } from "./ops/connectors.ops.js";
|
|
36
36
|
import { ConversationsOperations } from "./ops/conversations.ops.js";
|
|
37
|
-
import {
|
|
37
|
+
import { DevicesOperations } from "./ops/devices.ops.js";
|
|
38
38
|
import { EvalsOperations } from "./ops/evals.ops.js";
|
|
39
39
|
import { ExtensionsOperations } from "./ops/extensions.ops.js";
|
|
40
40
|
import { ExtensionsAdminOperations } from "./ops/extensions-admin.ops.js";
|
|
@@ -50,6 +50,7 @@ import { PreviewersOperations } from "./ops/previewers.ops.js";
|
|
|
50
50
|
import { RequestsOperations } from "./ops/requests.ops.js";
|
|
51
51
|
import { SkillsOperations } from "./ops/skills.ops.js";
|
|
52
52
|
import { ThemesOperations } from "./ops/themes.ops.js";
|
|
53
|
+
import { UiOperations } from "./ops/ui.ops.js";
|
|
53
54
|
import { PlatformModule } from "./platform.module.js";
|
|
54
55
|
/**
|
|
55
56
|
* One provider per domain, each taking `PlatformOperations` as its first constructor argument:
|
|
@@ -86,11 +87,13 @@ export const PLATFORM_OPERATION_PROVIDERS = [
|
|
|
86
87
|
EvalsOperations,
|
|
87
88
|
BackgroundOperations,
|
|
88
89
|
PreviewersOperations,
|
|
89
|
-
// Task 11b:
|
|
90
|
-
|
|
90
|
+
// Task 11b: devices, agent skills
|
|
91
|
+
DevicesOperations,
|
|
91
92
|
AgentSkillsOperations,
|
|
92
93
|
// themes (themes spec §9)
|
|
93
94
|
ThemesOperations,
|
|
95
|
+
// UI slot catalog (UI slots spec §3)
|
|
96
|
+
UiOperations,
|
|
94
97
|
];
|
|
95
98
|
/**
|
|
96
99
|
* The `Platform` tool's operations (ruling P2): one provider per domain, each registering its
|
|
@@ -116,7 +119,7 @@ PlatformOpsModule = __decorate([
|
|
|
116
119
|
InboxModule,
|
|
117
120
|
ChannelsModule,
|
|
118
121
|
EvalsModule,
|
|
119
|
-
|
|
122
|
+
DevicesModule,
|
|
120
123
|
BackgroundModule,
|
|
121
124
|
FilesModule,
|
|
122
125
|
ThemesModule,
|
|
@@ -11,7 +11,7 @@ import { ORG_EXTENSION_ROUTES } from "../catalog/org-extension.schemas.js";
|
|
|
11
11
|
import { ORG_PLUGIN_UPLOAD_ROUTES } from "../catalog/org-plugin-uploads.schemas.js";
|
|
12
12
|
import { CHANNEL_ROUTES } from "../channels/channels.schemas.js";
|
|
13
13
|
import { CONVERSATION_ROUTES } from "../conversations/conversations.schemas.js";
|
|
14
|
-
import {
|
|
14
|
+
import { DEVICE_ROUTES } from "../devices/devices.schemas.js";
|
|
15
15
|
import { EVAL_ROUTES } from "../evals/evals.schemas.js";
|
|
16
16
|
import { EXTENSION_POINT_ROUTES } from "../extensions/point-defaults.schemas.js";
|
|
17
17
|
import { FILE_PREVIEWER_ROUTES } from "../files/previewers.schemas.js";
|
|
@@ -23,6 +23,7 @@ import { SKILL_ROUTES } from "../skills/skills.schemas.js";
|
|
|
23
23
|
import { THEME_ROUTES } from "../themes/themes.schemas.js";
|
|
24
24
|
import { APPROVAL_ROUTES } from "../tools/approvals.schemas.js";
|
|
25
25
|
import { CONNECTOR_ROUTES } from "../tools/connectors.schemas.js";
|
|
26
|
+
import { UI_ROUTES } from "../ui/ui.schemas.js";
|
|
26
27
|
/**
|
|
27
28
|
* Every route a Platform operation mirrors, from the exported `<domain>.schemas.ts` next to each
|
|
28
29
|
* controller. An operation builds its input from these (`pick` / `omit` / `extend`); the parity
|
|
@@ -57,10 +58,12 @@ export const ROUTE_SCHEMAS = [
|
|
|
57
58
|
...EVAL_ROUTES,
|
|
58
59
|
...BACKGROUND_ROUTES,
|
|
59
60
|
...FILE_PREVIEWER_ROUTES,
|
|
60
|
-
// Task 11b:
|
|
61
|
-
...
|
|
61
|
+
// Task 11b: devices
|
|
62
|
+
...DEVICE_ROUTES,
|
|
62
63
|
// personal preferences (run context spec §3.4)
|
|
63
64
|
...ME_ROUTES,
|
|
64
65
|
// themes (themes spec §9)
|
|
65
66
|
...THEME_ROUTES,
|
|
67
|
+
// UI slots (UI slots spec §3)
|
|
68
|
+
...UI_ROUTES,
|
|
66
69
|
];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
3
3
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
4
|
-
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
5
4
|
import { ExtensionHost } from "../extensions/host.js";
|
|
6
5
|
import type { GatewayRoutes } from "../http/gateway-routes.js";
|
|
7
6
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
7
|
+
import { LocalDeviceGateway } from "../local-devices/local-device-gateway.js";
|
|
8
8
|
/**
|
|
9
9
|
* The gateway role's parts, driven by `KernelLifecycle` at fixed points of its start order and
|
|
10
10
|
* stop phases: the channel long-connection supervisor (leader election and share) and the webhook
|
|
11
11
|
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation), and the
|
|
12
|
-
* device connections ({@link
|
|
12
|
+
* device connections ({@link LocalDeviceGateway}, `GET /device/connect`). Provided
|
|
13
13
|
* only by `GatewayModule`, so a process without the gateway role has none of them.
|
|
14
14
|
*/
|
|
15
15
|
export declare class GatewayRole {
|
|
@@ -26,7 +26,7 @@ export declare class GatewayRole {
|
|
|
26
26
|
private unsubscribeWebhooks;
|
|
27
27
|
/** The supervisor was started: the runs phase stops it. */
|
|
28
28
|
private supervisorStarted;
|
|
29
|
-
constructor(config: KernelConfigService, host: ExtensionHost, bus: Pick<ExtensionContext["bus"], "subscribe">, supervisor: ChannelSupervisor, webhooks: ChannelWebhooks, devices:
|
|
29
|
+
constructor(config: KernelConfigService, host: ExtensionHost, bus: Pick<ExtensionContext["bus"], "subscribe">, supervisor: ChannelSupervisor, webhooks: ChannelWebhooks, devices: LocalDeviceGateway);
|
|
30
30
|
/**
|
|
31
31
|
* The gateway role's HTTP routes (the channel webhook, the device connection), registered when
|
|
32
32
|
* the app is built.
|
|
@@ -11,15 +11,15 @@ import { Inject, Injectable } from "@nestjs/common";
|
|
|
11
11
|
import { CHANNEL_CHANGED_TOPIC } from "../channels/connections.service.js";
|
|
12
12
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
13
13
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
14
|
-
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
15
14
|
import { ExtensionHost } from "../extensions/host.js";
|
|
16
15
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
17
16
|
import { KERNEL_BUS } from "../infra/tokens.js";
|
|
17
|
+
import { LocalDeviceGateway } from "../local-devices/local-device-gateway.js";
|
|
18
18
|
/**
|
|
19
19
|
* The gateway role's parts, driven by `KernelLifecycle` at fixed points of its start order and
|
|
20
20
|
* stop phases: the channel long-connection supervisor (leader election and share) and the webhook
|
|
21
21
|
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation), and the
|
|
22
|
-
* device connections ({@link
|
|
22
|
+
* device connections ({@link LocalDeviceGateway}, `GET /device/connect`). Provided
|
|
23
23
|
* only by `GatewayModule`, so a process without the gateway role has none of them.
|
|
24
24
|
*/
|
|
25
25
|
let GatewayRole = class GatewayRole {
|
|
@@ -55,7 +55,7 @@ let GatewayRole = class GatewayRole {
|
|
|
55
55
|
webhookMaxBodyBytes: config.channels.webhookMaxBodyBytes,
|
|
56
56
|
devices: {
|
|
57
57
|
gateway: this.devices,
|
|
58
|
-
maxFrameBytes: config.
|
|
58
|
+
maxFrameBytes: config.devices.local.maxFrameBytes,
|
|
59
59
|
},
|
|
60
60
|
};
|
|
61
61
|
}
|
|
@@ -113,6 +113,6 @@ GatewayRole = __decorate([
|
|
|
113
113
|
__param(2, Inject(KERNEL_BUS)),
|
|
114
114
|
__param(3, Inject(ChannelSupervisor)),
|
|
115
115
|
__param(4, Inject(ChannelWebhooks)),
|
|
116
|
-
__param(5, Inject(
|
|
116
|
+
__param(5, Inject(LocalDeviceGateway))
|
|
117
117
|
], GatewayRole);
|
|
118
118
|
export { GatewayRole };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The gateway role (nest kernel spec §2), imported by `KernelModule` only when `roles` includes
|
|
3
3
|
* `gateway`: the channel long-connection supervisor, the webhook inbound (served as a raw Fastify
|
|
4
|
-
* route, `http/gateway-routes.ts`), the device connections ({@link
|
|
4
|
+
* route, `http/gateway-routes.ts`), the device connections ({@link LocalDeviceGateway}, `GET
|
|
5
5
|
* /device/connect`) and {@link GatewayRole}, which `KernelLifecycle` drives. It runs
|
|
6
6
|
* no cron and no queue consumer (those are the worker role's).
|
|
7
7
|
*/
|
|
@@ -8,14 +8,14 @@ import { Module } from "@nestjs/common";
|
|
|
8
8
|
import { ChannelsModule } from "../channels/channels.module.js";
|
|
9
9
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
10
10
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
11
|
-
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
12
|
-
import { DevicesModule } from "../devices/devices.module.js";
|
|
13
11
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
12
|
+
import { LocalDeviceGateway } from "../local-devices/local-device-gateway.js";
|
|
13
|
+
import { LocalDevicesModule } from "../local-devices/local-devices.module.js";
|
|
14
14
|
import { GatewayRole } from "./gateway-role.js";
|
|
15
15
|
/**
|
|
16
16
|
* The gateway role (nest kernel spec §2), imported by `KernelModule` only when `roles` includes
|
|
17
17
|
* `gateway`: the channel long-connection supervisor, the webhook inbound (served as a raw Fastify
|
|
18
|
-
* route, `http/gateway-routes.ts`), the device connections ({@link
|
|
18
|
+
* route, `http/gateway-routes.ts`), the device connections ({@link LocalDeviceGateway}, `GET
|
|
19
19
|
* /device/connect`) and {@link GatewayRole}, which `KernelLifecycle` drives. It runs
|
|
20
20
|
* no cron and no queue consumer (those are the worker role's).
|
|
21
21
|
*/
|
|
@@ -23,8 +23,8 @@ let GatewayModule = class GatewayModule {
|
|
|
23
23
|
};
|
|
24
24
|
GatewayModule = __decorate([
|
|
25
25
|
Module({
|
|
26
|
-
imports: [ChannelsModule, ExtensionsModule,
|
|
27
|
-
providers: [ChannelSupervisor, ChannelWebhooks,
|
|
26
|
+
imports: [ChannelsModule, ExtensionsModule, LocalDevicesModule],
|
|
27
|
+
providers: [ChannelSupervisor, ChannelWebhooks, LocalDeviceGateway, GatewayRole],
|
|
28
28
|
exports: [GatewayRole],
|
|
29
29
|
})
|
|
30
30
|
], GatewayModule);
|
|
@@ -18,7 +18,7 @@ import { AttachmentsModule } from "../conversations/attachments.module.js";
|
|
|
18
18
|
import { AttachmentCleanupJob } from "../conversations/attachments.service.js";
|
|
19
19
|
import { EventLogModule } from "../conversations/event-log.js";
|
|
20
20
|
import { TitlesModule } from "../conversations/titles.js";
|
|
21
|
-
import { LegacySandboxCleanupJob } from "../
|
|
21
|
+
import { LegacySandboxCleanupJob } from "../devices/legacy-cleanup.job.js";
|
|
22
22
|
import { EvalWorkersModule } from "../evals/evals.module.js";
|
|
23
23
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
24
24
|
import { PluginsReconcileJob } from "../extensions/lifecycle.js";
|
|
@@ -233,7 +233,7 @@ export declare function summaryText(summary: string): string;
|
|
|
233
233
|
* `user_message` / `assistant_message`(content 原样,含 opaque;aborted 的也回放;媒体引用按
|
|
234
234
|
* {@link resolveMedia} 换成 base64 或文字占位)/ `reminder`
|
|
235
235
|
* (`role: "system"`,放在它所在的位置)/ `tools_changed` (`role: "system"`, the text of {@link toolsChangedText}, at its position) /
|
|
236
|
-
* `
|
|
236
|
+
* `device_paths` (`role: "system"` at its position, `devicePathsText`; covered by the
|
|
237
237
|
* summary: the latest one follows the summary) /
|
|
238
238
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
239
239
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
@@ -4,7 +4,7 @@ import { renderBackgroundFinished } from "../../background/render.js";
|
|
|
4
4
|
import { isAbsorbable, } from "../../conversations/events.js";
|
|
5
5
|
import { catalogText, isAlwaysLoaded } from "../../tools/builtins/tool-search.js";
|
|
6
6
|
import { renderSubagentDelivery } from "../subagents/render.js";
|
|
7
|
-
import {
|
|
7
|
+
import { devicePathsAt, devicePathsText } from "./device-paths.js";
|
|
8
8
|
import { LEGACY_CATALOG_HEADER, LEGACY_SKILL_TEXTS, usesLegacyToolNames } from "./legacy-text.js";
|
|
9
9
|
import { currentRunContext, runContextEvents, runContextFullText, runContextText, senderLines, } from "./run-context.js";
|
|
10
10
|
import { invokedSkillNames, invokedSkillsText, SKILL_TEXTS, skillsCatalogEvents, snapshotChangeText, snapshotCurrentText, snapshotSystemText, systemSkillsCatalog, } from "./skills.js";
|
|
@@ -337,7 +337,7 @@ export function summaryText(summary) {
|
|
|
337
337
|
* `user_message` / `assistant_message`(content 原样,含 opaque;aborted 的也回放;媒体引用按
|
|
338
338
|
* {@link resolveMedia} 换成 base64 或文字占位)/ `reminder`
|
|
339
339
|
* (`role: "system"`,放在它所在的位置)/ `tools_changed` (`role: "system"`, the text of {@link toolsChangedText}, at its position) /
|
|
340
|
-
* `
|
|
340
|
+
* `device_paths` (`role: "system"` at its position, `devicePathsText`; covered by the
|
|
341
341
|
* summary: the latest one follows the summary) /
|
|
342
342
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
343
343
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
@@ -552,9 +552,9 @@ export function buildModelRequest(input) {
|
|
|
552
552
|
}
|
|
553
553
|
// environments spec §13.1: the instance's directories told before the boundary follow the
|
|
554
554
|
// summary again (the model keeps using the real paths)
|
|
555
|
-
const paths =
|
|
555
|
+
const paths = devicePathsAt(events, after);
|
|
556
556
|
if (paths) {
|
|
557
|
-
messages.push({ role: "system", content:
|
|
557
|
+
messages.push({ role: "system", content: devicePathsText(paths.payload.roots) });
|
|
558
558
|
}
|
|
559
559
|
// 被摘要覆盖的变更提醒不再发送:摘要时生效的目录与 system 里的不同 → 紧随摘要补上完整的当前目录
|
|
560
560
|
const covered = skillSnaps.filter((e) => e.seq <= after).at(-1);
|
|
@@ -631,8 +631,8 @@ export function buildModelRequest(input) {
|
|
|
631
631
|
break;
|
|
632
632
|
// the instance's directories (environments spec §13.1): logged during a step's tool call,
|
|
633
633
|
// so rendered after that step's tool results
|
|
634
|
-
case "
|
|
635
|
-
messages.push({ role: "system", content:
|
|
634
|
+
case "device_paths":
|
|
635
|
+
messages.push({ role: "system", content: devicePathsText(e.payload.roots) });
|
|
636
636
|
break;
|
|
637
637
|
// hook_denied is not model-visible: the model sees only the denied tool call's tool_result
|
|
638
638
|
// (and nothing for a denied model request), so request building skips it
|
|
@@ -134,7 +134,7 @@ function transcriptLine(e) {
|
|
|
134
134
|
// phase D: a forked skill's report is user content of the request (build.ts)
|
|
135
135
|
case "skill_fork_result":
|
|
136
136
|
return `User: ${e.payload.text}`;
|
|
137
|
-
//
|
|
137
|
+
// device_paths is left out too: the latest one follows the summary (build.ts)
|
|
138
138
|
// run_context is left out: its current values follow the summary anyway (build.ts), and
|
|
139
139
|
// stale names should not be summarised as current
|
|
140
140
|
case "tool_result":
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import type { SandboxRoots } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { ConversationEvent } from "../../conversations/events.js";
|
|
3
3
|
/**
|
|
4
|
-
* The `
|
|
4
|
+
* The `device_paths` event (environments spec §13.1, ruling R22): the first sandbox tool call
|
|
5
5
|
* of every conversation records the instance's roots (the defaults included), and a later call
|
|
6
6
|
* records them again whenever they differ from the conversation's latest recorded ones (a
|
|
7
7
|
* connected machine's real paths, a mode switch back to the defaults). Model-visible: rendered at
|
|
8
8
|
* its position as a system message; in the log, so it needs no `DriftReason`.
|
|
9
9
|
*/
|
|
10
|
-
/** The system line of an `
|
|
11
|
-
export declare function
|
|
10
|
+
/** The system line of an `device_paths` event (model-visible, English). */
|
|
11
|
+
export declare function devicePathsText(r: SandboxRoots): string;
|
|
12
12
|
/** Whether two sets of roots are the same paths. */
|
|
13
13
|
export declare const sameRoots: (a: SandboxRoots, b: SandboxRoots) => boolean;
|
|
14
14
|
/**
|
|
15
|
-
* The idempotency key of an `
|
|
15
|
+
* The idempotency key of an `device_paths` event for `r` that follows the conversation's
|
|
16
16
|
* event at `prevSeq` (0: none). Unique per change (A → B → A gets three events), while parallel
|
|
17
17
|
* calls that saw the same latest event write it once.
|
|
18
18
|
*/
|
|
19
|
-
export declare const
|
|
19
|
+
export declare const devicePathsIdemKey: (r: SandboxRoots, prevSeq: number) => string;
|
|
20
20
|
/**
|
|
21
|
-
* The `
|
|
21
|
+
* The `device_paths` event to append when a sandbox call holds an instance with `roots` and
|
|
22
22
|
* the conversation's latest such event is `latest`; null when nothing changed.
|
|
23
23
|
*/
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function devicePathsChange(latest: {
|
|
25
25
|
seq: number;
|
|
26
26
|
payload: {
|
|
27
27
|
roots: SandboxRoots;
|
|
28
28
|
};
|
|
29
29
|
} | undefined, roots: SandboxRoots, runId: string | undefined): {
|
|
30
|
-
type: "
|
|
30
|
+
type: "device_paths";
|
|
31
31
|
runId?: string;
|
|
32
32
|
idemKey: string;
|
|
33
33
|
payload: {
|
|
34
34
|
roots: SandboxRoots;
|
|
35
35
|
};
|
|
36
36
|
} | null;
|
|
37
|
-
/** The latest `
|
|
38
|
-
export declare function
|
|
39
|
-
type: "
|
|
37
|
+
/** The latest `device_paths` event at or before `seq`; undefined without one. */
|
|
38
|
+
export declare function devicePathsAt(events: readonly ConversationEvent[], seq: number): Extract<ConversationEvent, {
|
|
39
|
+
type: "device_paths";
|
|
40
40
|
}> | undefined;
|
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
/**
|
|
3
|
-
* The `
|
|
3
|
+
* The `device_paths` event (environments spec §13.1, ruling R22): the first sandbox tool call
|
|
4
4
|
* of every conversation records the instance's roots (the defaults included), and a later call
|
|
5
5
|
* records them again whenever they differ from the conversation's latest recorded ones (a
|
|
6
6
|
* connected machine's real paths, a mode switch back to the defaults). Model-visible: rendered at
|
|
7
7
|
* its position as a system message; in the log, so it needs no `DriftReason`.
|
|
8
8
|
*/
|
|
9
|
-
/** The system line of an `
|
|
10
|
-
export function
|
|
11
|
-
return `This
|
|
9
|
+
/** The system line of an `device_paths` event (model-visible, English). */
|
|
10
|
+
export function devicePathsText(r) {
|
|
11
|
+
return `This device's directories: workspace ${r.workspace}, skills ${r.skills} (read only), temporary files ${r.tmp}. Use these paths in commands and tool calls.`;
|
|
12
12
|
}
|
|
13
13
|
/** Whether two sets of roots are the same paths. */
|
|
14
14
|
export const sameRoots = (a, b) => a.workspace === b.workspace && a.skills === b.skills && a.tmp === b.tmp;
|
|
15
15
|
/**
|
|
16
|
-
* The idempotency key of an `
|
|
16
|
+
* The idempotency key of an `device_paths` event for `r` that follows the conversation's
|
|
17
17
|
* event at `prevSeq` (0: none). Unique per change (A → B → A gets three events), while parallel
|
|
18
18
|
* calls that saw the same latest event write it once.
|
|
19
19
|
*/
|
|
20
|
-
export const
|
|
20
|
+
export const devicePathsIdemKey = (r, prevSeq) => `device_paths:${createHash("sha256")
|
|
21
21
|
.update(JSON.stringify([r.workspace, r.skills, r.tmp]))
|
|
22
22
|
.digest("hex")
|
|
23
23
|
.slice(0, 16)}:${prevSeq}`;
|
|
24
24
|
/**
|
|
25
|
-
* The `
|
|
25
|
+
* The `device_paths` event to append when a sandbox call holds an instance with `roots` and
|
|
26
26
|
* the conversation's latest such event is `latest`; null when nothing changed.
|
|
27
27
|
*/
|
|
28
|
-
export function
|
|
28
|
+
export function devicePathsChange(latest, roots, runId) {
|
|
29
29
|
if (latest && sameRoots(latest.payload.roots, roots))
|
|
30
30
|
return null;
|
|
31
31
|
return {
|
|
32
|
-
type: "
|
|
32
|
+
type: "device_paths",
|
|
33
33
|
...(runId !== undefined && { runId }),
|
|
34
|
-
idemKey:
|
|
34
|
+
idemKey: devicePathsIdemKey(roots, latest?.seq ?? 0),
|
|
35
35
|
payload: { roots: { workspace: roots.workspace, skills: roots.skills, tmp: roots.tmp } },
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
/** The latest `
|
|
39
|
-
export function
|
|
38
|
+
/** The latest `device_paths` event at or before `seq`; undefined without one. */
|
|
39
|
+
export function devicePathsAt(events, seq) {
|
|
40
40
|
let found;
|
|
41
41
|
for (const e of events) {
|
|
42
42
|
if (e.seq > seq)
|
|
43
43
|
break;
|
|
44
|
-
if (e.type === "
|
|
44
|
+
if (e.type === "device_paths")
|
|
45
45
|
found = e;
|
|
46
46
|
}
|
|
47
47
|
return found;
|
|
@@ -32,7 +32,7 @@ export declare class MaintenanceProcessor extends WorkerHost<Worker> {
|
|
|
32
32
|
private readonly scheduled;
|
|
33
33
|
private readonly log;
|
|
34
34
|
private readonly queue;
|
|
35
|
-
constructor(waits: Pick<Waits, "processTimeout">, sandbox: Pick<SandboxService, "
|
|
35
|
+
constructor(waits: Pick<Waits, "processTimeout">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
|
|
36
36
|
/**
|
|
37
37
|
* Removes the retired job schedulers (`RETIRED_SCHEDULER_IDS`, runtime/retired-schedulers.ts): every fixed-period
|
|
38
38
|
* maintenance job is a `@LockedCron` / `@LockedInterval` now. Idempotent; called once when a
|
package/dist/sandbox/jobs.d.ts
CHANGED
|
@@ -7,20 +7,20 @@ export declare const SANDBOX_SUSPEND_JOB = "sandbox-suspend-idle";
|
|
|
7
7
|
/** The inactivity destruction sweep (hourly, once per tick across replicas). */
|
|
8
8
|
export declare const SANDBOX_DESTROY_JOB = "sandbox-destroy-inactive";
|
|
9
9
|
/**
|
|
10
|
-
* One-off job: destroy a batch of
|
|
11
|
-
* deletion, offboarding; data `{
|
|
10
|
+
* One-off job: destroy a batch of devices now, instance and file system (device
|
|
11
|
+
* deletion, offboarding; data `{ deviceIds }`).
|
|
12
12
|
*/
|
|
13
13
|
export declare const SANDBOX_DESTROY_FOR_JOB = "sandbox-destroy-for";
|
|
14
14
|
/**
|
|
15
|
-
* One-off job: start
|
|
16
|
-
* `{
|
|
15
|
+
* One-off job: start a device over with empty files (`resetFileSystem`; data
|
|
16
|
+
* `{ deviceId }`, job id {@link resetFilesJobId}).
|
|
17
17
|
*/
|
|
18
18
|
export declare const SANDBOX_RESET_FILES_JOB = "sandbox-reset-files";
|
|
19
19
|
/**
|
|
20
|
-
* The deterministic id of
|
|
20
|
+
* The deterministic id of a device's reset job: a second request while one is waiting or
|
|
21
21
|
* running joins it (that reset still empties the files); once it ended, a new request is a new job.
|
|
22
22
|
*/
|
|
23
|
-
export declare const resetFilesJobId: (
|
|
23
|
+
export declare const resetFilesJobId: (deviceId: string) => string;
|
|
24
24
|
/**
|
|
25
25
|
* The producer of the sandbox maintenance jobs: {@link SANDBOX_DESTROY_FOR_JOB} (BullMQ's own job
|
|
26
26
|
* ids, as before: every enqueue is a new job, also after a batch failed all its attempts) and
|
|
@@ -30,16 +30,16 @@ export declare class SandboxJobs {
|
|
|
30
30
|
private readonly queue;
|
|
31
31
|
constructor(queue: Pick<Queue, "add">);
|
|
32
32
|
/**
|
|
33
|
-
* Enqueues the destruction of these
|
|
33
|
+
* Enqueues the destruction of these devices: a failed batch is retried whole (destroying
|
|
34
34
|
* one already destroyed does nothing).
|
|
35
35
|
*/
|
|
36
|
-
destroy(
|
|
36
|
+
destroy(deviceIds: string[]): Promise<void>;
|
|
37
37
|
/**
|
|
38
|
-
* Enqueues the reset of
|
|
38
|
+
* Enqueues the reset of a device's files (see {@link resetFilesJobId}): retried on failure
|
|
39
39
|
* (the exclusive lease not obtained in time, the provider failing); a job that failed all its
|
|
40
40
|
* attempts is removed, so the id never blocks a later request.
|
|
41
41
|
*/
|
|
42
|
-
resetFiles(
|
|
42
|
+
resetFiles(deviceId: string): Promise<void>;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* The worker side of {@link SANDBOX_RESET_FILES_JOB}: `resetFileSystem` under the exclusive lease;
|
|
@@ -47,14 +47,14 @@ export declare class SandboxJobs {
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function runSandboxResetFiles(sandbox: Pick<SandboxService, "resetFileSystem">, job: Pick<Job, "data">): Promise<void>;
|
|
49
49
|
/**
|
|
50
|
-
* The worker side of {@link SANDBOX_DESTROY_FOR_JOB}: `
|
|
50
|
+
* The worker side of {@link SANDBOX_DESTROY_FOR_JOB}: `destroyDevice` one by one; a failure is
|
|
51
51
|
* warned and fails the job (the whole batch is retried) after the others were destroyed.
|
|
52
52
|
*/
|
|
53
|
-
export declare function runSandboxDestroyFor(sandbox: Pick<SandboxService, "
|
|
53
|
+
export declare function runSandboxDestroyFor(sandbox: Pick<SandboxService, "destroyDevice">, log: Pick<KernelLogger, "warn">, job: Pick<Job, "data">): Promise<void>;
|
|
54
54
|
/**
|
|
55
55
|
* The sandbox sweeps (exactly once per tick across replicas): every minute the idle and overdue
|
|
56
56
|
* running instances are suspended, every hour the inactive instances are destroyed (files kept)
|
|
57
|
-
* and deleted
|
|
57
|
+
* and deleted devices, or those of deleted orgs, are destroyed with their files. Both do nothing without a sandbox provider. They replace the
|
|
58
58
|
* maintenance schedulers `sandbox-suspend-idle-every-minute` and `sandbox-destroy-inactive-hourly`
|
|
59
59
|
* (retired at worker start).
|
|
60
60
|
*/
|
package/dist/sandbox/jobs.js
CHANGED
|
@@ -19,20 +19,20 @@ export const SANDBOX_SUSPEND_JOB = "sandbox-suspend-idle";
|
|
|
19
19
|
/** The inactivity destruction sweep (hourly, once per tick across replicas). */
|
|
20
20
|
export const SANDBOX_DESTROY_JOB = "sandbox-destroy-inactive";
|
|
21
21
|
/**
|
|
22
|
-
* One-off job: destroy a batch of
|
|
23
|
-
* deletion, offboarding; data `{
|
|
22
|
+
* One-off job: destroy a batch of devices now, instance and file system (device
|
|
23
|
+
* deletion, offboarding; data `{ deviceIds }`).
|
|
24
24
|
*/
|
|
25
25
|
export const SANDBOX_DESTROY_FOR_JOB = "sandbox-destroy-for";
|
|
26
26
|
/**
|
|
27
|
-
* One-off job: start
|
|
28
|
-
* `{
|
|
27
|
+
* One-off job: start a device over with empty files (`resetFileSystem`; data
|
|
28
|
+
* `{ deviceId }`, job id {@link resetFilesJobId}).
|
|
29
29
|
*/
|
|
30
30
|
export const SANDBOX_RESET_FILES_JOB = "sandbox-reset-files";
|
|
31
31
|
/**
|
|
32
|
-
* The deterministic id of
|
|
32
|
+
* The deterministic id of a device's reset job: a second request while one is waiting or
|
|
33
33
|
* running joins it (that reset still empties the files); once it ended, a new request is a new job.
|
|
34
34
|
*/
|
|
35
|
-
export const resetFilesJobId = (
|
|
35
|
+
export const resetFilesJobId = (deviceId) => `${SANDBOX_RESET_FILES_JOB}-${deviceId}`;
|
|
36
36
|
/**
|
|
37
37
|
* The producer of the sandbox maintenance jobs: {@link SANDBOX_DESTROY_FOR_JOB} (BullMQ's own job
|
|
38
38
|
* ids, as before: every enqueue is a new job, also after a batch failed all its attempts) and
|
|
@@ -44,11 +44,11 @@ let SandboxJobs = class SandboxJobs {
|
|
|
44
44
|
this.queue = queue;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
-
* Enqueues the destruction of these
|
|
47
|
+
* Enqueues the destruction of these devices: a failed batch is retried whole (destroying
|
|
48
48
|
* one already destroyed does nothing).
|
|
49
49
|
*/
|
|
50
|
-
async destroy(
|
|
51
|
-
await this.queue.add(SANDBOX_DESTROY_FOR_JOB, {
|
|
50
|
+
async destroy(deviceIds) {
|
|
51
|
+
await this.queue.add(SANDBOX_DESTROY_FOR_JOB, { deviceIds }, {
|
|
52
52
|
attempts: 5,
|
|
53
53
|
backoff: { type: "exponential", delay: 30_000 },
|
|
54
54
|
removeOnComplete: true,
|
|
@@ -56,13 +56,13 @@ let SandboxJobs = class SandboxJobs {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Enqueues the reset of
|
|
59
|
+
* Enqueues the reset of a device's files (see {@link resetFilesJobId}): retried on failure
|
|
60
60
|
* (the exclusive lease not obtained in time, the provider failing); a job that failed all its
|
|
61
61
|
* attempts is removed, so the id never blocks a later request.
|
|
62
62
|
*/
|
|
63
|
-
async resetFiles(
|
|
64
|
-
await this.queue.add(SANDBOX_RESET_FILES_JOB, {
|
|
65
|
-
jobId: resetFilesJobId(
|
|
63
|
+
async resetFiles(deviceId) {
|
|
64
|
+
await this.queue.add(SANDBOX_RESET_FILES_JOB, { deviceId }, {
|
|
65
|
+
jobId: resetFilesJobId(deviceId),
|
|
66
66
|
attempts: 5,
|
|
67
67
|
backoff: { type: "exponential", delay: 30_000 },
|
|
68
68
|
removeOnComplete: true,
|
|
@@ -80,17 +80,17 @@ export { SandboxJobs };
|
|
|
80
80
|
* a failure fails the job (retried).
|
|
81
81
|
*/
|
|
82
82
|
export async function runSandboxResetFiles(sandbox, job) {
|
|
83
|
-
const id = job.data.
|
|
83
|
+
const id = job.data.deviceId;
|
|
84
84
|
if (typeof id !== "string")
|
|
85
85
|
return;
|
|
86
86
|
await sandbox.resetFileSystem(id);
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
* The worker side of {@link SANDBOX_DESTROY_FOR_JOB}: `
|
|
89
|
+
* The worker side of {@link SANDBOX_DESTROY_FOR_JOB}: `destroyDevice` one by one; a failure is
|
|
90
90
|
* warned and fails the job (the whole batch is retried) after the others were destroyed.
|
|
91
91
|
*/
|
|
92
92
|
export async function runSandboxDestroyFor(sandbox, log, job) {
|
|
93
|
-
const ids = job.data.
|
|
93
|
+
const ids = job.data.deviceIds;
|
|
94
94
|
if (!Array.isArray(ids))
|
|
95
95
|
return;
|
|
96
96
|
let failed = 0;
|
|
@@ -98,20 +98,20 @@ export async function runSandboxDestroyFor(sandbox, log, job) {
|
|
|
98
98
|
if (typeof id !== "string")
|
|
99
99
|
continue;
|
|
100
100
|
try {
|
|
101
|
-
await sandbox.
|
|
101
|
+
await sandbox.destroyDevice(id);
|
|
102
102
|
}
|
|
103
103
|
catch (err) {
|
|
104
104
|
failed++;
|
|
105
|
-
log.warn("cannot destroy
|
|
105
|
+
log.warn("cannot destroy device; will retry", { deviceId: id, err });
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
if (failed > 0)
|
|
109
|
-
throw new Error(`${failed} of ${ids.length}
|
|
109
|
+
throw new Error(`${failed} of ${ids.length} devices could not be destroyed`);
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* The sandbox sweeps (exactly once per tick across replicas): every minute the idle and overdue
|
|
113
113
|
* running instances are suspended, every hour the inactive instances are destroyed (files kept)
|
|
114
|
-
* and deleted
|
|
114
|
+
* and deleted devices, or those of deleted orgs, are destroyed with their files. Both do nothing without a sandbox provider. They replace the
|
|
115
115
|
* maintenance schedulers `sandbox-suspend-idle-every-minute` and `sandbox-destroy-inactive-hourly`
|
|
116
116
|
* (retired at worker start).
|
|
117
117
|
*/
|