diff --git a/.github/workflows/sdk-coverage.yml b/.github/workflows/sdk-coverage.yml
index 288c3dc29..7f273e2f8 100644
--- a/.github/workflows/sdk-coverage.yml
+++ b/.github/workflows/sdk-coverage.yml
@@ -78,9 +78,9 @@ jobs:
const testsPassed = '${{ steps.tests.outcome }}' === 'success';
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const COMMENT_MARKER = '';
-
+
let lines = [COMMENT_MARKER];
-
+
if (!testsPassed) {
let failedTests = [];
try {
@@ -173,17 +173,17 @@ jobs:
lines.push(`[📋 View workflow run](${runUrl})`);
}
}
-
+
const comment = lines.join('\n');
-
+
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
-
+
const existingComment = comments.find(c => c.body.includes(COMMENT_MARKER));
-
+
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
@@ -199,4 +199,3 @@ jobs:
body: comment
});
}
-
diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml
index 3ffc19b8e..0e483415e 100644
--- a/.github/workflows/smoke-tests.yml
+++ b/.github/workflows/smoke-tests.yml
@@ -11,7 +11,7 @@ on:
required: true
type: string
discriminator:
- default: "true"
+ default: 'true'
type: string
workflow_dispatch:
inputs:
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 515e014d0..46d76cd91 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -16,6 +16,7 @@ Runnable examples live in [`examples/`](./examples).
- [Secrets with Devbox and Agent Gateway](#secrets-with-devbox)
+
## Blueprint with Build Context
**Use case:** Create a blueprint using the object store to provide docker build context files, then verify files are copied into the image.
@@ -23,6 +24,7 @@ Runnable examples live in [`examples/`](./examples).
**Tags:** `blueprint`, `object-store`, `build-context`, `devbox`, `cleanup`
### Workflow
+
- Create a temporary directory with sample application files
- Upload the directory to object storage as build context
- Create a blueprint with a Dockerfile that copies the context files
@@ -31,14 +33,17 @@ Runnable examples live in [`examples/`](./examples).
- Shutdown devbox and delete blueprint and storage object
### Prerequisites
+
- `RUNLOOP_API_KEY`
### Run
+
```sh
yarn tsn -T examples/blueprint-with-build-context.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -46,6 +51,7 @@ yarn test:examples
**Source:** [`examples/blueprint-with-build-context.ts`](./examples/blueprint-with-build-context.ts)
+
## Devbox From Blueprint (Run Command, Shutdown)
**Use case:** Create a devbox from a blueprint, run a command, fetch logs, validate output, and cleanly tear everything down.
@@ -53,6 +59,7 @@ yarn test:examples
**Tags:** `devbox`, `blueprint`, `commands`, `logs`, `cleanup`
### Workflow
+
- Create a blueprint
- Fetch blueprint build logs
- Create a devbox from the blueprint
@@ -62,14 +69,17 @@ yarn test:examples
- Shutdown devbox and delete blueprint
### Prerequisites
+
- `RUNLOOP_API_KEY`
### Run
+
```sh
yarn tsn -T examples/devbox-from-blueprint-lifecycle.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -77,6 +87,7 @@ yarn test:examples
**Source:** [`examples/devbox-from-blueprint-lifecycle.ts`](./examples/devbox-from-blueprint-lifecycle.ts)
+
## Devbox Mounts (Agent, Code, Object)
**Use case:** Launch a devbox that combines an agent mount for Claude Code, a code mount for the Runloop CLI repo, and an object mount for startup files.
@@ -84,6 +95,7 @@ yarn test:examples
**Tags:** `devbox`, `mounts`, `agent`, `code`, `object`, `claude-code`, `agent-gateway`, `ttl`
### Workflow
+
- Create or reuse an agent by name
- Create a secret for an agent and route it through agent gateway
- Upload a temporary directory as a storage object with a TTL
@@ -93,15 +105,18 @@ yarn test:examples
- Shutdown the devbox and delete the temporary secret and object
### Prerequisites
+
- `RUNLOOP_API_KEY`
- `ANTHROPIC_API_KEY`
### Run
+
```sh
ANTHROPIC_API_KEY=sk-ant-xxx yarn tsn -T examples/devbox-mounts.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -109,6 +124,7 @@ yarn test:examples
**Source:** [`examples/devbox-mounts.ts`](./examples/devbox-mounts.ts)
+
## Devbox Snapshots (Suspend, Resume, Restore, Delete)
**Use case:** Upload a file to a devbox, preserve it across suspend and resume, create a disk snapshot, restore multiple devboxes from that snapshot, mutate each copy independently, and delete the snapshot when finished.
@@ -116,6 +132,7 @@ yarn test:examples
**Tags:** `devbox`, `snapshot`, `suspend`, `resume`, `files`, `cleanup`
### Workflow
+
- Create a source devbox
- Upload a file and mutate it into a shared baseline
- Suspend and resume the source devbox
@@ -125,14 +142,17 @@ yarn test:examples
- Shutdown the devboxes and delete the snapshot
### Prerequisites
+
- `RUNLOOP_API_KEY`
### Run
+
```sh
yarn tsn -T examples/devbox-snapshots.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -140,6 +160,7 @@ yarn test:examples
**Source:** [`examples/devbox-snapshots.ts`](./examples/devbox-snapshots.ts)
+
## Devbox Tunnel (HTTP Server Access)
**Use case:** Create a devbox with a tunnel, start an HTTP server, and access the server from the local machine through the tunnel.
@@ -147,6 +168,7 @@ yarn test:examples
**Tags:** `devbox`, `tunnel`, `networking`, `http`
### Workflow
+
- Create a devbox with a tunnel
- Start an HTTP server inside the devbox
- Read the tunnel details from the devbox
@@ -155,14 +177,17 @@ yarn test:examples
- Shutdown the devbox
### Prerequisites
+
- `RUNLOOP_API_KEY`
### Run
+
```sh
yarn tsn -T examples/devbox-tunnel.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -170,6 +195,7 @@ yarn test:examples
**Source:** [`examples/devbox-tunnel.ts`](./examples/devbox-tunnel.ts)
+
## MCP Hub + Claude Code + GitHub
**Use case:** Connect Claude Code running in a devbox to GitHub tools through MCP Hub without exposing raw GitHub credentials to the devbox.
@@ -177,6 +203,7 @@ yarn test:examples
**Tags:** `mcp`, `devbox`, `github`, `commands`, `cleanup`
### Workflow
+
- Create an MCP config for GitHub
- Store GitHub token as a Runloop secret
- Launch a devbox with MCP Hub wiring
@@ -185,16 +212,19 @@ yarn test:examples
- Shutdown devbox and clean up cloud resources
### Prerequisites
+
- `RUNLOOP_API_KEY`
- `GITHUB_TOKEN (GitHub PAT with repo scope)`
- `ANTHROPIC_API_KEY`
### Run
+
```sh
GITHUB_TOKEN=ghp_xxx ANTHROPIC_API_KEY=sk-ant-xxx yarn tsn -T examples/mcp-github-tools.ts
```
### Test
+
```sh
yarn test:examples
```
@@ -202,6 +232,7 @@ yarn test:examples
**Source:** [`examples/mcp-github-tools.ts`](./examples/mcp-github-tools.ts)
+
## Secrets with Devbox and Agent Gateway
**Use case:** Use a normal secret for sensitive app data in the devbox and agent gateway for upstream API credentials that should never be exposed to the agent.
@@ -209,6 +240,7 @@ yarn test:examples
**Tags:** `secrets`, `devbox`, `agent-gateway`, `credentials`, `environment-variables`, `cleanup`
### Workflow
+
- Create a secret for application data that should be available inside the devbox
- Create a separate secret for an upstream API credential
- Create an agent gateway config for an upstream API
@@ -217,14 +249,17 @@ yarn test:examples
- Shutdown the devbox and delete the gateway config and both secrets
### Prerequisites
+
- `RUNLOOP_API_KEY`
### Run
+
```sh
yarn tsn -T examples/secrets-with-devbox.ts
```
### Test
+
```sh
yarn test:examples
```
diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json
index 17640c75c..d8649021f 100644
--- a/packages/mcp-server/manifest.json
+++ b/packages/mcp-server/manifest.json
@@ -18,9 +18,7 @@
"entry_point": "index.js",
"mcp_config": {
"command": "node",
- "args": [
- "${__dirname}/index.js"
- ],
+ "args": ["${__dirname}/index.js"],
"env": {
"RUNLOOP_API_KEY": "${user_config.RUNLOOP_API_KEY}"
}
@@ -41,7 +39,5 @@
"node": ">=18.0.0"
}
},
- "keywords": [
- "api"
- ]
+ "keywords": ["api"]
}
diff --git a/scripts/generate-examples-md.cjs b/scripts/generate-examples-md.cjs
index 6d92b4874..3b2d1bce0 100644
--- a/scripts/generate-examples-md.cjs
+++ b/scripts/generate-examples-md.cjs
@@ -27,10 +27,7 @@ function readExampleFiles() {
.readdirSync(EXAMPLES_DIR)
.filter(
(file) =>
- file.endsWith('.ts') &&
- !file.startsWith('_') &&
- file !== 'types.ts' &&
- file !== 'registry.ts',
+ file.endsWith('.ts') && !file.startsWith('_') && file !== 'types.ts' && file !== 'registry.ts',
)
.sort();
}
@@ -107,9 +104,7 @@ function ensureLlmsReferences(examples) {
}
if (referencedFiles.size === 0) {
- throw new Error(
- `${path.relative(ROOT, LLMS_FILE)}: expected at least one reference to examples/*.ts`,
- );
+ throw new Error(`${path.relative(ROOT, LLMS_FILE)}: expected at least one reference to examples/*.ts`);
}
const generatedFiles = new Set(examples.map((example) => example.fileName));
@@ -125,6 +120,7 @@ function ensureLlmsReferences(examples) {
function markdownForExample(example) {
return [
``,
+ '',
`## ${example.title}`,
'',
`**Use case:** ${example.use_case}`,
@@ -132,17 +128,21 @@ function markdownForExample(example) {
`**Tags:** ${example.tags.map((tag) => `\`${tag}\``).join(', ')}`,
'',
'### Workflow',
+ '',
...example.workflow.map((step) => `- ${step}`),
'',
'### Prerequisites',
+ '',
...example.prerequisites.map((item) => `- \`${item}\``),
'',
'### Run',
+ '',
'```sh',
example.run,
'```',
'',
'### Test',
+ '',
'```sh',
example.test,
'```',
@@ -256,7 +256,9 @@ function main() {
fs.writeFileSync(OUTPUT_FILE, markdown);
fs.writeFileSync(OUTPUT_REGISTRY_FILE, registrySource);
process.stdout.write(`Wrote ${path.relative(ROOT, OUTPUT_FILE)} from ${examples.length} example(s)\n`);
- process.stdout.write(`Wrote ${path.relative(ROOT, OUTPUT_REGISTRY_FILE)} from ${examples.length} example(s)\n`);
+ process.stdout.write(
+ `Wrote ${path.relative(ROOT, OUTPUT_REGISTRY_FILE)} from ${examples.length} example(s)\n`,
+ );
}
main();
diff --git a/tests/lib/devbox-state.test.ts b/tests/lib/devbox-state.test.ts
index 8282df88c..15c2a79c7 100644
--- a/tests/lib/devbox-state.test.ts
+++ b/tests/lib/devbox-state.test.ts
@@ -4,7 +4,9 @@ import { APIError } from '../../src/error';
type MockDevbox = { status: string; id: string };
-function makeOptions(overrides: Partial> = {}): DevboxStateWaitOptions {
+function makeOptions(
+ overrides: Partial> = {},
+): DevboxStateWaitOptions {
return {
client: overrides.client ?? { post: jest.fn() },
devboxId: 'dbx-123',
@@ -47,37 +49,53 @@ describe('awaitDevboxState', () => {
const failure: MockDevbox = { status: 'failure', id: 'dbx-123' };
const post = jest.fn().mockResolvedValue(failure);
- await expect(
- awaitDevboxState(makeOptions({ client: { post } })),
- ).rejects.toThrow('Devbox dbx-123 ended in failure');
+ await expect(awaitDevboxState(makeOptions({ client: { post } }))).rejects.toThrow(
+ 'Devbox dbx-123 ended in failure',
+ );
});
test('should use timeoutMs field for the long-poll deadline', async () => {
const post = jest.fn().mockImplementation(
- (_url: string, opts: { signal?: AbortSignal }) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 100);
- opts?.signal?.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (_url: string, opts: { signal?: AbortSignal }) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 100);
+ opts?.signal?.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
- await expect(
- awaitDevboxState(makeOptions({ client: { post }, timeoutMs: 150 })),
- ).rejects.toThrow(PollingTimeoutError);
+ await expect(awaitDevboxState(makeOptions({ client: { post }, timeoutMs: 150 }))).rejects.toThrow(
+ PollingTimeoutError,
+ );
});
test('should enforce timeout mid-request by aborting the request', async () => {
const post = jest.fn().mockImplementation(
- (_url: string, opts: { signal?: AbortSignal }) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 5000);
- timer.unref();
- opts?.signal?.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (_url: string, opts: { signal?: AbortSignal }) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 5000);
+ timer.unref();
+ opts?.signal?.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
const start = Date.now();
- await expect(
- awaitDevboxState(makeOptions({ client: { post }, timeoutMs: 100 })),
- ).rejects.toThrow(PollingTimeoutError);
+ await expect(awaitDevboxState(makeOptions({ client: { post }, timeoutMs: 100 }))).rejects.toThrow(
+ PollingTimeoutError,
+ );
const elapsed = Date.now() - start;
expect(elapsed).toBeLessThan(1000);
});
diff --git a/tests/lib/long-poll-options.test.ts b/tests/lib/long-poll-options.test.ts
index 658109974..858e19fc3 100644
--- a/tests/lib/long-poll-options.test.ts
+++ b/tests/lib/long-poll-options.test.ts
@@ -1,9 +1,15 @@
-import { PollingTimeoutError, resolveLongPollTimeoutMs, _resetDeprecationWarning } from '../../src/lib/polling';
+import {
+ PollingTimeoutError,
+ resolveLongPollTimeoutMs,
+ _resetDeprecationWarning,
+} from '../../src/lib/polling';
import { awaitDevboxState, DevboxStateWaitOptions } from '../../src/lib/devbox-state';
type MockDevbox = { status: string; id: string };
-function makeOptions(overrides: Partial> = {}): DevboxStateWaitOptions {
+function makeOptions(
+ overrides: Partial> = {},
+): DevboxStateWaitOptions {
return {
client: overrides.client ?? { post: jest.fn() },
devboxId: 'dbx-123',
@@ -60,27 +66,32 @@ describe('LongPollRequestOptions resolution', () => {
describe('end-to-end via awaitDevboxState', () => {
function slowTransition(): jest.Mock {
- return jest.fn().mockImplementation(
- () => new Promise((resolve) => setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 100)),
- );
+ return jest
+ .fn()
+ .mockImplementation(
+ () =>
+ new Promise((resolve) =>
+ setTimeout(() => resolve({ status: 'provisioning', id: 'dbx-123' }), 100),
+ ),
+ );
}
test('times out via longPoll.timeoutMs path', async () => {
const post = slowTransition();
const timeoutMs = resolveTimeoutMs({ longPoll: { timeoutMs: 150 } });
- await expect(
- awaitDevboxState(makeOptions({ client: { post }, timeoutMs })),
- ).rejects.toThrow(PollingTimeoutError);
+ await expect(awaitDevboxState(makeOptions({ client: { post }, timeoutMs }))).rejects.toThrow(
+ PollingTimeoutError,
+ );
});
test('times out via deprecated polling.timeoutMs path', async () => {
const post = slowTransition();
const timeoutMs = resolveTimeoutMs({ polling: { timeoutMs: 150 } });
- await expect(
- awaitDevboxState(makeOptions({ client: { post }, timeoutMs })),
- ).rejects.toThrow(PollingTimeoutError);
+ await expect(awaitDevboxState(makeOptions({ client: { post }, timeoutMs }))).rejects.toThrow(
+ PollingTimeoutError,
+ );
});
test('longPoll.timeoutMs wins over polling.timeoutMs (generous longPoll, tiny polling)', async () => {
@@ -145,9 +156,7 @@ describe('resolveLongPollTimeoutMs deprecation warnings', () => {
test('warns when ignored polling fields are present', () => {
resolveLongPollTimeoutMs({ polling: { maxAttempts: 5 } } as any);
expect(warnSpy).toHaveBeenCalledTimes(1);
- expect(warnSpy).toHaveBeenCalledWith(
- expect.stringContaining('maxAttempts'),
- );
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('maxAttempts'));
});
test('lists all ignored fields in a single warning', () => {
@@ -170,9 +179,7 @@ describe('resolveLongPollTimeoutMs deprecation warnings', () => {
test('warns about deprecated polling.timeoutMs when longPoll is absent', () => {
resolveLongPollTimeoutMs({ polling: { timeoutMs: 3000 } });
expect(warnSpy).toHaveBeenCalledTimes(1);
- expect(warnSpy).toHaveBeenCalledWith(
- expect.stringContaining('deprecated'),
- );
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('deprecated'));
});
test('does not warn when only longPoll is used', () => {
@@ -189,7 +196,9 @@ describe('resolveLongPollTimeoutMs deprecation warnings', () => {
test('still returns the correct timeoutMs', () => {
expect(resolveLongPollTimeoutMs({ polling: { maxAttempts: 5, timeoutMs: 3000 } } as any)).toBe(3000);
_resetDeprecationWarning();
- expect(resolveLongPollTimeoutMs({ longPoll: { timeoutMs: 7000 }, polling: { timeoutMs: 1000 } })).toBe(7000);
+ expect(resolveLongPollTimeoutMs({ longPoll: { timeoutMs: 7000 }, polling: { timeoutMs: 1000 } })).toBe(
+ 7000,
+ );
_resetDeprecationWarning();
expect(resolveLongPollTimeoutMs(undefined)).toBeUndefined();
});
diff --git a/tests/lib/streaming-reconnection.test.ts b/tests/lib/streaming-reconnection.test.ts
index 8a140ed0e..86d2d530b 100644
--- a/tests/lib/streaming-reconnection.test.ts
+++ b/tests/lib/streaming-reconnection.test.ts
@@ -415,13 +415,10 @@ describe('withStreamAutoReconnect', () => {
controller: new AbortController(),
} as unknown as APIResponseProps;
- const p = new StreamBackedAPIPromise(
- Promise.resolve(props),
- () => {
- dataStarted = true;
- return Promise.resolve(new Stream(async function* () {}, props.controller));
- },
- );
+ const p = new StreamBackedAPIPromise(Promise.resolve(props), () => {
+ dataStarted = true;
+ return Promise.resolve(new Stream(async function* () {}, props.controller));
+ });
expect(dataStarted).toBe(false);
await p.asResponse();
diff --git a/tests/objects/axon.test.ts b/tests/objects/axon.test.ts
index 3b72c8793..61ead1983 100644
--- a/tests/objects/axon.test.ts
+++ b/tests/objects/axon.test.ts
@@ -162,11 +162,7 @@ describe('Axon', () => {
const stream = await axon.subscribeSse();
- expect(mockClient.axons.subscribeSse).toHaveBeenCalledWith(
- 'axn_123456789',
- undefined,
- undefined,
- );
+ expect(mockClient.axons.subscribeSse).toHaveBeenCalledWith('axn_123456789', undefined, undefined);
expect(stream).toBe(mockStream);
});
@@ -176,11 +172,9 @@ describe('Axon', () => {
await axon.subscribeSse(undefined, { timeout: 60000 });
- expect(mockClient.axons.subscribeSse).toHaveBeenCalledWith(
- 'axn_123456789',
- undefined,
- { timeout: 60000 },
- );
+ expect(mockClient.axons.subscribeSse).toHaveBeenCalledWith('axn_123456789', undefined, {
+ timeout: 60000,
+ });
});
});
diff --git a/tests/objects/scorer.test.ts b/tests/objects/scorer.test.ts
index ddcc82672..65daf93a5 100644
--- a/tests/objects/scorer.test.ts
+++ b/tests/objects/scorer.test.ts
@@ -1,8 +1,5 @@
import { Scorer } from '../../src/sdk/scorer';
-import type {
- ScorerRetrieveResponse,
- ScorerUpdateResponse,
-} from '../../src/resources/scenarios/scorers';
+import type { ScorerRetrieveResponse, ScorerUpdateResponse } from '../../src/resources/scenarios/scorers';
// Mock the Runloop client
jest.mock('../../src/index');
@@ -28,7 +25,6 @@ describe('Scorer', () => {
type: 'my_custom_scorer',
bash_script: 'echo "1.0"',
};
-
});
describe('fromId', () => {
@@ -187,6 +183,5 @@ describe('Scorer', () => {
'Update failed',
);
});
-
});
});
diff --git a/tests/polling.test.ts b/tests/polling.test.ts
index a3dbc7ff8..2f4369702 100644
--- a/tests/polling.test.ts
+++ b/tests/polling.test.ts
@@ -460,10 +460,7 @@ describe('Polling', () => {
const mockResult = { status: 'provisioning', id: 'x' };
const running = { status: 'running', id: 'x' };
const initialRequest = jest.fn().mockResolvedValue(mockResult);
- const pollingRequest = jest
- .fn()
- .mockResolvedValueOnce(mockResult)
- .mockResolvedValueOnce(running);
+ const pollingRequest = jest.fn().mockResolvedValueOnce(mockResult).mockResolvedValueOnce(running);
await poll(initialRequest, pollingRequest, {
shouldStop: (r: any) => r.status === 'running',
@@ -624,9 +621,7 @@ describe('longPollUntil', () => {
const serverError = new APIError(500, {}, 'Internal Server Error', {});
const request = jest.fn().mockRejectedValue(serverError);
- await expect(
- longPollUntil(request, { shouldStop: () => true }),
- ).rejects.toThrow(serverError);
+ await expect(longPollUntil(request, { shouldStop: () => true })).rejects.toThrow(serverError);
expect(request).toHaveBeenCalledTimes(1);
});
@@ -634,17 +629,23 @@ describe('longPollUntil', () => {
const error = new Error('Network failure');
const request = jest.fn().mockRejectedValue(error);
- await expect(
- longPollUntil(request, { shouldStop: () => true }),
- ).rejects.toThrow(error);
+ await expect(longPollUntil(request, { shouldStop: () => true })).rejects.toThrow(error);
});
test('should throw PollingTimeoutError when timeoutMs is exceeded', async () => {
const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 100);
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (signal: AbortSignal) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 100);
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
await expect(
@@ -657,11 +658,19 @@ describe('longPollUntil', () => {
test('should enforce timeout mid-request by aborting the request', async () => {
const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
- timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (signal: AbortSignal) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
+ timer.unref();
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
const start = Date.now();
@@ -678,13 +687,13 @@ describe('longPollUntil', () => {
test('should throw when timeoutMs is zero or negative', async () => {
const request = jest.fn();
- await expect(
- longPollUntil(request, { timeoutMs: 0, shouldStop: () => true }),
- ).rejects.toThrow('timeoutMs must be positive');
+ await expect(longPollUntil(request, { timeoutMs: 0, shouldStop: () => true })).rejects.toThrow(
+ 'timeoutMs must be positive',
+ );
- await expect(
- longPollUntil(request, { timeoutMs: -1, shouldStop: () => true }),
- ).rejects.toThrow('timeoutMs must be positive');
+ await expect(longPollUntil(request, { timeoutMs: -1, shouldStop: () => true })).rejects.toThrow(
+ 'timeoutMs must be positive',
+ );
});
test('should succeed within timeoutMs', async () => {
@@ -761,11 +770,19 @@ describe('longPollUntil', () => {
test('should throw LongPollAbortError when signal is aborted mid-request', async () => {
const controller = new AbortController();
const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
- timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (signal: AbortSignal) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
+ timer.unref();
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
const start = Date.now();
@@ -809,7 +826,14 @@ describe('longPollUntil', () => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
});
}
return Promise.resolve(provisioning);
@@ -833,11 +857,19 @@ describe('longPollUntil', () => {
test('abort signal should work together with timeoutMs', async () => {
const controller = new AbortController();
const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
- timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- }),
+ (signal: AbortSignal) =>
+ new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
+ timer.unref();
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ }),
);
setTimeout(() => controller.abort(), 50);
@@ -853,16 +885,21 @@ describe('longPollUntil', () => {
test('should pass AbortSignal to request and abort it on timeout', async () => {
const receivedSignals: AbortSignal[] = [];
- const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => {
- receivedSignals.push(signal);
- return new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
- timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- });
- },
- );
+ const request = jest.fn().mockImplementation((signal: AbortSignal) => {
+ receivedSignals.push(signal);
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
+ timer.unref();
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ });
+ });
await expect(
longPollUntil(request, {
@@ -878,16 +915,21 @@ describe('longPollUntil', () => {
test('should pass AbortSignal to request and abort it on external signal', async () => {
const controller = new AbortController();
const receivedSignals: AbortSignal[] = [];
- const request = jest.fn().mockImplementation(
- (signal: AbortSignal) => {
- receivedSignals.push(signal);
- return new Promise((resolve, reject) => {
- const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
- timer.unref();
- signal.addEventListener('abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true });
- });
- },
- );
+ const request = jest.fn().mockImplementation((signal: AbortSignal) => {
+ receivedSignals.push(signal);
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => resolve({ status: 'provisioning' }), 5000);
+ timer.unref();
+ signal.addEventListener(
+ 'abort',
+ () => {
+ clearTimeout(timer);
+ reject(new Error('aborted'));
+ },
+ { once: true },
+ );
+ });
+ });
setTimeout(() => controller.abort(), 50);
diff --git a/tests/resources/executions-stream-offset.test.ts b/tests/resources/executions-stream-offset.test.ts
index ff59a2f93..71a11ece7 100644
--- a/tests/resources/executions-stream-offset.test.ts
+++ b/tests/resources/executions-stream-offset.test.ts
@@ -4,10 +4,7 @@ import { Executions, type ExecutionUpdateChunk } from '../../src/resources/devbo
import { Stream } from '../../src/streaming';
import type { Runloop } from '../../src/index';
-function sseChunkStreamPromise(
- sseBody: string,
- path: string,
-): APIPromise> {
+function sseChunkStreamPromise(sseBody: string, path: string): APIPromise> {
const controller = new AbortController();
const stream = Stream.fromSSEResponse(
new Response(sseBody, { headers: { 'content-type': 'text/event-stream' } }) as any,
@@ -54,11 +51,7 @@ describe('Executions stream offset with reconnect', () => {
pull(c) {
pullCount += 1;
if (pullCount === 1) {
- c.enqueue(
- encoder.encode(
- `data: ${JSON.stringify({ output: 'a', offset: 60 })}\n\n`,
- ),
- );
+ c.enqueue(encoder.encode(`data: ${JSON.stringify({ output: 'a', offset: 60 })}\n\n`));
} else {
c.error(timeout);
}
diff --git a/tests/smoketests/devboxes.test.ts b/tests/smoketests/devboxes.test.ts
index 2ce6b15db..ab399050f 100644
--- a/tests/smoketests/devboxes.test.ts
+++ b/tests/smoketests/devboxes.test.ts
@@ -184,9 +184,9 @@ describe('smoketest: devboxes', () => {
test('await running (createAndAwaitRunning, deprecated polling path)', async () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation((...args: unknown[]) => {
- if (typeof args[0] === 'string' && args[0].includes('[runloop-api-client]')) return;
- process.stderr.write(`console.warn: ${args.join(' ')}\n`);
- });
+ if (typeof args[0] === 'string' && args[0].includes('[runloop-api-client]')) return;
+ process.stderr.write(`console.warn: ${args.join(' ')}\n`);
+ });
try {
const created = await client.devboxes.createAndAwaitRunning(
{
@@ -246,9 +246,9 @@ describe('smoketest: devboxes', () => {
'createAndAwaitRunning timeout (deprecated polling path)',
async () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation((...args: unknown[]) => {
- if (typeof args[0] === 'string' && args[0].includes('[runloop-api-client]')) return;
- process.stderr.write(`console.warn: ${args.join(' ')}\n`);
- });
+ if (typeof args[0] === 'string' && args[0].includes('[runloop-api-client]')) return;
+ process.stderr.write(`console.warn: ${args.join(' ')}\n`);
+ });
try {
await expect(
client.devboxes.createAndAwaitRunning(
diff --git a/tests/smoketests/polling-streaming-flow.test.ts b/tests/smoketests/polling-streaming-flow.test.ts
index 930a6135e..9c9c035ab 100644
--- a/tests/smoketests/polling-streaming-flow.test.ts
+++ b/tests/smoketests/polling-streaming-flow.test.ts
@@ -138,7 +138,11 @@ const client = makeClient();
longPoll: { timeoutMs: 10 * 60 * 1000 },
});
- const stream = await client.devboxes.executions.streamStdoutUpdates(devbox.id, started.execution_id, {});
+ const stream = await client.devboxes.executions.streamStdoutUpdates(
+ devbox.id,
+ started.execution_id,
+ {},
+ );
let out = '';
for await (const chunk of stream) {
out += chunk.output;