Skip to content

feat: snapshot and resume example#740

Open
james-rl wants to merge 3 commits intomainfrom
james/snp-example
Open

feat: snapshot and resume example#740
james-rl wants to merge 3 commits intomainfrom
james/snp-example

Conversation

@james-rl
Copy link
Contributor

@james-rl james-rl commented Mar 4, 2026

User description

⚠️ PR Title Must Follow Conventional Commits

Format: feat[optional scope]: <description>

Examples: feat: add new SDK method · feat(storage): support file uploads · feat!: breaking API change


Description

Motivation

Better document how to use our SDK

Changes

Testing

  • Unit tests added
  • Integration tests added
  • Smoke Tests added/updated
  • Tested locally

Breaking Changes

Checklist

  • PR title follows Conventional Commits format (feat: or feat(scope):)
  • Documentation updated (if needed)
  • Breaking changes documented (if applicable)

CodeAnt-AI Description

Add Devbox Snapshot and Resume example

What Changed

  • Adds a runnable example script that creates a devbox, snapshots its disk, launches a new devbox from that snapshot, modifies the original, and verifies the clone retains the original state
  • Registers the new example in the example registry so it appears in the examples CLI and tooling
  • Documents the example in EXAMPLES.md and the examples index so users can discover how to run and test the snapshot/resume workflow

Impact

✅ Clearer snapshot/resume guidance
✅ Easier to run and test disk snapshot workflows
✅ Fewer surprises when creating devbox clones

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 4, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Mar 4, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 4, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Possible Race Condition
    The code creates a devbox from a snapshot and then immediately reads a file from the clone. The clone may not be fully booted or the disk may not be mounted yet when the read is attempted, causing intermittent failures. Add an explicit readiness check or retry/polling before reading files from the clone.

  • Auto-generated File Edit
    The file header states this file is auto-generated. Manually adding imports or entries may be overwritten by the generator. Ensure the generator and its templates are updated, or document why a manual change is required.

Comment on lines +54 to +77
await dbxOriginal.file.write(FILE_PATH, ORIGINAL_CONTENT);

// Read and display the file contents
const catOriginalBefore = await dbxOriginal.cmd.exec(`cat ${FILE_PATH}`);
const originalContentBefore = await catOriginalBefore.stdout();

// Create a disk snapshot of the original devbox
const snapshot = await dbxOriginal.snapshotDisk({
name: 'my-snapshot',
});
cleanup.add(`snapshot:${snapshot.id}`, () => snapshot.delete());

// Create a new devbox from the snapshot
const dbxClone = await sdk.devbox.createFromSnapshot(snapshot.id, {
name: 'dbx_clone',
launch_parameters: {
resource_size_request: 'X_SMALL',
keep_alive_time_seconds: 60 * 5,
},
});
cleanup.add(`devbox:${dbxClone.id}`, () => sdk.devbox.fromId(dbxClone.id).shutdown());

// Modify the file on the original devbox
await dbxOriginal.file.write(FILE_PATH, MODIFIED_CONTENT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The calls to the devbox file write API are using the wrong argument shape (path, contents positional arguments) instead of the expected single params object, so at runtime the SDK will send an invalid request body to writeFileContents, likely causing the file not to be written and subsequent cat commands and checks to fail. [type error]

Severity Level: Major ⚠️
- ❌ Devbox snapshot/resume example script fails on first write.
- ⚠️ `yarn test:examples` will fail when this example runs.
- ⚠️ Documentation misleads about correct `devbox.file.write` usage.
Suggested change
await dbxOriginal.file.write(FILE_PATH, ORIGINAL_CONTENT);
// Read and display the file contents
const catOriginalBefore = await dbxOriginal.cmd.exec(`cat ${FILE_PATH}`);
const originalContentBefore = await catOriginalBefore.stdout();
// Create a disk snapshot of the original devbox
const snapshot = await dbxOriginal.snapshotDisk({
name: 'my-snapshot',
});
cleanup.add(`snapshot:${snapshot.id}`, () => snapshot.delete());
// Create a new devbox from the snapshot
const dbxClone = await sdk.devbox.createFromSnapshot(snapshot.id, {
name: 'dbx_clone',
launch_parameters: {
resource_size_request: 'X_SMALL',
keep_alive_time_seconds: 60 * 5,
},
});
cleanup.add(`devbox:${dbxClone.id}`, () => sdk.devbox.fromId(dbxClone.id).shutdown());
// Modify the file on the original devbox
await dbxOriginal.file.write(FILE_PATH, MODIFIED_CONTENT);
await dbxOriginal.file.write({ file_path: FILE_PATH, contents: ORIGINAL_CONTENT });
await dbxOriginal.file.write({ file_path: FILE_PATH, contents: MODIFIED_CONTENT });
Steps of Reproduction ✅
1. From the repo root `/workspace/api-client-ts`, run the documented example command `yarn
tsn -T examples/devbox-snapshot-resume.ts` (as specified in the front‑matter of
`examples/devbox-snapshot-resume.ts:23`).

2. Execution enters `recipe()` in `examples/devbox-snapshot-resume.ts:36-41`, constructs a
`RunloopSDK` instance, and creates a devbox via `sdk.devbox.create(...)` at lines 43-50,
yielding `dbxOriginal`.

3. The script calls `await dbxOriginal.file.write(FILE_PATH, ORIGINAL_CONTENT);` at
`examples/devbox-snapshot-resume.ts:54`, passing two positional arguments (string path and
string contents). At runtime this invokes `DevboxFileOps.write(params, options?)` defined
in `src/sdk/devbox.ts:469-485`, where `params` is expected to be a
`DevboxWriteFileContentsParams` object but actually receives the string `FILE_PATH`, and
`options` receives the string `ORIGINAL_CONTENT`.

4. Inside `DevboxFileOps.write`, the call
`this.client.devboxes.writeFileContents(this.devboxId, params, options)` forwards these
incorrect arguments to the generated API client. The API contract is validated by tests in
`tests/api-resources/devboxes/devboxes.test.ts:577-595`, which show that
`writeFileContents` requires `client.devboxes.writeFileContents('id', { contents:
'contents', file_path: 'file_path' })`. Because the example passes a bare string instead
of the required params object, the HTTP request body is malformed, causing the write to
fail so `/home/user/welcome.txt` is never created and the example flow (subsequent `cat`
and checks at lines 57-84) fails when run.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** examples/devbox-snapshot-resume.ts
**Line:** 54:77
**Comment:**
	*Type Error: The calls to the devbox file write API are using the wrong argument shape (`path, contents` positional arguments) instead of the expected single params object, so at runtime the SDK will send an invalid request body to `writeFileContents`, likely causing the file not to be written and subsequent `cat` commands and checks to fail.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 4, 2026

CodeAnt AI finished reviewing your PR.

@james-rl james-rl requested review from dines-rl and tode-rl March 4, 2026 00:09
await dbxOriginal.file.write({ file_path: FILE_PATH, contents: ORIGINAL_CONTENT });

// Read and display the file contents
const catOriginalBefore = await dbxOriginal.cmd.exec(`cat ${FILE_PATH}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catExecResult or something

keep_alive_time_seconds: 60 * 5,
},
});
cleanup.add(`devbox:${dbxClone.id}`, () => sdk.devbox.fromId(dbxClone.id).shutdown());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.addResource(x) we know which one it is with just the id xxx_yyy

},
{
name: 'snapshot created successfully',
passed: Boolean(snapshot.id),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird for an example maybe we lift this out of the example itself?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants