agent-web-os 0.1.18 → 0.2.0

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 CHANGED
@@ -2,79 +2,66 @@
2
2
 
3
3
  **[English](./README.md)** | **[中文](./README.zh-CN.md)**
4
4
 
5
- Browser-based operating system for your agents. Bash + Node.js + Python runtime with an observable in-memory filesystem. Install NPM or PIP packages, run shell commands, execute Node.js scripts, and manage files entirely in the browser — no server required. Supports Claude Code, Codex CLI, and OpenCode.
5
+ A browser-based operating system for AI agents. Full Bash shell, Node.js runtime, and Python 3.11 execution. Observable in-memory filesystem. No server required.
6
6
 
7
7
  ## Install
8
8
 
9
9
  ```bash
10
10
  npm install agent-web-os
11
+ bun add agent-web-os
12
+ pnpm add agent-web-os
13
+ yarn add agent-web-os
11
14
  ```
12
15
 
13
- ## Usage
16
+ ## Vite Integration
14
17
 
15
- ### Bash Session
16
-
17
- Create a full bash session with a virtual filesystem, shell, and Node.js runtime:
18
+ Install `agent-web-os`, create a session in the browser, and execute commands from your component or hook.
18
19
 
19
20
  ```ts
20
- import { createBrowserBashSession } from "agent-web-os"
21
-
22
- const session = createBrowserBashSession({
23
- rootPath: "/workspace",
24
- })
21
+ import { createBrowserBashSession, executeBrowserBash } from "agent-web-os"
25
22
 
26
- // Run shell commands
27
- const result = await executeBrowserBash(session, "echo hello world")
28
- console.log(result.stdout) // "hello world\n"
23
+ const session = createBrowserBashSession({ rootPath: "/workspace" })
29
24
 
30
- // Read/write files through the observable filesystem
31
- session.fs.writeFileSync("/workspace/index.js", 'console.log("hi")')
32
- await executeBrowserBash(session, "node index.js")
33
-
34
- // Clean up
35
- session.dispose()
25
+ export async function runAgentWebOsDemo() {
26
+ const result = await executeBrowserBash(session, "node --version")
27
+ console.log(result.stdout)
28
+ }
36
29
  ```
37
30
 
38
- ### Observable Filesystem
39
-
40
- A reactive in-memory filesystem that emits change events:
31
+ ## xterm Integration
41
32
 
42
- ```ts
43
- import { ObservableInMemoryFs } from "agent-web-os"
44
-
45
- const fs = new ObservableInMemoryFs()
46
-
47
- fs.subscribe((event) => {
48
- console.log(event.event, event.path) // "add", "/hello.txt"
49
- })
33
+ Install xterm separately, attach it to your DOM node, mirror stdout into the terminal, and send keystrokes into `writeStdin` for interactive tools.
50
34
 
51
- fs.writeFileSync("/hello.txt", "world")
35
+ ```bash
36
+ npm install @xterm/xterm @xterm/addon-fit
37
+ bun add @xterm/xterm @xterm/addon-fit
38
+ pnpm add @xterm/xterm @xterm/addon-fit
39
+ yarn add @xterm/xterm @xterm/addon-fit
52
40
  ```
53
41
 
54
- ### Node.js Runtime
55
-
56
- Run Node.js scripts and npm commands in the browser via [almostnode](https://www.npmjs.com/package/almostnode):
57
-
58
42
  ```ts
59
- import { createAlmostNodeSession } from "agent-web-os"
60
-
61
- const nodeSession = createAlmostNodeSession(fs)
62
- // Provides node, npm install, npm run, and Vite dev server support
63
- ```
43
+ import { Terminal } from "@xterm/xterm"
44
+ import { FitAddon } from "@xterm/addon-fit"
45
+ import "@xterm/xterm/css/xterm.css"
46
+ import { createBrowserBashSession, executeBrowserBash } from "agent-web-os"
64
47
 
65
- ### Service Worker Bridge
48
+ const session = createBrowserBashSession({ rootPath: "/workspace" })
49
+ const terminal = new Terminal({ convertEol: true, cursorBlink: true })
50
+ const fitAddon = new FitAddon()
66
51
 
67
- For HTTP server support within the browser, register the service worker:
52
+ terminal.loadAddon(fitAddon)
53
+ terminal.open(container)
54
+ fitAddon.fit()
68
55
 
69
- ```ts
70
- import { getServerBridge } from "agent-web-os"
56
+ void executeBrowserBash(session, "python --version")
57
+ session.setStdoutWriter((data) => terminal.write(data))
58
+ session.setTerminalSize(terminal.cols, terminal.rows)
71
59
 
72
- const bridge = getServerBridge()
73
- await bridge.initServiceWorker()
60
+ terminal.onData((data) => {
61
+ session.writeStdin(data)
62
+ })
74
63
  ```
75
64
 
76
- Copy `node_modules/agent-web-os/dist/__sw__.js` to your public directory so the service worker can be registered.
77
-
78
65
  ## License
79
66
 
80
67
  MIT
package/README.zh-CN.md CHANGED
@@ -2,79 +2,66 @@
2
2
 
3
3
  **[English](./README.md)** | **[中文](./README.zh-CN.md)**
4
4
 
5
- 基于浏览器的 Agent 操作系统。集成 Bash + Node.js + Python 运行时,内置可观测的内存文件系统。支持安装 NPM 或 PIP 包、执行 Shell 命令、运行 Node.js 脚本,并在浏览器中管理文件——无需服务器。兼容 Claude Code、Codex CLI 和 OpenCode。
5
+ 基于浏览器的 AI Agent 操作系统。完整的 Bash Shell、Node.js 运行时和 Python 3.11 执行环境。可观测的内存文件系统。无需服务器。
6
6
 
7
7
  ## 安装
8
8
 
9
9
  ```bash
10
10
  npm install agent-web-os
11
+ bun add agent-web-os
12
+ pnpm add agent-web-os
13
+ yarn add agent-web-os
11
14
  ```
12
15
 
13
- ## 使用方法
16
+ ## Vite 集成
14
17
 
15
- ### Bash 会话
16
-
17
- 创建一个完整的 Bash 会话,包含虚拟文件系统、Shell 和 Node.js 运行时:
18
+ 安装 `agent-web-os`,在浏览器中创建会话,然后从组件或 Hook 中执行命令。
18
19
 
19
20
  ```ts
20
- import { createBrowserBashSession } from "agent-web-os"
21
-
22
- const session = createBrowserBashSession({
23
- rootPath: "/workspace",
24
- })
21
+ import { createBrowserBashSession, executeBrowserBash } from "agent-web-os"
25
22
 
26
- // 执行 Shell 命令
27
- const result = await executeBrowserBash(session, "echo hello world")
28
- console.log(result.stdout) // "hello world\n"
23
+ const session = createBrowserBashSession({ rootPath: "/workspace" })
29
24
 
30
- // 通过可观测文件系统读写文件
31
- session.fs.writeFileSync("/workspace/index.js", 'console.log("hi")')
32
- await executeBrowserBash(session, "node index.js")
33
-
34
- // 清理资源
35
- session.dispose()
25
+ export async function runAgentWebOsDemo() {
26
+ const result = await executeBrowserBash(session, "node --version")
27
+ console.log(result.stdout)
28
+ }
36
29
  ```
37
30
 
38
- ### 可观测文件系统
39
-
40
- 响应式的内存文件系统,支持变更事件监听:
31
+ ## xterm 集成
41
32
 
42
- ```ts
43
- import { ObservableInMemoryFs } from "agent-web-os"
44
-
45
- const fs = new ObservableInMemoryFs()
46
-
47
- fs.subscribe((event) => {
48
- console.log(event.event, event.path) // "add", "/hello.txt"
49
- })
33
+ 单独安装 xterm,将其挂载到 DOM 节点,将 stdout 映射到终端,并将按键输入发送到 `writeStdin` 以支持交互式工具。
50
34
 
51
- fs.writeFileSync("/hello.txt", "world")
35
+ ```bash
36
+ npm install @xterm/xterm @xterm/addon-fit
37
+ bun add @xterm/xterm @xterm/addon-fit
38
+ pnpm add @xterm/xterm @xterm/addon-fit
39
+ yarn add @xterm/xterm @xterm/addon-fit
52
40
  ```
53
41
 
54
- ### Node.js 运行时
55
-
56
- 通过 [almostnode](https://www.npmjs.com/package/almostnode) 在浏览器中运行 Node.js 脚本和 npm 命令:
57
-
58
42
  ```ts
59
- import { createAlmostNodeSession } from "agent-web-os"
60
-
61
- const nodeSession = createAlmostNodeSession(fs)
62
- // 支持 node、npm install、npm run Vite 开发服务器
63
- ```
43
+ import { Terminal } from "@xterm/xterm"
44
+ import { FitAddon } from "@xterm/addon-fit"
45
+ import "@xterm/xterm/css/xterm.css"
46
+ import { createBrowserBashSession, executeBrowserBash } from "agent-web-os"
64
47
 
65
- ### Service Worker 桥接
48
+ const session = createBrowserBashSession({ rootPath: "/workspace" })
49
+ const terminal = new Terminal({ convertEol: true, cursorBlink: true })
50
+ const fitAddon = new FitAddon()
66
51
 
67
- 如需在浏览器中支持 HTTP 服务器,注册 Service Worker:
52
+ terminal.loadAddon(fitAddon)
53
+ terminal.open(container)
54
+ fitAddon.fit()
68
55
 
69
- ```ts
70
- import { getServerBridge } from "agent-web-os"
56
+ void executeBrowserBash(session, "python --version")
57
+ session.setStdoutWriter((data) => terminal.write(data))
58
+ session.setTerminalSize(terminal.cols, terminal.rows)
71
59
 
72
- const bridge = getServerBridge()
73
- await bridge.initServiceWorker()
60
+ terminal.onData((data) => {
61
+ session.writeStdin(data)
62
+ })
74
63
  ```
75
64
 
76
- 将 `node_modules/agent-web-os/dist/__sw__.js` 复制到你的 public 目录,以便注册 Service Worker。
77
-
78
65
  ## 许可证
79
66
 
80
67
  MIT
package/dist/index.cjs CHANGED
@@ -6019,12 +6019,10 @@ var init_index_web = __esm({
6019
6019
  var index_exports = {};
6020
6020
  __export(index_exports, {
6021
6021
  AGENT_WEB_OS_VERSION: () => AGENT_WEB_OS_VERSION,
6022
- AlmostNodeSession: () => AlmostNodeSession,
6023
6022
  Bash: () => Ju,
6024
6023
  DEFAULT_BASH_SHELL_ENV: () => DEFAULT_BASH_SHELL_ENV,
6025
6024
  ObservableInMemoryFs: () => ObservableInMemoryFs,
6026
6025
  assertObservableInMemoryFs: () => assertObservableInMemoryFs,
6027
- createAlmostNodeSession: () => createAlmostNodeSession,
6028
6026
  createBrowserBashSession: () => createBrowserBashSession,
6029
6027
  defineCommand: () => Dx,
6030
6028
  executeBrowserBash: () => executeBrowserBash,
@@ -62098,6 +62096,17 @@ ${packages.map(([name, version4]) => `+-- ${name}@${version4}`).join("\n")}
62098
62096
  stdoutAny.columns = this._terminalColumns;
62099
62097
  stdoutAny.rows = this._terminalRows;
62100
62098
  stdoutAny.getWindowSize = () => [this._terminalColumns, this._terminalRows];
62099
+ let rawModeRequested = false;
62100
+ if (process4.stdin) {
62101
+ const originalSetRawMode = process4.stdin.setRawMode;
62102
+ process4.stdin.setRawMode = (mode) => {
62103
+ rawModeRequested = rawModeRequested || mode;
62104
+ if (typeof originalSetRawMode === "function") {
62105
+ return originalSetRawMode.call(process4.stdin, mode);
62106
+ }
62107
+ return process4.stdin;
62108
+ };
62109
+ }
62101
62110
  this._stdinHandler = process4.stdin ? (data2) => {
62102
62111
  process4.stdin.emit("data", data2);
62103
62112
  } : null;
@@ -62165,7 +62174,11 @@ ${packages.map(([name, version4]) => `+-- ${name}@${version4}`).join("\n")}
62165
62174
  runtime.runFile(invocation.scriptPath);
62166
62175
  }
62167
62176
  syncExecution = false;
62168
- const isInteractive = process4.stdin && (process4.stdin.listenerCount("data") > 0 || process4.stdin.listenerCount("keypress") > 0);
62177
+ await new Promise((resolve5) => {
62178
+ setTimeout(resolve5, 0);
62179
+ });
62180
+ const hasStdinListeners = process4.stdin && (process4.stdin.listenerCount("data") > 0 || process4.stdin.listenerCount("keypress") > 0);
62181
+ const isInteractive = hasStdinListeners || rawModeRequested;
62169
62182
  const asyncExitCode = isInteractive ? await exitPromise : await Promise.race([
62170
62183
  exitPromise,
62171
62184
  new Promise((resolve5) => {
@@ -62544,7 +62557,13 @@ function globToRegex(pattern) {
62544
62557
  function escapeRegex(s10) {
62545
62558
  return s10.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62546
62559
  }
62547
- function parseIgnorePatterns(content) {
62560
+ async function loadIgnoreFile(fs, filePath) {
62561
+ let content;
62562
+ try {
62563
+ content = await fs.readFile(filePath);
62564
+ } catch {
62565
+ return [];
62566
+ }
62548
62567
  const matchers = [];
62549
62568
  for (const raw of content.split("\n")) {
62550
62569
  const line = raw.trim();
@@ -62562,19 +62581,8 @@ function parseIgnorePatterns(content) {
62562
62581
  }
62563
62582
  return matchers;
62564
62583
  }
62565
- async function loadIgnoreFile(fs, filePath) {
62566
- try {
62567
- const content = await fs.readFile(filePath);
62568
- return parseIgnorePatterns(content);
62569
- } catch {
62570
- return [];
62571
- }
62572
- }
62573
62584
  function resolvePath(base, rel) {
62574
- if (rel.startsWith("/")) return normalizePath2(rel);
62575
- return normalizePath2(`${base}/${rel}`);
62576
- }
62577
- function normalizePath2(p) {
62585
+ const p = rel.startsWith("/") ? rel : `${base}/${rel}`;
62578
62586
  const parts = p.split("/").filter(Boolean);
62579
62587
  const result = [];
62580
62588
  for (const part of parts) {
@@ -62639,13 +62647,6 @@ async function executeFd(args, ctx) {
62639
62647
  }
62640
62648
  const excludeMatchers = opts.excludes.map((p) => globToRegex(p));
62641
62649
  const ignoreMatchers = [];
62642
- if (!opts.noIgnore) {
62643
- for (const root2 of searchRoots) {
62644
- const gitignorePath = `${root2}/.gitignore`;
62645
- const loaded = await loadIgnoreFile(fs, gitignorePath);
62646
- ignoreMatchers.push(...loaded);
62647
- }
62648
- }
62649
62650
  for (const ignoreFile of opts.ignoreFiles) {
62650
62651
  const absPath = resolvePath(cwd, ignoreFile);
62651
62652
  const loaded = await loadIgnoreFile(fs, absPath);
@@ -62725,9 +62726,7 @@ async function executeFd(args, ctx) {
62725
62726
  if (!opts.noIgnore) {
62726
62727
  const rootGitignore = `${root2}/.gitignore`;
62727
62728
  const loaded = await loadIgnoreFile(fs, rootGitignore);
62728
- if (loaded.length > 0) {
62729
- ignoreMatchers.push(...loaded);
62730
- }
62729
+ ignoreMatchers.push(...loaded);
62731
62730
  }
62732
62731
  await walk(root2, root2, 1);
62733
62732
  }
@@ -62801,17 +62800,24 @@ function createBrowserBashSession(options2 = {}) {
62801
62800
  return {
62802
62801
  fs,
62803
62802
  bash,
62804
- almostNodeSession,
62805
62803
  rootPath,
62806
62804
  cwd: rootPath,
62805
+ setStdoutWriter: (writer) => almostNodeSession.setStdoutWriter(writer),
62806
+ writeStdin: (data2) => almostNodeSession.writeStdin(data2),
62807
+ setTerminalSize: (columns, rows) => almostNodeSession.setTerminalSize(columns, rows),
62807
62808
  dispose: () => {
62808
62809
  almostNodeSession.dispose();
62809
62810
  }
62810
62811
  };
62811
62812
  }
62812
- async function execBashWithTimeout(session, command, options2 = {}) {
62813
- const timeoutMs = options2.timeoutMs ?? DEFAULT_BASH_COMMAND_TIMEOUT_MS;
62814
- const env = options2.env ?? { ...DEFAULT_BASH_SHELL_ENV };
62813
+ async function executeBrowserBash(session, command, options2 = {}) {
62814
+ const trimmedCommand = command.trim();
62815
+ if (!trimmedCommand) {
62816
+ return { success: false, error: "Command is required", stderr: "Command is required", exit_code: 1, command: trimmedCommand, backend: "just-bash" };
62817
+ }
62818
+ const startedAt = Date.now();
62819
+ const outputLimit = options2.outputLimit ?? DEFAULT_BASH_OUTPUT_LIMIT;
62820
+ const timeoutMs = options2.commandTimeoutMs ?? DEFAULT_BASH_COMMAND_TIMEOUT_MS;
62815
62821
  const timeoutController = new AbortController();
62816
62822
  const combinedController = new AbortController();
62817
62823
  const abort = (reason) => {
@@ -62829,10 +62835,10 @@ async function execBashWithTimeout(session, command, options2 = {}) {
62829
62835
  timeoutController.abort(new Error(`Command timed out after ${timeoutMs}ms`));
62830
62836
  }, timeoutMs);
62831
62837
  try {
62832
- const result = await session.bash.exec(command, {
62838
+ const result = await session.bash.exec(trimmedCommand, {
62833
62839
  cwd: session.cwd,
62834
62840
  env: {
62835
- ...env,
62841
+ ...DEFAULT_BASH_SHELL_ENV,
62836
62842
  PWD: session.cwd
62837
62843
  },
62838
62844
  signal: combinedController.signal
@@ -62841,23 +62847,6 @@ async function execBashWithTimeout(session, command, options2 = {}) {
62841
62847
  if (nextCwd) {
62842
62848
  session.cwd = normalizeBashPath(nextCwd);
62843
62849
  }
62844
- return result;
62845
- } finally {
62846
- globalThis.clearTimeout(timeoutId);
62847
- }
62848
- }
62849
- async function executeBrowserBash(session, command, options2 = {}) {
62850
- const trimmedCommand = command.trim();
62851
- if (!trimmedCommand) {
62852
- return { success: false, error: "Command is required", stderr: "Command is required", exit_code: 1, command: trimmedCommand, backend: "just-bash" };
62853
- }
62854
- const startedAt = Date.now();
62855
- const outputLimit = options2.outputLimit ?? DEFAULT_BASH_OUTPUT_LIMIT;
62856
- try {
62857
- const result = await execBashWithTimeout(session, trimmedCommand, {
62858
- timeoutMs: options2.commandTimeoutMs,
62859
- signal: options2.signal
62860
- });
62861
62850
  const stdout = options2.truncateOutput === false ? result.stdout : truncateBashOutput(result.stdout, outputLimit);
62862
62851
  const stderr = options2.truncateOutput === false ? result.stderr : truncateBashOutput(result.stderr, outputLimit);
62863
62852
  const error = result.exitCode === 0 ? void 0 : stderr || void 0;
@@ -62875,11 +62864,13 @@ async function executeBrowserBash(session, command, options2 = {}) {
62875
62864
  } catch (error) {
62876
62865
  const message = error instanceof Error ? error.message : String(error);
62877
62866
  return { success: false, command: trimmedCommand, error: message, stderr: message, exit_code: 1, duration_ms: Date.now() - startedAt, backend: "just-bash" };
62867
+ } finally {
62868
+ globalThis.clearTimeout(timeoutId);
62878
62869
  }
62879
62870
  }
62880
62871
 
62881
62872
  // src/index.ts
62882
- var AGENT_WEB_OS_VERSION = "0.1.18";
62873
+ var AGENT_WEB_OS_VERSION = "0.2.0";
62883
62874
  console.log(`[agent-web-os] v${AGENT_WEB_OS_VERSION}`);
62884
62875
  var getServerBridge2 = getServerBridge;
62885
62876
  var resetServerBridge2 = resetServerBridge;