@capybara-hq/kernel 0.1.0-rc.21 → 0.1.0-rc.22
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/agents/admin-agents.d.ts +16 -3
- package/dist/agents/admin-agents.js +65 -7
- package/dist/agents/agents.schemas.d.ts +24 -6
- package/dist/agents/agents.service.d.ts +4 -3
- package/dist/agents/agents.service.js +39 -26
- package/dist/agents/definition.d.ts +44 -6
- package/dist/agents/definition.js +86 -18
- package/dist/agents/from-template.d.ts +6 -3
- package/dist/agents/from-template.js +57 -44
- package/dist/agents/org-templates.js +97 -41
- package/dist/agents/templates.d.ts +6 -0
- package/dist/agents/templates.js +15 -1
- package/dist/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- package/dist/catalog/catalog.module.js +3 -0
- package/dist/catalog/claude/report.d.ts +9 -2
- package/dist/catalog/claude/report.js +1 -0
- package/dist/catalog/claude/translate.js +1 -1
- package/dist/catalog/component-promotion.js +1 -2
- package/dist/catalog/component-state.d.ts +13 -1
- package/dist/catalog/component-state.js +14 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/marketplaces.service.js +2 -1
- package/dist/catalog/org-catalog.controller.d.ts +13 -3
- package/dist/catalog/org-catalog.controller.js +26 -4
- package/dist/catalog/org-catalog.schemas.d.ts +13 -0
- package/dist/catalog/org-catalog.schemas.js +34 -0
- package/dist/catalog/org-plugin-uploads.controller.js +0 -1
- package/dist/catalog/org-plugin-uploads.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.js +0 -3
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
- package/dist/catalog/platform-components.d.ts +0 -1
- package/dist/catalog/platform-components.js +3 -8
- package/dist/catalog/plugin-import.d.ts +0 -8
- package/dist/catalog/plugin-import.js +4 -35
- package/dist/catalog/plugin-install.d.ts +2 -2
- package/dist/catalog/plugin-install.js +2 -2
- package/dist/catalog/plugin-package.d.ts +7 -0
- package/dist/catalog/plugin-package.js +27 -1
- package/dist/catalog/plugin-upgrade.d.ts +5 -6
- package/dist/catalog/plugin-upgrade.js +5 -23
- package/dist/catalog/requests.service.d.ts +66 -10
- package/dist/catalog/requests.service.js +196 -15
- package/dist/channels/actions.d.ts +2 -0
- package/dist/channels/actions.js +19 -4
- package/dist/channels/cards.d.ts +16 -2
- package/dist/channels/cards.js +40 -7
- package/dist/channels/channels.module.js +3 -0
- package/dist/channels/outbound.d.ts +22 -1
- package/dist/channels/outbound.js +116 -7
- package/dist/channels/phrases.d.ts +25 -0
- package/dist/channels/phrases.js +19 -0
- package/dist/channels/threads.d.ts +12 -0
- package/dist/channels/threads.js +34 -0
- package/dist/config/schema.d.ts +23 -1
- package/dist/config/schema.js +105 -5
- package/dist/conversations/conversations.service.d.ts +42 -9
- package/dist/conversations/conversations.service.js +57 -25
- package/dist/conversations/events.d.ts +106 -11
- package/dist/conversations/events.js +121 -18
- package/dist/conversations/message-context.d.ts +6 -3
- package/dist/conversations/message-context.js +14 -10
- package/dist/conversations/queue.d.ts +15 -2
- package/dist/conversations/queue.js +22 -3
- package/dist/conversations/queue.service.d.ts +12 -6
- package/dist/conversations/queue.service.js +36 -13
- package/dist/db/kernel-schema.d.ts +105 -3
- package/dist/devices/conversation-devices.d.ts +8 -1
- package/dist/devices/conversation-devices.js +8 -0
- package/dist/devices/devices.controller.d.ts +21 -4
- package/dist/devices/devices.controller.js +46 -8
- package/dist/devices/devices.schemas.d.ts +9 -1
- package/dist/devices/devices.schemas.js +11 -3
- package/dist/devices/devices.service.d.ts +176 -4
- package/dist/devices/devices.service.js +341 -6
- package/dist/devices/dto.d.ts +56 -3
- package/dist/devices/dto.js +51 -1
- package/dist/devices/public-suffix.d.ts +6 -0
- package/dist/devices/public-suffix.js +79 -0
- package/dist/errors.d.ts +46 -5
- package/dist/errors.js +46 -5
- package/dist/evals/reconstruct.d.ts +18 -13
- package/dist/evals/reconstruct.js +50 -36
- package/dist/evals/recorded-tools.d.ts +7 -7
- package/dist/evals/recorded-tools.js +4 -3
- package/dist/evals/trajectory.d.ts +6 -1
- package/dist/evals/trajectory.js +7 -0
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- package/dist/inbox/inbox.module.js +2 -1
- package/dist/inbox/inbox.service.d.ts +19 -4
- package/dist/inbox/inbox.service.js +14 -3
- package/dist/infra/config/kernel-config.service.js +11 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/local-devices/device-policy.service.d.ts +65 -0
- package/dist/local-devices/device-policy.service.js +217 -0
- package/dist/local-devices/local-device-gateway.js +4 -1
- package/dist/local-devices/local-device-provider.d.ts +40 -3
- package/dist/local-devices/local-device-provider.js +109 -7
- package/dist/local-devices/local-devices.module.d.ts +2 -1
- package/dist/local-devices/local-devices.module.js +19 -3
- package/dist/local-devices/machines.d.ts +14 -3
- package/dist/local-devices/machines.js +41 -3
- package/dist/local-devices/network-grants.d.ts +104 -0
- package/dist/local-devices/network-grants.js +288 -0
- package/dist/local-devices/policy-summary.d.ts +20 -0
- package/dist/local-devices/policy-summary.js +63 -0
- package/dist/orgs/orgs.module.js +4 -10
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +4 -0
- package/dist/orgs/settings.controller.js +2 -1
- package/dist/orgs/settings.service.d.ts +164 -12
- package/dist/orgs/settings.service.js +212 -24
- package/dist/platform/exclusions.js +11 -1
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin-items.ops.js +1 -1
- package/dist/platform/ops/agent-skills.ops.js +2 -2
- package/dist/platform/ops/agent-templates.ops.js +2 -2
- package/dist/platform/ops/agents.ops.js +11 -15
- package/dist/platform/ops/devices.ops.js +3 -1
- package/dist/platform/ops/inbox.ops.js +19 -0
- package/dist/platform/ops/org-settings.ops.js +14 -5
- package/dist/platform/ops/platform.ops.d.ts +9 -1
- package/dist/platform/ops/platform.ops.js +34 -12
- package/dist/platform/ops/requests.ops.js +3 -2
- package/dist/platform/ops/schedules.ops.d.ts +21 -0
- package/dist/platform/ops/schedules.ops.js +470 -0
- package/dist/platform/ops/skills.ops.js +3 -3
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +3 -0
- package/dist/runtime/context/build.js +13 -4
- package/dist/runtime/context/compaction.js +6 -3
- package/dist/runtime/context/device-paths.d.ts +43 -21
- package/dist/runtime/context/device-paths.js +104 -21
- package/dist/runtime/context/run-context.d.ts +9 -0
- package/dist/runtime/context/run-context.js +8 -0
- package/dist/runtime/context/skills.d.ts +14 -1
- package/dist/runtime/context/skills.js +36 -5
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +16 -5
- package/dist/runtime/run-finish.d.ts +6 -1
- package/dist/runtime/run-finish.js +16 -2
- package/dist/runtime/run-queue.js +21 -2
- package/dist/runtime/run-worker.js +14 -33
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +32 -14
- package/dist/runtime/subagents/definition.js +106 -109
- package/dist/runtime/subagents/dispatch.js +4 -3
- package/dist/runtime/subagents/preamble.d.ts +2 -1
- package/dist/runtime/subagents/preamble.js +2 -1
- package/dist/runtime/waits.js +14 -4
- package/dist/sandbox/sandbox.service.d.ts +120 -10
- package/dist/sandbox/sandbox.service.js +335 -28
- package/dist/sandbox/tiers.d.ts +86 -0
- package/dist/sandbox/tiers.js +96 -0
- package/dist/schedules/effective-settings.d.ts +16 -0
- package/dist/schedules/effective-settings.js +17 -0
- package/dist/schedules/errors.d.ts +8 -0
- package/dist/schedules/errors.js +8 -0
- package/dist/schedules/fire.d.ts +118 -0
- package/dist/schedules/fire.js +625 -0
- package/dist/schedules/grant-rules.d.ts +94 -0
- package/dist/schedules/grant-rules.js +126 -0
- package/dist/schedules/job-ids.d.ts +32 -0
- package/dist/schedules/job-ids.js +27 -0
- package/dist/schedules/pause.d.ts +9 -0
- package/dist/schedules/pause.js +33 -0
- package/dist/schedules/run-tracker.d.ts +81 -0
- package/dist/schedules/run-tracker.js +333 -0
- package/dist/schedules/scan.job.d.ts +64 -0
- package/dist/schedules/scan.job.js +303 -0
- package/dist/schedules/schedule-math.d.ts +31 -0
- package/dist/schedules/schedule-math.js +223 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
- package/dist/schedules/scheduled-tasks.service.js +1193 -0
- package/dist/schedules/schedules.controller.d.ts +68 -0
- package/dist/schedules/schedules.controller.js +242 -0
- package/dist/schedules/schedules.module.d.ts +16 -0
- package/dist/schedules/schedules.module.js +65 -0
- package/dist/schedules/schedules.schemas.d.ts +316 -0
- package/dist/schedules/schedules.schemas.js +186 -0
- package/dist/schedules/store.d.ts +81 -0
- package/dist/schedules/store.js +390 -0
- package/dist/schedules/store.module.d.ts +6 -0
- package/dist/schedules/store.module.js +21 -0
- package/dist/schedules/types.d.ts +108 -0
- package/dist/schedules/types.js +1 -0
- package/dist/schedules/unattended.d.ts +117 -0
- package/dist/schedules/unattended.js +228 -0
- package/dist/setup/oauth-token-probe.js +8 -4
- package/dist/skills/agent-skills.service.d.ts +4 -8
- package/dist/skills/agent-skills.service.js +18 -53
- package/dist/skills/catalog.d.ts +65 -62
- package/dist/skills/catalog.js +109 -206
- package/dist/skills/conversation-catalog.d.ts +3 -3
- package/dist/skills/conversation-catalog.js +5 -12
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/skills.controller.d.ts +3 -1
- package/dist/skills/skills.service.d.ts +38 -37
- package/dist/skills/skills.service.js +168 -107
- package/dist/skills/usable.d.ts +57 -8
- package/dist/skills/usable.js +172 -38
- package/dist/tools/approvals.controller.d.ts +4 -2
- package/dist/tools/approvals.controller.js +17 -2
- package/dist/tools/approvals.schemas.d.ts +1 -0
- package/dist/tools/approvals.schemas.js +3 -1
- package/dist/tools/approvals.service.d.ts +182 -7
- package/dist/tools/approvals.service.js +597 -13
- package/dist/tools/builtin-names.d.ts +25 -4
- package/dist/tools/builtin-names.js +45 -4
- package/dist/tools/builtins/builtin-tools.d.ts +8 -0
- package/dist/tools/builtins/builtin-tools.js +23 -2
- package/dist/tools/builtins/sandbox/bash.js +16 -7
- package/dist/tools/builtins/sandbox/common.d.ts +1 -1
- package/dist/tools/builtins/sandbox/common.js +33 -10
- package/dist/tools/builtins/sandbox/edit.js +7 -3
- package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
- package/dist/tools/builtins/sandbox/file-state.js +33 -3
- package/dist/tools/builtins/sandbox/write.js +8 -4
- package/dist/tools/builtins/schedules.d.ts +107 -0
- package/dist/tools/builtins/schedules.js +789 -0
- package/dist/tools/elicitation.d.ts +2 -0
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/fixed-ask.d.ts +7 -0
- package/dist/tools/fixed-ask.js +9 -0
- package/dist/tools/network-asks.d.ts +109 -0
- package/dist/tools/network-asks.js +221 -0
- package/dist/tools/pipeline.d.ts +53 -6
- package/dist/tools/pipeline.js +202 -13
- package/dist/tools/policy.d.ts +21 -8
- package/dist/tools/policy.js +63 -9
- package/dist/tools/registry.d.ts +10 -1
- package/dist/tools/tools.module.js +12 -0
- package/package.json +2 -2
- package/src/migrations/0113_scheduled_tasks.sql +108 -0
- package/src/migrations/0114_network_asks.sql +23 -0
- package/src/migrations/0115_network_requests.sql +28 -0
- package/src/migrations/0116_skills_default_on.sql +37 -0
- package/src/migrations/0117_sandbox_tiers.sql +11 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CatalogRequestsService, type CatalogRequestView } from "../catalog/requests.service.js";
|
|
2
2
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
|
+
import { type ScheduleAttention, ScheduledTasksService } from "../schedules/scheduled-tasks.service.js";
|
|
3
4
|
import { SkillChangesService, type SkillChangeView } from "../skills/changes.service.js";
|
|
4
5
|
import { ApprovalsService, type ApprovalView } from "../tools/approvals.service.js";
|
|
5
6
|
import type { OrgNoticeDetails } from "./org-notices.js";
|
|
@@ -40,11 +41,16 @@ export declare const INBOX_MAX_ITEMS = 100;
|
|
|
40
41
|
* - `tool_approval`:当前用户能处理的待处理工具调用审批(与 `GET /approvals?status=pending` 同一视图);
|
|
41
42
|
* - `skill_change`: pending skill share requests and proposed versions of shared skills, when the
|
|
42
43
|
* current user is an org admin (`skillChange.kind`; spec §5.4, §5.5);
|
|
43
|
-
* - `catalog_request`: pending `enable` requests from members and pending
|
|
44
|
-
* requests (`request.kind`; `
|
|
45
|
-
* P4-T6-5
|
|
44
|
+
* - `catalog_request`: pending `enable` and `network_allowlist` requests from members and pending
|
|
45
|
+
* `connector_setup` requests (`request.kind`; for `connector_setup` `request.agent` and
|
|
46
|
+
* `request.connectors` set, `item` null, ruling P4-T6-5; for `network_allowlist`
|
|
47
|
+
* `request.network` set, network ask-to-allow spec §5.3), when the current user is an org admin
|
|
48
|
+
* (M6 T26, C22; plugin scope spec §5.1);
|
|
46
49
|
* - `catalog_request_decided`: the current user's own requests in this org that were approved or
|
|
47
50
|
* rejected (with the reason), until dismissed (`POST catalog-requests/:id/dismiss`);
|
|
51
|
+
* - `schedule_attention`: the current user's own scheduled task that needs them (spec §8): paused
|
|
52
|
+
* for any reason but their own pause, its latest run failed, or its latest run was refused tools
|
|
53
|
+
* or connections; derived live, gone once the task is resumed or runs clean;
|
|
48
54
|
* - the org notices (`skill_renamed`, `item_shared`, `shared_item_updated`, `upload_confirmed`,
|
|
49
55
|
* `upload_rejected`; `type` = the notice's kind): for org admins, until one dismisses it
|
|
50
56
|
* (`POST inbox/notices/:id/dismiss`).
|
|
@@ -70,12 +76,20 @@ export type InboxItem = {
|
|
|
70
76
|
/** When it was decided. */
|
|
71
77
|
createdAt: string;
|
|
72
78
|
request: CatalogRequestView;
|
|
79
|
+
} | {
|
|
80
|
+
type: "schedule_attention";
|
|
81
|
+
/** `<taskId>:paused`, `<taskId>:failed` or `<taskId>:grants`. */
|
|
82
|
+
id: string;
|
|
83
|
+
createdAt: string;
|
|
84
|
+
attention: ScheduleAttentionView;
|
|
73
85
|
} | {
|
|
74
86
|
type: OrgNoticeView["kind"];
|
|
75
87
|
id: string;
|
|
76
88
|
createdAt: string;
|
|
77
89
|
notice: OrgNoticeView;
|
|
78
90
|
};
|
|
91
|
+
/** A scheduled task that needs its creator ({@link ScheduleAttention}, dates as ISO strings). */
|
|
92
|
+
export type ScheduleAttentionView = Omit<ScheduleAttention, "at">;
|
|
79
93
|
/** `GET /inbox` 的结果:新的在前,最多 {@link INBOX_MAX_ITEMS} 项;`truncated` 为 true 表示还有更多。 */
|
|
80
94
|
export interface InboxView {
|
|
81
95
|
items: InboxItem[];
|
|
@@ -87,7 +101,8 @@ export declare class InboxService {
|
|
|
87
101
|
private readonly skillChanges;
|
|
88
102
|
private readonly kdb;
|
|
89
103
|
private readonly catalogRequests;
|
|
90
|
-
|
|
104
|
+
private readonly schedules;
|
|
105
|
+
constructor(approvals: Pick<ApprovalsService, "list">, skillChanges: Pick<SkillChangesService, "listPending">, kdb: KernelDb, catalogRequests: Pick<CatalogRequestsService, "listForOrgAdmin" | "listDecidedForRequester">, schedules: Pick<ScheduledTasksService, "attentionFor">);
|
|
91
106
|
list(orgId: string, accountId: string): Promise<InboxView>;
|
|
92
107
|
/** The org's open notices when the account is an org admin (else none), newest first. */
|
|
93
108
|
private notices;
|
|
@@ -13,6 +13,7 @@ import { CATALOG_REQUESTS_LIST_MAX, CatalogRequestsService, } from "../catalog/r
|
|
|
13
13
|
import { ApiError } from "../errors.js";
|
|
14
14
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
15
15
|
import { roleAtLeast } from "../orgs/roles.js";
|
|
16
|
+
import { ScheduledTasksService, } from "../schedules/scheduled-tasks.service.js";
|
|
16
17
|
import { SKILL_CHANGES_LIST_MAX, SkillChangesService, } from "../skills/changes.service.js";
|
|
17
18
|
import { APPROVALS_LIST_MAX, ApprovalsService, } from "../tools/approvals.service.js";
|
|
18
19
|
/** The most open org notices one inbox read returns. */
|
|
@@ -25,19 +26,22 @@ let InboxService = class InboxService {
|
|
|
25
26
|
skillChanges;
|
|
26
27
|
kdb;
|
|
27
28
|
catalogRequests;
|
|
28
|
-
|
|
29
|
+
schedules;
|
|
30
|
+
constructor(approvals, skillChanges, kdb, catalogRequests, schedules) {
|
|
29
31
|
this.approvals = approvals;
|
|
30
32
|
this.skillChanges = skillChanges;
|
|
31
33
|
this.kdb = kdb;
|
|
32
34
|
this.catalogRequests = catalogRequests;
|
|
35
|
+
this.schedules = schedules;
|
|
33
36
|
}
|
|
34
37
|
async list(orgId, accountId) {
|
|
35
|
-
const [approvals, changes, requests, decided, notices] = await Promise.all([
|
|
38
|
+
const [approvals, changes, requests, decided, notices, attention] = await Promise.all([
|
|
36
39
|
this.approvals.list(orgId, accountId, "pending"),
|
|
37
40
|
this.skillChanges.listPending(orgId, accountId),
|
|
38
41
|
this.catalogRequests.listForOrgAdmin(orgId, accountId),
|
|
39
42
|
this.catalogRequests.listDecidedForRequester(orgId, accountId),
|
|
40
43
|
this.notices(orgId, accountId),
|
|
44
|
+
this.schedules.attentionFor(orgId, accountId),
|
|
41
45
|
]);
|
|
42
46
|
const all = [
|
|
43
47
|
...approvals.map((a) => ({
|
|
@@ -64,6 +68,12 @@ let InboxService = class InboxService {
|
|
|
64
68
|
createdAt: r.decidedAt ?? r.createdAt,
|
|
65
69
|
request: r,
|
|
66
70
|
})),
|
|
71
|
+
...attention.map(({ at, ...view }) => ({
|
|
72
|
+
type: "schedule_attention",
|
|
73
|
+
id: `${view.taskId}:${view.reason === "missing_grants" ? "grants" : view.reason === "failed" ? "failed" : "paused"}`,
|
|
74
|
+
createdAt: at.toISOString(),
|
|
75
|
+
attention: view,
|
|
76
|
+
})),
|
|
67
77
|
...notices.map((n) => ({ type: n.kind, id: n.id, createdAt: n.createdAt, notice: n })),
|
|
68
78
|
];
|
|
69
79
|
// 新的在前;同一时刻按 id 定序(结果稳定)
|
|
@@ -142,7 +152,8 @@ InboxService = __decorate([
|
|
|
142
152
|
__param(0, Inject(ApprovalsService)),
|
|
143
153
|
__param(1, Inject(SkillChangesService)),
|
|
144
154
|
__param(2, Inject(KernelDb)),
|
|
145
|
-
__param(3, Inject(CatalogRequestsService))
|
|
155
|
+
__param(3, Inject(CatalogRequestsService)),
|
|
156
|
+
__param(4, Inject(ScheduledTasksService))
|
|
146
157
|
], InboxService);
|
|
147
158
|
export { InboxService };
|
|
148
159
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -33,6 +33,10 @@ const HOT_APPLIERS = {
|
|
|
33
33
|
"models.embedTimeoutMs": () => { },
|
|
34
34
|
// SandboxService reads the shared-lease cap at every acquisition.
|
|
35
35
|
"sandbox.maxConcurrentCommands": () => { },
|
|
36
|
+
// Sandbox tiers are read at every device use, org settings read and Platform call.
|
|
37
|
+
"sandbox.tiers.max": () => { },
|
|
38
|
+
"sandbox.tiers.default": () => { },
|
|
39
|
+
"sandbox.tiers.allowUnsandboxed": () => { },
|
|
36
40
|
// The devices service reads the personal limit at every create.
|
|
37
41
|
"devices.maxPersonal": () => { },
|
|
38
42
|
// The local device relay reads the reconnect grace at every disconnect and token response.
|
|
@@ -48,6 +52,13 @@ const HOT_APPLIERS = {
|
|
|
48
52
|
"web.fetch.maxAnswerTokens": () => { },
|
|
49
53
|
"web.fetch.cacheSeconds": () => { },
|
|
50
54
|
"web.fetch.timeoutMs": () => { },
|
|
55
|
+
// Scheduled-task settings are clamped to these bounds on every read.
|
|
56
|
+
"schedules.minIntervalFloorMinutes": () => { },
|
|
57
|
+
"schedules.maxTasksPerMemberCap": () => { },
|
|
58
|
+
"schedules.maxTasksPerOrgCap": () => { },
|
|
59
|
+
"schedules.maxRunsPerHourCap": () => { },
|
|
60
|
+
// Run start reads the skills catalog budget when it records a snapshot.
|
|
61
|
+
"skills.catalogBudgetChars": () => { },
|
|
51
62
|
};
|
|
52
63
|
/**
|
|
53
64
|
* Environment variables of renamed config keys, with their replacements: warned about once when
|
|
@@ -31,7 +31,8 @@ export interface KernelEventMap {
|
|
|
31
31
|
orgId: string;
|
|
32
32
|
conversationId: string;
|
|
33
33
|
runId: string;
|
|
34
|
-
|
|
34
|
+
/** `skipped`: a network ask closed by the run's starter's message (network ask-to-allow §6.7). */
|
|
35
|
+
status: "approved" | "denied" | "skipped";
|
|
35
36
|
};
|
|
36
37
|
/**
|
|
37
38
|
* A person answered a tool call's input request, or skipped it with a message (after the commit,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type DeviceFeature, type DeviceLocalPolicySummary, type DevicePolicyArgs } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
4
|
+
import type { KernelLogger } from "../log.js";
|
|
5
|
+
import { MachinesService } from "./machines.js";
|
|
6
|
+
import { LocalDeviceRelay } from "./relay.js";
|
|
7
|
+
/** How long the machine may take to apply a policy change. */
|
|
8
|
+
export declare const POLICY_DEADLINE_MS = 30000;
|
|
9
|
+
/** What became of a policy change. */
|
|
10
|
+
export type PolicyChangeOutcome = {
|
|
11
|
+
applied: true;
|
|
12
|
+
policy: DeviceLocalPolicySummary;
|
|
13
|
+
} | {
|
|
14
|
+
applied: false;
|
|
15
|
+
reason: "offline" | "refused" | "unsupported";
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Changes a local device's policy on its machine (network ask-to-allow spec §5.2; sandbox dev
|
|
20
|
+
* tiers spec §7.3: its hosts, its `tier`, and `isolation` back to `sandbox`): the kernel→device
|
|
21
|
+
* `policy` op over the {@link LocalDeviceRelay}. The machine is the authority: it
|
|
22
|
+
* refuses widening unless its owner allows remote grants, validates every host and answers with
|
|
23
|
+
* the policy as written, which the kernel then keeps on the active machine's row (§5.4) and
|
|
24
|
+
* audits as `device.policy_changed`.
|
|
25
|
+
*
|
|
26
|
+
* The op is sent outside any transaction (a decision records its outcome afterwards, in its own
|
|
27
|
+
* transaction); only the summary write and the audit row are transactional. Callers decide who
|
|
28
|
+
* may change a device's policy (the device owner, in a signed-in session); this service checks
|
|
29
|
+
* only that the device is a live device of the org.
|
|
30
|
+
*/
|
|
31
|
+
export declare class DevicePolicyService {
|
|
32
|
+
private readonly db;
|
|
33
|
+
readonly txHost: TransactionHost;
|
|
34
|
+
private readonly relay;
|
|
35
|
+
private readonly machines;
|
|
36
|
+
private readonly log;
|
|
37
|
+
constructor(db: KernelDb, txHost: TransactionHost, relay: Pick<LocalDeviceRelay, "call" | "holderLive">, machines: Pick<MachinesService, "activeFor">, log: Pick<KernelLogger, "warn">);
|
|
38
|
+
/**
|
|
39
|
+
* Sends the `policy` op (no transaction may be open), stores the returned summary and audits
|
|
40
|
+
* `device.policy_changed`. Never throws for an offline machine, a refusal or a program without
|
|
41
|
+
* the `policyOp` feature (nothing is sent to it); a device that is not a live device of `orgId`
|
|
42
|
+
* is `device.not_found`.
|
|
43
|
+
*/
|
|
44
|
+
apply(input: {
|
|
45
|
+
orgId: string;
|
|
46
|
+
deviceId: string;
|
|
47
|
+
change: Omit<DevicePolicyArgs, "source">;
|
|
48
|
+
source: DevicePolicyArgs["source"];
|
|
49
|
+
actorTokenId?: string;
|
|
50
|
+
}): Promise<PolicyChangeOutcome>;
|
|
51
|
+
/** The active machine's features and policy summary (null when no machine is connected). */
|
|
52
|
+
summary(deviceId: string): Promise<{
|
|
53
|
+
features: DeviceFeature[];
|
|
54
|
+
policy: DeviceLocalPolicySummary | null;
|
|
55
|
+
} | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Keeps the policy the machine wrote on its row and audits the change (`confirmed`); with no
|
|
58
|
+
* policy (the answer never came) only audits the attempt, unconfirmed. The detail's `isolation`
|
|
59
|
+
* is the one asked for, or the one the machine now reports when it changed (a tier change turns
|
|
60
|
+
* the sandbox back on).
|
|
61
|
+
*/
|
|
62
|
+
private record;
|
|
63
|
+
/** The outcome of a failed op: offline, or the machine's refusal; a deleted device is thrown. */
|
|
64
|
+
private notApplied;
|
|
65
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { ToolError, validateAllowHost, } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
12
|
+
import { ApiError } from "../errors.js";
|
|
13
|
+
import { audit } from "../identity/audit.js";
|
|
14
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
15
|
+
import { currentStore, TransactionHost } from "../infra/database/transaction-host.js";
|
|
16
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
17
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
18
|
+
import { deviceNotFound } from "../sandbox/sandbox.service.js";
|
|
19
|
+
import { MachinesService } from "./machines.js";
|
|
20
|
+
import { isolationOf } from "./policy-summary.js";
|
|
21
|
+
import { LocalDeviceRelay, ON_MACHINE_PREFIX } from "./relay.js";
|
|
22
|
+
/** How long the machine may take to apply a policy change. */
|
|
23
|
+
export const POLICY_DEADLINE_MS = 30_000;
|
|
24
|
+
/** The prefix the relay puts before a machine's `policy`-kind refusal. */
|
|
25
|
+
const REFUSED_PREFIX = "Refused by the machine's local policy: ";
|
|
26
|
+
const OFFLINE_MESSAGE = "The device's machine is offline, so its policy was not changed. Start capybara-device on it.";
|
|
27
|
+
const UNSUPPORTED_MESSAGE = "The device's capybara-device program cannot change its policy from the platform. Update capybara-device on the machine.";
|
|
28
|
+
const errMsg = (e) => (e instanceof Error ? e.message : String(e));
|
|
29
|
+
/**
|
|
30
|
+
* Changes a local device's policy on its machine (network ask-to-allow spec §5.2; sandbox dev
|
|
31
|
+
* tiers spec §7.3: its hosts, its `tier`, and `isolation` back to `sandbox`): the kernel→device
|
|
32
|
+
* `policy` op over the {@link LocalDeviceRelay}. The machine is the authority: it
|
|
33
|
+
* refuses widening unless its owner allows remote grants, validates every host and answers with
|
|
34
|
+
* the policy as written, which the kernel then keeps on the active machine's row (§5.4) and
|
|
35
|
+
* audits as `device.policy_changed`.
|
|
36
|
+
*
|
|
37
|
+
* The op is sent outside any transaction (a decision records its outcome afterwards, in its own
|
|
38
|
+
* transaction); only the summary write and the audit row are transactional. Callers decide who
|
|
39
|
+
* may change a device's policy (the device owner, in a signed-in session); this service checks
|
|
40
|
+
* only that the device is a live device of the org.
|
|
41
|
+
*/
|
|
42
|
+
let DevicePolicyService = class DevicePolicyService {
|
|
43
|
+
db;
|
|
44
|
+
txHost;
|
|
45
|
+
relay;
|
|
46
|
+
machines;
|
|
47
|
+
log;
|
|
48
|
+
constructor(db, txHost, relay, machines, log) {
|
|
49
|
+
this.db = db;
|
|
50
|
+
this.txHost = txHost;
|
|
51
|
+
this.relay = relay;
|
|
52
|
+
this.machines = machines;
|
|
53
|
+
this.log = log;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sends the `policy` op (no transaction may be open), stores the returned summary and audits
|
|
57
|
+
* `device.policy_changed`. Never throws for an offline machine, a refusal or a program without
|
|
58
|
+
* the `policyOp` feature (nothing is sent to it); a device that is not a live device of `orgId`
|
|
59
|
+
* is `device.not_found`.
|
|
60
|
+
*/
|
|
61
|
+
async apply(input) {
|
|
62
|
+
if (currentStore()) {
|
|
63
|
+
throw new Error("DevicePolicyService.apply() must not run inside a transaction: send the policy op " +
|
|
64
|
+
"first, then record the decision in a transaction.");
|
|
65
|
+
}
|
|
66
|
+
assertHosts(input.change, input.source.approvalId !== undefined);
|
|
67
|
+
const device = await this.db
|
|
68
|
+
.selectFrom("devices as e")
|
|
69
|
+
.innerJoin("orgs as o", "o.id", "e.org_id")
|
|
70
|
+
.select("e.source")
|
|
71
|
+
.where("e.id", "=", input.deviceId)
|
|
72
|
+
.where("e.org_id", "=", input.orgId)
|
|
73
|
+
.where("e.deleted_at", "is", null)
|
|
74
|
+
.where("o.deleted_at", "is", null)
|
|
75
|
+
.executeTakeFirst();
|
|
76
|
+
if (!device)
|
|
77
|
+
throw deviceNotFound();
|
|
78
|
+
if (device.source !== "local") {
|
|
79
|
+
return { applied: false, reason: "unsupported", message: UNSUPPORTED_MESSAGE };
|
|
80
|
+
}
|
|
81
|
+
const machine = await this.machines.activeFor(input.deviceId);
|
|
82
|
+
if (machine === null)
|
|
83
|
+
return { applied: false, reason: "offline", message: OFFLINE_MESSAGE };
|
|
84
|
+
if (!machine.features.includes("policyOp")) {
|
|
85
|
+
return { applied: false, reason: "unsupported", message: UNSUPPORTED_MESSAGE };
|
|
86
|
+
}
|
|
87
|
+
// not connected: nothing is sent (a queued op would wait for a reconnect nobody watches)
|
|
88
|
+
if (!(await this.relay.holderLive(input.deviceId))) {
|
|
89
|
+
return { applied: false, reason: "offline", message: OFFLINE_MESSAGE };
|
|
90
|
+
}
|
|
91
|
+
const args = { ...input.change, source: input.source };
|
|
92
|
+
let policy;
|
|
93
|
+
try {
|
|
94
|
+
({ policy } = await this.relay.call(input.deviceId, "policy", args, {
|
|
95
|
+
deadlineMs: POLICY_DEADLINE_MS,
|
|
96
|
+
// the relay gives up by itself (deadline plus the reconnect grace)
|
|
97
|
+
signal: new AbortController().signal,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
if (e instanceof ApiError && e.code === "device.offline") {
|
|
102
|
+
// the op was sent and the wait ran out: the machine may still apply it, so the attempt is
|
|
103
|
+
// audited unconfirmed; its next hello reports the policy as it is
|
|
104
|
+
await this.record({ ...input, machineId: machine.id, policy: null });
|
|
105
|
+
}
|
|
106
|
+
return this.notApplied(input.deviceId, e);
|
|
107
|
+
}
|
|
108
|
+
await this.record({ ...input, machineId: machine.id, policy });
|
|
109
|
+
return { applied: true, policy };
|
|
110
|
+
}
|
|
111
|
+
/** The active machine's features and policy summary (null when no machine is connected). */
|
|
112
|
+
async summary(deviceId) {
|
|
113
|
+
const m = await this.machines.activeFor(deviceId);
|
|
114
|
+
return m === null ? null : { features: m.features, policy: m.localPolicy };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Keeps the policy the machine wrote on its row and audits the change (`confirmed`); with no
|
|
118
|
+
* policy (the answer never came) only audits the attempt, unconfirmed. The detail's `isolation`
|
|
119
|
+
* is the one asked for, or the one the machine now reports when it changed (a tier change turns
|
|
120
|
+
* the sandbox back on).
|
|
121
|
+
*/
|
|
122
|
+
async record(input) {
|
|
123
|
+
// the isolation the machine reports now, when it differs from the stored one: a tier change
|
|
124
|
+
// turns an unsandboxed device's sandbox back on without asking for it (spec §5.4, §10.4)
|
|
125
|
+
let isolationNow;
|
|
126
|
+
if (input.policy !== null) {
|
|
127
|
+
const prev = await this.db
|
|
128
|
+
.selectFrom("device_machines")
|
|
129
|
+
.select("local_policy")
|
|
130
|
+
.where("id", "=", input.machineId)
|
|
131
|
+
.where("revoked_at", "is", null)
|
|
132
|
+
.forUpdate()
|
|
133
|
+
.executeTakeFirst();
|
|
134
|
+
const before = isolationOf(prev?.local_policy) ?? "sandbox";
|
|
135
|
+
if (input.policy.isolation !== before)
|
|
136
|
+
isolationNow = input.policy.isolation;
|
|
137
|
+
await this.db
|
|
138
|
+
.updateTable("device_machines")
|
|
139
|
+
.set({ local_policy: JSON.stringify(input.policy) })
|
|
140
|
+
.where("id", "=", input.machineId)
|
|
141
|
+
.where("revoked_at", "is", null)
|
|
142
|
+
.execute();
|
|
143
|
+
}
|
|
144
|
+
const { change, source } = input;
|
|
145
|
+
await audit(this.db.db, {
|
|
146
|
+
actorAccountId: source.accountId,
|
|
147
|
+
...(input.actorTokenId !== undefined && { actorTokenId: input.actorTokenId }),
|
|
148
|
+
orgId: input.orgId,
|
|
149
|
+
action: "device.policy_changed",
|
|
150
|
+
target: input.deviceId,
|
|
151
|
+
detail: {
|
|
152
|
+
add: change.add ?? [],
|
|
153
|
+
remove: change.remove ?? [],
|
|
154
|
+
// only when set (null: follow the org's default); the device page lists the change
|
|
155
|
+
...(change.tier !== undefined && { tier: change.tier }),
|
|
156
|
+
...((isolationNow ?? change.isolation) !== undefined && {
|
|
157
|
+
isolation: isolationNow ?? change.isolation,
|
|
158
|
+
}),
|
|
159
|
+
by: source.accountId,
|
|
160
|
+
via: source.via,
|
|
161
|
+
approvalId: source.approvalId ?? null,
|
|
162
|
+
confirmed: input.policy !== null,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** The outcome of a failed op: offline, or the machine's refusal; a deleted device is thrown. */
|
|
167
|
+
notApplied(deviceId, e) {
|
|
168
|
+
if (e instanceof ApiError) {
|
|
169
|
+
if (e.code === "device.not_found")
|
|
170
|
+
throw e;
|
|
171
|
+
// device.offline, or device.forbidden (the machine was disconnected meanwhile)
|
|
172
|
+
return { applied: false, reason: "offline", message: OFFLINE_MESSAGE };
|
|
173
|
+
}
|
|
174
|
+
if (e instanceof ToolError && e.message.startsWith(REFUSED_PREFIX)) {
|
|
175
|
+
return { applied: false, reason: "refused", message: e.message.slice(REFUSED_PREFIX.length) };
|
|
176
|
+
}
|
|
177
|
+
// anything else the machine answered (its own error, an invalid result): not applied
|
|
178
|
+
this.log.warn("a device policy change failed on the machine", { deviceId, error: errMsg(e) });
|
|
179
|
+
const message = errMsg(e);
|
|
180
|
+
return {
|
|
181
|
+
applied: false,
|
|
182
|
+
reason: "refused",
|
|
183
|
+
message: message.startsWith(ON_MACHINE_PREFIX)
|
|
184
|
+
? message.slice(ON_MACHINE_PREFIX.length)
|
|
185
|
+
: message,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
__decorate([
|
|
190
|
+
Transactional()
|
|
191
|
+
], DevicePolicyService.prototype, "record", null);
|
|
192
|
+
DevicePolicyService = __decorate([
|
|
193
|
+
Injectable(),
|
|
194
|
+
__param(0, Inject(KernelDb)),
|
|
195
|
+
__param(1, Inject(TransactionHost)),
|
|
196
|
+
__param(2, Inject(LocalDeviceRelay)),
|
|
197
|
+
__param(3, Inject(MachinesService)),
|
|
198
|
+
__param(4, Inject(KERNEL_LOGGER))
|
|
199
|
+
], DevicePolicyService);
|
|
200
|
+
export { DevicePolicyService };
|
|
201
|
+
/**
|
|
202
|
+
* Hosts the platform may send: an approval's `add` takes exact hosts only (a card never grants a
|
|
203
|
+
* wildcard); the owner's own change on the device page (no approval) may add a wildcard (spec §7);
|
|
204
|
+
* `remove` takes any valid entry (narrowing, a wildcard too).
|
|
205
|
+
*/
|
|
206
|
+
function assertHosts(change, fromApproval) {
|
|
207
|
+
const bad = (h) => typeof h !== "string" || validateAllowHost(h).length > 0;
|
|
208
|
+
for (const h of change.add ?? []) {
|
|
209
|
+
if (bad(h) || (fromApproval && h.startsWith("*."))) {
|
|
210
|
+
throw new Error(`not an exact host to add to a device policy: ${JSON.stringify(h)}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const h of change.remove ?? []) {
|
|
214
|
+
if (bad(h))
|
|
215
|
+
throw new Error(`not a host to remove from a device policy: ${JSON.stringify(h)}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -177,7 +177,10 @@ class DeviceConnection {
|
|
|
177
177
|
if (!(await this.claim()))
|
|
178
178
|
return;
|
|
179
179
|
try {
|
|
180
|
-
|
|
180
|
+
// the raw summary too: the frame parser drops one it cannot read whole, and its
|
|
181
|
+
// isolation must still reach the kernel
|
|
182
|
+
const rawPolicy = JSON.parse(text).policy;
|
|
183
|
+
await this.gw.machines.recordHello(this.identity.machineId, frame, rawPolicy);
|
|
181
184
|
}
|
|
182
185
|
catch (e) {
|
|
183
186
|
this.gw.log.warn("could not record a device hello", {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ExecResult, type FileSystemHandle, type SandboxHandle, type SandboxLimits, type SandboxProvider, type SandboxRoots } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
3
|
+
import type { KernelLogger } from "../log.js";
|
|
3
4
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
5
|
+
import { NetworkGrants } from "./network-grants.js";
|
|
4
6
|
import { LocalDeviceRelay } from "./relay.js";
|
|
5
7
|
import { DeviceTargets } from "./targets.js";
|
|
6
8
|
/** Extra time the machine gets beyond a command's own timeout (spawn, output, the trip back). */
|
|
@@ -15,6 +17,11 @@ export declare const OP_DEADLINE_MS = 30000;
|
|
|
15
17
|
export declare const FRAME_ENVELOPE_BYTES: number;
|
|
16
18
|
/** The most raw bytes whose base64 plus {@link FRAME_ENVELOPE_BYTES} fit in `maxFrameBytes`. */
|
|
17
19
|
export declare const maxReadBytes: (maxFrameBytes: number) => number;
|
|
20
|
+
/**
|
|
21
|
+
* The tool error of a device that runs commands without a sandbox the org does not allow (§4.3);
|
|
22
|
+
* also what a machine answers when it refuses such a command itself.
|
|
23
|
+
*/
|
|
24
|
+
export declare const UNSANDBOXED_NOT_ALLOWED_ERROR = "This device runs commands without a sandbox, which your organization does not allow. Its owner can turn the sandbox back on with `capybara-device policy set isolation sandbox`.";
|
|
18
25
|
/**
|
|
19
26
|
* The handle of an instance on a machine: the device id (ruling R1: the relay is keyed by
|
|
20
27
|
* device), the roots the machine reported at `create`, and the org's effective limits at
|
|
@@ -42,9 +49,11 @@ export declare class LocalDeviceProvider implements SandboxProvider {
|
|
|
42
49
|
private readonly targets;
|
|
43
50
|
private readonly sandbox;
|
|
44
51
|
private readonly config;
|
|
52
|
+
private readonly grants;
|
|
53
|
+
private readonly log;
|
|
45
54
|
readonly concurrentExec: true;
|
|
46
55
|
readonly sessionIsolation = false;
|
|
47
|
-
constructor(relay: LocalDeviceRelay, targets: Pick<DeviceTargets, "orgOf" | "machineRoots">, sandbox: Pick<SandboxService, "
|
|
56
|
+
constructor(relay: LocalDeviceRelay, targets: Pick<DeviceTargets, "orgOf" | "machineRoots">, sandbox: Pick<SandboxService, "devicePolicy" | "deviceLimits">, config: Pick<KernelConfigService, "get">, grants: Pick<NetworkGrants, "forExec" | "unattendedTier" | "unsandboxed" | "recordMissing">, log: Pick<KernelLogger, "warn">);
|
|
48
57
|
createFileSystem(args: {
|
|
49
58
|
name: string;
|
|
50
59
|
limits: SandboxLimits;
|
|
@@ -71,6 +80,23 @@ export declare class LocalDeviceProvider implements SandboxProvider {
|
|
|
71
80
|
destroyByName(_name: string, signal: AbortSignal): Promise<void>;
|
|
72
81
|
killAll(h: SandboxHandle, signal: AbortSignal): Promise<void>;
|
|
73
82
|
killTagged(h: SandboxHandle, tags: string[], signal: AbortSignal): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Runs the command on the machine with the org's limits now (ruling R27) and, for a command of a
|
|
85
|
+
* run, the hosts the kernel allows that run on this device beyond the local policy (network
|
|
86
|
+
* ask-to-allow spec §6.3; only those the org's limits admit, sent only when there are some). The device's report of refused hosts is re-layered
|
|
87
|
+
* against the limits this exec carried: the kernel never takes the device's word that the org
|
|
88
|
+
* allows a host (spec §4.1).
|
|
89
|
+
*
|
|
90
|
+
* The limits are the device's **ceiling** (sandbox dev tiers spec §4.2): `limits.tier` is the
|
|
91
|
+
* highest tier the org allows, its network that tier's, `defaultTier` the org's default (the
|
|
92
|
+
* device runs at `min(local tier ?? defaultTier, tier)`) and `devHosts` the dev hosts. A run of
|
|
93
|
+
* a scheduled task without a person and without the task's `tier:open` grant is capped at `dev`
|
|
94
|
+
* (spec §11.4). Before relaying, a machine that last reported running commands without a sandbox
|
|
95
|
+
* is refused when the org does not allow it, or for such a run (`device.unsandboxed_not_allowed`,
|
|
96
|
+
* spec §4.3). The kernel knows the local policy only from the last hello or policy op, so the
|
|
97
|
+
* same decision also travels in `limits.allowUnsandboxed` and the machine enforces it itself;
|
|
98
|
+
* its refusal ({@link UNSANDBOXED_NOT_ALLOWED_MESSAGE}) becomes the same error.
|
|
99
|
+
*/
|
|
74
100
|
exec(h: SandboxHandle, args: Parameters<SandboxProvider["exec"]>[1]): Promise<ExecResult>;
|
|
75
101
|
readFile(h: SandboxHandle, path: string, opts: {
|
|
76
102
|
maxBytes: number;
|
|
@@ -84,6 +110,17 @@ export declare class LocalDeviceProvider implements SandboxProvider {
|
|
|
84
110
|
size: number;
|
|
85
111
|
isDir: boolean;
|
|
86
112
|
} | null>;
|
|
87
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The device's org and the limits of one exec now (ruling R27): the ceiling, capped at `dev` for
|
|
115
|
+
* an unattended run without the `tier:open` grant (`cap`, set only when the platform and the org
|
|
116
|
+
* would otherwise allow an unsandboxed command); `allowUnsandboxed` whether this command may run
|
|
117
|
+
* without a sandbox. A deleted device is not found. Refuses an unsandboxed machine the org does
|
|
118
|
+
* not allow (or such a run), see {@link exec}.
|
|
119
|
+
*/
|
|
88
120
|
private currentLimits;
|
|
121
|
+
/**
|
|
122
|
+
* The `device.unsandboxed_not_allowed` error (spec §4.3); for an unattended run refused only for
|
|
123
|
+
* its missing `tier:open` grant, the run's text, with the missing grant recorded on the task run.
|
|
124
|
+
*/
|
|
125
|
+
private unsandboxedRefusal;
|
|
89
126
|
}
|