withruntime 0.18.0 → 0.23.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/COMPATIBILITY.md +32 -1
- package/README.md +19 -0
- package/README.src.md +19 -0
- package/dist/agents/index.d.ts +11 -0
- package/dist/agents/index.js +12 -0
- package/dist/agents-cli.d.ts +10 -0
- package/dist/agents-cli.js +362 -0
- package/dist/apps-cli.d.ts +2 -0
- package/dist/apps-cli.js +173 -0
- package/dist/builds/index.d.ts +10 -0
- package/dist/builds/index.js +11 -0
- package/dist/cli-words.js +1 -0
- package/dist/cli.js +176 -21
- package/dist/client.d.ts +19 -0
- package/dist/client.js +20 -0
- package/dist/cloudflare/errors.d.ts +23 -0
- package/dist/cloudflare/errors.js +16 -0
- package/dist/cloudflare/index.d.ts +82 -9
- package/dist/cloudflare/index.js +162 -38
- package/dist/cloudflare/interpreter.js +14 -5
- package/dist/cloudflare/mime.d.ts +1 -0
- package/dist/cloudflare/mime.js +66 -0
- package/dist/cloudflare/streams.d.ts +8 -0
- package/dist/cloudflare/streams.js +30 -0
- package/dist/cloudflare/types.d.ts +338 -0
- package/dist/cloudflare/types.js +80 -0
- package/dist/cloudflare/worker.d.ts +297 -10
- package/dist/cloudflare/worker.js +203 -17
- package/dist/containers/index.d.ts +10 -0
- package/dist/containers/index.js +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +10 -1
- package/dist/product-names.js +10 -0
- package/dist/products/agents.d.ts +272 -0
- package/dist/products/agents.js +251 -0
- package/dist/products/containers.d.ts +71 -0
- package/dist/products/containers.js +6 -0
- package/dist/products/index.d.ts +6 -0
- package/dist/products/index.js +7 -0
- package/dist/products/secrets.d.ts +19 -0
- package/dist/products/secrets.js +30 -0
- package/dist/sandbox.d.ts +4 -0
- package/dist/transport.d.ts +2 -2
- package/dist/transport.js +1 -1
- package/dist/tunnel.js +1 -1
- package/dist/types.d.ts +11 -0
- package/package.json +16 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/** Agents: a coding or support agent's harness (Claude Code, Codex, Gemini
|
|
2
|
+
* CLI, or your own code on an agents SDK) in a sandbox of its own per
|
|
3
|
+
* session, on your own model key. Your app drives its sessions through this
|
|
4
|
+
* API and webhooks. In a private beta: outside it every call is refused with
|
|
5
|
+
* `product_in_beta`.
|
|
6
|
+
*
|
|
7
|
+
* const agent = await runtime.agents.create({
|
|
8
|
+
* name: "support",
|
|
9
|
+
* definition: {
|
|
10
|
+
* ...runtime.agents.templates["claude-code"].definition,
|
|
11
|
+
* ...runtime.agents.uses.support.definition,
|
|
12
|
+
* instructions: "Answer questions about our product. Be brief.",
|
|
13
|
+
* },
|
|
14
|
+
* });
|
|
15
|
+
* const session = await runtime.agents.start("support", { input: "Hi!", key: "customer:42" });
|
|
16
|
+
* for await (const event of session.events()) if (event.kind === "reply") console.log(event.body.text);
|
|
17
|
+
*
|
|
18
|
+
* A session's sandbox is billed while it runs, at $0.03 per active vCPU-hour
|
|
19
|
+
* and $0.01 per GiB-hour of memory; a waiting session's sandbox is paused.
|
|
20
|
+
* No session fee, no seats. */
|
|
21
|
+
import { Page } from "../page.js";
|
|
22
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
23
|
+
export type AgentDefinition = {
|
|
24
|
+
/** What runs the harness, from the sandbox's home. */
|
|
25
|
+
command: string;
|
|
26
|
+
/** Where each session's sandbox starts: an image of yours, or a snapshot
|
|
27
|
+
* of a machine you set up by hand (a harness logged in to your own
|
|
28
|
+
* subscription included). The default image has Node and Python. */
|
|
29
|
+
from?: {
|
|
30
|
+
image: string;
|
|
31
|
+
} | {
|
|
32
|
+
snapshot: string;
|
|
33
|
+
};
|
|
34
|
+
/** 1, 2, 4, 8 or 16; default 4. */
|
|
35
|
+
vcpu?: number;
|
|
36
|
+
/** 512 to 65536, a multiple of 256; default 8192 (8 GiB). */
|
|
37
|
+
memoryMiB?: number;
|
|
38
|
+
/** The agent's standing words, at most 64 KiB. */
|
|
39
|
+
instructions?: string;
|
|
40
|
+
/** Settings the harness sees; never a secret. */
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
/** Names of your egress secrets the sandbox may use: it sees a placeholder,
|
|
43
|
+
* and the egress proxy adds the value toward the secret's hosts. Model
|
|
44
|
+
* keys of any provider go here (ANTHROPIC_API_KEY, OPENAI_API_KEY,
|
|
45
|
+
* GEMINI_API_KEY, OPENROUTER_API_KEY, or an OpenAI-compatible endpoint's
|
|
46
|
+
* key with env.OPENAI_BASE_URL). */
|
|
47
|
+
secrets?: string[];
|
|
48
|
+
/** MCP servers the harness may reach. */
|
|
49
|
+
mcp?: {
|
|
50
|
+
name: string;
|
|
51
|
+
url: string;
|
|
52
|
+
}[];
|
|
53
|
+
volumes?: {
|
|
54
|
+
volume: string;
|
|
55
|
+
path: string;
|
|
56
|
+
}[];
|
|
57
|
+
/** How long a session's sandbox runs with nothing happening before it
|
|
58
|
+
* pauses; default 60. */
|
|
59
|
+
idlePauseSeconds?: number;
|
|
60
|
+
/** A session's longest life; default 86400. */
|
|
61
|
+
maxSessionSeconds?: number;
|
|
62
|
+
/** Sessions running at once; more wait, queued. Default 10. */
|
|
63
|
+
maxConcurrent?: number;
|
|
64
|
+
/** The use it was set up for (`uses`). */
|
|
65
|
+
template?: "support" | "team";
|
|
66
|
+
};
|
|
67
|
+
export type Agent = {
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
version: number;
|
|
71
|
+
/** With its defaults written in. */
|
|
72
|
+
definition: AgentDefinition;
|
|
73
|
+
labels: Record<string, string>;
|
|
74
|
+
/** `subscription_for_customers`: its secrets name CLAUDE_CODE_OAUTH_TOKEN
|
|
75
|
+
* and it is set up to answer your customers (`template: "support"`). A
|
|
76
|
+
* subscription is for your own team's agents; an agent that serves your
|
|
77
|
+
* customers uses an API key. */
|
|
78
|
+
warnings: "subscription_for_customers"[];
|
|
79
|
+
createdAt: string;
|
|
80
|
+
updatedAt: string;
|
|
81
|
+
};
|
|
82
|
+
export type AgentSessionState = "queued" | "running" | "waiting" | "done" | "failed" | "stopped";
|
|
83
|
+
/** One session. Subagents' parentSessionId and rootSessionId, its cost and
|
|
84
|
+
* treeCost, historyBytes and attachmentBytes join it as the API gains
|
|
85
|
+
* them. */
|
|
86
|
+
export type AgentSession = {
|
|
87
|
+
id: string;
|
|
88
|
+
agentId: string;
|
|
89
|
+
agentVersion: number;
|
|
90
|
+
key: string | null;
|
|
91
|
+
trigger: "api" | AgentTriggerKind;
|
|
92
|
+
state: AgentSessionState;
|
|
93
|
+
/** Why it ended. */
|
|
94
|
+
reason: string | null;
|
|
95
|
+
/** What the agent said it ended with: a pull request's address, an answer. */
|
|
96
|
+
result: string | null;
|
|
97
|
+
sandboxId: string | null;
|
|
98
|
+
lastSeq: number;
|
|
99
|
+
labels: Record<string, string>;
|
|
100
|
+
createdAt: string;
|
|
101
|
+
startedAt: string | null;
|
|
102
|
+
endedAt: string | null;
|
|
103
|
+
};
|
|
104
|
+
export type AgentEvent = {
|
|
105
|
+
seq: number;
|
|
106
|
+
kind: "input" | "answer" | "reply" | "ask" | "status" | "result" | "error" | "note";
|
|
107
|
+
/** A key's principal id, slack:<user>, github:<login>, agent (the
|
|
108
|
+
* harness) or runtime. */
|
|
109
|
+
actor: string;
|
|
110
|
+
/** Words are body.text. */
|
|
111
|
+
body: {
|
|
112
|
+
text?: string;
|
|
113
|
+
} & Record<string, unknown>;
|
|
114
|
+
createdAt: string;
|
|
115
|
+
};
|
|
116
|
+
export type AgentTriggerKind = "slack" | "github" | "schedule" | "webhook";
|
|
117
|
+
export type AgentTrigger = {
|
|
118
|
+
id: string;
|
|
119
|
+
agentId: string;
|
|
120
|
+
kind: AgentTriggerKind;
|
|
121
|
+
config: Record<string, unknown>;
|
|
122
|
+
pausedAt: string | null;
|
|
123
|
+
pausedWhy: string | null;
|
|
124
|
+
createdAt: string;
|
|
125
|
+
/** A webhook's secret, which each of its calls carries: answered once,
|
|
126
|
+
* when it is added, and never again. Keep it where you keep your keys. */
|
|
127
|
+
secret?: string;
|
|
128
|
+
};
|
|
129
|
+
export type CreateAgent = {
|
|
130
|
+
name: string;
|
|
131
|
+
definition: AgentDefinition;
|
|
132
|
+
labels?: Record<string, string>;
|
|
133
|
+
};
|
|
134
|
+
export type StartAgentSession = {
|
|
135
|
+
input: string;
|
|
136
|
+
/** Words with the same key join its open session: a Slack thread, a pull
|
|
137
|
+
* request, one of your customers (`customer:42`). */
|
|
138
|
+
key?: string;
|
|
139
|
+
labels?: Record<string, string>;
|
|
140
|
+
};
|
|
141
|
+
export type CreateAgentTrigger = {
|
|
142
|
+
kind: "slack";
|
|
143
|
+
config: {
|
|
144
|
+
team: string;
|
|
145
|
+
channel: string;
|
|
146
|
+
};
|
|
147
|
+
} | {
|
|
148
|
+
kind: "github";
|
|
149
|
+
config: {
|
|
150
|
+
installation: number;
|
|
151
|
+
repository: string;
|
|
152
|
+
events?: string[];
|
|
153
|
+
};
|
|
154
|
+
} | {
|
|
155
|
+
kind: "schedule";
|
|
156
|
+
config: {
|
|
157
|
+
cron: string;
|
|
158
|
+
timezone?: string;
|
|
159
|
+
input: string;
|
|
160
|
+
};
|
|
161
|
+
} | {
|
|
162
|
+
kind: "webhook";
|
|
163
|
+
config: Record<string, never>;
|
|
164
|
+
};
|
|
165
|
+
/** The five harnesses' names, so `templates["claude-code"]` is known to be there. */
|
|
166
|
+
export type AgentTemplateKey = "claude-code" | "codex" | "gemini-cli" | "claude-agent-sdk" | "openai-agents-sdk";
|
|
167
|
+
/** A harness, as data (packages/cloud-guide/agent-templates.json, the same
|
|
168
|
+
* in every SDK and the guide). `definition` is what an agent's definition
|
|
169
|
+
* takes from it; add a use's (`uses`) and your instructions. */
|
|
170
|
+
export type AgentTemplate = {
|
|
171
|
+
key: AgentTemplateKey;
|
|
172
|
+
title: string;
|
|
173
|
+
command: string;
|
|
174
|
+
/** The egress secret its API key is stored as, and the host it is for. */
|
|
175
|
+
apiKeySecret: string;
|
|
176
|
+
apiKeyHost: string;
|
|
177
|
+
/** How your own subscription logs it in, for your own team's agents, or
|
|
178
|
+
* null when it takes an API key alone. */
|
|
179
|
+
subscription: string | null;
|
|
180
|
+
definition: {
|
|
181
|
+
command: string;
|
|
182
|
+
secrets: string[];
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
/** A use: what an agent is for, merged into a template's definition. */
|
|
186
|
+
export type AgentUse = {
|
|
187
|
+
key: "support" | "team";
|
|
188
|
+
title: string;
|
|
189
|
+
/** How its sessions are keyed: the key your app passes per customer, or
|
|
190
|
+
* none. */
|
|
191
|
+
sessionKey: string | null;
|
|
192
|
+
triggers: ("api" | AgentTriggerKind)[];
|
|
193
|
+
definition: Partial<AgentDefinition> & {
|
|
194
|
+
template: "support" | "team";
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
/** The five harnesses (agents-contract.md); tests/agents.test.ts holds them
|
|
198
|
+
* to packages/cloud-guide/agent-templates.json. */
|
|
199
|
+
export declare const AGENT_TEMPLATES: Readonly<Record<AgentTemplateKey, AgentTemplate>>;
|
|
200
|
+
export declare const AGENT_USES: Readonly<Record<"support" | "team", AgentUse>>;
|
|
201
|
+
/** One session, to talk to and follow. */
|
|
202
|
+
export type AgentSessionHandle = {
|
|
203
|
+
readonly id: string;
|
|
204
|
+
/** The session as it was when started or read. */
|
|
205
|
+
readonly session: AgentSession;
|
|
206
|
+
/** Whether the start's words joined a session already open with its key. */
|
|
207
|
+
readonly joined: boolean;
|
|
208
|
+
/** The seq of the words that started or joined it. */
|
|
209
|
+
readonly seq: number;
|
|
210
|
+
/** Words in; answers their seq. */
|
|
211
|
+
send(text: string, options?: RequestOptions): Promise<{
|
|
212
|
+
seq: number;
|
|
213
|
+
}>;
|
|
214
|
+
/** Its log after `after` (0 by default), each event as it comes, waiting
|
|
215
|
+
* up to 30 seconds a call; ends when the session has ended. */
|
|
216
|
+
events(options?: {
|
|
217
|
+
after?: number;
|
|
218
|
+
signal?: AbortSignal;
|
|
219
|
+
}): AsyncGenerator<AgentEvent>;
|
|
220
|
+
stop(options?: RequestOptions): Promise<AgentSession>;
|
|
221
|
+
/** Until it has ended; answers it then. */
|
|
222
|
+
wait(options?: {
|
|
223
|
+
signal?: AbortSignal;
|
|
224
|
+
}): Promise<AgentSession>;
|
|
225
|
+
get(options?: RequestOptions): Promise<AgentSession>;
|
|
226
|
+
};
|
|
227
|
+
export declare function agents(t: Transport): {
|
|
228
|
+
/** An agent: a name and a definition its sessions start from. */
|
|
229
|
+
create: (input: CreateAgent, options?: RequestOptions) => Promise<Agent>;
|
|
230
|
+
/** By its id or its name. */
|
|
231
|
+
get: (agent: string, options?: RequestOptions) => Promise<Agent>;
|
|
232
|
+
/** Your agents, oldest first. Await for a page, or `for await` for all. */
|
|
233
|
+
list: (query?: {
|
|
234
|
+
cursor?: string;
|
|
235
|
+
limit?: number;
|
|
236
|
+
}, options?: RequestOptions) => Promise<Page<Agent>>;
|
|
237
|
+
/** A new version of the definition, whole; sessions already started keep theirs. */
|
|
238
|
+
update: (agent: string, definition: AgentDefinition, options?: RequestOptions) => Promise<Agent>;
|
|
239
|
+
/** Its triggers go and its open sessions stop. */
|
|
240
|
+
delete: (agent: string, options?: RequestOptions) => Promise<void>;
|
|
241
|
+
/** A session with its first words, or the open one with the same key joined. */
|
|
242
|
+
start(agent: string, input: StartAgentSession, options?: RequestOptions): Promise<AgentSessionHandle>;
|
|
243
|
+
/** A session you already have, by its id. */
|
|
244
|
+
session(id: string, options?: RequestOptions): Promise<AgentSessionHandle>;
|
|
245
|
+
sessions: {
|
|
246
|
+
get: (id: string, options?: RequestOptions) => Promise<AgentSession>;
|
|
247
|
+
/** Newest first; of one agent, or in some states. */
|
|
248
|
+
list: (query?: {
|
|
249
|
+
agent?: string;
|
|
250
|
+
state?: AgentSessionState[];
|
|
251
|
+
cursor?: string;
|
|
252
|
+
limit?: number;
|
|
253
|
+
}, options?: RequestOptions) => Promise<Page<AgentSession>>;
|
|
254
|
+
/** An ended session and its log go. Its history is storage like any
|
|
255
|
+
* other, kept until you delete it; an open session is refused
|
|
256
|
+
* (`session_open`): stop it first. */
|
|
257
|
+
delete: (id: string, options?: RequestOptions) => Promise<void>;
|
|
258
|
+
};
|
|
259
|
+
triggers: {
|
|
260
|
+
create: (agent: string, input: CreateAgentTrigger, options?: RequestOptions) => Promise<AgentTrigger>;
|
|
261
|
+
list: (agent: string, options?: RequestOptions) => Promise<AgentTrigger[]>;
|
|
262
|
+
/** Its events are refused, saying `why`; nothing else is held. */
|
|
263
|
+
pause: (id: string, why: string, options?: RequestOptions) => Promise<AgentTrigger>;
|
|
264
|
+
resume: (id: string, options?: RequestOptions) => Promise<AgentTrigger>;
|
|
265
|
+
delete: (id: string, options?: RequestOptions) => Promise<void>;
|
|
266
|
+
};
|
|
267
|
+
/** The harnesses, as data: claude-code, codex, gemini-cli,
|
|
268
|
+
* claude-agent-sdk, openai-agents-sdk. */
|
|
269
|
+
templates: Readonly<Record<AgentTemplateKey, AgentTemplate>>;
|
|
270
|
+
/** The uses, as data: support (your customers) and team (your team). */
|
|
271
|
+
uses: Readonly<Record<"team" | "support", AgentUse>>;
|
|
272
|
+
};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/** Agents: a coding or support agent's harness (Claude Code, Codex, Gemini
|
|
2
|
+
* CLI, or your own code on an agents SDK) in a sandbox of its own per
|
|
3
|
+
* session, on your own model key. Your app drives its sessions through this
|
|
4
|
+
* API and webhooks. In a private beta: outside it every call is refused with
|
|
5
|
+
* `product_in_beta`.
|
|
6
|
+
*
|
|
7
|
+
* const agent = await runtime.agents.create({
|
|
8
|
+
* name: "support",
|
|
9
|
+
* definition: {
|
|
10
|
+
* ...runtime.agents.templates["claude-code"].definition,
|
|
11
|
+
* ...runtime.agents.uses.support.definition,
|
|
12
|
+
* instructions: "Answer questions about our product. Be brief.",
|
|
13
|
+
* },
|
|
14
|
+
* });
|
|
15
|
+
* const session = await runtime.agents.start("support", { input: "Hi!", key: "customer:42" });
|
|
16
|
+
* for await (const event of session.events()) if (event.kind === "reply") console.log(event.body.text);
|
|
17
|
+
*
|
|
18
|
+
* A session's sandbox is billed while it runs, at $0.03 per active vCPU-hour
|
|
19
|
+
* and $0.01 per GiB-hour of memory; a waiting session's sandbox is paused.
|
|
20
|
+
* No session fee, no seats. */
|
|
21
|
+
import { Page } from "../page.js";
|
|
22
|
+
const START = 'npx -y @anthropic-ai/claude-code --dangerously-skip-permissions --mcp-config ~/.runtime-agent/mcp.json -p "$(cat ~/.runtime-agent/start.md)"';
|
|
23
|
+
const CODEX = 'npx -y @openai/codex exec --dangerously-bypass-approvals-and-sandbox "$(cat ~/.runtime-agent/start.md)"';
|
|
24
|
+
const GEMINI = 'npx -y @google/gemini-cli --yolo -p "$(cat ~/.runtime-agent/start.md)"';
|
|
25
|
+
/** The five harnesses (agents-contract.md); tests/agents.test.ts holds them
|
|
26
|
+
* to packages/cloud-guide/agent-templates.json. */
|
|
27
|
+
export const AGENT_TEMPLATES = {
|
|
28
|
+
"claude-code": {
|
|
29
|
+
key: "claude-code",
|
|
30
|
+
title: "Claude Code",
|
|
31
|
+
command: START,
|
|
32
|
+
apiKeySecret: "ANTHROPIC_API_KEY",
|
|
33
|
+
apiKeyHost: "api.anthropic.com",
|
|
34
|
+
subscription: "token: run `claude setup-token` on your own computer (Pro/Max), then `runtime secrets set CLAUDE_CODE_OAUTH_TOKEN` scoped to api.anthropic.com",
|
|
35
|
+
definition: { command: START, secrets: ["ANTHROPIC_API_KEY"] },
|
|
36
|
+
},
|
|
37
|
+
codex: {
|
|
38
|
+
key: "codex",
|
|
39
|
+
title: "Codex CLI",
|
|
40
|
+
command: CODEX,
|
|
41
|
+
apiKeySecret: "OPENAI_API_KEY",
|
|
42
|
+
apiKeyHost: "api.openai.com",
|
|
43
|
+
subscription: "login once in the sandbox's terminal: `npx -y @openai/codex login --device-auth`, kept in the snapshot or a volume at `~/.codex`",
|
|
44
|
+
definition: { command: CODEX, secrets: ["OPENAI_API_KEY"] },
|
|
45
|
+
},
|
|
46
|
+
"gemini-cli": {
|
|
47
|
+
key: "gemini-cli",
|
|
48
|
+
title: "Gemini CLI",
|
|
49
|
+
command: GEMINI,
|
|
50
|
+
apiKeySecret: "GEMINI_API_KEY",
|
|
51
|
+
apiKeyHost: "generativelanguage.googleapis.com",
|
|
52
|
+
subscription: "login once in the sandbox's terminal (Sign in with Google), kept at `~/.gemini`",
|
|
53
|
+
definition: { command: GEMINI, secrets: ["GEMINI_API_KEY"] },
|
|
54
|
+
},
|
|
55
|
+
"claude-agent-sdk": {
|
|
56
|
+
key: "claude-agent-sdk",
|
|
57
|
+
title: "Claude Agent SDK",
|
|
58
|
+
command: "python3 ~/agent.py",
|
|
59
|
+
apiKeySecret: "ANTHROPIC_API_KEY",
|
|
60
|
+
apiKeyHost: "api.anthropic.com",
|
|
61
|
+
subscription: null,
|
|
62
|
+
definition: { command: "python3 ~/agent.py", secrets: ["ANTHROPIC_API_KEY"] },
|
|
63
|
+
},
|
|
64
|
+
"openai-agents-sdk": {
|
|
65
|
+
key: "openai-agents-sdk",
|
|
66
|
+
title: "OpenAI Agents SDK",
|
|
67
|
+
command: "python3 ~/agent.py",
|
|
68
|
+
apiKeySecret: "OPENAI_API_KEY",
|
|
69
|
+
apiKeyHost: "api.openai.com",
|
|
70
|
+
subscription: null,
|
|
71
|
+
definition: { command: "python3 ~/agent.py", secrets: ["OPENAI_API_KEY"] },
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
export const AGENT_USES = {
|
|
75
|
+
support: {
|
|
76
|
+
key: "support",
|
|
77
|
+
title: "Your customers",
|
|
78
|
+
sessionKey: "customer:<id>",
|
|
79
|
+
triggers: ["api", "webhook"],
|
|
80
|
+
definition: {
|
|
81
|
+
template: "support",
|
|
82
|
+
vcpu: 1,
|
|
83
|
+
memoryMiB: 1024,
|
|
84
|
+
idlePauseSeconds: 10,
|
|
85
|
+
maxConcurrent: 1000,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
team: {
|
|
89
|
+
key: "team",
|
|
90
|
+
title: "Your team",
|
|
91
|
+
sessionKey: null,
|
|
92
|
+
triggers: ["slack", "github"],
|
|
93
|
+
definition: { template: "team", vcpu: 4, memoryMiB: 8192 },
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const enc = encodeURIComponent;
|
|
97
|
+
const ENDED = ["done", "failed", "stopped"];
|
|
98
|
+
export function agents(t) {
|
|
99
|
+
const getSession = (id, options) => t.json({ method: "GET", path: `/v1/agents/sessions/${enc(id)}`, ...options });
|
|
100
|
+
async function* events(id, options = {}) {
|
|
101
|
+
let after = options.after ?? 0;
|
|
102
|
+
for (;;) {
|
|
103
|
+
options.signal?.throwIfAborted();
|
|
104
|
+
const read = await t.json({
|
|
105
|
+
method: "GET",
|
|
106
|
+
path: `/v1/agents/sessions/${enc(id)}/events`,
|
|
107
|
+
query: { after, wait: 30 },
|
|
108
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
109
|
+
timeoutMs: 0,
|
|
110
|
+
});
|
|
111
|
+
for (const event of read.data) {
|
|
112
|
+
after = event.seq;
|
|
113
|
+
yield event;
|
|
114
|
+
}
|
|
115
|
+
if (read.ended)
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const handle = (session, joined = false, seq = session.lastSeq) => ({
|
|
120
|
+
id: session.id,
|
|
121
|
+
session,
|
|
122
|
+
joined,
|
|
123
|
+
seq,
|
|
124
|
+
send: (text, options) => t.json({
|
|
125
|
+
method: "POST",
|
|
126
|
+
path: `/v1/agents/sessions/${enc(session.id)}/messages`,
|
|
127
|
+
body: { text },
|
|
128
|
+
...options,
|
|
129
|
+
}),
|
|
130
|
+
events: (options) => events(session.id, options),
|
|
131
|
+
stop: (options) => t.json({
|
|
132
|
+
method: "POST",
|
|
133
|
+
path: `/v1/agents/sessions/${enc(session.id)}/stop`,
|
|
134
|
+
body: {},
|
|
135
|
+
...options,
|
|
136
|
+
}),
|
|
137
|
+
async wait(options = {}) {
|
|
138
|
+
const now = await getSession(session.id, options);
|
|
139
|
+
if (ENDED.includes(now.state))
|
|
140
|
+
return now;
|
|
141
|
+
for await (const _ of events(session.id, { after: now.lastSeq, ...options }))
|
|
142
|
+
;
|
|
143
|
+
return getSession(session.id, options);
|
|
144
|
+
},
|
|
145
|
+
get: (options) => getSession(session.id, options),
|
|
146
|
+
});
|
|
147
|
+
const list = async (query = {}, options) => {
|
|
148
|
+
const page = await t.json({
|
|
149
|
+
method: "GET",
|
|
150
|
+
path: "/v1/agents",
|
|
151
|
+
query,
|
|
152
|
+
...options,
|
|
153
|
+
});
|
|
154
|
+
return new Page(page.data, page.nextCursor, (cursor) => list({ ...query, cursor }, options));
|
|
155
|
+
};
|
|
156
|
+
const listSessions = async (query = {}, options) => {
|
|
157
|
+
const page = await t.json({
|
|
158
|
+
method: "GET",
|
|
159
|
+
path: "/v1/agents/sessions",
|
|
160
|
+
query,
|
|
161
|
+
...options,
|
|
162
|
+
});
|
|
163
|
+
return new Page(page.data, page.nextCursor, (cursor) => listSessions({ ...query, cursor }, options));
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
/** An agent: a name and a definition its sessions start from. */
|
|
167
|
+
create: (input, options) => t.json({ method: "POST", path: "/v1/agents", body: input, ...options }),
|
|
168
|
+
/** By its id or its name. */
|
|
169
|
+
get: (agent, options) => t.json({ method: "GET", path: `/v1/agents/${enc(agent)}`, ...options }),
|
|
170
|
+
/** Your agents, oldest first. Await for a page, or `for await` for all. */
|
|
171
|
+
list,
|
|
172
|
+
/** A new version of the definition, whole; sessions already started keep theirs. */
|
|
173
|
+
update: (agent, definition, options) => t.json({
|
|
174
|
+
method: "PATCH",
|
|
175
|
+
path: `/v1/agents/${enc(agent)}`,
|
|
176
|
+
body: { definition },
|
|
177
|
+
...options,
|
|
178
|
+
}),
|
|
179
|
+
/** Its triggers go and its open sessions stop. */
|
|
180
|
+
delete: async (agent, options) => {
|
|
181
|
+
await t.json({ method: "DELETE", path: `/v1/agents/${enc(agent)}`, ...options });
|
|
182
|
+
},
|
|
183
|
+
/** A session with its first words, or the open one with the same key joined. */
|
|
184
|
+
async start(agent, input, options) {
|
|
185
|
+
const out = await t.json({
|
|
186
|
+
method: "POST",
|
|
187
|
+
path: `/v1/agents/${enc(agent)}/sessions`,
|
|
188
|
+
body: input,
|
|
189
|
+
...options,
|
|
190
|
+
});
|
|
191
|
+
return handle(out.session, out.joined, out.seq);
|
|
192
|
+
},
|
|
193
|
+
/** A session you already have, by its id. */
|
|
194
|
+
async session(id, options) {
|
|
195
|
+
return handle(await getSession(id, options));
|
|
196
|
+
},
|
|
197
|
+
sessions: {
|
|
198
|
+
get: getSession,
|
|
199
|
+
/** Newest first; of one agent, or in some states. */
|
|
200
|
+
list: listSessions,
|
|
201
|
+
/** An ended session and its log go. Its history is storage like any
|
|
202
|
+
* other, kept until you delete it; an open session is refused
|
|
203
|
+
* (`session_open`): stop it first. */
|
|
204
|
+
delete: async (id, options) => {
|
|
205
|
+
await t.json({
|
|
206
|
+
method: "DELETE",
|
|
207
|
+
path: `/v1/agents/sessions/${enc(id)}`,
|
|
208
|
+
...options,
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
triggers: {
|
|
213
|
+
create: (agent, input, options) => t.json({
|
|
214
|
+
method: "POST",
|
|
215
|
+
path: `/v1/agents/${enc(agent)}/triggers`,
|
|
216
|
+
body: input,
|
|
217
|
+
...options,
|
|
218
|
+
}),
|
|
219
|
+
list: async (agent, options) => (await t.json({
|
|
220
|
+
method: "GET",
|
|
221
|
+
path: `/v1/agents/${enc(agent)}/triggers`,
|
|
222
|
+
...options,
|
|
223
|
+
})).data,
|
|
224
|
+
/** Its events are refused, saying `why`; nothing else is held. */
|
|
225
|
+
pause: (id, why, options) => t.json({
|
|
226
|
+
method: "POST",
|
|
227
|
+
path: `/v1/agents/triggers/${enc(id)}/pause`,
|
|
228
|
+
body: { why },
|
|
229
|
+
...options,
|
|
230
|
+
}),
|
|
231
|
+
resume: (id, options) => t.json({
|
|
232
|
+
method: "POST",
|
|
233
|
+
path: `/v1/agents/triggers/${enc(id)}/resume`,
|
|
234
|
+
body: {},
|
|
235
|
+
...options,
|
|
236
|
+
}),
|
|
237
|
+
delete: async (id, options) => {
|
|
238
|
+
await t.json({
|
|
239
|
+
method: "DELETE",
|
|
240
|
+
path: `/v1/agents/triggers/${enc(id)}`,
|
|
241
|
+
...options,
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
/** The harnesses, as data: claude-code, codex, gemini-cli,
|
|
246
|
+
* claude-agent-sdk, openai-agents-sdk. */
|
|
247
|
+
templates: AGENT_TEMPLATES,
|
|
248
|
+
/** The uses, as data: support (your customers) and team (your team). */
|
|
249
|
+
uses: AGENT_USES,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Containers: one OCI image's process on its own microVM, billed at the
|
|
2
|
+
* sandbox rates (packages/cloud/src/api/products/containers.ts), at
|
|
3
|
+
* /v1/containers. In a private beta: an account outside it is refused with
|
|
4
|
+
* product_in_beta (403), and retrying does not change it. */
|
|
5
|
+
import type { Transport } from "../transport.js";
|
|
6
|
+
import { type ResourceRecord } from "./resources.js";
|
|
7
|
+
export type ContainerState = "waiting" | "running" | "sleeping" | "stopped" | "failed" | "deleting" | "deleted";
|
|
8
|
+
export type Container = ResourceRecord & {
|
|
9
|
+
name: string | null;
|
|
10
|
+
labels: Record<string, string>;
|
|
11
|
+
state: ContainerState;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
updatedAt: number;
|
|
14
|
+
deletedAt: number | null;
|
|
15
|
+
/** The image it runs, pinned to its digest (name@sha256:...). */
|
|
16
|
+
image: string;
|
|
17
|
+
entrypoint: string[];
|
|
18
|
+
command: string[];
|
|
19
|
+
workingDir: string;
|
|
20
|
+
user: string;
|
|
21
|
+
/** The TCP port it serves on, or null when it serves none. */
|
|
22
|
+
port: number | null;
|
|
23
|
+
/** The image's exposed ports, as N/tcp or N/udp. */
|
|
24
|
+
exposedPorts: string[];
|
|
25
|
+
onExit: "restart" | "stop";
|
|
26
|
+
sleep: boolean;
|
|
27
|
+
vcpu: number;
|
|
28
|
+
memoryMiB: number;
|
|
29
|
+
/** The environment's names. Values are never answered: they may be secrets. */
|
|
30
|
+
envNames: string[];
|
|
31
|
+
};
|
|
32
|
+
export type CreateContainer = {
|
|
33
|
+
/** An OCI image reference; the answer pins its digest. */
|
|
34
|
+
image: string;
|
|
35
|
+
/** 1 to 16. */
|
|
36
|
+
vcpu: number;
|
|
37
|
+
/** 128 to 65536 MiB. */
|
|
38
|
+
memoryMiB: number;
|
|
39
|
+
name?: string;
|
|
40
|
+
labels?: Record<string, string>;
|
|
41
|
+
/** Replaces the image's Entrypoint and drops its Cmd. */
|
|
42
|
+
entrypoint?: string[];
|
|
43
|
+
/** Replaces the image's Cmd; keeps the effective entrypoint. */
|
|
44
|
+
command?: string[];
|
|
45
|
+
/** Set over the image's own environment. */
|
|
46
|
+
env?: Record<string, string>;
|
|
47
|
+
/** An absolute path; the image's, else /. */
|
|
48
|
+
workingDir?: string;
|
|
49
|
+
/** The image's, else root. */
|
|
50
|
+
user?: string;
|
|
51
|
+
/** 1 to 65535. Required when the image exposes several TCP ports. */
|
|
52
|
+
port?: number;
|
|
53
|
+
/** Default: restart when it serves a port, otherwise stop. */
|
|
54
|
+
onExit?: "restart" | "stop";
|
|
55
|
+
/** Sleep when idle; false by default. */
|
|
56
|
+
sleep?: boolean;
|
|
57
|
+
};
|
|
58
|
+
/** Only the name and labels change; a null name clears it. */
|
|
59
|
+
export type UpdateContainer = {
|
|
60
|
+
name?: string | null;
|
|
61
|
+
labels?: Record<string, string>;
|
|
62
|
+
};
|
|
63
|
+
export declare function containers(t: Transport): {
|
|
64
|
+
base: string;
|
|
65
|
+
create: (input: CreateContainer, options?: import("../transport.js").RequestOptions) => Promise<Container>;
|
|
66
|
+
get: (id: string, options?: import("./resources.js").ResourceGetOptions<"stopped" | "running" | "deleted" | "failed" | "deleting" | "waiting" | "sleeping">) => Promise<Container>;
|
|
67
|
+
list: (query?: import("./resources.js").ResourceListQuery<"stopped" | "running" | "deleted" | "failed" | "deleting" | "waiting" | "sleeping">, options?: import("../transport.js").RequestOptions) => Promise<import("../page.js").Page<Container>>;
|
|
68
|
+
update: (id: string, input: UpdateContainer, options?: import("../transport.js").RequestOptions) => Promise<Container>;
|
|
69
|
+
wait: (id: string, target: "stopped" | "running" | "deleted" | "failed" | "deleting" | "waiting" | "sleeping", options?: import("./resources.js").ResourceWaitOptions) => Promise<Container>;
|
|
70
|
+
delete: (id: string, options?: import("./resources.js").ResourceDeleteOptions) => Promise<Container>;
|
|
71
|
+
};
|
package/dist/products/index.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ import { sandboxTailscale } from "./tailscale.js";
|
|
|
21
21
|
import { secrets } from "./secrets.js";
|
|
22
22
|
import { mcp, sandboxMcp } from "./mcp.js";
|
|
23
23
|
import { events, otel, sandboxMetrics, webhooks } from "./observability.js";
|
|
24
|
+
import { agents } from "./agents.js";
|
|
25
|
+
import { builds } from "./builds.js";
|
|
26
|
+
import { containers } from "./containers.js";
|
|
24
27
|
export declare const clientExtensions: {
|
|
25
28
|
images: typeof images;
|
|
26
29
|
volumes: typeof volumes;
|
|
@@ -41,6 +44,9 @@ export declare const clientExtensions: {
|
|
|
41
44
|
ports: typeof ports;
|
|
42
45
|
ips: typeof ips;
|
|
43
46
|
networks: typeof networks;
|
|
47
|
+
agents: typeof agents;
|
|
48
|
+
builds: typeof builds;
|
|
49
|
+
containers: typeof containers;
|
|
44
50
|
};
|
|
45
51
|
export declare const sandboxExtensions: {
|
|
46
52
|
interpreter: typeof sandboxInterpreter;
|
package/dist/products/index.js
CHANGED
|
@@ -19,6 +19,9 @@ import { sandboxTailscale } from "./tailscale.js";
|
|
|
19
19
|
import { secrets } from "./secrets.js";
|
|
20
20
|
import { mcp, sandboxMcp } from "./mcp.js";
|
|
21
21
|
import { events, otel, sandboxMetrics, webhooks } from "./observability.js";
|
|
22
|
+
import { agents } from "./agents.js";
|
|
23
|
+
import { builds } from "./builds.js";
|
|
24
|
+
import { containers } from "./containers.js";
|
|
22
25
|
/* HOW A LANE ADDS A PRODUCT TO THE SDK
|
|
23
26
|
*
|
|
24
27
|
* 1. Write src/products/<name>.ts exporting a factory:
|
|
@@ -54,6 +57,10 @@ export const clientExtensions = {
|
|
|
54
57
|
ports,
|
|
55
58
|
ips,
|
|
56
59
|
networks,
|
|
60
|
+
agents,
|
|
61
|
+
// Builds (beta): the API answers product_in_beta outside its list.
|
|
62
|
+
builds,
|
|
63
|
+
containers,
|
|
57
64
|
};
|
|
58
65
|
export const sandboxExtensions = {
|
|
59
66
|
interpreter: sandboxInterpreter,
|
|
@@ -75,6 +75,10 @@ export type JobSecret = {
|
|
|
75
75
|
name: string;
|
|
76
76
|
ownerId: string;
|
|
77
77
|
createdBy: string;
|
|
78
|
+
/** Agents besides `createdBy` that may use it, as `principalId`s from
|
|
79
|
+
* `runtime.me()`, or `"all"`: every key of the person. A secret stored on
|
|
80
|
+
* the website is `"all"`. */
|
|
81
|
+
usableBy: string[] | "all";
|
|
78
82
|
version: number;
|
|
79
83
|
createdAt: number;
|
|
80
84
|
updatedAt: number;
|
|
@@ -120,6 +124,16 @@ export declare function secrets(t: Transport): {
|
|
|
120
124
|
scope: (name: string, only: SecretScope, options?: RequestOptions) => Promise<Secret & {
|
|
121
125
|
enforced: boolean;
|
|
122
126
|
}>;
|
|
127
|
+
/** Narrows where a secret goes, without its value: `hosts` a subset of
|
|
128
|
+
* its hosts, `rules` allowing only requests its rules allow now (a secret
|
|
129
|
+
* without rules allows every request). Either absent stays as it is.
|
|
130
|
+
* Anything wider is refused: widening needs the value, through `set`. */
|
|
131
|
+
narrow: (name: string, to: {
|
|
132
|
+
hosts?: string[];
|
|
133
|
+
rules?: SecretRule[];
|
|
134
|
+
}, options?: RequestOptions) => Promise<Secret & {
|
|
135
|
+
enforced: boolean;
|
|
136
|
+
}>;
|
|
123
137
|
/** The sandboxes' secrets: names, hosts and placeholders. Never values.
|
|
124
138
|
* `all()` includes the jobs copies. */
|
|
125
139
|
list: (options?: RequestOptions) => Promise<Secret[]>;
|
|
@@ -140,6 +154,11 @@ export declare function secrets(t: Transport): {
|
|
|
140
154
|
version: number;
|
|
141
155
|
value: string;
|
|
142
156
|
}>;
|
|
157
|
+
/** Choose which of your keys may use the jobs copy besides the one that
|
|
158
|
+
* stored it: agent ids (`principalId` from `me()`), `[]` for only that
|
|
159
|
+
* key, or `"all"` for every key of yours. Replaces the list. Only your
|
|
160
|
+
* person or the key that stored it may call this. */
|
|
161
|
+
share: (name: string, agents: string[] | "all", options?: RequestOptions) => Promise<JobSecret>;
|
|
143
162
|
/** Delete a secret everywhere it is kept: the sandboxes' value is erased
|
|
144
163
|
* and its placeholder stops working, and the jobs copy stops being given
|
|
145
164
|
* to runs (its name stays taken). */
|