Skip to content

feat(sdk): expose the sandbox binding to TypeScript - #402

Open
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-5-backendsfrom
itamar/alien-75-sandbox-6-typescript
Open

feat(sdk): expose the sandbox binding to TypeScript#402
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-5-backendsfrom
itamar/alien-75-sandbox-6-typescript

Conversation

@ItamarZand88

Copy link
Copy Markdown
Contributor

Summary

Exposes the sandbox binding to TypeScript, so an application written in TypeScript can create a session, run a command, move files and stop it through the same contract the Rust bindings use.

When a TypeScript application asks for a sandbox binding:

  1. The generated N-API layer hands back the same binding the Rust provider resolved.
  2. The SDK wraps it in a typed surface — create, runCommand, readFile, writeFiles, terminate.
  3. A capability the platform does not support is a typed error naming both the platform and the capability, not a silent no-op.

What I did

The published capability set is the interesting part. A portable application cannot assume every backend does everything — preview exists only on AWS, hostname allowlists exist nowhere, and ceilings are enforced on some platforms and refused on others. Rather than let a call fail somewhere deep in a cloud SDK, the capability set is exposed to TypeScript as data, so an application can branch before it calls.

The types are generated from the Rust definitions rather than hand-written, so the two languages cannot drift: the binding JSON is the cross-language contract.

Files touched

  • crates/alien-bindings-node/ — the N-API surface.
  • packages/bindings/src/types.ts — the generated binding types.
  • packages/sdk/src/index.ts — the public export.

How I tested

  • packages/bindings/tests/sandbox.test.ts — the binding shape and the capability set as TypeScript sees them.
  • pnpm generate — confirms the committed types match the Rust definitions; CI fails the build if they drift.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

The PR exposes sandbox sessions and operations through the Node binding and the public TypeScript SDK.

  • Adds the N-API sandbox handle, command-frame stream, session lifecycle operations, file operations, and capability reporting.
  • Adds lazy TypeScript factories, typed sessions and command frames, structured validation errors, and SDK exports.
  • Ensures early iterator exit closes the native command stream and adds coverage for normal, exceptional, and early-exit cleanup.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported early-exit leak is addressed by unconditional stream closure, and the provider lifecycle terminates streaming commands when that stream is dropped.

Important Files Changed

Filename Overview
crates/alien-bindings-node/src/sandbox.rs Adds the native sandbox bridge, typed session and frame conversion, pull-based command streaming, capability reporting, and lifecycle/file operations.
packages/bindings/src/factories.ts Adds the lazy typed sandbox wrapper and reliably closes command streams when async iteration ends.
packages/bindings/src/loader.ts Defines the raw native sandbox, session, frame, and stream contracts consumed by the TypeScript wrapper.
packages/bindings/src/types.ts Adds the public TypeScript sandbox API, command-frame union, session type, and command options.
packages/sdk/src/index.ts Re-exports the sandbox factory and associated types through the public SDK facade.

Sequence Diagram

sequenceDiagram
  participant App as TypeScript application
  participant SDK as TypeScript sandbox wrapper
  participant NAPI as N-API SandboxHandle
  participant Provider as Rust Sandbox provider
  App->>SDK: runCommand(sessionId, command, options)
  SDK->>NAPI: runCommand(...)
  NAPI->>Provider: run_command(...)
  Provider-->>NAPI: command frame stream
  loop Pull frames
    App->>SDK: iterator.next()
    SDK->>NAPI: next()
    NAPI-->>SDK: stdout / stderr / exit frame
    SDK-->>App: typed CommandFrame
  end
  App->>SDK: break, return, throw, or finish
  SDK->>NAPI: close()
  NAPI->>Provider: drop stream
Loading

Reviews (5): Last reviewed commit: "feat(sdk): expose the sandbox binding to..." | Re-trigger Greptile

Comment on lines +187 to +204
runCommand: (sessionId, command, options) => ({
async *[Symbol.asyncIterator]() {
const stream = await guard(handle, raw =>
raw.runCommand(sessionId, command, options.deadlineMs, options.workingDirectory ?? null),
)

while (true) {
let next: RawCommandFrame | null
try {
next = await stream.next()
} catch (err) {
throw unwrapNapiError(err)
}
if (next === null) return
yield frame(next)
}
},
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Early exit leaves command running

When a caller breaks, returns, or throws from the for await loop before the terminal frame, the generator exits without explicitly closing the native stream. Cleanup then depends on garbage collection, causing the sandbox command to continue consuming compute until the wrapper is collected or its deadline expires.

Knowledge Base Used: SDK and bindings: how apps reach storage, KV, queue, and vault

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/bindings/src/factories.ts
Line: 187-204

Comment:
**Early exit leaves command running**

When a caller breaks, returns, or throws from the `for await` loop before the terminal frame, the generator exits without explicitly closing the native stream. Cleanup then depends on garbage collection, causing the sandbox command to continue consuming compute until the wrapper is collected or its deadline expires.

**Knowledge Base Used:** [SDK and bindings: how apps reach storage, KV, queue, and vault](https://app.greptile.com/alien/-/custom-context/knowledge-base/alienplatform/alien/-/docs/sdk-bindings.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex

@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-6-typescript branch from 1b99225 to b89ff3c Compare August 11, 2026 21:22
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from f81e5a8 to 64d4aeb Compare August 11, 2026 21:22
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-6-typescript branch from b89ff3c to 52bc095 Compare August 11, 2026 21:26
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch 2 times, most recently from 11c5234 to 469d8cf Compare August 11, 2026 22:16
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-6-typescript branch from 52bc095 to 28faa2f Compare August 11, 2026 22:16
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-5-backends branch from 469d8cf to a7c48f5 Compare August 11, 2026 22:24
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-6-typescript branch from 28faa2f to 57c6f58 Compare August 11, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant