Commit b829ea3
feat: executor tool invocation for js-exec
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.
## Native SDK integration (OpenAPI, GraphQL, MCP)
```ts
import { Bash } from "just-bash";
const bash = new Bash({
executor: {
setup: async (sdk) => {
// SDK auto-discovers tools from the OpenAPI spec
await sdk.sources.add({
kind: "openapi",
endpoint: "https://petstore3.swagger.io/api/v3",
specUrl: "https://petstore3.swagger.io/api/v3/openapi.json",
name: "petstore",
});
// GraphQL: introspects the schema
await sdk.sources.add({
kind: "graphql",
endpoint: "https://countries.trevorblades.com/graphql",
name: "countries",
});
// MCP: connects to a Model Context Protocol server
await sdk.sources.add({
kind: "mcp",
endpoint: "https://mcp.example.com/sse",
name: "internal",
transport: "sse",
});
},
},
});
// Code runs in QuickJS sandbox — SDK handles HTTP, auth, validation
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");
'`);
```
## With tool approval
```ts
const bash = new Bash({
executor: {
tools: {
"db.deleteUser": {
description: "Delete a user from the database",
execute: async (args) => {
await db.query("DELETE FROM users WHERE id = $1", [args.id]);
return { deleted: true };
},
},
},
setup: async (sdk) => {
await sdk.sources.add({
kind: "openapi",
endpoint: "https://api.stripe.com/v1",
specUrl: "https://raw.githubusercontent.com/.../stripe-openapi.json",
name: "stripe",
auth: { kind: "bearer", token: process.env.STRIPE_SECRET_KEY },
});
},
// SDK passes approval requests through this callback
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" };
},
},
});
```
## 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);
'`);
```
## 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)
- 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>1 parent d6a5ff0 commit b829ea3
17 files changed
Lines changed: 1180 additions & 5 deletions
File tree
- src
- commands
- js-exec
- examples
- worker-bridge
- interpreter
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
108 | 109 | | |
109 | 110 | | |
| 111 | + | |
110 | 112 | | |
111 | 113 | | |
112 | 114 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
95 | 166 | | |
96 | 167 | | |
97 | 168 | | |
| |||
137 | 208 | | |
138 | 209 | | |
139 | 210 | | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
140 | 218 | | |
141 | 219 | | |
142 | 220 | | |
| |||
277 | 355 | | |
278 | 356 | | |
279 | 357 | | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
280 | 366 | | |
281 | 367 | | |
282 | 368 | | |
| |||
450 | 536 | | |
451 | 537 | | |
452 | 538 | | |
453 | | - | |
| 539 | + | |
| 540 | + | |
454 | 541 | | |
455 | 542 | | |
456 | 543 | | |
| |||
464 | 551 | | |
465 | 552 | | |
466 | 553 | | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
467 | 584 | | |
468 | 585 | | |
469 | 586 | | |
| |||
523 | 640 | | |
524 | 641 | | |
525 | 642 | | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
526 | 732 | | |
527 | 733 | | |
528 | 734 | | |
529 | 735 | | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
530 | 739 | | |
531 | 740 | | |
532 | 741 | | |
| |||
666 | 875 | | |
667 | 876 | | |
668 | 877 | | |
| 878 | + | |
669 | 879 | | |
670 | 880 | | |
671 | 881 | | |
| |||
0 commit comments