libfx 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -30
- package/fx-core.wasm +0 -0
- package/fx-term.wasm +0 -0
- package/libfx.darwin-arm64.node +0 -0
- package/libfx.darwin-x64.node +0 -0
- package/libfx.linux-arm64.node +0 -0
- package/libfx.linux-x64.node +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,78 +1,357 @@
|
|
|
1
1
|
# libfx
|
|
2
2
|
|
|
3
|
-
`libfx` embeds fx agents and interactive terminals in JavaScript
|
|
3
|
+
`libfx` embeds fx agents and interactive terminals in JavaScript
|
|
4
|
+
applications. It supports Node.js hosts and browser environments with
|
|
5
|
+
JavaScript Promise Integration (JSPI).
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
- `createFxTerminal()` for the interactive terminal
|
|
7
|
-
- `supportsJspi()` for WebAssembly capability detection
|
|
8
|
-
- `encodeXtermKeyEvent()` and `xtermAdapter()` for terminal integration
|
|
7
|
+
## Installation
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
```sh
|
|
10
|
+
npm install libfx
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requirements:
|
|
14
|
+
|
|
15
|
+
- Node.js 20 or later
|
|
16
|
+
- Chrome or Edge 137 or later for browser WebAssembly
|
|
17
|
+
- JSPI when using the WebAssembly backend
|
|
18
|
+
- A Vercel AI Gateway credential or a host-provided authenticated `fetch`
|
|
19
|
+
|
|
20
|
+
The package includes:
|
|
21
|
+
|
|
22
|
+
- Native Node addons for Linux and macOS on x64 and arm64
|
|
23
|
+
- `fx-core.wasm` for headless agents
|
|
24
|
+
- `fx-term.wasm` for interactive terminals
|
|
25
|
+
- A dependency-free JavaScript host layer
|
|
11
26
|
|
|
12
|
-
|
|
27
|
+
## Exports
|
|
28
|
+
|
|
29
|
+
| Import | Environment | Description |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `libfx` | Node.js or browser | Environment-aware default |
|
|
32
|
+
| `libfx/node` | Node.js | Native-first Node entry point |
|
|
33
|
+
| `libfx/browser` | Browser | WebAssembly browser entry point |
|
|
34
|
+
| `libfx/wasm` | Browser or Node.js | Direct WebAssembly host layer |
|
|
35
|
+
|
|
36
|
+
Public exports:
|
|
37
|
+
|
|
38
|
+
- `createFxAgent()` creates a headless ACP agent.
|
|
39
|
+
- `createFxTerminal()` runs the interactive fx terminal.
|
|
40
|
+
- `supportsJspi()` detects WebAssembly JSPI support.
|
|
41
|
+
- `xtermAdapter()` connects fx to an xterm.js terminal.
|
|
42
|
+
- `encodeXtermKeyEvent()` translates browser keyboard events into terminal input.
|
|
43
|
+
|
|
44
|
+
## Headless agent
|
|
45
|
+
|
|
46
|
+
The default Node entry point prefers the native addon and falls back to
|
|
47
|
+
WebAssembly when necessary.
|
|
13
48
|
|
|
14
49
|
```js
|
|
15
|
-
import { createFxAgent } from "libfx
|
|
50
|
+
import { createFxAgent } from "libfx";
|
|
16
51
|
|
|
17
52
|
const agent = await createFxAgent({
|
|
18
|
-
env: {
|
|
53
|
+
env: {
|
|
54
|
+
AI_GATEWAY_API_KEY: process.env.AI_GATEWAY_API_KEY,
|
|
55
|
+
},
|
|
56
|
+
onEvent(event) {
|
|
57
|
+
console.log(event.type);
|
|
58
|
+
},
|
|
59
|
+
async onPermission(request) {
|
|
60
|
+
// Return one of request.options[*].optionId to approve it.
|
|
61
|
+
// Returning null or undefined cancels the request.
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
19
64
|
});
|
|
65
|
+
|
|
66
|
+
const session = await agent.createSession();
|
|
67
|
+
const turn = session.prompt("Explain the files in this project.");
|
|
68
|
+
|
|
69
|
+
for await (const update of turn) {
|
|
70
|
+
console.log(update);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log("Stopped:", await turn.stopReason);
|
|
74
|
+
|
|
75
|
+
await session.close();
|
|
76
|
+
await agent.close();
|
|
20
77
|
```
|
|
21
78
|
|
|
22
|
-
|
|
79
|
+
A prompt may be a string or an array of text and resource blocks:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
const turn = session.prompt([
|
|
83
|
+
{ type: "text", text: "Summarize this file." },
|
|
84
|
+
{
|
|
85
|
+
type: "resource",
|
|
86
|
+
resource: {
|
|
87
|
+
uri: "file:///workspace/README.md",
|
|
88
|
+
text: readmeContents,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Image prompt blocks are not currently supported.
|
|
95
|
+
|
|
96
|
+
### Agent lifecycle
|
|
97
|
+
|
|
98
|
+
The object returned by `createFxAgent()` provides:
|
|
99
|
+
|
|
100
|
+
| Member | Description |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `createSession()` | Creates a new active session |
|
|
103
|
+
| `openSession(id)` | Loads a stored session |
|
|
104
|
+
| `listSessions()` | Lists stored sessions |
|
|
105
|
+
| `close()` | Closes the active session and shuts down cleanly |
|
|
106
|
+
| `abort()` | Immediately aborts the runtime |
|
|
107
|
+
| `exited` | Promise that resolves with the process exit code |
|
|
23
108
|
|
|
24
|
-
|
|
109
|
+
A session provides:
|
|
25
110
|
|
|
26
|
-
|
|
111
|
+
| Member | Description |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `prompt(input, options?)` | Starts an async iterable turn |
|
|
114
|
+
| `setModel(model)` | Changes the active model |
|
|
115
|
+
| `setMode(mode)` | Changes the active mode |
|
|
116
|
+
| `setConfig(config)` | Applies multiple configuration values |
|
|
117
|
+
| `close()` | Closes the active session |
|
|
118
|
+
| `remove()` | Removes the stored session |
|
|
119
|
+
| `history` | Previously loaded session updates |
|
|
120
|
+
| `configOptions` | Current configurable values |
|
|
27
121
|
|
|
28
|
-
|
|
122
|
+
Each session allows one active prompt at a time. Cancel a turn directly or
|
|
123
|
+
with an `AbortSignal`:
|
|
29
124
|
|
|
30
125
|
```js
|
|
31
|
-
|
|
126
|
+
const controller = new AbortController();
|
|
127
|
+
const turn = session.prompt("Wait for more instructions.", {
|
|
128
|
+
signal: controller.signal,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
controller.abort();
|
|
132
|
+
console.log(await turn.stopReason); // "cancelled"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Browser agent
|
|
136
|
+
|
|
137
|
+
Browser hosts always use WebAssembly.
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
import {
|
|
141
|
+
createFxAgent,
|
|
142
|
+
supportsJspi,
|
|
143
|
+
} from "libfx/browser";
|
|
144
|
+
|
|
145
|
+
if (!supportsJspi()) {
|
|
146
|
+
throw new Error("This browser does not support WebAssembly JSPI.");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const agent = await createFxAgent({
|
|
150
|
+
env: {
|
|
151
|
+
AI_GATEWAY_API_KEY: "<short-lived credential>",
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const session = await agent.createSession();
|
|
156
|
+
const turn = session.prompt("Describe this workspace.");
|
|
157
|
+
|
|
158
|
+
for await (const update of turn) {
|
|
159
|
+
console.log(update);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The browser entry point resolves `fx-core.wasm` and `fx-term.wasm` relative to
|
|
164
|
+
the installed package. Pass `wasm` explicitly to provide a URL, `Response`,
|
|
165
|
+
`ArrayBuffer`, typed array, or precompiled `WebAssembly.Module`.
|
|
166
|
+
|
|
167
|
+
Do not embed a long-lived API key in public browser code. Use a short-lived
|
|
168
|
+
credential or an authenticated server-side proxy.
|
|
169
|
+
|
|
170
|
+
## Interactive terminal
|
|
171
|
+
|
|
172
|
+
Install xterm.js in the host application:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
npm install @xterm/xterm @xterm/addon-fit
|
|
176
|
+
```
|
|
32
177
|
|
|
178
|
+
Create the terminal and connect it to fx:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
import { Terminal } from "@xterm/xterm";
|
|
182
|
+
import { FitAddon } from "@xterm/addon-fit";
|
|
183
|
+
import "@xterm/xterm/css/xterm.css";
|
|
184
|
+
import {
|
|
185
|
+
createFxTerminal,
|
|
186
|
+
supportsJspi,
|
|
187
|
+
xtermAdapter,
|
|
188
|
+
} from "libfx/browser";
|
|
189
|
+
|
|
190
|
+
if (!supportsJspi()) {
|
|
191
|
+
throw new Error("This browser does not support WebAssembly JSPI.");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const terminal = new Terminal({
|
|
195
|
+
cursorBlink: true,
|
|
196
|
+
scrollback: 10_000,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const fit = new FitAddon();
|
|
200
|
+
terminal.loadAddon(fit);
|
|
201
|
+
terminal.open(document.querySelector("#terminal"));
|
|
202
|
+
fit.fit();
|
|
203
|
+
|
|
204
|
+
const runtime = await createFxTerminal({
|
|
205
|
+
terminal: xtermAdapter(terminal),
|
|
206
|
+
env: {
|
|
207
|
+
AI_GATEWAY_API_KEY: "<short-lived credential>",
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
await runtime.interactive;
|
|
212
|
+
|
|
213
|
+
window.addEventListener("resize", () => {
|
|
214
|
+
fit.fit();
|
|
215
|
+
runtime.resize();
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The terminal runtime provides:
|
|
220
|
+
|
|
221
|
+
| Member | Description |
|
|
222
|
+
| --- | --- |
|
|
223
|
+
| `interactive` | Resolves after the terminal is ready for input |
|
|
224
|
+
| `exited` | Resolves with the terminal exit code |
|
|
225
|
+
| `write(data)` | Writes input directly to fx |
|
|
226
|
+
| `resize()` | Notifies fx of terminal geometry changes |
|
|
227
|
+
| `abort()` | Stops the terminal and releases subscriptions |
|
|
228
|
+
|
|
229
|
+
Try the hosted terminal at [fx.sh/try](https://fx.sh/try).
|
|
230
|
+
|
|
231
|
+
## Backend selection
|
|
232
|
+
|
|
233
|
+
Node hosts may select a backend explicitly:
|
|
234
|
+
|
|
235
|
+
```js
|
|
33
236
|
const agent = await createFxAgent({
|
|
34
|
-
|
|
237
|
+
backend: "native",
|
|
35
238
|
});
|
|
36
239
|
```
|
|
37
240
|
|
|
38
|
-
|
|
241
|
+
| Backend | Behavior |
|
|
242
|
+
| --- | --- |
|
|
243
|
+
| `auto` | Prefer a compatible native addon and fall back to WebAssembly |
|
|
244
|
+
| `native` | Require the native backend and fail if it cannot load |
|
|
245
|
+
| `wasm` | Require WebAssembly and JSPI |
|
|
246
|
+
|
|
247
|
+
The native loader checks `libfx.node` followed by the platform-specific addon:
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
libfx.<platform>-<arch>.node
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Supported packaged targets:
|
|
39
254
|
|
|
40
|
-
|
|
255
|
+
- `linux-x64`
|
|
256
|
+
- `linux-arm64`
|
|
257
|
+
- `darwin-x64`
|
|
258
|
+
- `darwin-arm64`
|
|
41
259
|
|
|
42
|
-
|
|
260
|
+
If no compatible native backend is available and JSPI cannot run, startup
|
|
261
|
+
rejects with:
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
error.code === "LIBFX_JSPI_REQUIRED"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
On Node versions where JSPI remains behind a flag, start the process with:
|
|
43
268
|
|
|
44
269
|
```sh
|
|
45
270
|
node --experimental-wasm-jspi app.mjs
|
|
46
271
|
```
|
|
47
272
|
|
|
48
|
-
|
|
273
|
+
## Host integrations
|
|
274
|
+
|
|
275
|
+
Hosts may provide adapters for runtime state and external effects:
|
|
276
|
+
|
|
277
|
+
| Option | Purpose |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| `fetch` | Routes Gateway requests through the host |
|
|
280
|
+
| `env` | Supplies runtime configuration without changing process globals |
|
|
281
|
+
| `onEvent` | Receives runtime, ACP, terminal, and lifecycle events |
|
|
282
|
+
| `onPermission` | Resolves agent permission requests |
|
|
283
|
+
| `configStore` | Persists accepted configuration values |
|
|
284
|
+
| `sessionStore` | Persists agent or terminal sessions |
|
|
285
|
+
| `oauthSessionStore` | Persists browser device-login sessions |
|
|
286
|
+
| `promptHistoryStore` | Stores terminal prompt history |
|
|
287
|
+
| `openUrl` | Opens authentication and verification URLs |
|
|
288
|
+
| `workspace` | Provides the constrained browser workspace adapter |
|
|
289
|
+
|
|
290
|
+
## Security boundaries
|
|
291
|
+
|
|
292
|
+
`nativeAddon` and `env.FX_GATEWAY_CHAT_URL` are trusted host configuration. Do
|
|
293
|
+
not populate them from request, tenant, or other untrusted input.
|
|
294
|
+
|
|
295
|
+
The native backend sends production credentials only to the canonical Vercel
|
|
296
|
+
AI Gateway endpoint. Custom Gateway endpoints are limited to explicit loopback
|
|
297
|
+
HTTP URLs for local development.
|
|
298
|
+
|
|
299
|
+
The WebAssembly runtime intentionally does not provide:
|
|
300
|
+
|
|
301
|
+
- Native processes
|
|
302
|
+
- OS sandboxing
|
|
303
|
+
- Native MCP servers
|
|
304
|
+
- Subagents or skills
|
|
305
|
+
- Automatic upgrades
|
|
306
|
+
- Clipboard integration
|
|
307
|
+
- Arbitrary WASI filesystem access
|
|
308
|
+
- Web search
|
|
309
|
+
|
|
310
|
+
The optional browser workspace exposes foreground terminal execution through
|
|
311
|
+
the typed contract:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
{ action: "exec", command }
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The host remains responsible for admitting commands, enforcing limits, and
|
|
318
|
+
returning bounded output.
|
|
49
319
|
|
|
50
320
|
## Local development
|
|
51
321
|
|
|
52
|
-
|
|
322
|
+
From the fx repository root, build the native addon and both WebAssembly
|
|
323
|
+
surfaces:
|
|
53
324
|
|
|
54
325
|
```sh
|
|
55
326
|
zig build -Dnapi-surface=core -Doptimize=ReleaseSafe
|
|
56
327
|
zig build -Dwasm-surface=core -Doptimize=ReleaseSmall
|
|
57
328
|
zig build -Dwasm-surface=term -Doptimize=ReleaseSmall
|
|
58
|
-
python3 -m http.server 8080
|
|
59
329
|
```
|
|
60
330
|
|
|
61
|
-
|
|
331
|
+
Run the SDK test suites:
|
|
62
332
|
|
|
63
|
-
|
|
64
|
-
|
|
333
|
+
```sh
|
|
334
|
+
npm ci --prefix sdk/node
|
|
335
|
+
npm run --prefix sdk test:node-napi
|
|
336
|
+
npm run --prefix sdk test:node-wasm
|
|
337
|
+
```
|
|
65
338
|
|
|
66
|
-
|
|
339
|
+
Serve the repository:
|
|
67
340
|
|
|
68
341
|
```sh
|
|
69
|
-
|
|
342
|
+
python3 -m http.server 8080
|
|
70
343
|
```
|
|
71
344
|
|
|
72
|
-
|
|
345
|
+
After starting the server, open these local URLs:
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
Core debugger: http://localhost:8080/sdk/index.html
|
|
349
|
+
Interactive terminal: http://localhost:8080/sdk/term-demo.html
|
|
350
|
+
```
|
|
73
351
|
|
|
74
|
-
|
|
352
|
+
These are local development pages and are not publicly hosted links.
|
|
75
353
|
|
|
76
|
-
|
|
354
|
+
Maintainer references:
|
|
77
355
|
|
|
78
|
-
|
|
356
|
+
- [SDK contributor guide](https://github.com/vercel-labs/fx/blob/main/sdk/AGENTS.md)
|
|
357
|
+
- [Native Node-API design and security model](https://github.com/vercel-labs/fx/blob/main/sdk/NAPI.md)
|
package/fx-core.wasm
CHANGED
|
Binary file
|
package/fx-term.wasm
CHANGED
|
Binary file
|
package/libfx.darwin-arm64.node
CHANGED
|
Binary file
|
package/libfx.darwin-x64.node
CHANGED
|
Binary file
|
package/libfx.linux-arm64.node
CHANGED
|
Binary file
|
package/libfx.linux-x64.node
CHANGED
|
Binary file
|