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 +35 -48
- package/README.zh-CN.md +35 -48
- package/dist/index.cjs +41 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -147
- package/dist/index.d.ts +6 -147
- package/dist/index.js +41 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,79 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
**[English](./README.md)** | **[中文](./README.zh-CN.md)**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
##
|
|
16
|
+
## Vite Integration
|
|
14
17
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
A reactive in-memory filesystem that emits change events:
|
|
31
|
+
## xterm Integration
|
|
41
32
|
|
|
42
|
-
|
|
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
|
-
|
|
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 {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
48
|
+
const session = createBrowserBashSession({ rootPath: "/workspace" })
|
|
49
|
+
const terminal = new Terminal({ convertEol: true, cursorBlink: true })
|
|
50
|
+
const fitAddon = new FitAddon()
|
|
66
51
|
|
|
67
|
-
|
|
52
|
+
terminal.loadAddon(fitAddon)
|
|
53
|
+
terminal.open(container)
|
|
54
|
+
fitAddon.fit()
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
void executeBrowserBash(session, "python --version")
|
|
57
|
+
session.setStdoutWriter((data) => terminal.write(data))
|
|
58
|
+
session.setTerminalSize(terminal.cols, terminal.rows)
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
48
|
+
const session = createBrowserBashSession({ rootPath: "/workspace" })
|
|
49
|
+
const terminal = new Terminal({ convertEol: true, cursorBlink: true })
|
|
50
|
+
const fitAddon = new FitAddon()
|
|
66
51
|
|
|
67
|
-
|
|
52
|
+
terminal.loadAddon(fitAddon)
|
|
53
|
+
terminal.open(container)
|
|
54
|
+
fitAddon.fit()
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
void executeBrowserBash(session, "python --version")
|
|
57
|
+
session.setStdoutWriter((data) => terminal.write(data))
|
|
58
|
+
session.setTerminalSize(terminal.cols, terminal.rows)
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
62813
|
-
const
|
|
62814
|
-
|
|
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(
|
|
62838
|
+
const result = await session.bash.exec(trimmedCommand, {
|
|
62833
62839
|
cwd: session.cwd,
|
|
62834
62840
|
env: {
|
|
62835
|
-
...
|
|
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.
|
|
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;
|