Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Add `executor` option to `BashOptions` that makes a `tools` proxy available
to JavaScript code running in js-exec. Tool calls are synchronous from the
QuickJS sandbox's perspective — they block via SharedArrayBuffer/Atomics
while the host resolves them asynchronously.
Natively integrates with `@executor/sdk` — the `setup` callback receives
the SDK instance for adding OpenAPI, GraphQL, and MCP sources that
auto-discover tools. `onToolApproval` controls which tools are allowed.
## Native SDK integration (OpenAPI, GraphQL, MCP)
```ts
import { Bash } from "just-bash";
const bash = new Bash({
executor: {
setup: async (sdk) => {
await sdk.sources.add({
kind: "openapi",
endpoint: "https://petstore3.swagger.io/api/v3",
specUrl: "https://petstore3.swagger.io/api/v3/openapi.json",
name: "petstore",
});
await sdk.sources.add({
kind: "graphql",
endpoint: "https://countries.trevorblades.com/graphql",
name: "countries",
});
await sdk.sources.add({
kind: "mcp",
endpoint: "https://mcp.example.com/sse",
name: "internal",
transport: "sse",
});
},
onToolApproval: async (request) => {
// Auto-approve reads, require confirmation for writes/deletes
if (request.operationKind === "read") return { approved: true };
const ok = await promptUser(
`Allow ${request.toolPath} (${request.operationKind})?`
);
return ok ? { approved: true } : { approved: false, reason: "denied" };
},
},
});
await bash.exec(`js-exec -c '
const pets = await tools.petstore.findPetsByStatus({ status: "available" });
const country = await tools.countries.country({ code: "US" });
const docs = await tools.internal.searchDocs({ query: "deploy" });
console.log(pets.length, "pets,", country.name, ",", docs.hits.length, "docs");
'`);
```
## Inline tools (no SDK needed)
```ts
const bash = new Bash({
executor: {
tools: {
"math.add": {
description: "Add two numbers",
execute: (args) => ({ sum: args.a + args.b }),
},
"db.query": {
execute: async (args) => {
const rows = await pg.query(args.sql);
return { rows };
},
},
},
},
});
await bash.exec(`js-exec -c '
const sum = await tools.math.add({ a: 3, b: 4 });
console.log(sum.sum);
const data = await tools.db.query({ sql: "SELECT * FROM users" });
for (const row of data.rows) console.log(row.name);
'`);
```
## Both: inline tools + SDK sources
```ts
const bash = new Bash({
executor: {
tools: {
"util.timestamp": {
execute: () => ({ ts: Math.floor(Date.now() / 1000) }),
},
},
setup: async (sdk) => {
await sdk.sources.add({ kind: "openapi", endpoint: "...", specUrl: "...", name: "api" });
},
onToolApproval: "allow-all",
},
});
```
## Implementation
- New INVOKE_TOOL (400) opcode in the SharedArrayBuffer bridge protocol
- SyncBackend.invokeTool() — worker-side sync call via Atomics.wait
- BridgeHandler accepts optional invokeTool callback, handles new opcode
- Worker registers __invokeTool native function + tools Proxy when
hasExecutorTools is set
- Tool invoker threads from BashOptions → Bash → InterpreterOptions →
InterpreterContext → CommandContext → js-exec → BridgeHandler → worker
- executor.setup lazily initializes @executor/sdk on first exec
(dynamic import — SDK is only loaded when setup is provided)
- executor.onToolApproval wired through to createExecutor() — controls
approval for SDK-discovered tools (inline tools are always allowed)
- SDK's CodeExecutor runtime delegates to js-exec's executeForExecutor
- Full executor mode (log capture + result capture) available via
executorMode flag for direct SDK integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
executoroption toBashOptionsthat makes atoolsproxy available to JavaScript code running in js-exec. Tool calls are synchronous from the QuickJS sandbox's perspective — they block via SharedArrayBuffer/Atomics while the host resolves them asynchronously.Natively integrates with
@executor/sdk— thesetupcallback receives the SDK instance for adding OpenAPI, GraphQL, and MCP sources that auto-discover tools.onToolApprovalcontrols which tools are allowed.Native SDK integration (OpenAPI, GraphQL, MCP)
Inline tools (no SDK needed)
Both: inline tools + SDK sources
Implementation
INVOKE_TOOL(400) opcode in the SharedArrayBuffer bridge protocolSyncBackend.invokeTool()— worker-side sync call viaAtomics.waitBridgeHandleraccepts optionalinvokeToolcallback, handles new opcode__invokeToolnative function +toolsProxy whenhasExecutorToolsis setBashOptions→Bash→InterpreterOptions→InterpreterContext→CommandContext→ js-exec →BridgeHandler→ workerexecutor.setuplazily initializes@executor/sdkon first exec (dynamic import — SDK is only loaded whensetupis provided)executor.onToolApprovalwired through tocreateExecutor()— controls approval for SDK-discovered tools (inline tools are always allowed)CodeExecutorruntime delegates to js-exec'sexecuteForExecutorexecutorModeflag for direct SDK integration