@opencoredev/sandbox-sdk 0.2.0 → 1.0.0-beta.0
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/CHANGELOG.md +32 -11
- package/README.md +350 -25
- package/dist/ai.d.ts +5 -0
- package/dist/ai.js +1 -0
- package/dist/chunk-59t3rqja.js +2 -0
- package/dist/chunk-cwx6cc31.js +2 -0
- package/dist/chunk-exv1kf89.js +60 -0
- package/dist/chunk-gc8zdxyb.js +2 -0
- package/dist/chunk-hjzhc3qg.js +40 -0
- package/dist/chunk-md6fmprc.js +1 -0
- package/dist/chunk-t5vr9nd8.js +8 -0
- package/dist/chunk-v6y7eqxw.js +27 -0
- package/dist/chunk-yed8mvs7.js +2 -0
- package/dist/chunk-yrsb6hws.js +5 -0
- package/dist/chunk-z9rjzn28.js +2 -0
- package/dist/errors.d.ts +60 -0
- package/dist/files.d.ts +57 -0
- package/dist/git.d.ts +28 -0
- package/dist/handle.d.ts +4 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1 -0
- package/dist/keys.d.ts +5 -0
- package/dist/logs.d.ts +8 -0
- package/dist/mastra.d.ts +43 -0
- package/dist/mastra.js +1 -0
- package/dist/memory.d.ts +34 -0
- package/dist/memory.js +11 -0
- package/dist/openai.d.ts +39 -0
- package/dist/openai.js +1 -0
- package/dist/peer.d.ts +5 -0
- package/dist/processes.d.ts +71 -0
- package/dist/provider.d.ts +138 -0
- package/dist/provider.js +0 -0
- package/dist/providers/agentos/errors.d.ts +4 -0
- package/dist/providers/agentos/index.d.ts +11 -0
- package/dist/providers/agentos/index.js +2 -0
- package/dist/providers/boat/index.d.ts +23 -0
- package/dist/providers/boat/index.js +1 -0
- package/dist/providers/boat-api/errors.d.ts +6 -0
- package/dist/providers/boat-api/exec.d.ts +11 -0
- package/dist/providers/boat-api/index.d.ts +22 -0
- package/dist/providers/boxd/errors.d.ts +3 -0
- package/dist/providers/boxd/execute.d.ts +3 -0
- package/dist/providers/boxd/guest.d.ts +14 -0
- package/dist/providers/boxd/index.d.ts +23 -0
- package/dist/providers/boxd/index.js +85 -0
- package/dist/providers/boxd/transfers.d.ts +19 -0
- package/dist/providers/daytona/buffered.d.ts +3 -0
- package/dist/providers/daytona/errors.d.ts +3 -0
- package/dist/providers/daytona/exec.d.ts +14 -0
- package/dist/providers/daytona/index.d.ts +24 -0
- package/dist/providers/daytona/index.js +88 -0
- package/dist/providers/e2b/errors.d.ts +3 -0
- package/dist/providers/e2b/index.d.ts +15 -0
- package/dist/providers/e2b/index.js +1 -0
- package/dist/providers/freestyle/deadline.d.ts +4 -0
- package/dist/providers/freestyle/errors.d.ts +16 -0
- package/dist/providers/freestyle/execute.d.ts +12 -0
- package/dist/providers/freestyle/index.d.ts +14 -0
- package/dist/providers/freestyle/index.js +133 -0
- package/dist/providers/freestyle/wire.d.ts +10 -0
- package/dist/providers/local/docker.d.ts +32 -0
- package/dist/providers/local/index.d.ts +17 -0
- package/dist/providers/local/index.js +2 -0
- package/dist/providers/railway/errors.d.ts +4 -0
- package/dist/providers/railway/index.d.ts +24 -0
- package/dist/providers/railway/index.js +1 -0
- package/dist/providers/sandboxer/index.d.ts +33 -0
- package/dist/providers/sandboxer/index.js +1 -0
- package/dist/providers/superserve/bootstrap.d.ts +11 -0
- package/dist/providers/superserve/errors.d.ts +3 -0
- package/dist/providers/superserve/execute.d.ts +13 -0
- package/dist/providers/superserve/index.d.ts +23 -0
- package/dist/providers/superserve/index.js +126 -0
- package/dist/providers/tenki/deadline.d.ts +2 -0
- package/dist/providers/tenki/errors.d.ts +10 -0
- package/dist/providers/tenki/exec.d.ts +4 -0
- package/dist/providers/tenki/files.d.ts +3 -0
- package/dist/providers/tenki/index.d.ts +28 -0
- package/dist/providers/tenki/index.js +172 -0
- package/dist/providers/tenki/ready.d.ts +7 -0
- package/dist/providers/tenki/stream.d.ts +3 -0
- package/dist/providers/upstash/buffered.d.ts +3 -0
- package/dist/providers/upstash/errors.d.ts +3 -0
- package/dist/providers/upstash/exec.d.ts +18 -0
- package/dist/providers/upstash/files.d.ts +15 -0
- package/dist/providers/upstash/index.d.ts +38 -0
- package/dist/providers/upstash/index.js +111 -0
- package/dist/providers/vercel/errors.d.ts +3 -0
- package/dist/providers/vercel/index.d.ts +14 -0
- package/dist/providers/vercel/index.js +1 -0
- package/dist/providers/vercel/provider.d.ts +10 -0
- package/dist/providers/vercel/transport.d.ts +40 -0
- package/dist/run.d.ts +41 -0
- package/dist/testing.d.ts +17 -0
- package/dist/testing.js +6 -0
- package/dist/tool-security.d.ts +9 -0
- package/dist/tools.d.ts +117 -0
- package/dist/tools.js +1 -0
- package/dist/types.d.ts +189 -0
- package/package.json +164 -106
- package/dist/agentos-CbrZW7R0.mjs +0 -390
- package/dist/agentos-CbrZW7R0.mjs.map +0 -1
- package/dist/ai/harness.d.mts +0 -16
- package/dist/ai/harness.mjs +0 -69
- package/dist/ai/harness.mjs.map +0 -1
- package/dist/ai/index.d.mts +0 -54
- package/dist/ai/index.mjs +0 -4
- package/dist/ai-DeFa8yPk.mjs +0 -243
- package/dist/ai-DeFa8yPk.mjs.map +0 -1
- package/dist/capabilities-BWvOVFbH.d.mts +0 -15
- package/dist/capabilities-CaSNd6J1.mjs +0 -125
- package/dist/capabilities-CaSNd6J1.mjs.map +0 -1
- package/dist/capabilities-xs9kaHrK.mjs +0 -28
- package/dist/capabilities-xs9kaHrK.mjs.map +0 -1
- package/dist/errors-BuF9bYOQ.mjs +0 -68
- package/dist/errors-BuF9bYOQ.mjs.map +0 -1
- package/dist/eve/index.d.mts +0 -19
- package/dist/eve/index.mjs +0 -155
- package/dist/eve/index.mjs.map +0 -1
- package/dist/index-CkTwqD0X.d.mts +0 -25
- package/dist/index-Dh9xob2s.d.mts +0 -31
- package/dist/index.d.mts +0 -5
- package/dist/index.mjs +0 -40
- package/dist/index.mjs.map +0 -1
- package/dist/managed-provider-BsZGVQ4I.mjs +0 -148
- package/dist/managed-provider-BsZGVQ4I.mjs.map +0 -1
- package/dist/mastra/index.d.mts +0 -118
- package/dist/mastra/index.mjs +0 -662
- package/dist/mastra/index.mjs.map +0 -1
- package/dist/metadata.d.mts +0 -34
- package/dist/metadata.mjs +0 -135
- package/dist/metadata.mjs.map +0 -1
- package/dist/provider-DOBErkBO.d.mts +0 -91
- package/dist/provider-utils-CfB9nvJx.mjs +0 -57
- package/dist/provider-utils-CfB9nvJx.mjs.map +0 -1
- package/dist/providers/agentos/index.d.mts +0 -7
- package/dist/providers/agentos/index.mjs +0 -9
- package/dist/providers/box/index.d.mts +0 -34
- package/dist/providers/box/index.mjs +0 -348
- package/dist/providers/box/index.mjs.map +0 -1
- package/dist/providers/daytona/index.d.mts +0 -21
- package/dist/providers/daytona/index.mjs +0 -206
- package/dist/providers/daytona/index.mjs.map +0 -1
- package/dist/providers/e2b/index.d.mts +0 -17
- package/dist/providers/e2b/index.mjs +0 -169
- package/dist/providers/e2b/index.mjs.map +0 -1
- package/dist/providers/local/index.d.mts +0 -22
- package/dist/providers/local/index.mjs +0 -22
- package/dist/providers/local/index.mjs.map +0 -1
- package/dist/providers/railway/index.d.mts +0 -29
- package/dist/providers/railway/index.mjs +0 -211
- package/dist/providers/railway/index.mjs.map +0 -1
- package/dist/providers/upstash/index.d.mts +0 -17
- package/dist/providers/upstash/index.mjs +0 -211
- package/dist/providers/upstash/index.mjs.map +0 -1
- package/dist/providers/vercel/index.d.mts +0 -28
- package/dist/providers/vercel/index.mjs +0 -186
- package/dist/providers/vercel/index.mjs.map +0 -1
- package/dist/sandbox-DdEqoXJF.d.mts +0 -26
- package/dist/sandbox-DgtY5hvt.mjs +0 -112
- package/dist/sandbox-DgtY5hvt.mjs.map +0 -1
- package/dist/testing/index.d.mts +0 -27
- package/dist/testing/index.mjs +0 -173
- package/dist/testing/index.mjs.map +0 -1
- package/dist/types-Bv6I2U1c.d.mts +0 -103
package/dist/peer.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Loads an optional peer SDK on first use. A missing install becomes an
|
|
2
|
+
* unavailable error naming the package, instead of the adapter's generic
|
|
3
|
+
* provider failure. Callers pass a literal import() so bundlers keep it external.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadPeer<T>(name: string, provider: string, load: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Provider, Preview } from "./provider.js";
|
|
2
|
+
import type { Files } from "./files.js";
|
|
3
|
+
import { type Redactor } from "./errors.js";
|
|
4
|
+
/** A stable process name for a command: its program name plus a short FNV-1a hash of the full command. */
|
|
5
|
+
export declare function defaultProcessName(command: string): string;
|
|
6
|
+
export type Readiness = {
|
|
7
|
+
port: number;
|
|
8
|
+
} | {
|
|
9
|
+
http: {
|
|
10
|
+
port: number;
|
|
11
|
+
path?: string;
|
|
12
|
+
};
|
|
13
|
+
} | {
|
|
14
|
+
log: RegExp;
|
|
15
|
+
};
|
|
16
|
+
/** `name` defaults to a stable name derived from the command, so the same command always maps to the same process. */
|
|
17
|
+
export interface StartOptions {
|
|
18
|
+
name?: string;
|
|
19
|
+
cwd?: string;
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
ready?: Readiness;
|
|
22
|
+
readyTimeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ProcessInfo {
|
|
25
|
+
name: string;
|
|
26
|
+
command: string;
|
|
27
|
+
status: "starting" | "running" | "exited" | "failed";
|
|
28
|
+
pid: number;
|
|
29
|
+
ports: number[];
|
|
30
|
+
startedAt: number;
|
|
31
|
+
exitCode: number | null;
|
|
32
|
+
}
|
|
33
|
+
export interface ProcessHandle {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly url?: string;
|
|
36
|
+
readonly ready: boolean;
|
|
37
|
+
readonly status: ProcessInfo["status"];
|
|
38
|
+
logs(options?: {
|
|
39
|
+
after?: string;
|
|
40
|
+
maxBytes?: number;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
text: string;
|
|
43
|
+
nextCursor: string;
|
|
44
|
+
truncated: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
lines(options?: {
|
|
47
|
+
after?: string;
|
|
48
|
+
}): AsyncIterable<{
|
|
49
|
+
text: string;
|
|
50
|
+
stream: "stdout" | "stderr";
|
|
51
|
+
cursor: string;
|
|
52
|
+
}>;
|
|
53
|
+
stop(options?: {
|
|
54
|
+
graceMs?: number;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
restart(): Promise<void>;
|
|
57
|
+
wait(): Promise<{
|
|
58
|
+
exitCode: number | null;
|
|
59
|
+
signal: string | null;
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
export declare const delay: (ms: number) => Promise<void>;
|
|
63
|
+
export declare function makeProcesses(provider: Provider, id: string, files: Files, redactor: Redactor, env: Record<string, string>): {
|
|
64
|
+
start: (command: string, input?: StartOptions) => Promise<ProcessHandle>;
|
|
65
|
+
process: (name: string) => ProcessHandle;
|
|
66
|
+
preview: (port: number, options?: {
|
|
67
|
+
path?: string;
|
|
68
|
+
waitMs?: number;
|
|
69
|
+
}) => Promise<Preview>;
|
|
70
|
+
processes: () => Promise<ProcessInfo[]>;
|
|
71
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** Primitive boundary for adapters. Paths are absolute POSIX paths; times are epoch ms.
|
|
2
|
+
* Adapters translate native failures to SandboxError. Exec must close stdin, emit
|
|
3
|
+
* chunks in arrival order, and kill the ENTIRE remote process group before settling
|
|
4
|
+
* on timeout/abort. Optional methods must agree with capabilities. No core policy
|
|
5
|
+
* (redaction, edit, git, process records, readiness) belongs in an adapter.
|
|
6
|
+
*/
|
|
7
|
+
export type Capability = "pause" | "snapshot" | "terminal" | "network-policy" | "size";
|
|
8
|
+
export type Labels = Record<string, string>;
|
|
9
|
+
export interface SandboxInfo {
|
|
10
|
+
id: string;
|
|
11
|
+
key?: string;
|
|
12
|
+
labels: Labels;
|
|
13
|
+
state: "running" | "paused" | "stopped";
|
|
14
|
+
createdAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CreateInput {
|
|
17
|
+
key?: string;
|
|
18
|
+
size?: "small" | "medium" | "large" | {
|
|
19
|
+
cpu: number;
|
|
20
|
+
memoryMb: number;
|
|
21
|
+
diskGb?: number;
|
|
22
|
+
};
|
|
23
|
+
env?: Record<string, string>;
|
|
24
|
+
network?: "all" | "none" | {
|
|
25
|
+
allow: string[];
|
|
26
|
+
};
|
|
27
|
+
ports?: number[];
|
|
28
|
+
idleTimeoutMs?: number;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
labels: Labels;
|
|
31
|
+
from?: string;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export interface OutputChunk {
|
|
35
|
+
stream: "stdout" | "stderr";
|
|
36
|
+
text: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ExecInput {
|
|
39
|
+
command: string;
|
|
40
|
+
cwd: string;
|
|
41
|
+
env: Record<string, string>;
|
|
42
|
+
/** Stable handle environment; adapters may reuse private guest env files. */
|
|
43
|
+
envMode?: "sandbox";
|
|
44
|
+
stdin?: string;
|
|
45
|
+
timeoutMs: number;
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
/** Core-owned activity record, maintained atomically by opted-in exec transports. */
|
|
48
|
+
activityPath?: string;
|
|
49
|
+
/** false permits buffered execution; true requests live chunks. Omission preserves adapter defaults. */
|
|
50
|
+
streaming?: boolean;
|
|
51
|
+
onChunk(chunk: OutputChunk): void;
|
|
52
|
+
}
|
|
53
|
+
export interface ExecResult {
|
|
54
|
+
exitCode: number | null;
|
|
55
|
+
timedOut: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface FileInfo {
|
|
58
|
+
path: string;
|
|
59
|
+
type: "file" | "directory" | "symlink";
|
|
60
|
+
size: number;
|
|
61
|
+
modifiedAt: number;
|
|
62
|
+
}
|
|
63
|
+
export interface ProviderFiles {
|
|
64
|
+
/** Use primitive file transfers when guest tar is incompatible with GNU archives. */
|
|
65
|
+
readonly transfer?: "files";
|
|
66
|
+
/** Write handles missing parents itself, avoiding an unconditional mkdir RPC. */
|
|
67
|
+
readonly writeCreatesParents?: boolean;
|
|
68
|
+
/** Top-level /workspace entries the provider itself owns. File checkpoints and changes() skip them. */
|
|
69
|
+
readonly internal?: readonly string[];
|
|
70
|
+
readRange?(id: string, path: string, options: {
|
|
71
|
+
offset: number;
|
|
72
|
+
length: number;
|
|
73
|
+
}): Promise<Uint8Array>;
|
|
74
|
+
read(id: string, path: string): Promise<Uint8Array>;
|
|
75
|
+
write(id: string, path: string, data: Uint8Array): Promise<void>;
|
|
76
|
+
list(id: string, path: string): Promise<FileInfo[]>;
|
|
77
|
+
stat(id: string, path: string): Promise<FileInfo>;
|
|
78
|
+
remove(id: string, path: string, options: {
|
|
79
|
+
recursive: boolean;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
mkdir(id: string, path: string): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
export interface Preview {
|
|
84
|
+
url: string;
|
|
85
|
+
headers?: Record<string, string>;
|
|
86
|
+
expiresAt?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface Terminal {
|
|
89
|
+
write(data: string): void | Promise<void>;
|
|
90
|
+
resize(cols: number, rows: number): void | Promise<void>;
|
|
91
|
+
onData(callback: (data: string) => void): () => void;
|
|
92
|
+
close(): void | Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
/** Native hooks are optional optimizations. Persistent records remain core-owned. */
|
|
95
|
+
export interface NativeProcess {
|
|
96
|
+
pid: number;
|
|
97
|
+
wait(): Promise<{
|
|
98
|
+
exitCode: number | null;
|
|
99
|
+
signal: string | null;
|
|
100
|
+
}>;
|
|
101
|
+
stop(options: {
|
|
102
|
+
graceMs: number;
|
|
103
|
+
}): Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
export interface Provider<Native = unknown, C extends Capability = Capability> {
|
|
106
|
+
readonly name: string;
|
|
107
|
+
readonly keyMode: "exact" | "best-effort";
|
|
108
|
+
/** Omission uses native hooks or the portable shell process-record shim. */
|
|
109
|
+
readonly processMode?: "shell" | "unsupported";
|
|
110
|
+
/** Exec creates/removes input.activityPath without separate file RPCs. */
|
|
111
|
+
readonly inlineActivity?: boolean;
|
|
112
|
+
/** Native command dialect for portable run wrappers; defaults to POSIX sh. */
|
|
113
|
+
readonly commandShell?: "bash";
|
|
114
|
+
/** Durable absolute directory for core-owned process records. */
|
|
115
|
+
readonly processRecordRoot?: string;
|
|
116
|
+
readonly capabilities: ReadonlySet<C>;
|
|
117
|
+
create(input: CreateInput): Promise<SandboxInfo>;
|
|
118
|
+
find(key: string): Promise<SandboxInfo | undefined>;
|
|
119
|
+
connect(id: string): Promise<SandboxInfo>;
|
|
120
|
+
list(labels: Labels): Promise<SandboxInfo[]>;
|
|
121
|
+
destroy(id: string): Promise<void>;
|
|
122
|
+
exec(id: string, input: ExecInput): Promise<ExecResult>;
|
|
123
|
+
readonly files: ProviderFiles;
|
|
124
|
+
previewUrl(id: string, port: number): Promise<Preview>;
|
|
125
|
+
native(id: string): Native;
|
|
126
|
+
close?(id: string): Promise<void>;
|
|
127
|
+
usage?(id: string): Promise<Record<string, number>>;
|
|
128
|
+
pause?(id: string): Promise<void>;
|
|
129
|
+
resume?(id: string): Promise<void>;
|
|
130
|
+
snapshot?(id: string): Promise<{
|
|
131
|
+
id: string;
|
|
132
|
+
}>;
|
|
133
|
+
terminal?(id: string, options: {
|
|
134
|
+
cols: number;
|
|
135
|
+
rows: number;
|
|
136
|
+
}): Promise<Terminal>;
|
|
137
|
+
startProcess?(id: string, input: Omit<ExecInput, "timeoutMs">): Promise<NativeProcess>;
|
|
138
|
+
}
|
package/dist/provider.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SandboxError, type ErrorCode } from "../../errors.js";
|
|
2
|
+
export declare function failure(message: string, code?: ErrorCode, sandboxId?: string): SandboxError;
|
|
3
|
+
/** Native errors may include file contents, environment variables, or credentials. */
|
|
4
|
+
export declare function agentosError(cause: unknown, sandboxId?: string, file?: boolean): SandboxError;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentOs } from "@rivet-dev/agentos-core";
|
|
2
|
+
import type { Provider } from "../../provider.js";
|
|
3
|
+
export interface AgentosOptions {
|
|
4
|
+
/** Trusted local .aospkg file; otherwise install Rivet's pinned Git release. */
|
|
5
|
+
gitPackagePath?: string;
|
|
6
|
+
}
|
|
7
|
+
type AgentosCapability = "network-policy";
|
|
8
|
+
export type AgentosProvider = Provider<AgentOs, AgentosCapability>;
|
|
9
|
+
/** Embedded, Docker-free V8/WebAssembly runtime. Docker remains local()'s default. */
|
|
10
|
+
export declare function agentos(options?: AgentosOptions): AgentosProvider;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{e}from"../../chunk-gc8zdxyb.js";import{randomUUID as S}from"node:crypto";import{posix as k}from"node:path";function l(o,i="provider",m){return new e(`${o}. Check the agentOS runtime or use local() with Docker.`,{code:i,provider:"agentos",sandboxId:m})}function x(o,i,m=!1){if(o instanceof e)return o;let d=o instanceof Error?o.message:"",c=/ENOENT|not found|no such file/i.test(d)?m?"file_not_found":"not_found":/limit|quota|ENOSPC/i.test(d)?"quota":/sidecar.*(exit|closed|missing)|cannot find package|module not found/i.test(d)?"unavailable":/EACCES|EPERM|invalid|EINVAL/i.test(d)?"invalid_argument":"provider";return l(`agentOS operation failed (${c})`,c,i)}var w=new Map,h=new Map,A=new Map,P="sha256:35ae142556050de3604342574494373c318adff3ac3b50ea6b7420c0a033d2d8",E=`https://releases.rivet.dev/agent-os/0.4.1/software/packages/git/${P.slice(7)}.aospkg`;function b(o){let i=w.get(o);if(!i)throw l("Sandbox does not exist in this application process","not_found",o);return i}function v(o){return{...o,labels:{...o.labels}}}var C=(o)=>`'${o.replace(/'/g,"'\\''")}'`;function I(o,i){return{path:o,type:i.isSymbolicLink?"symlink":i.isDirectory?"directory":"file",size:i.size,modifiedAt:i.mtimeMs}}async function O(o,i,m){if(!(m??(i!=="/"&&(await o.filesystem.readdirEntries(k.dirname(i))).find((t)=>t.name===k.basename(i))?.isSymbolicLink)))return I(i,await o.filesystem.stat(i));let c=await o.javascript.evaluate(`(() => { const s = require("node:fs").lstatSync(${JSON.stringify(i)}); return { size: s.size, modifiedAt: s.mtimeMs }; })()`);if(c.outcome!=="succeeded"||!c.value||typeof c.value!=="object"||Array.isArray(c.value)||typeof c.value.size!=="number"||typeof c.value.modifiedAt!=="number")throw l("agentOS could not read symbolic link metadata","provider");return{path:i,type:"symlink",size:c.value.size,modifiedAt:c.value.modifiedAt}}function j(o={}){let i,m=()=>i??=import("@rivet-dev/agentos-core").catch(()=>{throw l("Install @rivet-dev/agentos-core@0.4.1 and its platform sidecar package","unavailable")});async function d(t,n,r=!1){try{return await n(b(t).vm)}catch(s){throw x(s,t,r)}}let c={name:"agentos",inlineActivity:!0,keyMode:"exact",processMode:"unsupported",capabilities:new Set(["network-policy"]),async create(t){if(t.size!==void 0||t.from!==void 0)throw l("agentOS cannot enforce machine sizes or restore SDK snapshots","unsupported");if(t.idleTimeoutMs!==void 0||t.timeoutMs!==void 0)throw l("agentOS adapter does not enforce sandbox lifetime or idle deadlines","unsupported");if(t.signal?.aborted)throw l("Creation aborted","aborted");let n=t.network??"all",r={network:n==="none"?"deny":"allow"};if(typeof n==="object"){for(let a of n.allow)if(!/^(?=.{1,253}$)[a-zA-Z0-9](?:[a-zA-Z0-9.-]*[a-zA-Z0-9])?$/.test(a))throw l("Network allow-list entries must be literal hostnames","unsupported");r={network:{default:"deny",rules:n.allow.map((a)=>({mode:"allow",operations:["*"],patterns:[`dns://${a}`,`tcp://${a}:*`]}))}}}async function s(){let a;try{let{AgentOs:u}=await m();if(a=await u.create({permissions:r,...Object.keys(t.env??{}).length?{environment:{PATH:"/opt/agentos/bin:/usr/local/bin:/usr/bin:/bin",HOME:"/home/agentos",...t.env}}:{}}),await a.software.install(o.gitPackagePath?{type:"path",path:o.gitPackagePath}:{type:"url",url:E,expectedDigest:P}),await a.filesystem.mkdir("/workspace",{recursive:!0}),t.signal?.aborted)throw l("Creation aborted","aborted");let f={id:`agentos-${S()}`,key:t.key,labels:{...t.labels,"sandbox-sdk":"1"},state:"running",createdAt:Date.now()};return w.set(f.id,{vm:a,info:f}),v(f)}catch(u){if(a)await a.dispose().catch(()=>{});throw x(u)}}if(!t.key)return s();if(await c.find(t.key))throw l("Key already exists in this application process","invalid_argument");let g=h.get(t.key);if(g)throw await g,l("Key was created concurrently in this application process","invalid_argument");let y=s();h.set(t.key,y);try{return await y}finally{h.delete(t.key)}},async find(t){for(let n of w.values())if(n.info.key===t)return v(n.info);return},async connect(t){return v(b(t).info)},async list(t){return[...w.values()].filter((n)=>Object.entries(t).every(([r,s])=>n.info.labels[r]===s)).map((n)=>v(n.info))},async destroy(t){if(!w.get(t))return;let r=A.get(t);if(r)return r;let s=d(t,(p)=>p.dispose()).then(()=>{w.delete(t)});A.set(t,s);try{await s}finally{A.delete(t)}},async exec(t,n){if(n.signal?.aborted)return{exitCode:null,timedOut:!0};return d(t,async(r)=>{let s=new TextDecoder,p=new TextDecoder,g=`/tmp/.sandbox-sdk/stdin-${S()}`;if(n.stdin!==void 0)await r.filesystem.mkdir("/tmp/.sandbox-sdk",{recursive:!0}),await r.filesystem.writeFile(g,n.stdin);try{if(n.signal?.aborted)return{exitCode:null,timedOut:!0};let y=await r.process.exec(`exec < ${n.stdin===void 0?"/dev/null":C(g)}
|
|
2
|
+
${n.command}`,{cwd:n.cwd,env:n.env,timeoutMs:n.timeoutMs,signal:n.signal,output:{capture:"none"},onStdout(u){let f=s.decode(u,{stream:!0});if(f)n.onChunk({stream:"stdout",text:f})},onStderr(u){let f=p.decode(u,{stream:!0});if(f)n.onChunk({stream:"stderr",text:f})}});for(let[u,f]of[["stdout",s.decode()],["stderr",p.decode()]])if(f)n.onChunk({stream:u,text:f});let a=y.outcome==="timed_out"||y.outcome==="cancelled";if(!a&&y.exitCode===void 0)throw l("agentOS did not report a command exit","outcome_unknown",t);return{exitCode:a?null:y.exitCode??null,timedOut:a}}finally{if(n.stdin!==void 0)await r.filesystem.remove(g).catch(()=>{})}})},files:{writeCreatesParents:!0,transfer:"files",read:(t,n)=>d(t,async(r)=>new Uint8Array(await r.filesystem.readFile(n)),!0),write:(t,n,r)=>d(t,async(s)=>{await s.filesystem.mkdir(k.dirname(n),{recursive:!0}),await s.filesystem.writeFile(n,r)},!0),stat:(t,n)=>d(t,async(r)=>O(r,n),!0),list:(t,n)=>d(t,async(r)=>Promise.all((await r.filesystem.readdirEntries(n)).map((s)=>O(r,k.join(n,s.name),s.isSymbolicLink))),!0),mkdir:(t,n)=>d(t,(r)=>r.filesystem.mkdir(n,{recursive:!0}),!0),remove:(t,n,r)=>d(t,(s)=>s.filesystem.remove(n,r),!0)},async previewUrl(t){throw b(t),l("Embedded agentOS has no preview URLs; forward HTTP with native.network.httpRequest","unsupported",t)},native(t){return b(t).vm}};return c}export{j as agentos};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BoatApi, CreateSandboxRequest, Sandbox as BoatSandbox } from "@boatdev/sdk";
|
|
2
|
+
import type { Provider } from "../../provider.js";
|
|
3
|
+
export interface BoatOptions {
|
|
4
|
+
/** Defaults to BOAT_API_KEY; required on first use. */
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
/** Defaults to BOAT_API_BASE, then https://boat.dev/api/v1. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** @deprecated Use baseUrl. Removed in 1.0.0. */
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
/** Native machine size; default: Boat default (4 CPU/8 GB). Per-create size wins. */
|
|
11
|
+
machine?: CreateSandboxRequest["type"];
|
|
12
|
+
/** @deprecated Use machine. Removed in 1.0.0. */
|
|
13
|
+
type?: CreateSandboxRequest["type"];
|
|
14
|
+
/** Deadline for each control-plane request. Default: 90 seconds. */
|
|
15
|
+
requestTimeoutMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface BoatNative {
|
|
18
|
+
readonly sandbox: BoatSandbox;
|
|
19
|
+
readonly client: BoatApi;
|
|
20
|
+
}
|
|
21
|
+
export type BoatProvider = Provider<BoatNative, "size">;
|
|
22
|
+
/** Direct optional SDK with no installed remote agent or local daemon. */
|
|
23
|
+
export declare function boat(options?: BoatOptions): BoatProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"../../chunk-t5vr9nd8.js";import{x}from"../../chunk-yrsb6hws.js";function o(e={}){return x(e)}export{o as boat};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SandboxError, type ErrorCode } from "../../errors.js";
|
|
2
|
+
/** `detailed` adds the HTTP status and the envelope's fixed error code, never free text. */
|
|
3
|
+
export declare function errors(provider?: string, label?: string, keyEnv?: string, detailed?: boolean): {
|
|
4
|
+
boatError: (cause: unknown, sandboxId?: string) => SandboxError;
|
|
5
|
+
failure: (message: string, code?: ErrorCode, sandboxId?: string) => SandboxError;
|
|
6
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BoatApi } from "@boatdev/sdk";
|
|
2
|
+
import type { ExecInput, ExecResult } from "../../provider.js";
|
|
3
|
+
export declare const workspace = "flock -x /tmp/.sandbox-sdk-workspace-ready.lock sh -c 'if [ ! -f /tmp/.sandbox-sdk/prepared-workspace ]; then sudo mkdir -p /workspace && sudo chown \"$(id -u):$(id -g)\" /workspace && command -v node git tar setsid nohup mkfifo sed base64 >/dev/null && mkdir -p /tmp/.sandbox-sdk && touch /tmp/.sandbox-sdk/prepared-workspace || exit 1; fi' || exit 1";
|
|
4
|
+
export declare const quote: (value: string) => string;
|
|
5
|
+
/** Detached commands have no 600-second limit. Boat's stopCommand kills the
|
|
6
|
+
* command and all descendants. Aborting a stream alone does not stop remote work.
|
|
7
|
+
*/
|
|
8
|
+
export declare function execute(api: BoatApi, id: string, input: ExecInput, sandboxEnv?: Record<string, string>, environmentFiles?: Map<string, Promise<string>>, diagnostics?: {
|
|
9
|
+
boatError: (cause: unknown, sandboxId?: string) => import("../../errors.js").SandboxError;
|
|
10
|
+
failure: (message: string, code?: import("../../errors.js").ErrorCode, sandboxId?: string) => import("../../errors.js").SandboxError;
|
|
11
|
+
}): Promise<ExecResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BoatApi } from "@boatdev/sdk";
|
|
2
|
+
import type { Provider } from "../../provider.js";
|
|
3
|
+
import type { BoatOptions, BoatNative } from "../boat/index.js";
|
|
4
|
+
export interface BoatApiSettings {
|
|
5
|
+
name: string;
|
|
6
|
+
label: string;
|
|
7
|
+
keyEnv: string;
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
baseEnv?: string;
|
|
10
|
+
createFrom?: boolean;
|
|
11
|
+
pausedStates?: readonly string[];
|
|
12
|
+
downloadMaxBytes?: number;
|
|
13
|
+
destroy?: (api: BoatApi, id: string) => Promise<void>;
|
|
14
|
+
fetch?: typeof fetch;
|
|
15
|
+
download?: (api: BoatApi, id: string, path: string) => Promise<Uint8Array>;
|
|
16
|
+
preview?: (url: string) => Promise<{
|
|
17
|
+
url: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
/** Direct optional SDK with no installed remote agent or local daemon. */
|
|
22
|
+
export declare function boatApi(options?: BoatOptions, settings?: BoatApiSettings): Provider<BoatNative, "size">;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProviderFiles } from "../../provider.js";
|
|
2
|
+
export declare const quote: (s: string) => string;
|
|
3
|
+
export declare const python: (code: string, ...args: string[]) => string;
|
|
4
|
+
export declare const guestProcess = "def stdin_file():\n try: return os.fdopen(os.memfd_create('sandbox-sdk-stdin'),'w+b')\n except (AttributeError,OSError):\n import tempfile\n return tempfile.TemporaryFile()\nclass Process:\n def __init__(self,v,env,stdin,session=False):\n if not all(hasattr(os,name) for name in ['posix_spawn','waitstatus_to_exitcode']):\n self.legacy(v,env,stdin,session); return\n directory=os.open('.',os.O_RDONLY)\n r,w=os.pipe(); e,x=os.pipe()\n try:\n os.chdir(v['cwd'])\n error=FileNotFoundError(2,os.strerror(2),'sh')\n signals=[getattr(signal,s) for s in ['SIGPIPE','SIGXFZ','SIGXFSZ'] if hasattr(signal,s)]\n for path in os.get_exec_path(env):\n try:\n self.pid=os.posix_spawn(os.path.join(path,'sh'),['sh','-c',v['command']],env,file_actions=[(os.POSIX_SPAWN_DUP2,stdin.fileno(),0),(os.POSIX_SPAWN_DUP2,w,1),(os.POSIX_SPAWN_DUP2,x,2)],setsid=session,setsigdef=signals)\n break\n except (FileNotFoundError,NotADirectoryError): pass\n except OSError as failure:\n if failure.errno==38: raise NotImplementedError()\n error=failure\n else: raise error\n except NotImplementedError:\n os.close(r); os.close(e)\n self.legacy(v,env,stdin,session); return\n except BaseException:\n os.close(r); os.close(e); raise\n finally:\n os.fchdir(directory); os.close(directory); os.close(w); os.close(x)\n self.stdout=os.fdopen(r,'rb',buffering=0); self.stderr=os.fdopen(e,'rb',buffering=0)\n self.code=None\n def legacy(self,v,env,stdin,session):\n import subprocess\n self.child=subprocess.Popen(['sh','-c',v['command']],cwd=v['cwd'],env=env,stdin=stdin,stdout=subprocess.PIPE,stderr=subprocess.PIPE,start_new_session=session)\n self.pid=self.child.pid; self.stdout=self.child.stdout; self.stderr=self.child.stderr\n def poll(self):\n if hasattr(self,'child'): return self.child.poll()\n if self.code is None:\n pid,status=os.waitpid(self.pid,os.WNOHANG)\n if pid: self.code=os.waitstatus_to_exitcode(status)\n return self.code\n def wait(self):\n if hasattr(self,'child'): return self.child.wait()\n if self.code is None: self.code=os.waitstatus_to_exitcode(os.waitpid(self.pid,0)[1])\n return self.code\n";
|
|
5
|
+
export interface Guest {
|
|
6
|
+
name: string;
|
|
7
|
+
raw(command: string): Promise<string>;
|
|
8
|
+
upload(path: string, bytes: Uint8Array): Promise<void>;
|
|
9
|
+
read(path: string, range?: {
|
|
10
|
+
offset: number;
|
|
11
|
+
length: number;
|
|
12
|
+
}): Promise<Uint8Array>;
|
|
13
|
+
}
|
|
14
|
+
export declare function files(get: (id: string) => Promise<Guest>, safe: <T>(id: string, action: () => Promise<T>) => Promise<T>): ProviderFiles;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Verified against @boxd-sh/sdk 0.2.17 and https://docs.boxd.sh/reference/typescript-sdk. */
|
|
2
|
+
import type { Boxd, Machine } from "@boxd-sh/sdk";
|
|
3
|
+
import type { Provider } from "../../provider.js";
|
|
4
|
+
export interface BoxdOptions {
|
|
5
|
+
/** Defaults to BOXD_API_KEY; required on first use. */
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
/** Defaults to BOXD_BASE_URL, then BOXD_API_URL, then https://boxd.sh:9443. */
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
/** @deprecated Use baseUrl. Removed in 1.0.0. */
|
|
10
|
+
baseURL?: string;
|
|
11
|
+
/** Container image. Defaults to the boxd service image. */
|
|
12
|
+
image?: string;
|
|
13
|
+
/** Default: 30 s for control calls (including create); readiness: 90 s. */
|
|
14
|
+
requestTimeoutMs?: number;
|
|
15
|
+
/** Cancels file transfers; default: no adapter-wide cancellation. */
|
|
16
|
+
fileTransferSignal?: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
export interface BoxdNative {
|
|
19
|
+
machine: Machine;
|
|
20
|
+
client: Boxd;
|
|
21
|
+
}
|
|
22
|
+
export type BoxdProvider = Provider<BoxdNative, "pause" | "size" | "snapshot">;
|
|
23
|
+
export declare function boxd(options?: BoxdOptions): BoxdProvider;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import{a,n,d,h}from"../../chunk-exv1kf89.js";import{o}from"../../chunk-yed8mvs7.js";import{e}from"../../chunk-gc8zdxyb.js";function u(r,c="provider",s){return new e(c==="auth"?r:`${r}. Check boxd configuration and retry.`,{code:c,provider:"boxd",sandboxId:s})}function T(r,c){if(r instanceof e)return r;let s=typeof r==="object"&&r!==null&&"grpcCode"in r?r.grpcCode:void 0,p={1:"aborted",3:"invalid_argument",4:"timeout",5:"not_found",6:"invalid_argument",9:"invalid_argument",7:"auth",8:"quota",12:"unsupported",14:"unavailable",16:"auth"},g={AuthenticationError:"auth",PermissionDeniedError:"auth",NotFoundError:"not_found",RateLimitError:"quota",APIConnectionError:"unavailable"};if(r instanceof Error&&r.name==="BoxdError"&&/^exec timed out after \d+ms$/.test(r.message))return u("boxd request timed out","timeout",c);let m=r instanceof Error&&/permission denied/i.test(r.message);return u(m?"boxd guest filesystem permission denied":"boxd request failed",typeof s==="number"?p[s]??"provider":r instanceof Error?g[r.name]??"provider":"provider",c)}var U=`import sys,json,os,fcntl,selectors,signal
|
|
2
|
+
${d}
|
|
3
|
+
# Keep the output owner alive through native TERM grace. Caught handlers reset
|
|
4
|
+
# to the default disposition when the user shell execs.
|
|
5
|
+
signal.signal(signal.SIGTERM,lambda *_:None)
|
|
6
|
+
v=json.load(sys.stdin)
|
|
7
|
+
if v.get('preparing'): print('SDK_PREPARED:',end='',flush=True)
|
|
8
|
+
a=v.get('activityPath')
|
|
9
|
+
m=v['pidPath']
|
|
10
|
+
try:
|
|
11
|
+
with stdin_file() as f:
|
|
12
|
+
f.write(v.get('stdin','').encode()); f.seek(0)
|
|
13
|
+
with open(m+'.lock','a') as lock:
|
|
14
|
+
fcntl.flock(lock,fcntl.LOCK_EX)
|
|
15
|
+
if os.path.exists(m+'.cancel'): sys.exit(137)
|
|
16
|
+
with open(m,'w') as pid: pid.write(str(os.getpgrp()))
|
|
17
|
+
if a:
|
|
18
|
+
os.makedirs(os.path.dirname(a),exist_ok=True)
|
|
19
|
+
with open(a,'w') as activity: activity.write('1')
|
|
20
|
+
try: p=Process(v,{**os.environ,**v['env']},f)
|
|
21
|
+
except OSError as e:
|
|
22
|
+
print(str(e),file=sys.stderr); sys.exit(127)
|
|
23
|
+
# Keep the supervisor and marker until inherited output pipes close, even
|
|
24
|
+
# after the direct shell exits and only background descendants remain.
|
|
25
|
+
sel=selectors.DefaultSelector()
|
|
26
|
+
sel.register(p.stdout,selectors.EVENT_READ,sys.stdout.buffer)
|
|
27
|
+
sel.register(p.stderr,selectors.EVENT_READ,sys.stderr.buffer)
|
|
28
|
+
while sel.get_map():
|
|
29
|
+
for key,_ in sel.select():
|
|
30
|
+
data=os.read(key.fd,16384)
|
|
31
|
+
if data: key.data.write(data); key.data.flush()
|
|
32
|
+
else: sel.unregister(key.fileobj); key.fileobj.close()
|
|
33
|
+
code=p.wait()
|
|
34
|
+
sys.exit(code if code>=0 else 128-code)
|
|
35
|
+
finally:
|
|
36
|
+
# The direct shell may exit while a background child still owns the pipes.
|
|
37
|
+
# Keep the group identity until no user descendants remain for cancellation.
|
|
38
|
+
ancestors=set(); parent=os.getpid()
|
|
39
|
+
while parent>1:
|
|
40
|
+
ancestors.add(parent)
|
|
41
|
+
try:
|
|
42
|
+
with open('/proc/'+str(parent)+'/stat') as f: parent=int(f.read().rsplit(')',1)[1].split()[1])
|
|
43
|
+
except (FileNotFoundError,ProcessLookupError): break
|
|
44
|
+
descendants=False
|
|
45
|
+
for name in os.listdir('/proc'):
|
|
46
|
+
if not name.isdigit() or int(name) in ancestors: continue
|
|
47
|
+
try:
|
|
48
|
+
with open('/proc/'+name+'/stat') as f: fields=f.read().rsplit(')',1)[1].split()
|
|
49
|
+
if int(fields[2])==os.getpgrp() and fields[0]!='Z': descendants=True
|
|
50
|
+
except (FileNotFoundError,ProcessLookupError): pass
|
|
51
|
+
if not descendants:
|
|
52
|
+
try: os.unlink(m)
|
|
53
|
+
except FileNotFoundError: pass
|
|
54
|
+
if not os.path.exists(m+'.cancel'):
|
|
55
|
+
try: os.unlink(m+'.lock')
|
|
56
|
+
except FileNotFoundError: pass
|
|
57
|
+
if a:
|
|
58
|
+
try: os.unlink(a)
|
|
59
|
+
except FileNotFoundError: pass
|
|
60
|
+
`;async function M(r,c,s,p="",g){if(s.signal?.aborted)return{exitCode:null,timedOut:!0};let m=`/tmp/sandbox-sdk-${crypto.randomUUID()}.pid`,b=r.machines.streamExec(c,{command:p+n(U)}),k=!1,P=!1,C=b.wait().then((i)=>({kind:"exit",exitCode:i}),(i)=>({kind:"error",error:i})),I={stdout:new TextDecoder,stderr:new TextDecoder},S=!p,E="";for(let i of["stdout","stderr"])b[i].push=(l)=>{let w=I[i].decode(l,{stream:!0});if(i==="stdout"&&!S){if(E+=w,"SDK_PREPARED:".startsWith(E)&&E.length<13)return;if(E.startsWith("SDK_PREPARED:"))S=!0,g?.(),w=E.slice(13);else w=E;E=""}if(w)s.onChunk({stream:i,text:w})};let _=()=>{},D=new Promise((i)=>{_=i}),t=()=>{if(k)return;k=!0,b.close(),_()},f=setTimeout(t,s.timeoutMs);s.signal?.addEventListener("abort",t,{once:!0});try{b.write(JSON.stringify({pidPath:m,preparing:Boolean(p),command:s.command,cwd:s.cwd,env:s.env,stdin:s.stdin??"",activityPath:s.activityPath})),b.end();let i=await Promise.race([C,D.then(()=>({kind:"cancel"}))]);if(k){let l,w=await Promise.race([C.then((y)=>y.kind==="exit"&&y.exitCode>=0?!0:new Promise(()=>{})),new Promise((y)=>{l=setTimeout(()=>y(!1),1000)})]);if(clearTimeout(l),!w){let y=await r.machines.exec(c,{command:n(`import os,sys,signal,time,fcntl
|
|
61
|
+
p=sys.argv[1]
|
|
62
|
+
with open(p+'.lock','a') as lock:
|
|
63
|
+
fcntl.flock(lock,fcntl.LOCK_EX)
|
|
64
|
+
with open(p+'.cancel','w') as f: f.write('1')
|
|
65
|
+
try:
|
|
66
|
+
with open(p) as f: pgid=int(f.read())
|
|
67
|
+
except FileNotFoundError:
|
|
68
|
+
print('confirmed'); sys.exit(0)
|
|
69
|
+
try: os.killpg(pgid,signal.SIGKILL)
|
|
70
|
+
except ProcessLookupError: pass
|
|
71
|
+
end=time.monotonic()+2
|
|
72
|
+
while time.monotonic()<end:
|
|
73
|
+
alive=False
|
|
74
|
+
for name in os.listdir('/proc'):
|
|
75
|
+
if not name.isdigit(): continue
|
|
76
|
+
try:
|
|
77
|
+
with open('/proc/'+name+'/stat') as f: fields=f.read().rsplit(')',1)[1].split()
|
|
78
|
+
if int(fields[2])==pgid and fields[0]!='Z': alive=True
|
|
79
|
+
except (FileNotFoundError,ProcessLookupError): pass
|
|
80
|
+
if not alive:
|
|
81
|
+
try: os.unlink(p)
|
|
82
|
+
except FileNotFoundError: pass
|
|
83
|
+
print('confirmed'); sys.exit(0)
|
|
84
|
+
time.sleep(.01)
|
|
85
|
+
sys.exit(1)`,m),timeout:5000});if(y.exitCode!==0||y.stdout.trim()!=="confirmed")throw u("Process termination could not be confirmed","outcome_unknown",c)}}else if(i.kind==="error")throw i.error;for(let l of["stdout","stderr"]){let w=I[l].decode();if(w)s.onChunk({stream:l,text:w})}return{exitCode:k?null:i.kind==="exit"?i.exitCode:null,timedOut:k}}catch(i){if(P=!0,k)throw u("Process termination could not be confirmed","outcome_unknown",c);throw b.close(),i}finally{if(clearTimeout(f),s.signal?.removeEventListener("abort",t),(k||P)&&s.activityPath)await r.machines.exec(c,{command:`rm -f -- ${a(s.activityPath)} ${a(m)}`,timeout:5000}).catch((i)=>{if(!P)throw i})}}import{createRequire as B}from"node:module";import{pathToFileURL as q}from"node:url";import{Worker as K}from"node:worker_threads";async function O(r,c,s){let{Boxd:p}=await s(c.moduleURL),g=new p(c.options);r.on("message",async(m)=>{try{let b=m.kind==="upload"?await g.machines.files.upload(m.machine,m.path,m.bytes):await g.machines.files.download(m.machine,m.path);r.postMessage({requestId:m.requestId,value:b})}catch(b){r.postMessage({requestId:m.requestId,error:{name:b instanceof Error?b.name:"Error",grpcCode:typeof b==="object"&&b!==null&&"grpcCode"in b&&typeof b.grpcCode==="number"?b.grpcCode:void 0,permissionDenied:b instanceof Error&&/permission denied/i.test(b.message)}})}})}class R{options;signal;workers=new Set;idle=[];stopping=new Map;nextId=0;pending=new Map;constructor(r,c){this.options=r;this.signal=c}start(){let r=this.idle.pop();if(r)return r;let c=new K(`import("node:worker_threads").then(({parentPort,workerData})=> (${O.toString()})(parentPort,workerData,url=>import(url)))`,{eval:!0,workerData:{moduleURL:q(B(import.meta.url).resolve("@boxd-sh/sdk/web")).href,options:this.options}});return this.workers.add(c),c.on("message",(s)=>{if(!this.workers.has(c))return;if(!s||typeof s!=="object"||!("requestId"in s)||typeof s.requestId!=="number"){this.stop(c,u("Invalid file transfer response"));return}let p=this.pending.get(s.requestId);if(!p||p.worker!==c)return;if(this.pending.delete(s.requestId),p.cleanup(),"value"in s&&(typeof s.value==="number"||s.value instanceof Uint8Array))p.resolve(s.value);else if("error"in s&&s.error&&typeof s.error==="object"){let g=s.error;p.reject(T(Object.assign(Error("permissionDenied"in g&&g.permissionDenied===!0?"permission denied":"File transfer failed"),{name:"name"in g&&typeof g.name==="string"?g.name:"Error",grpcCode:"grpcCode"in g&&typeof g.grpcCode==="number"?g.grpcCode:void 0}),p.machine))}else p.reject(u("Invalid file transfer response","provider",p.machine));this.idle.push(c),c.unref()}),c.on("error",()=>{if(this.workers.has(c))this.stop(c,u("File transfer worker failed","unavailable"))}),c.on("exit",()=>{if(this.workers.has(c))this.stop(c,u("File transfer worker exited","unavailable"))}),c.unref(),c}stop(r,c){let s=this.stopping.get(r);if(s)return s.promise;this.workers.delete(r),this.idle=this.idle.filter((m)=>m!==r);let p=[...this.pending].filter(([,m])=>m.worker===r);for(let[m,b]of p)this.pending.delete(m),b.cleanup();let g=r.terminate().then(()=>{for(let[,m]of p)m.reject(c);this.stopping.delete(r)});return this.stopping.set(r,{machine:p[0]?.[1].machine,promise:g}),g}async transfer(r){if(this.signal?.aborted)throw u("File transfer aborted","aborted",r.machine);let c=this.start();c.ref();let s=this.nextId++;return new Promise((p,g)=>{let m=()=>{this.stop(c,u("File transfer aborted","aborted",r.machine))},b=setTimeout(()=>{this.stop(c,u("File transfer deadline exceeded; check the destination before retrying a write","timeout",r.machine))},this.options.timeout??30000),k=()=>{clearTimeout(b),this.signal?.removeEventListener("abort",m)};if(this.pending.set(s,{worker:c,machine:r.machine,resolve:p,reject:g,cleanup:k}),this.signal?.addEventListener("abort",m,{once:!0}),this.signal?.aborted){m();return}try{c.postMessage({...r,requestId:s})}catch{this.stop(c,u("Could not submit file transfer","unavailable",r.machine))}})}async upload(r,c,s){let p=await this.transfer({kind:"upload",machine:r,path:c,bytes:s});if(typeof p!=="number")throw u("Invalid upload response","provider",r);return p}async download(r,c){let s=await this.transfer({kind:"download",machine:r,path:c});if(!(s instanceof Uint8Array))throw u("Invalid download response","provider",r);return s}async closeMachine(r){let c=new Set([...this.pending.values()].filter((s)=>s.machine===r).map((s)=>s.worker));await Promise.all([...c].map((s)=>this.stop(s,u("File transfers closed","aborted",r)))),await Promise.all([...this.stopping.values()].filter((s)=>s.machine===r).map((s)=>s.promise))}async releaseIdle(){let r=this.idle.splice(0);await Promise.all(r.map((c)=>this.stop(c,u("Idle file transfer worker closed","aborted")))),await Promise.all([...this.stopping.values()].map((c)=>c.promise))}async close(){await Promise.all([...this.workers].map((r)=>this.stop(r,u("File transfers closed","aborted")))),await Promise.all([...this.stopping.values()].map((r)=>r.promise))}}var F="/var/lib/sandbox-sdk/metadata.json";function j(r){if(r===void 0)return;if(typeof r==="string")return{small:1,medium:2,large:4}[r];if(![1,2,4,8].includes(r.cpu))throw u("boxd sizes are 1 vCPU / 4 GiB, 2 / 8, 4 / 16, or 8 / 32, each with 100 GB disk","unsupported");if(r.memoryMb!==r.cpu*4096||(r.diskGb??100)!==100)throw u(`boxd gives ${r.cpu} vCPU / ${r.cpu*4} GiB memory / 100 GB disk; CPU and memory cannot be selected independently`,"unsupported");return r.cpu}function oe(r={}){let c,s,p,g=new Map;async function m(){return c??=(async()=>{if(r.requestTimeoutMs!==void 0&&(!Number.isSafeInteger(r.requestTimeoutMs)||r.requestTimeoutMs<=0))throw u("requestTimeoutMs must be a positive integer","invalid_argument");let t=r.apiKey??process.env.BOXD_API_KEY;if(!t?.trim())throw u("boxd API key is missing. Authentication is required. Set BOXD_API_KEY or pass apiKey.","auth");let{Boxd:f}=await o("@boxd-sh/sdk","boxd",()=>import("@boxd-sh/sdk"));return s=new f({apiKey:t,baseURL:r.baseUrl??r.baseURL,timeout:r.requestTimeoutMs??30000,maxRetries:0})})()}async function b(){return await m(),p??=new R({apiKey:r.apiKey??process.env.BOXD_API_KEY,baseURL:r.baseUrl??r.baseURL,timeout:r.requestTimeoutMs??30000,maxRetries:0},r.fileTransferSignal)}async function k(t,f){try{return await f()}catch(i){throw T(i,t)}}let P=new Map;function C(t){return`if ! mkdir -p /workspace /var/lib/sandbox-sdk 2>/dev/null || [ ! -w /workspace ] || [ ! -w /var/lib/sandbox-sdk ]; then sudo -n install -d -o "$(id -u)" -g "$(id -g)" /workspace /var/lib/sandbox-sdk; fi && test -w /workspace && test -w /var/lib/sandbox-sdk && mkdir -p /tmp/.sandbox-sdk && printf %s ${a(t)} > ${a(F)} && `}function I(t){return P.get(t)??""}async function S(t){let f=await m();return{name:"boxd",raw:async(i)=>{let l=I(t),w="SDK_PREPARED:",y=await f.machines.exec(t,{command:l?l+`printf %s ${a("SDK_PREPARED:")} && `+i:i,timeout:r.requestTimeoutMs??30000}),v=l&&y.stdout.startsWith("SDK_PREPARED:");if(v)P.delete(t);if(y.exitCode!==0)throw u("Guest operation failed","provider",t);return v?y.stdout.slice(13):y.stdout},upload:async(i,l)=>{let w=I(t);if(w){if((await M(f,t,{command:n("import os,sys,base64; os.makedirs(os.path.dirname(sys.argv[1]),exist_ok=True); f=open(sys.argv[1],'wb'); f.write(base64.b64decode(sys.stdin.buffer.read())); f.close()",i),cwd:"/",env:{},stdin:Buffer.from(l).toString("base64"),timeoutMs:r.requestTimeoutMs??30000,onChunk(){}},w,()=>P.delete(t))).exitCode!==0)throw u("File upload failed","provider",t);return}if(await(await b()).upload(t,i,l)!==l.length)throw u("Incomplete file upload","provider",t)},read:async(i,l)=>{if(!l)return(await b()).download(t,i);let w=await f.machines.exec(t,{command:n("import sys; f=open(sys.argv[1],'rb'); f.seek(int(sys.argv[2])); sys.stdout.buffer.write(f.read(int(sys.argv[3])))",i,String(l.offset),String(l.length)),encoding:"buffer",timeout:r.requestTimeoutMs??30000});if(w.exitCode!==0)throw u("File does not exist","file_not_found",t);return new Uint8Array(w.stdout)}}}async function E(t){if(!t.name.startsWith("sbxsdk-"))throw u("Not an SDK machine","not_found",t.id);let f,i=!1;try{f=new TextDecoder().decode(await(await b()).download(t.id,F))}catch(y){if(!["not_found","file_not_found"].includes(T(y).code))throw y;let v=t.name.split("-");if(v[1]!=="v2"||!["0","1"].includes(v[2]??""))throw u("SDK metadata is missing","not_found",t.id);let x=Number.parseInt(v[3]??"",36);if(!Number.isFinite(x)||x<=0)throw u("Invalid SDK metadata","not_found",t.id);f=JSON.stringify({key:null,labels:v[2]==="1"?{"sandbox-sdk":"1"}:{},createdAt:x}),i=!0}let l;try{l=JSON.parse(f)}catch{throw u("Invalid SDK metadata","not_found",t.id)}if(!l||typeof l!=="object"||!("labels"in l)||!l.labels||typeof l.labels!=="object"||Array.isArray(l.labels)||!("createdAt"in l)||typeof l.createdAt!=="number"||!("key"in l)||l.key!==null&&typeof l.key!=="string")throw u("Invalid SDK metadata","not_found",t.id);if(i&&!P.has(t.id))P.set(t.id,C(f));let w={};for(let[y,v]of Object.entries(l.labels)){if(typeof v!=="string")throw u("Invalid SDK labels","not_found",t.id);Object.defineProperty(w,y,{value:v,enumerable:!0})}return{id:t.id,key:l.key??void 0,labels:w,createdAt:t.createdAt?.getTime()??l.createdAt,state:t.status==="running"?"running":["suspended","hibernated"].includes(t.status)?"paused":"stopped"}}let _={name:"boxd",keyMode:"best-effort",inlineActivity:!0,capabilities:new Set(["pause","size","snapshot"]),create:(t)=>k(void 0,async()=>{if(t.network&&t.network!=="all"||t.idleTimeoutMs!==void 0||t.timeoutMs!==void 0)throw u("Network restrictions and idle/lifetime limits are not implemented","unsupported");if(t.from!==void 0&&!t.from.trim())throw u("Snapshot restore requires a nonempty snapshot ID or name","invalid_argument");if(t.from!==void 0&&(t.size!==void 0||Object.keys(t.env??{}).length>0||r.image!==void 0))throw u("boxd snapshot restore inherits size, image and environment; overrides are not supported","unsupported");let f=j(t.size);if(t.signal?.aborted)throw u("Creation aborted","aborted");let i=await m(),l=Date.now(),w=JSON.stringify({key:t.key??null,labels:t.labels,createdAt:l}),y=t.key===void 0&&Object.keys(t.labels).every((x)=>x==="sandbox-sdk"&&t.labels[x]==="1")&&t.from===void 0,v;try{let x=y?`sbxsdk-v2-${t.labels["sandbox-sdk"]==="1"?"1":"0"}-${l.toString(36)}-${crypto.randomUUID()}`:`sbxsdk-${crypto.randomUUID()}`;v=await i.machines.create(t.from!==void 0?{name:x,fromSnapshot:t.from,isolated:!0,config:{autoSuspendTimeout:0}}:{name:x,image:r.image,env:t.env,isolated:!0,config:{autoSuspendTimeout:0,autoDestroyTimeout:0,vcpu:f}})}catch(x){let A=T(x);if(A.code==="unavailable"||A.code==="timeout")throw u("Machine creation outcome is unknown; inspect the account before retrying","outcome_unknown");throw x}try{if(t.signal?.aborted)throw u("Creation aborted","aborted");if(v.status!=="running")v=await i.machines.waitUntilReady(v.id,{timeout:r.requestTimeoutMs??90000});let x={id:v.id,key:t.key,labels:t.labels,createdAt:v.createdAt?.getTime()??l,state:"running"};if(P.set(v.id,C(w)),g.set(v.id,v),!y)await(await S(v.id)).raw("true");return x}catch(x){throw await i.machines.delete(v.id),x}}),list:(t)=>k(void 0,async()=>{try{let f=[];for(let i of await(await m()).machines.list()){if(!i.name.startsWith("sbxsdk-")||i.status==="destroyed")continue;try{let l=await E(i);if(Object.entries(t).every(([w,y])=>l.labels[w]===y))f.push(l)}catch(l){if(T(l).code!=="not_found")throw l}}return f}finally{if(!g.size)await p?.releaseIdle()}}),async find(t){let f=(await _.list({})).find((i)=>i.key===t);return f?_.connect(f.id):void 0},connect:(t)=>k(t,async()=>{try{let f=await(await m()).machines.get(t),i=await E(f);return g.set(t,f),i}finally{if(!g.size)await p?.releaseIdle()}}),destroy:(t)=>k(t,async()=>{try{await(await m()).machines.delete(t)}catch(f){if(T(f).code!=="not_found")throw f}if(g.delete(t),P.delete(t),await p?.closeMachine(t),!g.size)await p?.releaseIdle()}),async close(t){if(g.delete(t),await p?.closeMachine(t),!g.size)await p?.releaseIdle()},exec:(t,f)=>k(t,async()=>{let i=I(t);return await M(await m(),t,f,i,()=>P.delete(t))}),files:h(S,k),previewUrl:(t,f)=>k(t,async()=>{let i=await m(),l=(await i.machines.proxies.list(t)).find((w)=>w.isDefault);if(!l)throw u("Default proxy is not ready","unavailable",t);return await i.machines.proxies.setPort(t,f),await i.machines.setBotProtection(t,"off"),{url:`https://${l.domain}`}}),pause:(t)=>k(t,async()=>{await(await m()).machines.pause(t)}),resume:(t)=>k(t,async()=>{await(await m()).machines.resume(t)}),snapshot:(t)=>k(t,async()=>{let f=await m(),i=`sbxsdk-${crypto.randomUUID()}`,l=Date.now()+(r.requestTimeoutMs??90000);try{await f.snapshots.create(t,i);while(Date.now()<l){let w=await f.snapshots.get(i);if(w.status==="ready")return{id:i};if(w.status==="failed")throw u("boxd snapshot capture failed","provider",t);await new Promise((y)=>setTimeout(y,250))}throw u("boxd snapshot did not become ready","timeout",t)}catch(w){try{await f.snapshots.delete(i)}catch(y){if(T(y,t).code!=="not_found")throw u(`Snapshot ${i} cleanup is unconfirmed after ${T(w,t).code}; inspect and remove it before retrying`,"outcome_unknown",t)}throw w}}),native(t){let f=g.get(t);if(!f)throw u("Connect the machine first","not_found",t);if(!c)throw u("Client is detached","not_found",t);return{machine:f,client:D()}}};function D(){if(!s)throw u("Client is detached","not_found");return s}return _}export{oe as boxd};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BoxdOptions } from "@boxd-sh/sdk/web";
|
|
2
|
+
export declare class FileTransfers {
|
|
3
|
+
private readonly options;
|
|
4
|
+
private readonly signal?;
|
|
5
|
+
private workers;
|
|
6
|
+
private idle;
|
|
7
|
+
private stopping;
|
|
8
|
+
private nextId;
|
|
9
|
+
private pending;
|
|
10
|
+
constructor(options: BoxdOptions, signal?: AbortSignal | undefined);
|
|
11
|
+
private start;
|
|
12
|
+
private stop;
|
|
13
|
+
private transfer;
|
|
14
|
+
upload(machine: string, path: string, bytes: Uint8Array): Promise<number>;
|
|
15
|
+
download(machine: string, path: string): Promise<Uint8Array>;
|
|
16
|
+
closeMachine(machine: string): Promise<void>;
|
|
17
|
+
releaseIdle(): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SandboxError } from "../../errors.js";
|
|
2
|
+
export declare function daytonaError(error: unknown, sandboxId?: string, file?: boolean, operation?: string, apiKey?: string | undefined, knownSecrets?: Iterable<string>): SandboxError;
|
|
3
|
+
export declare function unsupported(message: string): never;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Sandbox } from "@daytonaio/sdk";
|
|
2
|
+
import type { ExecInput, ExecResult, NativeProcess } from "../../provider.js";
|
|
3
|
+
export declare const quote: (value: string) => string;
|
|
4
|
+
/** Each call has a new session and process group. Stdin comes from a private file
|
|
5
|
+
* because session input has no documented EOF operation. Native process hooks
|
|
6
|
+
* run core's durable wrapper unchanged, so logs survive client detachment.
|
|
7
|
+
*/
|
|
8
|
+
export declare const workspaceCommand = "if [ ! -w /workspace ]; then sudo -n install -d -o \"$(id -u)\" -g \"$(id -g)\" -m 755 /workspace; fi; test -w /workspace";
|
|
9
|
+
export declare function sessionProcess(box: Sandbox, input: Omit<ExecInput, "timeoutMs">, timeoutMs?: number, prepare?: string): Promise<NativeProcess & {
|
|
10
|
+
detach(): void;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function execute(box: Sandbox, input: ExecInput & {
|
|
13
|
+
streaming?: boolean;
|
|
14
|
+
}, prepare?: string, prepared?: () => void): Promise<ExecResult>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Sandbox as DaytonaSandbox } from "@daytonaio/sdk";
|
|
2
|
+
import type { Provider } from "../../provider.js";
|
|
3
|
+
export interface DaytonaOptions {
|
|
4
|
+
/** Defaults to DAYTONA_API_KEY; required on first use. */
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
/** Defaults to DAYTONA_API_URL, then DAYTONA_SERVER_URL, then https://app.daytona.io/api. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** @deprecated Use baseUrl. Removed in 1.0.0. */
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
/** Defaults to DAYTONA_TARGET, then us. */
|
|
11
|
+
target?: string;
|
|
12
|
+
/** Container image; default: use the default or named snapshot. */
|
|
13
|
+
image?: string;
|
|
14
|
+
/** Snapshot name; default: the Daytona service default snapshot. */
|
|
15
|
+
snapshot?: string;
|
|
16
|
+
/** Control-plane deadline. Default: 30 s. */
|
|
17
|
+
requestTimeoutMs?: number;
|
|
18
|
+
/** Snapshot completion budget. Default: 600 seconds; maximum: 15 minutes. */
|
|
19
|
+
snapshotTimeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export type DaytonaCapability = "snapshot" | "size" | "network-policy";
|
|
22
|
+
export type DaytonaProvider = Provider<DaytonaSandbox, DaytonaCapability>;
|
|
23
|
+
/** Direct Daytona adapter. The optional SDK is loaded only on first operation. */
|
|
24
|
+
export declare function daytona(options?: DaytonaOptions): DaytonaProvider;
|