@agentstep/agent-sdk 0.1.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.
Files changed (105) hide show
  1. package/package.json +45 -0
  2. package/src/auth/middleware.ts +38 -0
  3. package/src/backends/claude/args.ts +88 -0
  4. package/src/backends/claude/index.ts +193 -0
  5. package/src/backends/claude/permission-hook.ts +152 -0
  6. package/src/backends/claude/tool-bridge.ts +211 -0
  7. package/src/backends/claude/translator.ts +209 -0
  8. package/src/backends/claude/wrapper-script.ts +45 -0
  9. package/src/backends/codex/args.ts +69 -0
  10. package/src/backends/codex/auth.ts +35 -0
  11. package/src/backends/codex/index.ts +57 -0
  12. package/src/backends/codex/setup.ts +37 -0
  13. package/src/backends/codex/translator.ts +223 -0
  14. package/src/backends/codex/wrapper-script.ts +26 -0
  15. package/src/backends/factory/args.ts +45 -0
  16. package/src/backends/factory/auth.ts +30 -0
  17. package/src/backends/factory/index.ts +56 -0
  18. package/src/backends/factory/setup.ts +34 -0
  19. package/src/backends/factory/translator.ts +139 -0
  20. package/src/backends/factory/wrapper-script.ts +33 -0
  21. package/src/backends/gemini/args.ts +44 -0
  22. package/src/backends/gemini/auth.ts +30 -0
  23. package/src/backends/gemini/index.ts +53 -0
  24. package/src/backends/gemini/setup.ts +34 -0
  25. package/src/backends/gemini/translator.ts +139 -0
  26. package/src/backends/gemini/wrapper-script.ts +26 -0
  27. package/src/backends/opencode/args.ts +53 -0
  28. package/src/backends/opencode/auth.ts +53 -0
  29. package/src/backends/opencode/index.ts +70 -0
  30. package/src/backends/opencode/mcp.ts +67 -0
  31. package/src/backends/opencode/setup.ts +54 -0
  32. package/src/backends/opencode/translator.ts +168 -0
  33. package/src/backends/opencode/wrapper-script.ts +46 -0
  34. package/src/backends/registry.ts +38 -0
  35. package/src/backends/shared/ndjson.ts +29 -0
  36. package/src/backends/shared/translator-types.ts +69 -0
  37. package/src/backends/shared/wrap-prompt.ts +17 -0
  38. package/src/backends/types.ts +85 -0
  39. package/src/config/index.ts +95 -0
  40. package/src/db/agents.ts +185 -0
  41. package/src/db/api_keys.ts +78 -0
  42. package/src/db/batch.ts +142 -0
  43. package/src/db/client.ts +81 -0
  44. package/src/db/environments.ts +127 -0
  45. package/src/db/events.ts +208 -0
  46. package/src/db/memory.ts +143 -0
  47. package/src/db/migrations.ts +295 -0
  48. package/src/db/proxy.ts +37 -0
  49. package/src/db/sessions.ts +295 -0
  50. package/src/db/vaults.ts +110 -0
  51. package/src/errors.ts +53 -0
  52. package/src/handlers/agents.ts +194 -0
  53. package/src/handlers/batch.ts +41 -0
  54. package/src/handlers/docs.ts +87 -0
  55. package/src/handlers/environments.ts +154 -0
  56. package/src/handlers/events.ts +234 -0
  57. package/src/handlers/index.ts +12 -0
  58. package/src/handlers/memory.ts +141 -0
  59. package/src/handlers/openapi.ts +14 -0
  60. package/src/handlers/sessions.ts +223 -0
  61. package/src/handlers/stream.ts +76 -0
  62. package/src/handlers/threads.ts +26 -0
  63. package/src/handlers/ui/app.js +984 -0
  64. package/src/handlers/ui/index.html +112 -0
  65. package/src/handlers/ui/style.css +164 -0
  66. package/src/handlers/ui.ts +1281 -0
  67. package/src/handlers/vaults.ts +99 -0
  68. package/src/http.ts +35 -0
  69. package/src/index.ts +104 -0
  70. package/src/init.ts +227 -0
  71. package/src/openapi/registry.ts +8 -0
  72. package/src/openapi/schemas.ts +625 -0
  73. package/src/openapi/spec.ts +691 -0
  74. package/src/providers/apple.ts +220 -0
  75. package/src/providers/daytona.ts +217 -0
  76. package/src/providers/docker.ts +264 -0
  77. package/src/providers/e2b.ts +203 -0
  78. package/src/providers/fly.ts +276 -0
  79. package/src/providers/modal.ts +222 -0
  80. package/src/providers/podman.ts +206 -0
  81. package/src/providers/registry.ts +28 -0
  82. package/src/providers/shared.ts +11 -0
  83. package/src/providers/sprites.ts +55 -0
  84. package/src/providers/types.ts +73 -0
  85. package/src/providers/vercel.ts +208 -0
  86. package/src/proxy/forward.ts +111 -0
  87. package/src/queue/index.ts +111 -0
  88. package/src/sessions/actor.ts +53 -0
  89. package/src/sessions/bus.ts +155 -0
  90. package/src/sessions/driver.ts +818 -0
  91. package/src/sessions/grader.ts +120 -0
  92. package/src/sessions/interrupt.ts +14 -0
  93. package/src/sessions/sweeper.ts +136 -0
  94. package/src/sessions/threads.ts +126 -0
  95. package/src/sessions/tools.ts +50 -0
  96. package/src/shutdown.ts +78 -0
  97. package/src/sprite/client.ts +294 -0
  98. package/src/sprite/exec.ts +161 -0
  99. package/src/sprite/lifecycle.ts +339 -0
  100. package/src/sprite/pool.ts +65 -0
  101. package/src/sprite/setup.ts +159 -0
  102. package/src/state.ts +61 -0
  103. package/src/types.ts +339 -0
  104. package/src/util/clock.ts +7 -0
  105. package/src/util/ids.ts +11 -0
@@ -0,0 +1,294 @@
1
+ /**
2
+ * sprites.dev REST client.
3
+ *
4
+ * All endpoints Bearer-authenticated. Per-operation timeouts via AbortSignal.
5
+ * Checkpoint/restore consume the NDJSON progress stream and reject on an
6
+ * `error` event. See https://docs.sprites.dev/api/v001-rc30/ for the spec.
7
+ *
8
+ * URL/auth pattern inspired by
9
+ *
10
+ */
11
+ import { getConfig } from "../config";
12
+ import { ApiError } from "../errors";
13
+
14
+ export interface Sprite {
15
+ name: string;
16
+ status: "cold" | "warm" | "running";
17
+ http_endpoint?: string;
18
+ url_settings?: { auth: "public" | "sprite" };
19
+ created_at?: string;
20
+ updated_at?: string;
21
+ last_started_at?: string | null;
22
+ last_active_at?: string | null;
23
+ }
24
+
25
+ export interface ListSpritesResponse {
26
+ sprites: Sprite[];
27
+ has_more: boolean;
28
+ next_continuation_token?: string | null;
29
+ }
30
+
31
+ export interface Checkpoint {
32
+ id: string;
33
+ comment?: string | null;
34
+ created_at?: string;
35
+ }
36
+
37
+ function authHeaders(): Record<string, string> {
38
+ const cfg = getConfig();
39
+ if (!cfg.spriteToken) {
40
+ throw new ApiError(
41
+ 500,
42
+ "server_error",
43
+ "SPRITE_TOKEN is not configured (set env var or settings.sprite_token)",
44
+ );
45
+ }
46
+ return { Authorization: `Bearer ${cfg.spriteToken}` };
47
+ }
48
+
49
+ function api(): string {
50
+ return getConfig().spriteApi.replace(/\/+$/, "");
51
+ }
52
+
53
+ function signalFor(timeoutMs?: number): AbortSignal | undefined {
54
+ const t = timeoutMs ?? getConfig().spriteTimeoutMs;
55
+ if (!t) return undefined;
56
+ return AbortSignal.timeout(t);
57
+ }
58
+
59
+ async function handleJson<T>(res: Response, op: string): Promise<T> {
60
+ if (!res.ok) {
61
+ const text = await res.text().catch(() => "");
62
+ throw new ApiError(
63
+ res.status >= 500 ? 502 : 500,
64
+ "server_error",
65
+ `sprites.dev ${op} failed (${res.status}): ${text.slice(0, 500)}`,
66
+ );
67
+ }
68
+ return (await res.json()) as T;
69
+ }
70
+
71
+ async function consumeNdjsonProgress(res: Response, op: string): Promise<void> {
72
+ if (!res.ok) {
73
+ const text = await res.text().catch(() => "");
74
+ throw new ApiError(
75
+ res.status >= 500 ? 502 : 500,
76
+ "server_error",
77
+ `sprites.dev ${op} failed (${res.status}): ${text.slice(0, 500)}`,
78
+ );
79
+ }
80
+ if (!res.body) return;
81
+ const reader = res.body.getReader();
82
+ const decoder = new TextDecoder();
83
+ let buf = "";
84
+ for (;;) {
85
+ const { done, value } = await reader.read();
86
+ if (done) break;
87
+ buf += decoder.decode(value, { stream: true });
88
+ let nl = buf.indexOf("\n");
89
+ while (nl !== -1) {
90
+ const line = buf.slice(0, nl).trim();
91
+ buf = buf.slice(nl + 1);
92
+ nl = buf.indexOf("\n");
93
+ if (!line) continue;
94
+ try {
95
+ const evt = JSON.parse(line) as { type?: string; message?: string; error?: string };
96
+ if (evt.type === "error") {
97
+ throw new ApiError(502, "server_error", `${op}: ${evt.message || evt.error || "error"}`);
98
+ }
99
+ } catch (err) {
100
+ if (err instanceof ApiError) throw err;
101
+ // ignore non-JSON progress lines
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Sprite CRUD
109
+ // ---------------------------------------------------------------------------
110
+
111
+ export async function createSprite(input: {
112
+ name: string;
113
+ wait_for_capacity?: boolean;
114
+ url_settings?: { auth?: "public" | "sprite" };
115
+ }): Promise<Sprite> {
116
+ const res = await fetch(`${api()}/v1/sprites`, {
117
+ method: "POST",
118
+ headers: { ...authHeaders(), "content-type": "application/json" },
119
+ body: JSON.stringify(input),
120
+ signal: signalFor(),
121
+ });
122
+ return handleJson<Sprite>(res, "createSprite");
123
+ }
124
+
125
+ export async function getSprite(name: string): Promise<Sprite | null> {
126
+ const res = await fetch(
127
+ `${api()}/v1/sprites/${encodeURIComponent(name)}`,
128
+ { headers: authHeaders(), signal: signalFor() },
129
+ );
130
+ if (res.status === 404) return null;
131
+ return handleJson<Sprite>(res, "getSprite");
132
+ }
133
+
134
+ export async function deleteSprite(name: string): Promise<void> {
135
+ const res = await fetch(
136
+ `${api()}/v1/sprites/${encodeURIComponent(name)}`,
137
+ { method: "DELETE", headers: authHeaders(), signal: signalFor() },
138
+ );
139
+ if (!res.ok && res.status !== 404) {
140
+ const text = await res.text().catch(() => "");
141
+ throw new ApiError(502, "server_error", `deleteSprite: ${res.status} ${text.slice(0, 200)}`);
142
+ }
143
+ }
144
+
145
+ export async function listSprites(opts: {
146
+ prefix?: string;
147
+ max_results?: number;
148
+ continuation_token?: string;
149
+ } = {}): Promise<ListSpritesResponse> {
150
+ const params = new URLSearchParams();
151
+ if (opts.prefix) params.set("prefix", opts.prefix);
152
+ if (opts.max_results != null) params.set("max_results", String(opts.max_results));
153
+ if (opts.continuation_token) params.set("continuation_token", opts.continuation_token);
154
+ const res = await fetch(
155
+ `${api()}/v1/sprites?${params.toString()}`,
156
+ { headers: authHeaders(), signal: signalFor() },
157
+ );
158
+ return handleJson<ListSpritesResponse>(res, "listSprites");
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Checkpoints
163
+ // ---------------------------------------------------------------------------
164
+
165
+ export async function createCheckpoint(name: string, comment?: string): Promise<Checkpoint> {
166
+ // create returns streaming NDJSON progress; the final message carries the id.
167
+ const res = await fetch(
168
+ `${api()}/v1/sprites/${encodeURIComponent(name)}/checkpoint`,
169
+ {
170
+ method: "POST",
171
+ headers: { ...authHeaders(), "content-type": "application/json" },
172
+ body: JSON.stringify({ comment: comment ?? null }),
173
+ signal: signalFor(getConfig().agentTimeoutMs),
174
+ },
175
+ );
176
+ if (!res.ok || !res.body) {
177
+ const text = await res.text().catch(() => "");
178
+ throw new ApiError(502, "server_error", `createCheckpoint: ${res.status} ${text.slice(0, 200)}`);
179
+ }
180
+
181
+ const reader = res.body.getReader();
182
+ const decoder = new TextDecoder();
183
+ let buf = "";
184
+ let id: string | null = null;
185
+ for (;;) {
186
+ const { done, value } = await reader.read();
187
+ if (done) break;
188
+ buf += decoder.decode(value, { stream: true });
189
+ let nl = buf.indexOf("\n");
190
+ while (nl !== -1) {
191
+ const line = buf.slice(0, nl).trim();
192
+ buf = buf.slice(nl + 1);
193
+ nl = buf.indexOf("\n");
194
+ if (!line) continue;
195
+ try {
196
+ const evt = JSON.parse(line) as {
197
+ type?: string;
198
+ data?: string;
199
+ message?: string;
200
+ checkpoint_id?: string;
201
+ id?: string;
202
+ };
203
+ if (evt.type === "error") {
204
+ throw new ApiError(502, "server_error", `createCheckpoint: ${evt.data || evt.message || "error"}`);
205
+ }
206
+ // The API embeds the checkpoint ID in multiple places:
207
+ // - complete event: "Checkpoint v1 created successfully"
208
+ // - info event: " ID: v1"
209
+ // Try structured fields first, then parse from data strings.
210
+ if (evt.checkpoint_id || evt.id) {
211
+ id = evt.checkpoint_id || evt.id || null;
212
+ }
213
+ if (!id && evt.type === "info" && evt.data) {
214
+ const idMatch = evt.data.match(/^\s*ID:\s*(.+)$/);
215
+ if (idMatch) id = idMatch[1].trim();
216
+ }
217
+ if (!id && evt.type === "complete" && evt.data) {
218
+ const ckptMatch = evt.data.match(/Checkpoint\s+(\S+)/);
219
+ if (ckptMatch) id = ckptMatch[1];
220
+ }
221
+ } catch (err) {
222
+ if (err instanceof ApiError) throw err;
223
+ // ignore non-JSON
224
+ }
225
+ }
226
+ }
227
+ if (!id) {
228
+ throw new ApiError(502, "server_error", "createCheckpoint: no checkpoint id in progress stream");
229
+ }
230
+ return { id };
231
+ }
232
+
233
+ export async function restoreCheckpoint(name: string, checkpointId: string): Promise<void> {
234
+ const res = await fetch(
235
+ `${api()}/v1/sprites/${encodeURIComponent(name)}/checkpoints/${encodeURIComponent(checkpointId)}/restore`,
236
+ {
237
+ method: "POST",
238
+ headers: authHeaders(),
239
+ signal: signalFor(getConfig().agentTimeoutMs),
240
+ },
241
+ );
242
+ await consumeNdjsonProgress(res, "restoreCheckpoint");
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Exec (HTTP variant — for one-shot setup commands only)
247
+ // ---------------------------------------------------------------------------
248
+
249
+ /**
250
+ * HTTP POST exec variant. Suitable only for non-streaming, non-interruptible
251
+ * operations like setup-script execution. For normal turns and anything that
252
+ * might be killed, use `lib/sprite/exec.ts` (WebSocket) instead.
253
+ */
254
+ export async function httpExec(
255
+ name: string,
256
+ argv: string[],
257
+ opts: { stdin?: string; timeoutMs?: number } = {},
258
+ ): Promise<{ stdout: string; stderr: string; exit_code: number }> {
259
+ const params = new URLSearchParams();
260
+ for (const c of argv) params.append("cmd", c);
261
+ if (opts.stdin != null) params.set("stdin", "true");
262
+ const res = await fetch(
263
+ `${api()}/v1/sprites/${encodeURIComponent(name)}/exec?${params.toString()}`,
264
+ {
265
+ method: "POST",
266
+ headers: authHeaders(),
267
+ body: opts.stdin ?? undefined,
268
+ signal: signalFor(opts.timeoutMs ?? getConfig().agentTimeoutMs),
269
+ },
270
+ );
271
+ if (!res.ok) {
272
+ const text = await res.text().catch(() => "");
273
+ throw new ApiError(502, "server_error", `httpExec: ${res.status} ${text.slice(0, 300)}`);
274
+ }
275
+ const ct = res.headers.get("content-type") || "";
276
+ if (ct.includes("application/json")) {
277
+ return (await res.json()) as { stdout: string; stderr: string; exit_code: number };
278
+ }
279
+ const text = await res.text();
280
+ return { stdout: text, stderr: "", exit_code: 0 };
281
+ }
282
+
283
+ export async function killExecSession(spriteName: string, execSessionId: string): Promise<void> {
284
+ const res = await fetch(
285
+ `${api()}/v1/sprites/${encodeURIComponent(spriteName)}/exec/${encodeURIComponent(execSessionId)}/kill`,
286
+ { method: "POST", headers: authHeaders(), signal: signalFor() },
287
+ );
288
+ if (!res.ok && res.status !== 404) {
289
+ const text = await res.text().catch(() => "");
290
+ throw new ApiError(502, "server_error", `killExecSession: ${res.status} ${text.slice(0, 200)}`);
291
+ }
292
+ // The kill endpoint also streams NDJSON progress; consume and discard.
293
+ await consumeNdjsonProgress(res, "killExecSession").catch(() => {});
294
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * sprites.dev exec client.
3
+ *
4
+ * HISTORY:
5
+ * The plan called for WebSocket exec from M1 so we could use the
6
+ * `POST /v1/sprites/{name}/exec/{session_id}/kill` endpoint for clean
7
+ * interrupts. Spike S4 decoded the WS protocol (session_info JSON,
8
+ * \x01-prefixed stdout frames, \x03 exit marker) but a follow-up probe
9
+ * revealed that **WS stdin delivery does not work for processes that
10
+ * read to EOF** — claude hangs indefinitely waiting for input even after
11
+ * we send the stdin text frame, and no EOF-signaling mechanism we tried
12
+ * (empty frame, \x04 EOT, JSON control, socket close) delivered stdin
13
+ * cleanly. See /tmp/ca-ws-test2.mjs for the probe matrix.
14
+ *
15
+ * HTTP POST exec handles stdin cleanly — the request body is the stdin
16
+ * and the connection close signals EOF. So we use HTTP exec here, and
17
+ * implement interrupt as an abort on the HTTP fetch. That gives us
18
+ * clean *client-visible* semantics (session.status_idle{stop_reason:
19
+ * interrupted} is emitted within milliseconds of the interrupt) even
20
+ * though the sprite-side process may run to its natural completion.
21
+ *
22
+ * If sprites.dev later publishes the WS stdin protocol or a kill-by-name
23
+ * HTTP endpoint, the kill path can be upgraded without touching the
24
+ * driver — it's all behind the ExecSession interface.
25
+ */
26
+ import { getConfig } from "../config";
27
+ import { ApiError } from "../errors";
28
+
29
+ export interface ExecResult {
30
+ code: number;
31
+ }
32
+
33
+ export interface ExecSession {
34
+ /** Readable stream of raw stdout bytes (HTTP streaming body) */
35
+ stdout: ReadableStream<Uint8Array>;
36
+ /** Resolves when the exec finishes */
37
+ exit: Promise<ExecResult>;
38
+ /** Best-effort kill — aborts the HTTP fetch */
39
+ kill(): Promise<void>;
40
+ /** Server-issued exec session id (not available for HTTP exec) */
41
+ execSessionId?: string;
42
+ }
43
+
44
+ export interface ExecOptions {
45
+ argv: string[];
46
+ stdin?: string;
47
+ /** Signal that, when aborted, cancels the exec */
48
+ signal?: AbortSignal;
49
+ /** Timeout in ms for the whole exec (defaults to config.agentTimeoutMs) */
50
+ timeoutMs?: number;
51
+ }
52
+
53
+ function api(): string {
54
+ return getConfig().spriteApi.replace(/\/+$/, "");
55
+ }
56
+
57
+ function authHeaders(): Record<string, string> {
58
+ const cfg = getConfig();
59
+ if (!cfg.spriteToken) {
60
+ throw new ApiError(500, "server_error", "SPRITE_TOKEN is not configured");
61
+ }
62
+ return { Authorization: `Bearer ${cfg.spriteToken}` };
63
+ }
64
+
65
+ /**
66
+ * Start a streaming HTTP exec on a sprite. The `argv` becomes `?cmd=a&cmd=b...`
67
+ * query params; stdin (if any) is posted as the request body; the response
68
+ * body is the streamed stdout.
69
+ *
70
+ * When the AbortSignal fires, the underlying fetch is aborted — the HTTP
71
+ * connection closes from our side, which the driver treats as an interrupted
72
+ * turn. The sprite-side process is unaffected; it will finish naturally.
73
+ */
74
+ export async function startExec(
75
+ spriteName: string,
76
+ opts: ExecOptions,
77
+ ): Promise<ExecSession> {
78
+ const params = new URLSearchParams();
79
+ for (const c of opts.argv) params.append("cmd", c);
80
+ if (opts.stdin != null) params.set("stdin", "true");
81
+
82
+ const localAbort = new AbortController();
83
+ const timeoutMs = opts.timeoutMs ?? getConfig().agentTimeoutMs;
84
+ const timeoutId = setTimeout(
85
+ () => localAbort.abort(new DOMException("exec timeout", "AbortError")),
86
+ timeoutMs,
87
+ );
88
+
89
+ // Link caller's signal to our internal abort
90
+ if (opts.signal) {
91
+ if (opts.signal.aborted) localAbort.abort(opts.signal.reason);
92
+ else opts.signal.addEventListener("abort", () => localAbort.abort(opts.signal!.reason));
93
+ }
94
+
95
+ const res = await fetch(
96
+ `${api()}/v1/sprites/${encodeURIComponent(spriteName)}/exec?${params.toString()}`,
97
+ {
98
+ method: "POST",
99
+ headers: authHeaders(),
100
+ body: opts.stdin ?? undefined,
101
+ signal: localAbort.signal,
102
+ },
103
+ );
104
+
105
+ if (!res.ok) {
106
+ clearTimeout(timeoutId);
107
+ const text = await res.text().catch(() => "");
108
+ throw new ApiError(
109
+ 502,
110
+ "server_error",
111
+ `sprite exec failed (${res.status}): ${text.slice(0, 300)}`,
112
+ );
113
+ }
114
+ if (!res.body) {
115
+ clearTimeout(timeoutId);
116
+ throw new ApiError(502, "server_error", "sprite exec returned no body");
117
+ }
118
+
119
+ let exitResolve: (v: ExecResult) => void = () => {};
120
+ let exitReject: (e: unknown) => void = () => {};
121
+ const exit = new Promise<ExecResult>((resolve, reject) => {
122
+ exitResolve = resolve;
123
+ exitReject = reject;
124
+ });
125
+
126
+ // Tee the body: one side goes to the consumer, the other is a watcher that
127
+ // resolves `exit` when the stream ends or rejects on abort.
128
+ const [consumerStream, watchStream] = res.body.tee();
129
+
130
+ (async () => {
131
+ const reader = watchStream.getReader();
132
+ try {
133
+ for (;;) {
134
+ const { done } = await reader.read();
135
+ if (done) break;
136
+ }
137
+ exitResolve({ code: 0 });
138
+ } catch (err) {
139
+ if (localAbort.signal.aborted) {
140
+ exitReject(new DOMException("aborted", "AbortError"));
141
+ } else {
142
+ exitReject(err);
143
+ }
144
+ } finally {
145
+ clearTimeout(timeoutId);
146
+ try {
147
+ reader.releaseLock();
148
+ } catch {
149
+ /* ignore */
150
+ }
151
+ }
152
+ })();
153
+
154
+ return {
155
+ stdout: consumerStream,
156
+ exit,
157
+ async kill() {
158
+ localAbort.abort(new DOMException("killed", "AbortError"));
159
+ },
160
+ };
161
+ }