@capybara-hq/kernel 0.1.0-rc.8 → 0.1.0-rc.9
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/background/finish.js +2 -1
- package/dist/background/launch.d.ts +13 -7
- package/dist/background/launch.js +15 -8
- package/dist/background/poll.job.d.ts +1 -1
- package/dist/background/poll.job.js +8 -3
- package/dist/background/session.d.ts +2 -2
- package/dist/background/session.js +3 -2
- package/dist/background/status.d.ts +1 -1
- package/dist/background/status.js +1 -1
- package/dist/background/stop.js +4 -3
- package/dist/background/store.d.ts +6 -1
- package/dist/background/store.js +5 -1
- package/dist/background/tasks.service.d.ts +1 -1
- package/dist/background/tasks.service.js +4 -4
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/config/docs.js +2 -0
- package/dist/config/schema.d.ts +7 -0
- package/dist/config/schema.js +45 -0
- package/dist/conversations/event-log.d.ts +4 -3
- package/dist/conversations/event-log.js +4 -3
- package/dist/conversations/events.d.ts +9 -0
- package/dist/conversations/events.js +14 -0
- package/dist/db/kernel-schema.d.ts +26 -1
- package/dist/devices/device-auth.controller.d.ts +42 -0
- package/dist/devices/device-auth.controller.js +116 -0
- package/dist/devices/device-auth.d.ts +119 -0
- package/dist/devices/device-auth.js +403 -0
- package/dist/devices/device-auth.module.d.ts +7 -0
- package/dist/devices/device-auth.module.js +25 -0
- package/dist/devices/device-gateway.d.ts +128 -0
- package/dist/devices/device-gateway.js +530 -0
- package/dist/devices/device-provider.d.ts +89 -0
- package/dist/devices/device-provider.js +151 -0
- package/dist/devices/device-routes.d.ts +36 -0
- package/dist/devices/device-routes.js +90 -0
- package/dist/devices/devices.module.d.ts +16 -0
- package/dist/devices/devices.module.js +44 -0
- package/dist/devices/dto.d.ts +66 -0
- package/dist/devices/dto.js +44 -0
- package/dist/devices/keys.d.ts +25 -0
- package/dist/devices/keys.js +25 -0
- package/dist/devices/machines.d.ts +106 -0
- package/dist/devices/machines.js +301 -0
- package/dist/devices/relay.d.ts +100 -0
- package/dist/devices/relay.js +373 -0
- package/dist/devices/targets.d.ts +28 -0
- package/dist/devices/targets.js +73 -0
- package/dist/environments/access.d.ts +11 -6
- package/dist/environments/access.js +23 -8
- package/dist/environments/conversation-environments.d.ts +20 -6
- package/dist/environments/conversation-environments.js +70 -9
- package/dist/environments/environments.controller.d.ts +4 -1
- package/dist/environments/environments.controller.js +15 -1
- package/dist/environments/environments.module.js +3 -1
- package/dist/environments/environments.service.d.ts +54 -7
- package/dist/environments/environments.service.js +104 -20
- package/dist/environments/legacy-cleanup.job.d.ts +1 -1
- package/dist/environments/legacy-cleanup.job.js +4 -4
- package/dist/errors.d.ts +24 -0
- package/dist/errors.js +24 -0
- package/dist/evals/recorded-tools.d.ts +9 -0
- package/dist/evals/recorded-tools.js +27 -0
- package/dist/http/api.module.js +4 -0
- package/dist/http/gateway-routes.d.ts +7 -3
- package/dist/http/gateway-routes.js +6 -3
- package/dist/infra/config/kernel-config.service.js +2 -0
- package/dist/infra/throttler/named-throttlers.d.ts +5 -0
- package/dist/infra/throttler/named-throttlers.js +38 -0
- package/dist/kernel.module.js +3 -0
- package/dist/lifecycle/kernel-lifecycle.js +5 -0
- package/dist/platform/exclusions.js +33 -0
- package/dist/platform/ops/environments.ops.js +21 -3
- package/dist/roles/gateway-role.d.ts +14 -3
- package/dist/roles/gateway-role.js +25 -5
- package/dist/roles/gateway.module.d.ts +2 -1
- package/dist/roles/gateway.module.js +6 -3
- package/dist/runtime/context/build.d.ts +2 -0
- package/dist/runtime/context/build.js +14 -0
- package/dist/runtime/context/compaction.js +1 -0
- package/dist/runtime/context/environment-paths.d.ts +40 -0
- package/dist/runtime/context/environment-paths.js +48 -0
- package/dist/runtime/model-call.js +2 -0
- package/dist/sandbox/providers.d.ts +29 -7
- package/dist/sandbox/providers.js +43 -8
- package/dist/sandbox/sandbox.service.d.ts +70 -18
- package/dist/sandbox/sandbox.service.js +176 -59
- package/dist/tools/builtins/sandbox/bash.d.ts +2 -2
- package/dist/tools/builtins/sandbox/bash.js +39 -27
- package/dist/tools/builtins/sandbox/common.d.ts +60 -25
- package/dist/tools/builtins/sandbox/common.js +121 -30
- package/dist/tools/builtins/sandbox/deliver.js +9 -6
- package/dist/tools/builtins/sandbox/edit.js +40 -37
- package/dist/tools/builtins/sandbox/glob.js +13 -8
- package/dist/tools/builtins/sandbox/grep.js +10 -6
- package/dist/tools/builtins/sandbox/index.d.ts +6 -4
- package/dist/tools/builtins/sandbox/index.js +6 -4
- package/dist/tools/builtins/sandbox/read.js +9 -6
- package/dist/tools/builtins/sandbox/task-output.d.ts +7 -3
- package/dist/tools/builtins/sandbox/task-output.js +14 -10
- package/dist/tools/builtins/sandbox/task-stop.js +3 -3
- package/dist/tools/builtins/sandbox/write.js +33 -30
- package/dist/tools/builtins/skill-load.d.ts +8 -5
- package/dist/tools/builtins/skill-load.js +21 -15
- package/dist/tools/builtins/skill-write.js +2 -2
- package/dist/tools/pipeline.js +2 -0
- package/dist/tools/registry.d.ts +21 -4
- package/dist/tools/registry.js +10 -2
- package/package.json +5 -2
- package/src/migrations/0093_connected_environments.sql +37 -0
- package/src/migrations/0094_environment_paths_idx.sql +7 -0
|
@@ -13,6 +13,7 @@ import { afterCommit } from "../infra/database/after-commit.js";
|
|
|
13
13
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
14
14
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
15
15
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
16
|
+
import { taskOutputFile } from "./launch.js";
|
|
16
17
|
import { LAST_LINES_MAX_CHARS } from "./session.js";
|
|
17
18
|
import { BackgroundTaskStore } from "./store.js";
|
|
18
19
|
/** The idempotency key of a task's `background_task_finished` event. */
|
|
@@ -63,7 +64,7 @@ let BackgroundTaskFinisher = class BackgroundTaskFinisher {
|
|
|
63
64
|
lastLines: lastLines.length > LAST_LINES_MAX_CHARS
|
|
64
65
|
? lastLines.slice(-LAST_LINES_MAX_CHARS)
|
|
65
66
|
: lastLines,
|
|
66
|
-
outputPath:
|
|
67
|
+
outputPath: taskOutputFile(task.outputPath),
|
|
67
68
|
reported,
|
|
68
69
|
...(to.lostReason !== undefined && { lostReason: to.lostReason }),
|
|
69
70
|
...(opts.stoppedBy !== undefined && { stoppedBy: opts.stoppedBy }),
|
|
@@ -2,8 +2,8 @@ import type { KernelConfig } from "../config/schema.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
|
|
4
4
|
* one short exec that starts the command detached from the exec, and the model-visible result.
|
|
5
|
-
* Every path is relative to
|
|
6
|
-
*
|
|
5
|
+
* Every path is relative to the instance's workspace root (the launch exec's `cwd`), so the
|
|
6
|
+
* script is the same whatever the instance's roots are.
|
|
7
7
|
*/
|
|
8
8
|
/** The launch exec's own time limit: it only writes a file and forks. */
|
|
9
9
|
export declare const LAUNCH_EXEC_TIMEOUT_MS = 30000;
|
|
@@ -12,11 +12,17 @@ export declare const LAUNCH_EXEC_TIMEOUT_MS = 30000;
|
|
|
12
12
|
* `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
|
|
13
13
|
*/
|
|
14
14
|
export declare const backgroundMaxSeconds: (cfg: Pick<KernelConfig, "sandbox">) => number;
|
|
15
|
-
/** The directory of the task files, relative to
|
|
15
|
+
/** The directory of the task files, relative to the workspace root. */
|
|
16
16
|
export declare const TASKS_DIR = ".capybara/tasks";
|
|
17
|
-
/** A task's output file, relative to
|
|
17
|
+
/** A task's output file, relative to the workspace root (`output_path` while reserved). */
|
|
18
18
|
export declare const taskOutputPath: (id: string) => string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The absolute output file of a task from its `output_path`: absolute once the task started (the
|
|
21
|
+
* launch records `<workspace>/.capybara/tasks/<id>.out` of its instance); a reservation's relative
|
|
22
|
+
* path is read under the default workspace.
|
|
23
|
+
*/
|
|
24
|
+
export declare const taskOutputFile: (outputPath: string) => string;
|
|
25
|
+
/** The file the launch writes the session id to, relative to the workspace root. */
|
|
20
26
|
export declare const taskSidPath: (id: string) => string;
|
|
21
27
|
/** A new task id: `bt_` plus 6 lowercase base32 characters. */
|
|
22
28
|
export declare function newTaskId(): string;
|
|
@@ -32,5 +38,5 @@ export declare const TASK_ID_RE: RegExp;
|
|
|
32
38
|
* positive integer (both go into the script unquoted).
|
|
33
39
|
*/
|
|
34
40
|
export declare function launchScript(id: string, command: string, seconds: number): string;
|
|
35
|
-
/** The model-visible result of a launch (spec §2.1). */
|
|
36
|
-
export declare function startedText(id: string, description: string): string;
|
|
41
|
+
/** The model-visible result of a launch (spec §2.1); `workspace`: the instance's workspace root. */
|
|
42
|
+
export declare function startedText(id: string, description: string, workspace?: string): string;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { DEFAULT_SANDBOX_ROOTS } from "@capybara-hq/plugin-sdk";
|
|
2
3
|
/**
|
|
3
4
|
* Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
|
|
4
5
|
* one short exec that starts the command detached from the exec, and the model-visible result.
|
|
5
|
-
* Every path is relative to
|
|
6
|
-
*
|
|
6
|
+
* Every path is relative to the instance's workspace root (the launch exec's `cwd`), so the
|
|
7
|
+
* script is the same whatever the instance's roots are.
|
|
7
8
|
*/
|
|
8
9
|
/** The launch exec's own time limit: it only writes a file and forks. */
|
|
9
10
|
export const LAUNCH_EXEC_TIMEOUT_MS = 30_000;
|
|
@@ -12,11 +13,17 @@ export const LAUNCH_EXEC_TIMEOUT_MS = 30_000;
|
|
|
12
13
|
* `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
|
|
13
14
|
*/
|
|
14
15
|
export const backgroundMaxSeconds = (cfg) => Math.min(cfg.sandbox.background.maxSeconds, cfg.sandbox.limits.maxCommandSeconds);
|
|
15
|
-
/** The directory of the task files, relative to
|
|
16
|
+
/** The directory of the task files, relative to the workspace root. */
|
|
16
17
|
export const TASKS_DIR = ".capybara/tasks";
|
|
17
|
-
/** A task's output file, relative to
|
|
18
|
+
/** A task's output file, relative to the workspace root (`output_path` while reserved). */
|
|
18
19
|
export const taskOutputPath = (id) => `${TASKS_DIR}/${id}.out`;
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* The absolute output file of a task from its `output_path`: absolute once the task started (the
|
|
22
|
+
* launch records `<workspace>/.capybara/tasks/<id>.out` of its instance); a reservation's relative
|
|
23
|
+
* path is read under the default workspace.
|
|
24
|
+
*/
|
|
25
|
+
export const taskOutputFile = (outputPath) => outputPath.startsWith("/") ? outputPath : `${DEFAULT_SANDBOX_ROOTS.workspace}/${outputPath}`;
|
|
26
|
+
/** The file the launch writes the session id to, relative to the workspace root. */
|
|
20
27
|
export const taskSidPath = (id) => `${TASKS_DIR}/${id}.sid`;
|
|
21
28
|
const BASE32 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
22
29
|
/** A new task id: `bt_` plus 6 lowercase base32 characters. */
|
|
@@ -60,11 +67,11 @@ export function launchScript(id, command, seconds) {
|
|
|
60
67
|
`cat ${t}.sid`,
|
|
61
68
|
].join("\n");
|
|
62
69
|
}
|
|
63
|
-
/** The model-visible result of a launch (spec §2.1). */
|
|
64
|
-
export function startedText(id, description) {
|
|
70
|
+
/** The model-visible result of a launch (spec §2.1); `workspace`: the instance's workspace root. */
|
|
71
|
+
export function startedText(id, description, workspace = DEFAULT_SANDBOX_ROOTS.workspace) {
|
|
65
72
|
return [
|
|
66
73
|
`Started background task ${id} ("${description}").`,
|
|
67
|
-
`Output:
|
|
74
|
+
`Output: ${workspace}/${taskOutputPath(id)}`,
|
|
68
75
|
"Read it with TaskOutput (or Read), stop it with TaskStop. You will be told when it finishes.",
|
|
69
76
|
].join("\n");
|
|
70
77
|
}
|
|
@@ -37,7 +37,7 @@ export declare class BackgroundTaskPollJob {
|
|
|
37
37
|
readonly config: KernelConfigService;
|
|
38
38
|
readonly lockService: LockService;
|
|
39
39
|
readonly log: KernelLogger;
|
|
40
|
-
constructor(store: Pick<BackgroundTaskStore, "runningStarted" | "pruneFinished" | "failAbandonedLaunches">, finisher: BackgroundTaskFinisher, eventLog: Pick<EventLog, "publishChanged">, sandbox: Pick<SandboxService, "
|
|
40
|
+
constructor(store: Pick<BackgroundTaskStore, "runningStarted" | "pruneFinished" | "failAbandonedLaunches">, finisher: BackgroundTaskFinisher, eventLog: Pick<EventLog, "publishChanged">, sandbox: Pick<SandboxService, "cloudAvailable" | "availableFor" | "ifRunning">, config: KernelConfigService, lockService: LockService, log: KernelLogger);
|
|
41
41
|
poll(signal?: AbortSignal): Promise<void>;
|
|
42
42
|
/**
|
|
43
43
|
* One poll of every running task (the body of {@link poll}); returns how many tasks finished.
|
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
+
import { rootsOf } from "@capybara-hq/plugin-sdk";
|
|
10
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
12
|
import { EventLog } from "../conversations/event-log.js";
|
|
12
13
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
@@ -88,8 +89,6 @@ let BackgroundTaskPollJob = class BackgroundTaskPollJob {
|
|
|
88
89
|
for (const c of conversations)
|
|
89
90
|
await this.eventLog.publishChanged(c, "background");
|
|
90
91
|
}
|
|
91
|
-
if (!this.sandbox.available)
|
|
92
|
-
return 0;
|
|
93
92
|
const groups = new Map();
|
|
94
93
|
for (const t of await this.store.runningStarted()) {
|
|
95
94
|
if (t.sid === null || t.sandboxCreatedAt === null)
|
|
@@ -103,6 +102,12 @@ let BackgroundTaskPollJob = class BackgroundTaskPollJob {
|
|
|
103
102
|
if (signal.aborted)
|
|
104
103
|
break;
|
|
105
104
|
try {
|
|
105
|
+
// the environment's provider is not there (no cloud provider, `@device` not served):
|
|
106
|
+
// its tasks stay as they are until it is
|
|
107
|
+
const env = tasks[0]?.environmentId ?? null;
|
|
108
|
+
if (env === null ? !this.sandbox.cloudAvailable : !(await this.sandbox.availableFor(env))) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
106
111
|
finished += await this.pollSandbox(tasks, signal);
|
|
107
112
|
}
|
|
108
113
|
catch (err) {
|
|
@@ -153,7 +158,7 @@ let BackgroundTaskPollJob = class BackgroundTaskPollJob {
|
|
|
153
158
|
async pollExec(p, h, tasks, signal) {
|
|
154
159
|
const res = await p.exec(h, {
|
|
155
160
|
command: pollScript(tasks.map((t) => ({ id: t.id, sid: t.sid }))),
|
|
156
|
-
cwd:
|
|
161
|
+
cwd: rootsOf(h).workspace,
|
|
157
162
|
timeoutMs: POLL_EXEC_TIMEOUT_MS,
|
|
158
163
|
maxOutputBytes: POLL_MAX_OUTPUT_BYTES,
|
|
159
164
|
signal,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SandboxActor, type SandboxHandle } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { HeldProvider, TaggedProvider } from "../sandbox/sandbox.service.js";
|
|
3
3
|
/**
|
|
4
4
|
* A background task's session inside the sandbox (spec 2026-11-05-background-bash-design §4.2):
|
|
@@ -41,7 +41,7 @@ export declare function killSession(p: Pick<HeldProvider, "exec">, h: SandboxHan
|
|
|
41
41
|
export declare const LAST_LINES_MAX_CHARS = 4000;
|
|
42
42
|
/**
|
|
43
43
|
* A shell pipeline printing the last 20 lines of task `id`'s output (at most {@link TAIL_BYTES}),
|
|
44
|
-
* base64 on one line (empty when there is no output file). Relative to
|
|
44
|
+
* base64 on one line (empty when there is no output file). Relative to the workspace root.
|
|
45
45
|
*/
|
|
46
46
|
export declare function tailCommand(id: string): string;
|
|
47
47
|
/** Decodes a {@link tailCommand} result into `lastLines`: its end, at most 4 000 characters. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rootsOf } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import { TASK_ID_RE, TASKS_DIR } from "./launch.js";
|
|
2
3
|
/**
|
|
3
4
|
* A background task's session inside the sandbox (spec 2026-11-05-background-bash-design §4.2):
|
|
@@ -53,7 +54,7 @@ export async function killSession(p, h, sid) {
|
|
|
53
54
|
const r = await p
|
|
54
55
|
.exec(h, {
|
|
55
56
|
command: `${killSessionScript(sid)}; exit "$ks"`,
|
|
56
|
-
cwd:
|
|
57
|
+
cwd: rootsOf(h).workspace,
|
|
57
58
|
timeoutMs: KILL_SESSION_TIMEOUT_MS,
|
|
58
59
|
maxOutputBytes: 4096,
|
|
59
60
|
signal: AbortSignal.timeout(KILL_SESSION_TIMEOUT_MS),
|
|
@@ -67,7 +68,7 @@ const TAIL_BYTES = 12_000;
|
|
|
67
68
|
export const LAST_LINES_MAX_CHARS = 4000;
|
|
68
69
|
/**
|
|
69
70
|
* A shell pipeline printing the last 20 lines of task `id`'s output (at most {@link TAIL_BYTES}),
|
|
70
|
-
* base64 on one line (empty when there is no output file). Relative to
|
|
71
|
+
* base64 on one line (empty when there is no output file). Relative to the workspace root.
|
|
71
72
|
*/
|
|
72
73
|
export function tailCommand(id) {
|
|
73
74
|
return `{ tail -n 20 ${TASKS_DIR}/${id}.out 2>/dev/null | tail -c ${TAIL_BYTES} | base64 | tr -d '\\n'; }`;
|
|
@@ -19,7 +19,7 @@ export interface TaskReport {
|
|
|
19
19
|
* processes (it left `server &` running) has the session killed in the same exec (so under the
|
|
20
20
|
* caller's lease), and is reported without a process once the session was confirmed empty: the
|
|
21
21
|
* task ends when its command exits, and nothing of it runs on untracked. The tail (base64) is read
|
|
22
|
-
* only for a task that exited or has no process left. Relative to
|
|
22
|
+
* only for a task that exited or has no process left. Relative to the workspace root.
|
|
23
23
|
*/
|
|
24
24
|
export declare function pollScript(tasks: {
|
|
25
25
|
id: string;
|
|
@@ -8,7 +8,7 @@ import { exitedAs } from "./stop.js";
|
|
|
8
8
|
* processes (it left `server &` running) has the session killed in the same exec (so under the
|
|
9
9
|
* caller's lease), and is reported without a process once the session was confirmed empty: the
|
|
10
10
|
* task ends when its command exits, and nothing of it runs on untracked. The tail (base64) is read
|
|
11
|
-
* only for a task that exited or has no process left. Relative to
|
|
11
|
+
* only for a task that exited or has no process left. Relative to the workspace root.
|
|
12
12
|
*/
|
|
13
13
|
export function pollScript(tasks) {
|
|
14
14
|
const lines = tasks.map(({ id, sid }) => {
|
package/dist/background/stop.js
CHANGED
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
+
import { rootsOf } from "@capybara-hq/plugin-sdk";
|
|
10
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
12
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
12
13
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
@@ -24,7 +25,7 @@ const TAIL_EXEC_TIMEOUT_MS = 10_000;
|
|
|
24
25
|
/**
|
|
25
26
|
* Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
|
|
26
27
|
* else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
|
|
27
|
-
* prints `term`. Relative to
|
|
28
|
+
* prints `term`. Relative to the workspace root.
|
|
28
29
|
*/
|
|
29
30
|
function termScript(id, sid) {
|
|
30
31
|
const f = `${TASKS_DIR}/${id}`;
|
|
@@ -146,7 +147,7 @@ let BackgroundTaskStopper = class BackgroundTaskStopper {
|
|
|
146
147
|
const p = actingAs(tp, taskActor(cur));
|
|
147
148
|
const term = await p.exec(h, {
|
|
148
149
|
command: termScript(cur.id, sid),
|
|
149
|
-
cwd:
|
|
150
|
+
cwd: rootsOf(h).workspace,
|
|
150
151
|
timeoutMs: TERM_EXEC_TIMEOUT_MS,
|
|
151
152
|
maxOutputBytes: 4096,
|
|
152
153
|
signal: sig,
|
|
@@ -161,7 +162,7 @@ let BackgroundTaskStopper = class BackgroundTaskStopper {
|
|
|
161
162
|
}
|
|
162
163
|
const tail = await p.exec(h, {
|
|
163
164
|
command: tailCommand(cur.id),
|
|
164
|
-
cwd:
|
|
165
|
+
cwd: rootsOf(h).workspace,
|
|
165
166
|
timeoutMs: TAIL_EXEC_TIMEOUT_MS,
|
|
166
167
|
maxOutputBytes: 64 * 1024,
|
|
167
168
|
signal: sig,
|
|
@@ -28,7 +28,11 @@ export interface BackgroundTaskRow {
|
|
|
28
28
|
* started: readers that act on the process (the poller, stops) skip such a row.
|
|
29
29
|
*/
|
|
30
30
|
sid: string | null;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* The output file: absolute once the task started (`markStarted` records it under its
|
|
33
|
+
* instance's workspace root), relative to the workspace root while reserved
|
|
34
|
+
* (`taskOutputFile` in launch.ts).
|
|
35
|
+
*/
|
|
32
36
|
outputPath: string;
|
|
33
37
|
status: BackgroundTaskStatus;
|
|
34
38
|
lostReason: string | null;
|
|
@@ -85,6 +89,7 @@ export declare class BackgroundTaskStore {
|
|
|
85
89
|
markStarted(id: string, to: {
|
|
86
90
|
sid: string;
|
|
87
91
|
sandboxCreatedAt: Date;
|
|
92
|
+
outputPath?: string;
|
|
88
93
|
}): Promise<"started" | "stopping" | false>;
|
|
89
94
|
/**
|
|
90
95
|
* Finishes a launch whose reservation a stop claimed, once the launch killed what it started:
|
package/dist/background/store.js
CHANGED
|
@@ -86,7 +86,11 @@ let BackgroundTaskStore = class BackgroundTaskStore {
|
|
|
86
86
|
async markStarted(id, to) {
|
|
87
87
|
const r = await this.db
|
|
88
88
|
.updateTable("background_tasks")
|
|
89
|
-
.set({
|
|
89
|
+
.set({
|
|
90
|
+
sid: to.sid,
|
|
91
|
+
sandbox_created_at: to.sandboxCreatedAt,
|
|
92
|
+
...(to.outputPath !== undefined && { output_path: to.outputPath }),
|
|
93
|
+
})
|
|
90
94
|
.where("id", "=", id)
|
|
91
95
|
.where("status", "=", "running")
|
|
92
96
|
.where("sid", "is", null)
|
|
@@ -46,7 +46,7 @@ export declare class BackgroundTasks {
|
|
|
46
46
|
private readonly eventLog;
|
|
47
47
|
private readonly db;
|
|
48
48
|
private readonly environments;
|
|
49
|
-
constructor(access: Pick<ConversationAccess, "isAccessible">, store: Pick<BackgroundTaskStore, "listFor" | "get">, stopper: Pick<BackgroundTaskStopper, "stop">, sandbox: Pick<SandboxService, "
|
|
49
|
+
constructor(access: Pick<ConversationAccess, "isAccessible">, store: Pick<BackgroundTaskStore, "listFor" | "get">, stopper: Pick<BackgroundTaskStopper, "stop">, sandbox: Pick<SandboxService, "availableFor" | "ifRunning">, eventLog: Pick<EventLog, "findByIdem">, db: KernelDb, environments: Pick<ConversationEnvironments, "forTool">);
|
|
50
50
|
/**
|
|
51
51
|
* The conversation and every sub-agent conversation below it (grandchildren too: their tasks run
|
|
52
52
|
* in the root's sandbox, agent map spec §6); `conversation.not_found` when not visible.
|
|
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { isSandboxGoneError } from "@capybara-hq/plugin-sdk";
|
|
10
|
+
import { isSandboxGoneError, rootsOf } from "@capybara-hq/plugin-sdk";
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { ConversationAccess } from "../conversations/access.js";
|
|
13
13
|
import { EventLog } from "../conversations/event-log.js";
|
|
@@ -144,7 +144,7 @@ let BackgroundTasks = class BackgroundTasks {
|
|
|
144
144
|
if (task.sid !== null &&
|
|
145
145
|
task.sandboxCreatedAt !== null &&
|
|
146
146
|
task.environmentId !== null &&
|
|
147
|
-
this.sandbox.
|
|
147
|
+
(await this.sandbox.availableFor(task.environmentId)) &&
|
|
148
148
|
(await this.environmentUsable(orgId, task.conversationId))) {
|
|
149
149
|
const createdAt = task.sandboxCreatedAt;
|
|
150
150
|
// the person reading it acts in the task's conversation
|
|
@@ -153,7 +153,7 @@ let BackgroundTasks = class BackgroundTasks {
|
|
|
153
153
|
const r = await this.sandbox.ifRunning(task.environmentId, { createdAt }, async (p, h, signal) => {
|
|
154
154
|
const res = await actingAs(p, actor).exec(h, {
|
|
155
155
|
command: tailScript(task.id, tail),
|
|
156
|
-
cwd:
|
|
156
|
+
cwd: rootsOf(h).workspace,
|
|
157
157
|
timeoutMs: OUTPUT_READ_TIMEOUT_MS,
|
|
158
158
|
maxOutputBytes: 2 * OUTPUT_TAIL_MAX_BYTES + 4096,
|
|
159
159
|
signal,
|
|
@@ -188,7 +188,7 @@ BackgroundTasks = __decorate([
|
|
|
188
188
|
__param(6, Inject(ConversationEnvironments))
|
|
189
189
|
], BackgroundTasks);
|
|
190
190
|
export { BackgroundTasks };
|
|
191
|
-
/** Prints `size <bytes>` and `out <base64 of the last `tail` bytes>`. Relative to
|
|
191
|
+
/** Prints `size <bytes>` and `out <base64 of the last `tail` bytes>`. Relative to the workspace root. */
|
|
192
192
|
function tailScript(id, tail) {
|
|
193
193
|
if (!TASK_ID_RE.test(id))
|
|
194
194
|
throw new Error(`invalid background task id: ${id}`);
|
|
@@ -181,7 +181,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
|
|
|
181
181
|
/** The host versions (an entry's `minKernel` is judged against them). */
|
|
182
182
|
export declare const HOST_VERSIONS: {
|
|
183
183
|
readonly kernel: string;
|
|
184
|
-
readonly sdk: "0.1.0-rc.
|
|
184
|
+
readonly sdk: "0.1.0-rc.6";
|
|
185
185
|
};
|
|
186
186
|
/** Whether this deployment satisfies the entry's `minKernel`. */
|
|
187
187
|
export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
|
package/dist/config/docs.js
CHANGED
|
@@ -65,6 +65,7 @@ export function renderConfigDocs(schema) {
|
|
|
65
65
|
// CAPYBARA_WEB_TRUST_PROXY: apps/web/src/lib/peer.ts; CAPYBARA_WEB_SHUTDOWN_GRACE_MS: apps/web/server.mjs.
|
|
66
66
|
// CAPYBARA_WEB_KEEP_ALIVE_TIMEOUT_MS: apps/web/src/lib/keep-alive.ts.
|
|
67
67
|
// CAPYBARA_WEB_OBJECT_STORE_*: apps/web/src/lib/object-store-proxy.ts.
|
|
68
|
+
// CAPYBARA_WEB_GATEWAY_URL: apps/web/src/lib/device-connect-proxy.ts.
|
|
68
69
|
"## Web tier (apps/web) environment variables",
|
|
69
70
|
"",
|
|
70
71
|
"The web tier is a separate process (started through `apps/web/server.mjs`); it does not read the kernel config and only honors the following environment variables:",
|
|
@@ -81,6 +82,7 @@ export function renderConfigDocs(schema) {
|
|
|
81
82
|
"| `CAPYBARA_WEB_OBJECT_STORE_URL` | Object storage on the web origin: the object store's address as web reaches it (e.g. `http://seaweedfs-s3:8333`). When set, web proxies `/<bucket>/**` (and `/<bucket>` itself for POST only) there, so browsers upload attachments (presigned POST) and download attachments and artifacts (presigned GET) through web; set the object-store extension's `publicEndpoint` to the web origin with `forcePathStyle: true`. Only GET, HEAD and POST; the path is forwarded unchanged; cookies and authorization are not forwarded; responses carry `X-Content-Type-Options: nosniff` and `Content-Security-Policy: sandbox`; uploads are capped at `CAPYBARA_WEB_MAX_PACKAGE_BYTES`. Unset (default): off |",
|
|
82
83
|
"| `CAPYBARA_WEB_OBJECT_STORE_BUCKET` | With `CAPYBARA_WEB_OBJECT_STORE_URL`: the bucket, equal to the object-store extension's `bucket` (it is the first path segment web proxies) |",
|
|
83
84
|
"| `CAPYBARA_WEB_OBJECT_STORE_PUBLIC_HOST` | With `CAPYBARA_WEB_OBJECT_STORE_URL`: the host of the extension's `publicEndpoint` (a host such as `capybara.example.com`, or that URL). Sent upstream as `Host` instead of the incoming one, because presigned URLs sign the host they were issued for and front proxies rewrite it |",
|
|
85
|
+
"| `CAPYBARA_WEB_GATEWAY_URL` | Connected machines on the web origin: a `gateway` replica's address as web reaches it (an http(s) URL, e.g. `http://gateway:8080`). When set, WebSocket upgrades on exactly `/device/connect` are forwarded to it (`capybara-device` connects at `<http.publicUrl>/device/connect`); unset, such upgrades get 404. Not needed when an external proxy routes `/device/connect` to the gateway replicas; a malformed value stops the web tier at startup |",
|
|
84
86
|
"| `CAPYBARA_DEV_UI` | When set to `1`, production builds also serve the `/dev/ui` component catalog page (off by default) |",
|
|
85
87
|
"",
|
|
86
88
|
].join("\n");
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -221,6 +221,13 @@ export declare const KernelConfigSchema: z.ZodObject<{
|
|
|
221
221
|
}, z.core.$strip>>;
|
|
222
222
|
environments: z.ZodPrefault<z.ZodObject<{
|
|
223
223
|
maxPersonal: z.ZodDefault<z.ZodNumber>;
|
|
224
|
+
device: z.ZodPrefault<z.ZodObject<{
|
|
225
|
+
reconnectGraceSeconds: z.ZodDefault<z.ZodNumber>;
|
|
226
|
+
leaseTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
227
|
+
authCodeTtlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
228
|
+
pollIntervalSeconds: z.ZodDefault<z.ZodNumber>;
|
|
229
|
+
maxFrameBytes: z.ZodDefault<z.ZodNumber>;
|
|
230
|
+
}, z.core.$strip>>;
|
|
224
231
|
}, z.core.$strip>>;
|
|
225
232
|
evals: z.ZodPrefault<z.ZodObject<{
|
|
226
233
|
orgConcurrency: z.ZodDefault<z.ZodNumber>;
|
package/dist/config/schema.js
CHANGED
|
@@ -1184,6 +1184,51 @@ export const KernelConfigSchema = z.object({
|
|
|
1184
1184
|
hot: true,
|
|
1185
1185
|
description: "Most personal environments a member may have in one org, the default one included; creating one more fails with environment.limit_reached",
|
|
1186
1186
|
})),
|
|
1187
|
+
device: z
|
|
1188
|
+
.object({
|
|
1189
|
+
reconnectGraceSeconds: z
|
|
1190
|
+
.number()
|
|
1191
|
+
.int()
|
|
1192
|
+
.positive()
|
|
1193
|
+
.default(120)
|
|
1194
|
+
.meta(meta({
|
|
1195
|
+
hot: true,
|
|
1196
|
+
description: "Seconds a connected machine may stay disconnected before its environment is reported offline; also sent to the device program at sign-in",
|
|
1197
|
+
})),
|
|
1198
|
+
leaseTtlMs: z
|
|
1199
|
+
.number()
|
|
1200
|
+
.int()
|
|
1201
|
+
.min(6000)
|
|
1202
|
+
.default(30000)
|
|
1203
|
+
.meta(meta({
|
|
1204
|
+
description: "Milliseconds the replica holding a machine's connection keeps its lease without renewing it; at least 6000",
|
|
1205
|
+
})),
|
|
1206
|
+
authCodeTtlSeconds: z
|
|
1207
|
+
.number()
|
|
1208
|
+
.int()
|
|
1209
|
+
.positive()
|
|
1210
|
+
.default(600)
|
|
1211
|
+
.meta(meta({
|
|
1212
|
+
description: "Seconds a device sign-in code stays valid before the program must start over (device.expired)",
|
|
1213
|
+
})),
|
|
1214
|
+
pollIntervalSeconds: z
|
|
1215
|
+
.number()
|
|
1216
|
+
.int()
|
|
1217
|
+
.positive()
|
|
1218
|
+
.default(5)
|
|
1219
|
+
.meta(meta({
|
|
1220
|
+
description: "Seconds the device program waits between token polls during sign-in; polling faster gets device.slow_down",
|
|
1221
|
+
})),
|
|
1222
|
+
maxFrameBytes: z
|
|
1223
|
+
.number()
|
|
1224
|
+
.int()
|
|
1225
|
+
.positive()
|
|
1226
|
+
.default(16 * 1024 * 1024)
|
|
1227
|
+
.meta(meta({
|
|
1228
|
+
description: "Largest WebSocket frame in bytes accepted from or sent to a connected machine",
|
|
1229
|
+
})),
|
|
1230
|
+
})
|
|
1231
|
+
.prefault({}),
|
|
1187
1232
|
})
|
|
1188
1233
|
.prefault({}),
|
|
1189
1234
|
evals: z
|
|
@@ -131,9 +131,10 @@ export declare class EventLog {
|
|
|
131
131
|
/** 按幂等键取事件;没有 → undefined。 */
|
|
132
132
|
findByIdem(conversationId: string, idemKey: string): Promise<ConversationEvent | undefined>;
|
|
133
133
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
134
|
+
* The conversation's latest event of `type` (by seq); undefined without one. `skills_catalog`
|
|
135
|
+
* (0025) and `environment_paths` (0094) have partial indexes that answer it directly; other
|
|
136
|
+
* types scan the conversation's events backwards on the (conversation_id, seq) key, the whole
|
|
137
|
+
* conversation when it has none.
|
|
137
138
|
*/
|
|
138
139
|
latestOfType<T extends ConversationEventType>(conversationId: string, type: T): Promise<Extract<ConversationEvent, {
|
|
139
140
|
type: T;
|
|
@@ -245,9 +245,10 @@ let EventLog = class EventLog {
|
|
|
245
245
|
return r && toEvent(r);
|
|
246
246
|
}
|
|
247
247
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
248
|
+
* The conversation's latest event of `type` (by seq); undefined without one. `skills_catalog`
|
|
249
|
+
* (0025) and `environment_paths` (0094) have partial indexes that answer it directly; other
|
|
250
|
+
* types scan the conversation's events backwards on the (conversation_id, seq) key, the whole
|
|
251
|
+
* conversation when it has none.
|
|
251
252
|
*/
|
|
252
253
|
async latestOfType(conversationId, type) {
|
|
253
254
|
const r = await this.db
|
|
@@ -515,6 +515,15 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
515
515
|
added: z.ZodArray<z.ZodString>;
|
|
516
516
|
removed: z.ZodArray<z.ZodString>;
|
|
517
517
|
}, z.core.$strip>;
|
|
518
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
519
|
+
type: z.ZodLiteral<"environment_paths">;
|
|
520
|
+
payload: z.ZodObject<{
|
|
521
|
+
roots: z.ZodObject<{
|
|
522
|
+
workspace: z.ZodString;
|
|
523
|
+
skills: z.ZodString;
|
|
524
|
+
tmp: z.ZodString;
|
|
525
|
+
}, z.core.$strip>;
|
|
526
|
+
}, z.core.$strip>;
|
|
518
527
|
}, z.core.$strip>, z.ZodObject<{
|
|
519
528
|
type: z.ZodLiteral<"tool_call_started">;
|
|
520
529
|
payload: z.ZodObject<{
|
|
@@ -442,6 +442,20 @@ export const ConversationEventSchema = z.discriminatedUnion("type", [
|
|
|
442
442
|
* position as a system message (`toolsChangedText`); dropped once a summary covers it.
|
|
443
443
|
*/
|
|
444
444
|
ev("tools_changed", z.object({ step: count, added: z.array(z.string()), removed: z.array(z.string()) })),
|
|
445
|
+
/**
|
|
446
|
+
* The directories of the instance a sandbox tool call entered, when they differ from the
|
|
447
|
+
* defaults (environments spec §13.1: a connected machine's real paths). Written once per
|
|
448
|
+
* conversation and roots, before that call's result. **Model-visible**: rendered at its position
|
|
449
|
+
* as a system message (`environmentPathsText`); a summary covering it is followed by the latest
|
|
450
|
+
* one again.
|
|
451
|
+
*/
|
|
452
|
+
ev("environment_paths", z.object({
|
|
453
|
+
roots: z.object({
|
|
454
|
+
workspace: z.string().min(1).max(4096),
|
|
455
|
+
skills: z.string().min(1).max(4096),
|
|
456
|
+
tmp: z.string().min(1).max(4096),
|
|
457
|
+
}),
|
|
458
|
+
})),
|
|
445
459
|
/** 工具调用开始执行之前写入(续跑时「有开始无结果」的调用记为结果未知,不再执行)。 */
|
|
446
460
|
ev("tool_call_started", z.object({ step: count, callId: z.string(), toolKey: z.string(), specHash: z.string() })),
|
|
447
461
|
/**
|
|
@@ -800,7 +800,8 @@ export interface EnvironmentsTable {
|
|
|
800
800
|
owner_account_id: string;
|
|
801
801
|
name: ColumnType<string | null, string | null | undefined, string | null>;
|
|
802
802
|
is_default: ColumnType<boolean, boolean | undefined, boolean>;
|
|
803
|
-
|
|
803
|
+
/** 0093: `device` is a connected machine (environment_machines). */
|
|
804
|
+
source: "cloud" | "device";
|
|
804
805
|
provider: ColumnType<string | null, string | null | undefined, string | null>;
|
|
805
806
|
created_at: Ts;
|
|
806
807
|
updated_at: Ts;
|
|
@@ -830,6 +831,29 @@ export interface EnvironmentInstancesTable {
|
|
|
830
831
|
prev_handle: InstanceHandle;
|
|
831
832
|
sweep_failed_at: TsNullable;
|
|
832
833
|
}
|
|
834
|
+
/**
|
|
835
|
+
* 0093_connected_environments.sql: the machine connected to a `device` environment. At most one
|
|
836
|
+
* unrevoked row per environment; revoking sets `revoked_at` and keeps the row.
|
|
837
|
+
*/
|
|
838
|
+
export interface EnvironmentMachinesTable {
|
|
839
|
+
id: Generated<string>;
|
|
840
|
+
org_id: string;
|
|
841
|
+
environment_id: string;
|
|
842
|
+
account_id: string;
|
|
843
|
+
/** A hash of the machine credential, never the credential itself. */
|
|
844
|
+
credential_hash: string;
|
|
845
|
+
name: string;
|
|
846
|
+
os: string;
|
|
847
|
+
arch: string;
|
|
848
|
+
program_version: ColumnType<string | null, string | null | undefined, string | null>;
|
|
849
|
+
mode: ColumnType<"os" | "docker" | null, "os" | "docker" | null | undefined, "os" | "docker" | null>;
|
|
850
|
+
/** The roots the program exposes, as it reported them (a JSON array). */
|
|
851
|
+
roots: ColumnType<unknown, string | null | undefined, string | null>;
|
|
852
|
+
session_isolation: ColumnType<boolean, boolean | undefined, boolean>;
|
|
853
|
+
last_seen_at: TsNullable;
|
|
854
|
+
created_at: Ts;
|
|
855
|
+
revoked_at: TsNullable;
|
|
856
|
+
}
|
|
833
857
|
/** 0088_environments.sql: the environment a member's new conversations use by default. */
|
|
834
858
|
export interface EnvironmentDefaultsTable {
|
|
835
859
|
org_id: string;
|
|
@@ -1103,6 +1127,7 @@ export interface KernelDatabase {
|
|
|
1103
1127
|
environments: EnvironmentsTable;
|
|
1104
1128
|
environment_instances: EnvironmentInstancesTable;
|
|
1105
1129
|
environment_defaults: EnvironmentDefaultsTable;
|
|
1130
|
+
environment_machines: EnvironmentMachinesTable;
|
|
1106
1131
|
background_tasks: BackgroundTasksTable;
|
|
1107
1132
|
skills: SkillsTable;
|
|
1108
1133
|
skill_versions: SkillVersionsTable;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { type Principal } from "../identity/principal.js";
|
|
4
|
+
import { CredentialThrottler } from "../infra/throttler/credential-throttler.js";
|
|
5
|
+
import { type DeviceAuthorization, DeviceAuthService, type DeviceGrant, type DeviceMachineInfo } from "./device-auth.js";
|
|
6
|
+
import { type DeviceAuthorizeBody, type DeviceConfirmBody, type DeviceTokenBody, type DeviceUserCodeBody, type DeviceUserCodeParams } from "./dto.js";
|
|
7
|
+
/**
|
|
8
|
+
* Device sign-in (`/api/v1/device`, environments spec §13.3; the shape of RFC 8628).
|
|
9
|
+
*
|
|
10
|
+
* The program's routes are public (it has no credential yet); like every public unsafe route
|
|
11
|
+
* they need `X-Capybara-Csrf: 1`, which `capybara-device` sends:
|
|
12
|
+
* - `POST /authorize { name, os, arch, programVersion }` → `{ deviceCode, userCode,
|
|
13
|
+
* verificationUri, verificationUriComplete, interval, expiresIn }` (`device-authorize-ip`);
|
|
14
|
+
* - `POST /token { deviceCode }` → `{ credential, orgId, environmentId, connectUrl,
|
|
15
|
+
* reconnectGraceSeconds, maxFrameBytes }` once, else `device.authorization_pending` / `device.slow_down` /
|
|
16
|
+
* `device.denied` / `device.expired` (`device-token-ip`).
|
|
17
|
+
*
|
|
18
|
+
* The person's routes need a web session (the confirmation page `/device`); each try of a user
|
|
19
|
+
* code counts on `device-code-account` (refunded on success):
|
|
20
|
+
* - `GET /pending/:userCode` → `{ machine, expiresAt }`;
|
|
21
|
+
* - `POST /confirm { userCode, orgId, environmentId | name }` → `{ environmentId }`
|
|
22
|
+
* (`device.expired` / `device.unavailable` when the credential could not be handed over: the
|
|
23
|
+
* machine is revoked again and the person runs login again);
|
|
24
|
+
* - `POST /deny { userCode }` → 204.
|
|
25
|
+
*/
|
|
26
|
+
export declare class DeviceAuthController {
|
|
27
|
+
private readonly auth;
|
|
28
|
+
private readonly throttler;
|
|
29
|
+
constructor(auth: DeviceAuthService, throttler: CredentialThrottler);
|
|
30
|
+
authorize(body: z.infer<typeof DeviceAuthorizeBody>): Promise<DeviceAuthorization>;
|
|
31
|
+
token(body: z.infer<typeof DeviceTokenBody>): Promise<DeviceGrant>;
|
|
32
|
+
pending(principal: Principal, params: z.infer<typeof DeviceUserCodeParams>, req: FastifyRequest, reply: FastifyReply): Promise<{
|
|
33
|
+
machine: DeviceMachineInfo;
|
|
34
|
+
expiresAt: string;
|
|
35
|
+
}>;
|
|
36
|
+
confirm(principal: Principal, body: z.infer<typeof DeviceConfirmBody>, req: FastifyRequest, reply: FastifyReply): Promise<{
|
|
37
|
+
environmentId: string;
|
|
38
|
+
}>;
|
|
39
|
+
deny(principal: Principal, body: z.infer<typeof DeviceUserCodeBody>, req: FastifyRequest, reply: FastifyReply): Promise<void>;
|
|
40
|
+
/** Runs `fn` as one counted try of a user code; a success gives the try back. */
|
|
41
|
+
private counted;
|
|
42
|
+
}
|