@wavexzore/sandbox 0.1.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/Dockerfile +14 -0
- package/LICENSE +661 -0
- package/NOTICE +3 -0
- package/README.md +153 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/sandbox/cli/install.d.ts +5 -0
- package/dist/sandbox/cli/install.js +335 -0
- package/dist/sandbox/cli/local-store.d.ts +87 -0
- package/dist/sandbox/cli/local-store.js +604 -0
- package/dist/sandbox/cli/opencode-config.d.ts +25 -0
- package/dist/sandbox/cli/opencode-config.js +240 -0
- package/dist/sandbox/cli/path.d.ts +64 -0
- package/dist/sandbox/cli/path.js +127 -0
- package/dist/sandbox/cli/types.d.ts +145 -0
- package/dist/sandbox/cli/types.js +6 -0
- package/dist/sandbox/cli/wavexzore-sandbox.d.ts +65 -0
- package/dist/sandbox/cli/wavexzore-sandbox.js +577 -0
- package/dist/sandbox/core/cli-helper.d.ts +19 -0
- package/dist/sandbox/core/cli-helper.js +64 -0
- package/dist/sandbox/core/docker-archive-utils.d.ts +3 -0
- package/dist/sandbox/core/docker-archive-utils.js +50 -0
- package/dist/sandbox/core/docker-sandbox.d.ts +51 -0
- package/dist/sandbox/core/docker-sandbox.js +675 -0
- package/dist/sandbox/core/edit/filediff.d.ts +16 -0
- package/dist/sandbox/core/edit/filediff.js +21 -0
- package/dist/sandbox/core/edit/index.d.ts +5 -0
- package/dist/sandbox/core/edit/index.js +5 -0
- package/dist/sandbox/core/edit/line-endings.d.ts +4 -0
- package/dist/sandbox/core/edit/line-endings.js +10 -0
- package/dist/sandbox/core/edit/lock.d.ts +1 -0
- package/dist/sandbox/core/edit/lock.js +18 -0
- package/dist/sandbox/core/edit/replace.d.ts +10 -0
- package/dist/sandbox/core/edit/replace.js +14 -0
- package/dist/sandbox/core/edit/replacers.d.ts +15 -0
- package/dist/sandbox/core/edit/replacers.js +241 -0
- package/dist/sandbox/core/logger.d.ts +15 -0
- package/dist/sandbox/core/logger.js +59 -0
- package/dist/sandbox/core/lsp/client.d.ts +63 -0
- package/dist/sandbox/core/lsp/client.js +533 -0
- package/dist/sandbox/core/lsp/config.d.ts +13 -0
- package/dist/sandbox/core/lsp/config.js +36 -0
- package/dist/sandbox/core/lsp/diagnostics.d.ts +12 -0
- package/dist/sandbox/core/lsp/diagnostics.js +65 -0
- package/dist/sandbox/core/lsp/index.d.ts +4 -0
- package/dist/sandbox/core/lsp/index.js +4 -0
- package/dist/sandbox/core/lsp/language.d.ts +24 -0
- package/dist/sandbox/core/lsp/language.js +249 -0
- package/dist/sandbox/core/lsp/manager.d.ts +77 -0
- package/dist/sandbox/core/lsp/manager.js +237 -0
- package/dist/sandbox/core/lsp/tooling.d.ts +14 -0
- package/dist/sandbox/core/lsp/tooling.js +78 -0
- package/dist/sandbox/core/patch-parser.d.ts +23 -0
- package/dist/sandbox/core/patch-parser.js +248 -0
- package/dist/sandbox/core/path-map.d.ts +9 -0
- package/dist/sandbox/core/path-map.js +73 -0
- package/dist/sandbox/core/project-data-storage.d.ts +42 -0
- package/dist/sandbox/core/project-data-storage.js +167 -0
- package/dist/sandbox/core/read/binary.d.ts +4 -0
- package/dist/sandbox/core/read/binary.js +80 -0
- package/dist/sandbox/core/read/format.d.ts +38 -0
- package/dist/sandbox/core/read/format.js +85 -0
- package/dist/sandbox/core/read/index.d.ts +3 -0
- package/dist/sandbox/core/read/index.js +3 -0
- package/dist/sandbox/core/read/permissions.d.ts +7 -0
- package/dist/sandbox/core/read/permissions.js +13 -0
- package/dist/sandbox/core/session-manager.d.ts +29 -0
- package/dist/sandbox/core/session-manager.js +338 -0
- package/dist/sandbox/core/shell/config.d.ts +7 -0
- package/dist/sandbox/core/shell/config.js +82 -0
- package/dist/sandbox/core/shell/output.d.ts +35 -0
- package/dist/sandbox/core/shell/output.js +80 -0
- package/dist/sandbox/core/shell/parser.d.ts +7 -0
- package/dist/sandbox/core/shell/parser.js +122 -0
- package/dist/sandbox/core/shell/permissions.d.ts +13 -0
- package/dist/sandbox/core/shell/permissions.js +33 -0
- package/dist/sandbox/core/shell/workdir.d.ts +4 -0
- package/dist/sandbox/core/shell/workdir.js +19 -0
- package/dist/sandbox/core/stream-utils.d.ts +23 -0
- package/dist/sandbox/core/stream-utils.js +97 -0
- package/dist/sandbox/core/toast.d.ts +47 -0
- package/dist/sandbox/core/toast.js +73 -0
- package/dist/sandbox/core/types.d.ts +159 -0
- package/dist/sandbox/core/types.js +11 -0
- package/dist/sandbox/core/write/bom.d.ts +8 -0
- package/dist/sandbox/core/write/bom.js +15 -0
- package/dist/sandbox/core/write/config.d.ts +14 -0
- package/dist/sandbox/core/write/config.js +188 -0
- package/dist/sandbox/core/write/diagnostics.d.ts +19 -0
- package/dist/sandbox/core/write/diagnostics.js +120 -0
- package/dist/sandbox/core/write/diff.d.ts +7 -0
- package/dist/sandbox/core/write/diff.js +21 -0
- package/dist/sandbox/core/write/formatter.d.ts +16 -0
- package/dist/sandbox/core/write/formatter.js +51 -0
- package/dist/sandbox/core/write/index.d.ts +6 -0
- package/dist/sandbox/core/write/index.js +5 -0
- package/dist/sandbox/core/write/permissions.d.ts +13 -0
- package/dist/sandbox/core/write/permissions.js +19 -0
- package/dist/sandbox/core/write/pipeline.d.ts +48 -0
- package/dist/sandbox/core/write/pipeline.js +229 -0
- package/dist/sandbox/core/write/read-tracker.d.ts +13 -0
- package/dist/sandbox/core/write/read-tracker.js +30 -0
- package/dist/sandbox/git/host-git-manager.d.ts +40 -0
- package/dist/sandbox/git/host-git-manager.js +278 -0
- package/dist/sandbox/git/index.d.ts +5 -0
- package/dist/sandbox/git/index.js +5 -0
- package/dist/sandbox/git/sandbox-git-manager.d.ts +14 -0
- package/dist/sandbox/git/sandbox-git-manager.js +54 -0
- package/dist/sandbox/git/session-git-manager.d.ts +18 -0
- package/dist/sandbox/git/session-git-manager.js +85 -0
- package/dist/sandbox/index.d.ts +205 -0
- package/dist/sandbox/index.js +70 -0
- package/dist/sandbox/plugins/custom-tools.d.ts +203 -0
- package/dist/sandbox/plugins/custom-tools.js +15 -0
- package/dist/sandbox/plugins/session-events.d.ts +10 -0
- package/dist/sandbox/plugins/session-events.js +56 -0
- package/dist/sandbox/plugins/system-transform.d.ts +10 -0
- package/dist/sandbox/plugins/system-transform.js +23 -0
- package/dist/sandbox/tools/bash-output.d.ts +17 -0
- package/dist/sandbox/tools/bash-output.js +35 -0
- package/dist/sandbox/tools/bash-status.d.ts +13 -0
- package/dist/sandbox/tools/bash-status.js +29 -0
- package/dist/sandbox/tools/bash-stop.d.ts +13 -0
- package/dist/sandbox/tools/bash-stop.js +28 -0
- package/dist/sandbox/tools/bash.d.ts +26 -0
- package/dist/sandbox/tools/bash.js +120 -0
- package/dist/sandbox/tools/edit.d.ts +20 -0
- package/dist/sandbox/tools/edit.js +87 -0
- package/dist/sandbox/tools/get-preview-url.d.ts +17 -0
- package/dist/sandbox/tools/get-preview-url.js +16 -0
- package/dist/sandbox/tools/glob.d.ts +17 -0
- package/dist/sandbox/tools/glob.js +23 -0
- package/dist/sandbox/tools/grep.d.ts +17 -0
- package/dist/sandbox/tools/grep.js +23 -0
- package/dist/sandbox/tools/ls.d.ts +17 -0
- package/dist/sandbox/tools/ls.js +21 -0
- package/dist/sandbox/tools/lsp.d.ts +41 -0
- package/dist/sandbox/tools/lsp.js +198 -0
- package/dist/sandbox/tools/multiedit.d.ts +24 -0
- package/dist/sandbox/tools/multiedit.js +83 -0
- package/dist/sandbox/tools/patch.d.ts +14 -0
- package/dist/sandbox/tools/patch.js +260 -0
- package/dist/sandbox/tools/read.d.ts +22 -0
- package/dist/sandbox/tools/read.js +105 -0
- package/dist/sandbox/tools/write.d.ts +16 -0
- package/dist/sandbox/tools/write.js +27 -0
- package/dist/sandbox/tools.d.ts +200 -0
- package/dist/sandbox/tools.js +43 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# @wavexzore/sandbox
|
|
2
|
+
|
|
3
|
+
基于本地 Docker 的 OpenCode 编码沙箱插件。
|
|
4
|
+
|
|
5
|
+
## 功能概述
|
|
6
|
+
|
|
7
|
+
- 通过 `@opencode-ai/plugin` 注册 OpenCode 插件钩子
|
|
8
|
+
- 用 Docker 后端实现替换 14 个核心工具:`bash`、`bash-stop`、`bash-output`、`bash-status`、`read`、`write`、`edit`、`multiedit`、`apply_patch`、`glob`、`grep`、`ls`、`lsp`、`get-preview-url`
|
|
9
|
+
- 通过 `dockerode` 为每个 OpenCode 会话创建独立的 Docker 容器
|
|
10
|
+
- 将宿主机工作目录 bind mount 到容器内
|
|
11
|
+
- 在容器内执行 Shell 命令,在宿主机保留 LSP 诊断能力
|
|
12
|
+
- OpenCode 会话删除时自动清理容器
|
|
13
|
+
|
|
14
|
+
## 架构概览
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
插件层 custom-tools · session-events · system-transform
|
|
18
|
+
│
|
|
19
|
+
工具层 14 个沙箱工具 (bash/read/write/edit/patch/glob/grep/ls/lsp/...)
|
|
20
|
+
│
|
|
21
|
+
核心层 session-manager ──► docker-sandbox (Sandbox 接口)
|
|
22
|
+
shell/ · write/ · read/ · edit/ · lsp/
|
|
23
|
+
path-map (宿主机↔容器) · project-data-storage
|
|
24
|
+
│
|
|
25
|
+
基础设施层 dockerode · tar-stream · diff · vscode-jsonrpc
|
|
26
|
+
Docker Engine ──► bind mount (宿主机 worktree ↔ 容器 /repo)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
关键设计要点:
|
|
30
|
+
|
|
31
|
+
- **写入管线**:权限检查 → BOM 处理 → 文件上传 → 格式化 → LSP 诊断,完整链路一条龙
|
|
32
|
+
- **先读后写守卫**:文件必须先被读取后才能写入(由 `readRegistry` 强制执行)
|
|
33
|
+
- **9 策略编辑回退**:精确匹配 → 行首匹配 → 块锚点 → 空白忽略 → 缩进忽略 → 转义处理等
|
|
34
|
+
- **LSP 懒加载**:语言服务器仅在首次访问文件时启动
|
|
35
|
+
- **双轨 Git**:容器端通过 `docker exec` 执行 git 命令,宿主机端通过 `child_process` 执行
|
|
36
|
+
|
|
37
|
+
## 安装
|
|
38
|
+
|
|
39
|
+
在 `$HOME` 目录下运行安装命令(默认 user scope):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx @wavexzore/sandbox install
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
安装器会自动完成:将插件写入 OpenCode 全局配置、创建版本化存储目录、生成 bin shim 并设置 PATH。
|
|
46
|
+
|
|
47
|
+
其他命令:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx @wavexzore/sandbox install --dry-run --json # 预览安装内容
|
|
51
|
+
npx @wavexzore/sandbox doctor --json # 诊断安装状态
|
|
52
|
+
npx @wavexzore/sandbox uninstall # 卸载
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 运行要求
|
|
56
|
+
|
|
57
|
+
- 本地运行的 Docker 守护进程
|
|
58
|
+
- 沙箱镜像,默认为 `opencode-sandbox:latest`
|
|
59
|
+
- Node.js(安装器/helper 需要)
|
|
60
|
+
|
|
61
|
+
### 环境变量
|
|
62
|
+
|
|
63
|
+
| 变量 | 默认值 | 说明 |
|
|
64
|
+
|------|--------|------|
|
|
65
|
+
| `SANDBOX_IMAGE` | `opencode-sandbox:latest` | 沙箱容器使用的 Docker 镜像 |
|
|
66
|
+
| `DOCKER_SOCKET` | `/var/run/docker.sock` | Docker 守护进程 socket 路径 |
|
|
67
|
+
|
|
68
|
+
### 项目级配置(`.opencode/sandbox.json`)
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"image": "opencode-sandbox:latest",
|
|
73
|
+
"shell": {
|
|
74
|
+
"defaultTimeoutMs": 120000,
|
|
75
|
+
"maxOutputBytes": 200000,
|
|
76
|
+
"maxOutputLines": 2000,
|
|
77
|
+
"allowWorkdirOutsideProject": false
|
|
78
|
+
},
|
|
79
|
+
"lsp": {
|
|
80
|
+
"enabled": true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 沙箱镜像
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
docker build -t opencode-sandbox:latest -f packages/sandbox/Dockerfile packages/sandbox
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
默认容器项目路径为 `/home/sandbox/project`。
|
|
92
|
+
|
|
93
|
+
## 宿主机 LSP
|
|
94
|
+
|
|
95
|
+
工作目录通过 bind mount 挂载,因此映射文件在宿主机和容器中均可访问。插件在宿主机上运行 LSP 服务器,提供诊断和代码导航:
|
|
96
|
+
|
|
97
|
+
- TypeScript/JavaScript:`typescript-language-server`
|
|
98
|
+
- Go:`gopls`
|
|
99
|
+
- Python:`python-lsp-server`
|
|
100
|
+
|
|
101
|
+
这些是用于编辑中项目的工具,不是产品运行时模块。
|
|
102
|
+
|
|
103
|
+
## 工具参考
|
|
104
|
+
|
|
105
|
+
| 工具 | 说明 | 执行位置 |
|
|
106
|
+
|------|------|----------|
|
|
107
|
+
| `bash` | 执行 Shell 命令(前台/后台模式) | 容器 |
|
|
108
|
+
| `bash-stop` | 停止后台命令 | 容器 |
|
|
109
|
+
| `bash-output` | 读取后台命令输出 | 容器 |
|
|
110
|
+
| `bash-status` | 查询后台命令状态 | 容器 |
|
|
111
|
+
| `read` | 读取文件或目录(含二进制检测) | 容器 |
|
|
112
|
+
| `write` | 覆写文件(diff + 格式化 + 诊断) | 容器 |
|
|
113
|
+
| `edit` | 文本替换编辑(9 策略回退匹配) | 容器 |
|
|
114
|
+
| `multiedit` | 对单个文件原子应用多个编辑 | 容器 |
|
|
115
|
+
| `patch` | 应用统一补丁(add/delete/update/move) | 容器 |
|
|
116
|
+
| `glob` | 按 glob 模式搜索文件 | 容器 |
|
|
117
|
+
| `grep` | 按文本模式搜索文件内容 | 容器 |
|
|
118
|
+
| `ls` | 列出目录内容 | 容器 |
|
|
119
|
+
| `lsp` | 语言服务操作(hover/定义/引用/诊断等 14 种操作) | 宿主机 |
|
|
120
|
+
| `get-preview-url` | 获取沙箱端口的预览 URL | 容器 |
|
|
121
|
+
|
|
122
|
+
## 包目录结构
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
src/
|
|
126
|
+
├── index.ts # 包入口(re-export)
|
|
127
|
+
└── sandbox/
|
|
128
|
+
├── index.ts # 插件入口 (sandboxPlugin)
|
|
129
|
+
├── tools.ts # 工具工厂注册中心
|
|
130
|
+
├── cli/ # CLI: install/uninstall/doctor/setup-path/help
|
|
131
|
+
├── core/ # 核心层
|
|
132
|
+
│ ├── docker-sandbox.ts # Sandbox 接口实现
|
|
133
|
+
│ ├── session-manager.ts # 会话生命周期管理
|
|
134
|
+
│ ├── shell/ # Shell 执行子系统
|
|
135
|
+
│ ├── write/ # 写入管线子系统
|
|
136
|
+
│ ├── read/ # 读取子系统
|
|
137
|
+
│ ├── edit/ # 编辑子系统(9 策略回退)
|
|
138
|
+
│ └── lsp/ # LSP 客户端池 + 诊断
|
|
139
|
+
├── tools/ # 14 个工具实现
|
|
140
|
+
├── git/ # 双轨 Git(容器 + 宿主机)
|
|
141
|
+
└── plugins/ # OpenCode 钩子(工具、事件、系统提示)
|
|
142
|
+
dist/ # 生成的 ESM(npm pack 目标)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 开发
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm --workspace @wavexzore/sandbox run typecheck
|
|
149
|
+
npm --workspace @wavexzore/sandbox run lint
|
|
150
|
+
npm --workspace @wavexzore/sandbox test
|
|
151
|
+
npm --workspace @wavexzore/sandbox run build
|
|
152
|
+
npm --workspace @wavexzore/sandbox run verify
|
|
153
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WavexzoreSandboxDoctorOptions, type WavexzoreSandboxDoctorResult, type WavexzoreSandboxInstallOptions, type WavexzoreSandboxInstallResult, type WavexzoreSandboxPathOptions, type WavexzoreSandboxPathResult, type WavexzoreSandboxUninstallOptions, type WavexzoreSandboxUninstallResult } from './types.js';
|
|
2
|
+
export declare function installWavexzoreSandbox(rootInput?: string, options?: WavexzoreSandboxInstallOptions): WavexzoreSandboxInstallResult;
|
|
3
|
+
export declare function uninstallWavexzoreSandbox(rootInput?: string, options?: WavexzoreSandboxUninstallOptions): WavexzoreSandboxUninstallResult;
|
|
4
|
+
export declare function doctorWavexzoreSandbox(options?: WavexzoreSandboxDoctorOptions): Promise<WavexzoreSandboxDoctorResult>;
|
|
5
|
+
export declare function setupWavexzoreSandboxPathCommand(options?: WavexzoreSandboxPathOptions): WavexzoreSandboxPathResult;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { installOpenCodeConfig, normalizePluginEntries, readOpenCodeConfig, uninstallOpenCodeConfig, } from './opencode-config.js';
|
|
5
|
+
import { isDirectoryOnPath, pathHintForPlatform, resolveOpenCodeConfigPath, resolveWavexzoreSandboxBinDir, resolveWavexzoreSandboxHomeDir, resolveWavexzoreSandboxStoreDir, runtimePlatform, } from './path.js';
|
|
6
|
+
import { prepareCurrentPackageVersion, readWavexzoreSandboxManifest, removeEmptyDirectory, removePathIfExists, removeWavexzoreSandboxPath, resolveWavexzoreSandboxLoaderDir, resolveWavexzoreSandboxManifestPath, setupWavexzoreSandboxPath, nextManifest, pathToPluginFileSpec, resolveOpenCodeHelperPath, resolveWavexzoreSandboxBinShimPaths, resolveWavexzoreSandboxVersionDir, uninstallWavexzoreSandboxBinShims, writeLocalLoader, writeManifest, writeWavexzoreSandboxBinShims, } from './local-store.js';
|
|
7
|
+
import { WAVEXZORE_SANDBOX_CLI, } from './types.js';
|
|
8
|
+
function resolvedRoot(root) {
|
|
9
|
+
return resolve(root ?? '.');
|
|
10
|
+
}
|
|
11
|
+
function resolvedScope(scope) {
|
|
12
|
+
return scope ?? 'user';
|
|
13
|
+
}
|
|
14
|
+
function uniqueStrings(items) {
|
|
15
|
+
return Array.from(new Set(items));
|
|
16
|
+
}
|
|
17
|
+
export function installWavexzoreSandbox(rootInput = '.', options = {}) {
|
|
18
|
+
const root = resolvedRoot(options.root ?? rootInput);
|
|
19
|
+
const scope = resolvedScope(options.scope);
|
|
20
|
+
const platform = runtimePlatform(options.platform);
|
|
21
|
+
const homeDir = resolveWavexzoreSandboxHomeDir({
|
|
22
|
+
root,
|
|
23
|
+
scope,
|
|
24
|
+
homeDir: options.homeDir,
|
|
25
|
+
storeDir: options.storeDir,
|
|
26
|
+
env: options.env,
|
|
27
|
+
platform,
|
|
28
|
+
});
|
|
29
|
+
const storeDir = resolveWavexzoreSandboxStoreDir({
|
|
30
|
+
root,
|
|
31
|
+
scope,
|
|
32
|
+
homeDir,
|
|
33
|
+
storeDir: options.storeDir,
|
|
34
|
+
env: options.env,
|
|
35
|
+
platform,
|
|
36
|
+
});
|
|
37
|
+
const binDir = resolveWavexzoreSandboxBinDir({
|
|
38
|
+
root,
|
|
39
|
+
scope,
|
|
40
|
+
homeDir,
|
|
41
|
+
storeDir,
|
|
42
|
+
binDir: options.binDir,
|
|
43
|
+
env: options.env,
|
|
44
|
+
platform,
|
|
45
|
+
});
|
|
46
|
+
const loaderDir = resolveWavexzoreSandboxLoaderDir(storeDir);
|
|
47
|
+
const pluginEntry = pathToPluginFileSpec(loaderDir);
|
|
48
|
+
const cliPath = resolveOpenCodeHelperPath(binDir, platform);
|
|
49
|
+
const configPath = resolveOpenCodeConfigPath({
|
|
50
|
+
root,
|
|
51
|
+
scope,
|
|
52
|
+
configDir: options.configDir,
|
|
53
|
+
configPath: options.configPath,
|
|
54
|
+
env: options.env,
|
|
55
|
+
platform,
|
|
56
|
+
});
|
|
57
|
+
const manifestPath = resolveWavexzoreSandboxManifestPath(storeDir);
|
|
58
|
+
const currentManifest = existsSync(manifestPath) ? readWavexzoreSandboxManifest(storeDir) : undefined;
|
|
59
|
+
const prepared = prepareCurrentPackageVersion({
|
|
60
|
+
storeDir,
|
|
61
|
+
packageRoot: options.packageRoot,
|
|
62
|
+
dryRun: options.dryRun,
|
|
63
|
+
env: options.env,
|
|
64
|
+
});
|
|
65
|
+
const loaderStatus = writeLocalLoader(loaderDir, options.dryRun);
|
|
66
|
+
const manifest = nextManifest({
|
|
67
|
+
current: currentManifest,
|
|
68
|
+
activeVersion: prepared.version,
|
|
69
|
+
configPath,
|
|
70
|
+
pluginSpec: pluginEntry,
|
|
71
|
+
cliPath,
|
|
72
|
+
channel: options.channel,
|
|
73
|
+
});
|
|
74
|
+
const manifestStatus = writeManifest(manifestPath, manifest, options.dryRun);
|
|
75
|
+
const config = installOpenCodeConfig({ configPath, pluginEntry, dryRun: options.dryRun });
|
|
76
|
+
const installBin = options.installBin !== false;
|
|
77
|
+
const bin = writeWavexzoreSandboxBinShims({
|
|
78
|
+
binDir,
|
|
79
|
+
storeDir,
|
|
80
|
+
dryRun: options.dryRun,
|
|
81
|
+
enabled: installBin,
|
|
82
|
+
platform: options.platform,
|
|
83
|
+
});
|
|
84
|
+
const path = !installBin
|
|
85
|
+
? { pathStatus: 'skipped', pathHint: pathHintForPlatform(binDir, platform), pathConfigPath: undefined }
|
|
86
|
+
: options.setupPath
|
|
87
|
+
? setupWavexzoreSandboxPath({
|
|
88
|
+
root,
|
|
89
|
+
scope,
|
|
90
|
+
homeDir,
|
|
91
|
+
storeDir,
|
|
92
|
+
binDir,
|
|
93
|
+
dryRun: options.dryRun,
|
|
94
|
+
envPath: options.envPath,
|
|
95
|
+
pathConfigPath: options.pathConfigPath,
|
|
96
|
+
platform,
|
|
97
|
+
shell: options.shell,
|
|
98
|
+
env: options.env,
|
|
99
|
+
})
|
|
100
|
+
: {
|
|
101
|
+
pathStatus: isDirectoryOnPath(binDir, options.envPath, platform)
|
|
102
|
+
? 'present'
|
|
103
|
+
: 'missing',
|
|
104
|
+
pathHint: pathHintForPlatform(binDir, platform),
|
|
105
|
+
pathConfigPath: undefined,
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
root,
|
|
109
|
+
dryRun: options.dryRun === true,
|
|
110
|
+
scope,
|
|
111
|
+
homeDir,
|
|
112
|
+
storeDir,
|
|
113
|
+
binDir,
|
|
114
|
+
cliPath,
|
|
115
|
+
binStatus: bin.binStatus,
|
|
116
|
+
pathStatus: path.pathStatus,
|
|
117
|
+
pathConfigPath: path.pathConfigPath,
|
|
118
|
+
pathHint: path.pathHint,
|
|
119
|
+
manifestPath,
|
|
120
|
+
manifestStatus,
|
|
121
|
+
loaderDir,
|
|
122
|
+
loaderStatus,
|
|
123
|
+
activeVersion: prepared.version,
|
|
124
|
+
versionPath: prepared.versionPath,
|
|
125
|
+
versionStatus: prepared.versionStatus,
|
|
126
|
+
configPath,
|
|
127
|
+
configStatus: config.configStatus,
|
|
128
|
+
pluginStatus: config.pluginStatus,
|
|
129
|
+
removedPluginEntries: uniqueStrings(config.removedPluginEntries),
|
|
130
|
+
pluginEntry,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export function uninstallWavexzoreSandbox(rootInput = '.', options = {}) {
|
|
134
|
+
const root = resolvedRoot(options.root ?? rootInput);
|
|
135
|
+
const scope = resolvedScope(options.scope);
|
|
136
|
+
const platform = runtimePlatform(options.platform);
|
|
137
|
+
const homeDir = resolveWavexzoreSandboxHomeDir({
|
|
138
|
+
root,
|
|
139
|
+
scope,
|
|
140
|
+
homeDir: options.homeDir,
|
|
141
|
+
storeDir: options.storeDir,
|
|
142
|
+
env: options.env,
|
|
143
|
+
platform,
|
|
144
|
+
});
|
|
145
|
+
const storeDir = resolveWavexzoreSandboxStoreDir({
|
|
146
|
+
root,
|
|
147
|
+
scope,
|
|
148
|
+
homeDir,
|
|
149
|
+
storeDir: options.storeDir,
|
|
150
|
+
env: options.env,
|
|
151
|
+
platform,
|
|
152
|
+
});
|
|
153
|
+
const binDir = resolveWavexzoreSandboxBinDir({
|
|
154
|
+
root,
|
|
155
|
+
scope,
|
|
156
|
+
homeDir,
|
|
157
|
+
storeDir,
|
|
158
|
+
binDir: options.binDir,
|
|
159
|
+
env: options.env,
|
|
160
|
+
platform,
|
|
161
|
+
});
|
|
162
|
+
const loaderDir = resolveWavexzoreSandboxLoaderDir(storeDir);
|
|
163
|
+
const pluginEntry = pathToPluginFileSpec(loaderDir);
|
|
164
|
+
const configPath = resolveOpenCodeConfigPath({
|
|
165
|
+
root,
|
|
166
|
+
scope,
|
|
167
|
+
configDir: options.configDir,
|
|
168
|
+
configPath: options.configPath,
|
|
169
|
+
env: options.env,
|
|
170
|
+
platform,
|
|
171
|
+
});
|
|
172
|
+
const manifestPath = resolveWavexzoreSandboxManifestPath(storeDir);
|
|
173
|
+
const config = uninstallOpenCodeConfig({
|
|
174
|
+
configPath,
|
|
175
|
+
pluginEntry,
|
|
176
|
+
dryRun: options.dryRun,
|
|
177
|
+
keepConfig: options.keepConfig,
|
|
178
|
+
});
|
|
179
|
+
const storeStatus = options.keepStore ? 'kept' : removePathIfExists(storeDir, options.dryRun);
|
|
180
|
+
const bin = uninstallWavexzoreSandboxBinShims({ binDir, dryRun: options.dryRun, keepBin: options.keepBin });
|
|
181
|
+
const path = options.removePath
|
|
182
|
+
? removeWavexzoreSandboxPath({
|
|
183
|
+
root,
|
|
184
|
+
scope,
|
|
185
|
+
homeDir,
|
|
186
|
+
storeDir,
|
|
187
|
+
binDir,
|
|
188
|
+
dryRun: options.dryRun,
|
|
189
|
+
envPath: options.envPath,
|
|
190
|
+
pathConfigPath: options.pathConfigPath,
|
|
191
|
+
platform: options.platform,
|
|
192
|
+
shell: options.shell,
|
|
193
|
+
env: options.env,
|
|
194
|
+
})
|
|
195
|
+
: {
|
|
196
|
+
pathStatus: 'kept',
|
|
197
|
+
pathConfigPath: undefined,
|
|
198
|
+
pathHint: pathHintForPlatform(binDir, platform),
|
|
199
|
+
homeDir,
|
|
200
|
+
binDir,
|
|
201
|
+
};
|
|
202
|
+
if (!options.keepBin)
|
|
203
|
+
removeEmptyDirectory(binDir, options.dryRun);
|
|
204
|
+
if (!options.keepStore && !options.keepBin)
|
|
205
|
+
removeEmptyDirectory(homeDir, options.dryRun);
|
|
206
|
+
return {
|
|
207
|
+
root,
|
|
208
|
+
dryRun: options.dryRun === true,
|
|
209
|
+
scope,
|
|
210
|
+
homeDir,
|
|
211
|
+
storeDir,
|
|
212
|
+
binDir,
|
|
213
|
+
binStatus: bin.binStatus,
|
|
214
|
+
pathStatus: path.pathStatus,
|
|
215
|
+
pathConfigPath: path.pathConfigPath,
|
|
216
|
+
storeStatus,
|
|
217
|
+
manifestPath,
|
|
218
|
+
configPath,
|
|
219
|
+
configStatus: config.configStatus,
|
|
220
|
+
pluginEntry,
|
|
221
|
+
removedPluginEntries: uniqueStrings(config.removedPluginEntries),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function check(name, status, message, path) {
|
|
225
|
+
return { name, status, message, path };
|
|
226
|
+
}
|
|
227
|
+
async function canImport(path) {
|
|
228
|
+
try {
|
|
229
|
+
await import(pathToFileURL(path).href);
|
|
230
|
+
return { ok: true, message: 'import succeeded' };
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
return { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function configContainsPlugin(configPath, pluginEntry) {
|
|
237
|
+
if (!existsSync(configPath))
|
|
238
|
+
return false;
|
|
239
|
+
const config = readOpenCodeConfig(configPath);
|
|
240
|
+
return normalizePluginEntries(config.plugin).some((item) => {
|
|
241
|
+
if (typeof item === 'string')
|
|
242
|
+
return item === pluginEntry;
|
|
243
|
+
if (Array.isArray(item) && item[0] === pluginEntry)
|
|
244
|
+
return true;
|
|
245
|
+
if (item && typeof item === 'object' && item.name === pluginEntry)
|
|
246
|
+
return true;
|
|
247
|
+
return false;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
export async function doctorWavexzoreSandbox(options = {}) {
|
|
251
|
+
const root = resolvedRoot(options.root ?? options.projectRoot ?? '.');
|
|
252
|
+
const scope = resolvedScope(options.scope);
|
|
253
|
+
const platform = runtimePlatform(options.platform);
|
|
254
|
+
const homeDir = resolveWavexzoreSandboxHomeDir({
|
|
255
|
+
root,
|
|
256
|
+
scope,
|
|
257
|
+
homeDir: options.homeDir,
|
|
258
|
+
storeDir: options.storeDir,
|
|
259
|
+
env: options.env,
|
|
260
|
+
platform,
|
|
261
|
+
});
|
|
262
|
+
const storeDir = resolveWavexzoreSandboxStoreDir({
|
|
263
|
+
root,
|
|
264
|
+
scope,
|
|
265
|
+
homeDir,
|
|
266
|
+
storeDir: options.storeDir,
|
|
267
|
+
env: options.env,
|
|
268
|
+
platform,
|
|
269
|
+
});
|
|
270
|
+
const binDir = resolveWavexzoreSandboxBinDir({
|
|
271
|
+
root,
|
|
272
|
+
scope,
|
|
273
|
+
homeDir,
|
|
274
|
+
storeDir,
|
|
275
|
+
binDir: options.binDir,
|
|
276
|
+
env: options.env,
|
|
277
|
+
platform,
|
|
278
|
+
});
|
|
279
|
+
const configPath = resolveOpenCodeConfigPath({
|
|
280
|
+
root,
|
|
281
|
+
scope,
|
|
282
|
+
configDir: options.configDir,
|
|
283
|
+
configPath: options.configPath,
|
|
284
|
+
env: options.env,
|
|
285
|
+
platform,
|
|
286
|
+
});
|
|
287
|
+
const loaderDir = resolveWavexzoreSandboxLoaderDir(storeDir);
|
|
288
|
+
const pluginEntry = pathToPluginFileSpec(loaderDir);
|
|
289
|
+
const cliPath = resolveOpenCodeHelperPath(binDir, platform);
|
|
290
|
+
const manifestPath = resolveWavexzoreSandboxManifestPath(storeDir);
|
|
291
|
+
const checks = [];
|
|
292
|
+
checks.push(check('config', existsSync(configPath) ? 'passed' : 'failed', existsSync(configPath) ? 'OpenCode config exists' : 'OpenCode config is missing', configPath));
|
|
293
|
+
checks.push(check('config-plugin', configContainsPlugin(configPath, pluginEntry) ? 'passed' : 'failed', configContainsPlugin(configPath, pluginEntry)
|
|
294
|
+
? 'OpenCode config references the local loader'
|
|
295
|
+
: 'OpenCode config does not reference the local loader', configPath));
|
|
296
|
+
checks.push(check('loader', existsSync(join(loaderDir, 'index.mjs')) ? 'passed' : 'failed', existsSync(join(loaderDir, 'index.mjs')) ? 'local loader exists' : 'local loader is missing', loaderDir));
|
|
297
|
+
let activeVersion;
|
|
298
|
+
try {
|
|
299
|
+
const manifest = readWavexzoreSandboxManifest(storeDir);
|
|
300
|
+
activeVersion = manifest?.activeVersion;
|
|
301
|
+
checks.push(check('manifest', manifest ? 'passed' : 'failed', manifest ? `active version ${manifest.activeVersion}` : 'manifest is missing', manifestPath));
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
checks.push(check('manifest', 'failed', error instanceof Error ? error.message : String(error), manifestPath));
|
|
305
|
+
}
|
|
306
|
+
if (activeVersion) {
|
|
307
|
+
const pluginJs = join(resolveWavexzoreSandboxVersionDir(storeDir, activeVersion), 'dist', 'index.js');
|
|
308
|
+
const cliJs = join(resolveWavexzoreSandboxVersionDir(storeDir, activeVersion), 'dist', 'sandbox', 'cli', `${WAVEXZORE_SANDBOX_CLI}.js`);
|
|
309
|
+
checks.push(check('plugin-entry', existsSync(pluginJs) ? 'passed' : 'failed', existsSync(pluginJs) ? 'plugin entry exists' : 'plugin entry is missing', pluginJs));
|
|
310
|
+
checks.push(check('cli-entry', existsSync(cliJs) ? 'passed' : 'failed', existsSync(cliJs) ? 'CLI entry exists' : 'CLI entry is missing', cliJs));
|
|
311
|
+
if (existsSync(join(loaderDir, 'index.mjs'))) {
|
|
312
|
+
const imported = await canImport(join(loaderDir, 'index.mjs'));
|
|
313
|
+
checks.push(check('loader-import', imported.ok ? 'passed' : 'failed', imported.message, join(loaderDir, 'index.mjs')));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const shims = resolveWavexzoreSandboxBinShimPaths(binDir);
|
|
317
|
+
checks.push(check('bin', existsSync(shims.js) && existsSync(shims.unix) ? 'passed' : 'failed', existsSync(shims.js) && existsSync(shims.unix) ? 'bin shims exist' : 'bin shims are missing', binDir));
|
|
318
|
+
checks.push(check('opencode-helper', existsSync(cliPath) ? 'passed' : 'failed', existsSync(cliPath) ? `OpenCode helper exists at ${cliPath}` : `OpenCode helper is missing at ${cliPath}`, cliPath));
|
|
319
|
+
return {
|
|
320
|
+
status: checks.every((item) => item.status === 'passed') ? 'passed' : 'failed',
|
|
321
|
+
homeDir,
|
|
322
|
+
storeDir,
|
|
323
|
+
binDir,
|
|
324
|
+
cliPath,
|
|
325
|
+
configPath,
|
|
326
|
+
pluginEntry,
|
|
327
|
+
activeVersion,
|
|
328
|
+
checks,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
export function setupWavexzoreSandboxPathCommand(options = {}) {
|
|
332
|
+
const root = resolvedRoot(options.root ?? '.');
|
|
333
|
+
const scope = resolvedScope(options.scope);
|
|
334
|
+
return setupWavexzoreSandboxPath({ ...options, root, scope });
|
|
335
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type BinStatus, type InstallStatus, type RuntimeEnv, type WavexzoreSandboxManifest, type WavexzoreSandboxPathResult, type StoreScope, type UninstallStatus } from './types.js';
|
|
2
|
+
export declare function nowIso(now?: number): string;
|
|
3
|
+
export declare function writeFileAtomic(path: string, contents: string, dryRun?: boolean): void;
|
|
4
|
+
export declare function writeJsonAtomic(path: string, value: unknown, dryRun?: boolean): void;
|
|
5
|
+
export declare function statusForText(path: string, desired: string): InstallStatus;
|
|
6
|
+
export declare function assertSafeVersion(version: string): void;
|
|
7
|
+
export declare function resolveWavexzoreSandboxManifestPath(storeDir: string): string;
|
|
8
|
+
export declare function resolveWavexzoreSandboxLoaderDir(storeDir: string): string;
|
|
9
|
+
export declare function resolveWavexzoreSandboxVersionDir(storeDir: string, version: string): string;
|
|
10
|
+
export declare function pathToPluginFileSpec(loaderDir: string): string;
|
|
11
|
+
export declare function resolveWavexzoreSandboxBinShimPaths(binDir: string): {
|
|
12
|
+
js: string;
|
|
13
|
+
unix: string;
|
|
14
|
+
cmd: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function resolveOpenCodeHelperPath(binDir: string, platform?: string): string;
|
|
17
|
+
export declare function resolveCurrentPackageRoot(explicit?: string, env?: RuntimeEnv): string;
|
|
18
|
+
export declare function readCurrentPackageVersion(packageRoot?: string): string;
|
|
19
|
+
export declare function prepareCurrentPackageVersion(args: {
|
|
20
|
+
storeDir: string;
|
|
21
|
+
packageRoot?: string;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
env?: RuntimeEnv;
|
|
24
|
+
}): {
|
|
25
|
+
version: string;
|
|
26
|
+
versionPath: string;
|
|
27
|
+
versionStatus: InstallStatus;
|
|
28
|
+
};
|
|
29
|
+
export declare function writeLocalLoader(loaderDir: string, dryRun?: boolean): InstallStatus;
|
|
30
|
+
export declare function readWavexzoreSandboxManifest(storeDir: string): WavexzoreSandboxManifest | undefined;
|
|
31
|
+
export declare function nextManifest(args: {
|
|
32
|
+
current?: WavexzoreSandboxManifest;
|
|
33
|
+
activeVersion: string;
|
|
34
|
+
configPath: string;
|
|
35
|
+
pluginSpec: string;
|
|
36
|
+
cliPath: string;
|
|
37
|
+
channel?: string;
|
|
38
|
+
now?: number;
|
|
39
|
+
}): WavexzoreSandboxManifest;
|
|
40
|
+
export declare function writeManifest(manifestPath: string, manifest: WavexzoreSandboxManifest, dryRun?: boolean): InstallStatus;
|
|
41
|
+
export declare function writeWavexzoreSandboxBinShims(args: {
|
|
42
|
+
binDir: string;
|
|
43
|
+
storeDir: string;
|
|
44
|
+
dryRun?: boolean;
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
platform?: string;
|
|
47
|
+
}): {
|
|
48
|
+
binDir: string;
|
|
49
|
+
binStatus: BinStatus;
|
|
50
|
+
};
|
|
51
|
+
export declare function uninstallWavexzoreSandboxBinShims(args: {
|
|
52
|
+
binDir: string;
|
|
53
|
+
dryRun?: boolean;
|
|
54
|
+
keepBin?: boolean;
|
|
55
|
+
}): {
|
|
56
|
+
binDir: string;
|
|
57
|
+
binStatus: UninstallStatus;
|
|
58
|
+
};
|
|
59
|
+
export declare function removePathIfExists(path: string, dryRun?: boolean): UninstallStatus;
|
|
60
|
+
export declare function removeEmptyDirectory(path: string, dryRun?: boolean): void;
|
|
61
|
+
export declare function setupWavexzoreSandboxPath(options?: {
|
|
62
|
+
root?: string;
|
|
63
|
+
scope?: StoreScope;
|
|
64
|
+
homeDir?: string;
|
|
65
|
+
storeDir?: string;
|
|
66
|
+
binDir?: string;
|
|
67
|
+
dryRun?: boolean;
|
|
68
|
+
envPath?: string;
|
|
69
|
+
pathConfigPath?: string;
|
|
70
|
+
platform?: string;
|
|
71
|
+
shell?: string;
|
|
72
|
+
env?: RuntimeEnv;
|
|
73
|
+
}): WavexzoreSandboxPathResult;
|
|
74
|
+
export declare function removeWavexzoreSandboxPath(options?: {
|
|
75
|
+
root?: string;
|
|
76
|
+
scope?: StoreScope;
|
|
77
|
+
homeDir?: string;
|
|
78
|
+
storeDir?: string;
|
|
79
|
+
binDir?: string;
|
|
80
|
+
dryRun?: boolean;
|
|
81
|
+
envPath?: string;
|
|
82
|
+
pathConfigPath?: string;
|
|
83
|
+
platform?: string;
|
|
84
|
+
shell?: string;
|
|
85
|
+
env?: RuntimeEnv;
|
|
86
|
+
}): WavexzoreSandboxPathResult;
|
|
87
|
+
export declare function relativeWithin(root: string, target: string): string;
|