@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.
Files changed (149) hide show
  1. package/Dockerfile +14 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +3 -0
  4. package/README.md +153 -0
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.js +9 -0
  7. package/dist/sandbox/cli/install.d.ts +5 -0
  8. package/dist/sandbox/cli/install.js +335 -0
  9. package/dist/sandbox/cli/local-store.d.ts +87 -0
  10. package/dist/sandbox/cli/local-store.js +604 -0
  11. package/dist/sandbox/cli/opencode-config.d.ts +25 -0
  12. package/dist/sandbox/cli/opencode-config.js +240 -0
  13. package/dist/sandbox/cli/path.d.ts +64 -0
  14. package/dist/sandbox/cli/path.js +127 -0
  15. package/dist/sandbox/cli/types.d.ts +145 -0
  16. package/dist/sandbox/cli/types.js +6 -0
  17. package/dist/sandbox/cli/wavexzore-sandbox.d.ts +65 -0
  18. package/dist/sandbox/cli/wavexzore-sandbox.js +577 -0
  19. package/dist/sandbox/core/cli-helper.d.ts +19 -0
  20. package/dist/sandbox/core/cli-helper.js +64 -0
  21. package/dist/sandbox/core/docker-archive-utils.d.ts +3 -0
  22. package/dist/sandbox/core/docker-archive-utils.js +50 -0
  23. package/dist/sandbox/core/docker-sandbox.d.ts +51 -0
  24. package/dist/sandbox/core/docker-sandbox.js +675 -0
  25. package/dist/sandbox/core/edit/filediff.d.ts +16 -0
  26. package/dist/sandbox/core/edit/filediff.js +21 -0
  27. package/dist/sandbox/core/edit/index.d.ts +5 -0
  28. package/dist/sandbox/core/edit/index.js +5 -0
  29. package/dist/sandbox/core/edit/line-endings.d.ts +4 -0
  30. package/dist/sandbox/core/edit/line-endings.js +10 -0
  31. package/dist/sandbox/core/edit/lock.d.ts +1 -0
  32. package/dist/sandbox/core/edit/lock.js +18 -0
  33. package/dist/sandbox/core/edit/replace.d.ts +10 -0
  34. package/dist/sandbox/core/edit/replace.js +14 -0
  35. package/dist/sandbox/core/edit/replacers.d.ts +15 -0
  36. package/dist/sandbox/core/edit/replacers.js +241 -0
  37. package/dist/sandbox/core/logger.d.ts +15 -0
  38. package/dist/sandbox/core/logger.js +59 -0
  39. package/dist/sandbox/core/lsp/client.d.ts +63 -0
  40. package/dist/sandbox/core/lsp/client.js +533 -0
  41. package/dist/sandbox/core/lsp/config.d.ts +13 -0
  42. package/dist/sandbox/core/lsp/config.js +36 -0
  43. package/dist/sandbox/core/lsp/diagnostics.d.ts +12 -0
  44. package/dist/sandbox/core/lsp/diagnostics.js +65 -0
  45. package/dist/sandbox/core/lsp/index.d.ts +4 -0
  46. package/dist/sandbox/core/lsp/index.js +4 -0
  47. package/dist/sandbox/core/lsp/language.d.ts +24 -0
  48. package/dist/sandbox/core/lsp/language.js +249 -0
  49. package/dist/sandbox/core/lsp/manager.d.ts +77 -0
  50. package/dist/sandbox/core/lsp/manager.js +237 -0
  51. package/dist/sandbox/core/lsp/tooling.d.ts +14 -0
  52. package/dist/sandbox/core/lsp/tooling.js +78 -0
  53. package/dist/sandbox/core/patch-parser.d.ts +23 -0
  54. package/dist/sandbox/core/patch-parser.js +248 -0
  55. package/dist/sandbox/core/path-map.d.ts +9 -0
  56. package/dist/sandbox/core/path-map.js +73 -0
  57. package/dist/sandbox/core/project-data-storage.d.ts +42 -0
  58. package/dist/sandbox/core/project-data-storage.js +167 -0
  59. package/dist/sandbox/core/read/binary.d.ts +4 -0
  60. package/dist/sandbox/core/read/binary.js +80 -0
  61. package/dist/sandbox/core/read/format.d.ts +38 -0
  62. package/dist/sandbox/core/read/format.js +85 -0
  63. package/dist/sandbox/core/read/index.d.ts +3 -0
  64. package/dist/sandbox/core/read/index.js +3 -0
  65. package/dist/sandbox/core/read/permissions.d.ts +7 -0
  66. package/dist/sandbox/core/read/permissions.js +13 -0
  67. package/dist/sandbox/core/session-manager.d.ts +29 -0
  68. package/dist/sandbox/core/session-manager.js +338 -0
  69. package/dist/sandbox/core/shell/config.d.ts +7 -0
  70. package/dist/sandbox/core/shell/config.js +82 -0
  71. package/dist/sandbox/core/shell/output.d.ts +35 -0
  72. package/dist/sandbox/core/shell/output.js +80 -0
  73. package/dist/sandbox/core/shell/parser.d.ts +7 -0
  74. package/dist/sandbox/core/shell/parser.js +122 -0
  75. package/dist/sandbox/core/shell/permissions.d.ts +13 -0
  76. package/dist/sandbox/core/shell/permissions.js +33 -0
  77. package/dist/sandbox/core/shell/workdir.d.ts +4 -0
  78. package/dist/sandbox/core/shell/workdir.js +19 -0
  79. package/dist/sandbox/core/stream-utils.d.ts +23 -0
  80. package/dist/sandbox/core/stream-utils.js +97 -0
  81. package/dist/sandbox/core/toast.d.ts +47 -0
  82. package/dist/sandbox/core/toast.js +73 -0
  83. package/dist/sandbox/core/types.d.ts +159 -0
  84. package/dist/sandbox/core/types.js +11 -0
  85. package/dist/sandbox/core/write/bom.d.ts +8 -0
  86. package/dist/sandbox/core/write/bom.js +15 -0
  87. package/dist/sandbox/core/write/config.d.ts +14 -0
  88. package/dist/sandbox/core/write/config.js +188 -0
  89. package/dist/sandbox/core/write/diagnostics.d.ts +19 -0
  90. package/dist/sandbox/core/write/diagnostics.js +120 -0
  91. package/dist/sandbox/core/write/diff.d.ts +7 -0
  92. package/dist/sandbox/core/write/diff.js +21 -0
  93. package/dist/sandbox/core/write/formatter.d.ts +16 -0
  94. package/dist/sandbox/core/write/formatter.js +51 -0
  95. package/dist/sandbox/core/write/index.d.ts +6 -0
  96. package/dist/sandbox/core/write/index.js +5 -0
  97. package/dist/sandbox/core/write/permissions.d.ts +13 -0
  98. package/dist/sandbox/core/write/permissions.js +19 -0
  99. package/dist/sandbox/core/write/pipeline.d.ts +48 -0
  100. package/dist/sandbox/core/write/pipeline.js +229 -0
  101. package/dist/sandbox/core/write/read-tracker.d.ts +13 -0
  102. package/dist/sandbox/core/write/read-tracker.js +30 -0
  103. package/dist/sandbox/git/host-git-manager.d.ts +40 -0
  104. package/dist/sandbox/git/host-git-manager.js +278 -0
  105. package/dist/sandbox/git/index.d.ts +5 -0
  106. package/dist/sandbox/git/index.js +5 -0
  107. package/dist/sandbox/git/sandbox-git-manager.d.ts +14 -0
  108. package/dist/sandbox/git/sandbox-git-manager.js +54 -0
  109. package/dist/sandbox/git/session-git-manager.d.ts +18 -0
  110. package/dist/sandbox/git/session-git-manager.js +85 -0
  111. package/dist/sandbox/index.d.ts +205 -0
  112. package/dist/sandbox/index.js +70 -0
  113. package/dist/sandbox/plugins/custom-tools.d.ts +203 -0
  114. package/dist/sandbox/plugins/custom-tools.js +15 -0
  115. package/dist/sandbox/plugins/session-events.d.ts +10 -0
  116. package/dist/sandbox/plugins/session-events.js +56 -0
  117. package/dist/sandbox/plugins/system-transform.d.ts +10 -0
  118. package/dist/sandbox/plugins/system-transform.js +23 -0
  119. package/dist/sandbox/tools/bash-output.d.ts +17 -0
  120. package/dist/sandbox/tools/bash-output.js +35 -0
  121. package/dist/sandbox/tools/bash-status.d.ts +13 -0
  122. package/dist/sandbox/tools/bash-status.js +29 -0
  123. package/dist/sandbox/tools/bash-stop.d.ts +13 -0
  124. package/dist/sandbox/tools/bash-stop.js +28 -0
  125. package/dist/sandbox/tools/bash.d.ts +26 -0
  126. package/dist/sandbox/tools/bash.js +120 -0
  127. package/dist/sandbox/tools/edit.d.ts +20 -0
  128. package/dist/sandbox/tools/edit.js +87 -0
  129. package/dist/sandbox/tools/get-preview-url.d.ts +17 -0
  130. package/dist/sandbox/tools/get-preview-url.js +16 -0
  131. package/dist/sandbox/tools/glob.d.ts +17 -0
  132. package/dist/sandbox/tools/glob.js +23 -0
  133. package/dist/sandbox/tools/grep.d.ts +17 -0
  134. package/dist/sandbox/tools/grep.js +23 -0
  135. package/dist/sandbox/tools/ls.d.ts +17 -0
  136. package/dist/sandbox/tools/ls.js +21 -0
  137. package/dist/sandbox/tools/lsp.d.ts +41 -0
  138. package/dist/sandbox/tools/lsp.js +198 -0
  139. package/dist/sandbox/tools/multiedit.d.ts +24 -0
  140. package/dist/sandbox/tools/multiedit.js +83 -0
  141. package/dist/sandbox/tools/patch.d.ts +14 -0
  142. package/dist/sandbox/tools/patch.js +260 -0
  143. package/dist/sandbox/tools/read.d.ts +22 -0
  144. package/dist/sandbox/tools/read.js +105 -0
  145. package/dist/sandbox/tools/write.d.ts +16 -0
  146. package/dist/sandbox/tools/write.js +27 -0
  147. package/dist/sandbox/tools.d.ts +200 -0
  148. package/dist/sandbox/tools.js +43 -0
  149. package/package.json +55 -0
@@ -0,0 +1,240 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import { HOST_CONFIG_SCHEMA_URL, WAVEXZORE_SANDBOX_PACKAGE, } from './types.js';
4
+ import { statusForText, writeFileAtomic } from './local-store.js';
5
+ function stripJsonComments(raw) {
6
+ let result = '';
7
+ let inString = false;
8
+ let stringChar = '';
9
+ let escaped = false;
10
+ let inLineComment = false;
11
+ let inBlockComment = false;
12
+ for (let index = 0; index < raw.length; index += 1) {
13
+ const char = raw[index];
14
+ const next = raw[index + 1];
15
+ if (inLineComment) {
16
+ if (char === '\n') {
17
+ inLineComment = false;
18
+ result += char;
19
+ }
20
+ continue;
21
+ }
22
+ if (inBlockComment) {
23
+ if (char === '*' && next === '/') {
24
+ inBlockComment = false;
25
+ index += 1;
26
+ }
27
+ continue;
28
+ }
29
+ if (inString) {
30
+ result += char;
31
+ if (escaped) {
32
+ escaped = false;
33
+ continue;
34
+ }
35
+ if (char === '\\') {
36
+ escaped = true;
37
+ continue;
38
+ }
39
+ if (char === stringChar) {
40
+ inString = false;
41
+ stringChar = '';
42
+ }
43
+ continue;
44
+ }
45
+ if (char === '"' || char === "'") {
46
+ inString = true;
47
+ stringChar = char;
48
+ result += char;
49
+ continue;
50
+ }
51
+ if (char === '/' && next === '/') {
52
+ inLineComment = true;
53
+ index += 1;
54
+ continue;
55
+ }
56
+ if (char === '/' && next === '*') {
57
+ inBlockComment = true;
58
+ index += 1;
59
+ continue;
60
+ }
61
+ result += char;
62
+ }
63
+ return result;
64
+ }
65
+ export function readOpenCodeConfig(configPath) {
66
+ if (!existsSync(configPath))
67
+ return {};
68
+ const raw = readFileSync(configPath, 'utf8');
69
+ try {
70
+ return JSON.parse(raw);
71
+ }
72
+ catch {
73
+ return JSON.parse(stripJsonComments(raw).replace(/,\s*([}\]])/g, '$1'));
74
+ }
75
+ }
76
+ function pluginSpec(item) {
77
+ if (typeof item === 'string') {
78
+ const spec = item.trim();
79
+ return spec.length > 0 ? spec : undefined;
80
+ }
81
+ if (Array.isArray(item) && typeof item[0] === 'string') {
82
+ const spec = item[0].trim();
83
+ return spec.length > 0 ? spec : undefined;
84
+ }
85
+ if (item && typeof item === 'object' && typeof item.name === 'string') {
86
+ const spec = item.name.trim();
87
+ return spec.length > 0 ? spec : undefined;
88
+ }
89
+ return undefined;
90
+ }
91
+ export function normalizePluginEntries(value) {
92
+ if (Array.isArray(value))
93
+ return [...value];
94
+ if (typeof value === 'string' && value.trim().length > 0)
95
+ return [value.trim()];
96
+ return [];
97
+ }
98
+ function normalizeSpec(spec) {
99
+ return spec.trim().replace(/^npm:/, '').replace(/\/+$/, '');
100
+ }
101
+ function isPackageSpec(normalized, packageName) {
102
+ return normalized === packageName || normalized.startsWith(`${packageName}@`);
103
+ }
104
+ function normalizeUrlPath(value) {
105
+ return value.replace(/\\/g, '/').replace(/\/+$/, '');
106
+ }
107
+ function isKnownFileSpec(spec) {
108
+ const normalizedSpec = normalizeUrlPath(spec);
109
+ let path = normalizedSpec;
110
+ if (spec.startsWith('file://')) {
111
+ try {
112
+ path = normalizeUrlPath(fileURLToPath(spec));
113
+ }
114
+ catch {
115
+ // Leave the original spec text in place when file URL decoding fails.
116
+ }
117
+ }
118
+ return (path.endsWith('/.wavexzore/opencode/sandbox/opencode-plugin') ||
119
+ path.includes('/.wavexzore/opencode/sandbox/opencode-plugin') ||
120
+ path.includes('/node_modules/@wavexzore/sandbox'));
121
+ }
122
+ export function isKnownWavexzoreSandboxPluginEntry(spec) {
123
+ const normalized = normalizeSpec(spec);
124
+ if (isPackageSpec(normalized, WAVEXZORE_SANDBOX_PACKAGE))
125
+ return true;
126
+ if (normalized === 'wavexzore-sandbox')
127
+ return true;
128
+ return isKnownFileSpec(spec);
129
+ }
130
+ export function nextPluginEntries(current, pluginEntry) {
131
+ const plugins = [];
132
+ const removed = [];
133
+ let alreadyInstalled = false;
134
+ for (const item of current) {
135
+ const spec = pluginSpec(item);
136
+ if (!spec) {
137
+ plugins.push(item);
138
+ continue;
139
+ }
140
+ if (spec === pluginEntry) {
141
+ if (!alreadyInstalled) {
142
+ plugins.push(item);
143
+ alreadyInstalled = true;
144
+ }
145
+ else {
146
+ removed.push(spec);
147
+ }
148
+ continue;
149
+ }
150
+ if (isKnownWavexzoreSandboxPluginEntry(spec)) {
151
+ removed.push(spec);
152
+ continue;
153
+ }
154
+ plugins.push(item);
155
+ }
156
+ if (!alreadyInstalled)
157
+ plugins.push(pluginEntry);
158
+ const same = JSON.stringify(current) === JSON.stringify(plugins);
159
+ return {
160
+ plugins,
161
+ removed,
162
+ pluginStatus: current.length === 0 && !alreadyInstalled ? 'created' : same ? 'existing' : 'updated',
163
+ };
164
+ }
165
+ export function installOpenCodeConfig(args) {
166
+ const existed = existsSync(args.configPath);
167
+ const current = readOpenCodeConfig(args.configPath);
168
+ const next = { ...current };
169
+ if (typeof next.$schema !== 'string' || next.$schema.length === 0)
170
+ next.$schema = HOST_CONFIG_SCHEMA_URL;
171
+ const pluginUpdate = nextPluginEntries(normalizePluginEntries(current.plugin), args.pluginEntry);
172
+ next.plugin = pluginUpdate.plugins;
173
+ const desired = `${JSON.stringify(next, null, 2)}\n`;
174
+ const configStatus = statusForText(args.configPath, desired);
175
+ if (!args.dryRun)
176
+ writeFileAtomic(args.configPath, desired);
177
+ return {
178
+ configPath: args.configPath,
179
+ configStatus: !existed ? 'created' : configStatus,
180
+ pluginEntry: args.pluginEntry,
181
+ pluginStatus: pluginUpdate.pluginStatus,
182
+ removedPluginEntries: pluginUpdate.removed,
183
+ };
184
+ }
185
+ function combineConfigStatus(removedPluginEntries, changed, exists, keepConfig) {
186
+ if (keepConfig)
187
+ return 'kept';
188
+ if (!exists)
189
+ return 'missing';
190
+ if (removedPluginEntries.length === 0)
191
+ return 'unchanged';
192
+ return changed ? 'updated' : 'unchanged';
193
+ }
194
+ export function uninstallOpenCodeConfig(args) {
195
+ if (args.keepConfig) {
196
+ return {
197
+ configPath: args.configPath,
198
+ configStatus: 'kept',
199
+ pluginEntry: args.pluginEntry,
200
+ removedPluginEntries: [],
201
+ };
202
+ }
203
+ if (!existsSync(args.configPath)) {
204
+ return {
205
+ configPath: args.configPath,
206
+ configStatus: 'missing',
207
+ pluginEntry: args.pluginEntry,
208
+ removedPluginEntries: [],
209
+ };
210
+ }
211
+ const current = readOpenCodeConfig(args.configPath);
212
+ const next = { ...current };
213
+ const removedPluginEntries = [];
214
+ const nextPlugins = [];
215
+ for (const item of normalizePluginEntries(current.plugin)) {
216
+ const spec = pluginSpec(item);
217
+ if (spec && (spec === args.pluginEntry || isKnownWavexzoreSandboxPluginEntry(spec))) {
218
+ removedPluginEntries.push(spec);
219
+ continue;
220
+ }
221
+ nextPlugins.push(item);
222
+ }
223
+ if (removedPluginEntries.length > 0) {
224
+ if (nextPlugins.length > 0)
225
+ next.plugin = nextPlugins;
226
+ else
227
+ delete next.plugin;
228
+ }
229
+ const desired = `${JSON.stringify(next, null, 2)}\n`;
230
+ const currentRaw = readFileSync(args.configPath, 'utf8');
231
+ const changed = desired !== currentRaw;
232
+ if (!args.dryRun && changed && removedPluginEntries.length > 0)
233
+ writeFileAtomic(args.configPath, desired);
234
+ return {
235
+ configPath: args.configPath,
236
+ configStatus: combineConfigStatus(removedPluginEntries, changed, true, args.keepConfig),
237
+ pluginEntry: args.pluginEntry,
238
+ removedPluginEntries,
239
+ };
240
+ }
@@ -0,0 +1,64 @@
1
+ import type { RuntimeEnv, StoreScope } from './types.js';
2
+ export declare function runtimeEnv(env?: RuntimeEnv): RuntimeEnv;
3
+ export declare function runtimePlatform(platform?: string): string;
4
+ export declare function runtimeCwd(): string;
5
+ export declare function defaultHome(env?: RuntimeEnv, platform?: string): string;
6
+ export declare function resolveWavexzoreSandboxHomeDir(args?: {
7
+ root?: string;
8
+ scope?: StoreScope;
9
+ homeDir?: string;
10
+ storeDir?: string;
11
+ env?: RuntimeEnv;
12
+ platform?: string;
13
+ }): string;
14
+ export declare function resolveWavexzoreSandboxStoreDir(args?: {
15
+ root?: string;
16
+ scope?: StoreScope;
17
+ homeDir?: string;
18
+ storeDir?: string;
19
+ env?: RuntimeEnv;
20
+ platform?: string;
21
+ }): string;
22
+ export declare function resolveWavexzoreSandboxBinDir(args?: {
23
+ root?: string;
24
+ scope?: StoreScope;
25
+ homeDir?: string;
26
+ storeDir?: string;
27
+ binDir?: string;
28
+ env?: RuntimeEnv;
29
+ platform?: string;
30
+ }): string;
31
+ export declare function resolveOpenCodeConfigDir(args?: {
32
+ configDir?: string;
33
+ env?: RuntimeEnv;
34
+ platform?: string;
35
+ }): string;
36
+ export declare function resolveOpenCodeGlobalConfigCandidates(args?: {
37
+ configDir?: string;
38
+ env?: RuntimeEnv;
39
+ platform?: string;
40
+ }): string[];
41
+ export declare function resolveOpenCodeProjectConfigCandidates(args?: {
42
+ root?: string;
43
+ platform?: string;
44
+ }): string[];
45
+ export declare function resolveOpenCodeConfigPath(args?: {
46
+ root?: string;
47
+ scope?: StoreScope;
48
+ configDir?: string;
49
+ configPath?: string;
50
+ env?: RuntimeEnv;
51
+ platform?: string;
52
+ }): string;
53
+ export declare function pathSeparatorForPlatform(platform?: string): string;
54
+ export declare function normalizePathForCompare(input: string, platform?: string): string;
55
+ export declare function isDirectoryOnPath(binDir: string, envPath?: string, platform?: string): boolean;
56
+ export declare function shellQuote(value: string): string;
57
+ export declare function powershellSingleQuote(value: string): string;
58
+ export declare function pathHintForPlatform(binDir: string, platform?: string): string;
59
+ export declare function shellProfilePath(args: {
60
+ homeDir: string;
61
+ pathConfigPath?: string;
62
+ shell?: string;
63
+ env?: RuntimeEnv;
64
+ }): string;
@@ -0,0 +1,127 @@
1
+ import { existsSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { basename, join, win32, posix } from 'path';
4
+ const OPENCODE_GLOBAL_CONFIG_FILES = ['opencode.jsonc', 'opencode.json', 'config.json'];
5
+ const OPENCODE_PROJECT_CONFIG_FILES = ['opencode.jsonc', 'opencode.json'];
6
+ export function runtimeEnv(env) {
7
+ return env ?? process.env;
8
+ }
9
+ export function runtimePlatform(platform) {
10
+ return platform ?? process.platform;
11
+ }
12
+ export function runtimeCwd() {
13
+ return process.cwd();
14
+ }
15
+ function pathForPlatform(platform = runtimePlatform()) {
16
+ return platform === 'win32' ? win32 : posix;
17
+ }
18
+ export function defaultHome(env, platform = runtimePlatform()) {
19
+ const current = runtimeEnv(env);
20
+ if (platform === 'win32')
21
+ return current.USERPROFILE || current.HOME || homedir();
22
+ return current.HOME || current.USERPROFILE || homedir();
23
+ }
24
+ export function resolveWavexzoreSandboxHomeDir(args = {}) {
25
+ if (args.homeDir)
26
+ return args.homeDir;
27
+ const pathImpl = pathForPlatform(args.platform);
28
+ if (args.storeDir)
29
+ return pathImpl.dirname(args.storeDir);
30
+ const env = runtimeEnv(args.env);
31
+ const envHome = env.WAVEXZORE_SANDBOX_HOME || env.WAVEXZORE_SANDBOX_OPENCODE_HOME;
32
+ if (envHome)
33
+ return envHome;
34
+ if (args.scope === 'project')
35
+ return pathImpl.join(pathImpl.resolve(args.root ?? runtimeCwd()), '.wavexzore', 'opencode');
36
+ return pathImpl.join(defaultHome(env, args.platform), '.wavexzore', 'opencode');
37
+ }
38
+ export function resolveWavexzoreSandboxStoreDir(args = {}) {
39
+ if (args.storeDir)
40
+ return args.storeDir;
41
+ return pathForPlatform(args.platform).join(resolveWavexzoreSandboxHomeDir(args), 'sandbox');
42
+ }
43
+ export function resolveWavexzoreSandboxBinDir(args = {}) {
44
+ if (args.binDir)
45
+ return args.binDir;
46
+ return pathForPlatform(args.platform).join(resolveWavexzoreSandboxHomeDir(args), 'bin');
47
+ }
48
+ export function resolveOpenCodeConfigDir(args = {}) {
49
+ if (args.configDir)
50
+ return args.configDir;
51
+ const env = runtimeEnv(args.env);
52
+ const pathImpl = pathForPlatform(args.platform);
53
+ const configHome = env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0
54
+ ? env.XDG_CONFIG_HOME
55
+ : pathImpl.join(defaultHome(env, args.platform), '.config');
56
+ return pathImpl.join(configHome, 'opencode');
57
+ }
58
+ function firstExistingOrDefault(candidates) {
59
+ for (const candidate of candidates) {
60
+ if (existsSync(candidate))
61
+ return candidate;
62
+ }
63
+ return candidates[0] ?? '';
64
+ }
65
+ export function resolveOpenCodeGlobalConfigCandidates(args = {}) {
66
+ const pathImpl = pathForPlatform(args.platform);
67
+ const dir = resolveOpenCodeConfigDir(args);
68
+ return OPENCODE_GLOBAL_CONFIG_FILES.map((file) => pathImpl.join(dir, file));
69
+ }
70
+ export function resolveOpenCodeProjectConfigCandidates(args = {}) {
71
+ const pathImpl = pathForPlatform(args.platform);
72
+ const root = pathImpl.resolve(args.root ?? runtimeCwd());
73
+ return [
74
+ ...OPENCODE_PROJECT_CONFIG_FILES.map((file) => pathImpl.join(root, file)),
75
+ ...OPENCODE_PROJECT_CONFIG_FILES.map((file) => pathImpl.join(root, '.opencode', file)),
76
+ ];
77
+ }
78
+ export function resolveOpenCodeConfigPath(args = {}) {
79
+ if (args.configPath)
80
+ return args.configPath;
81
+ const env = runtimeEnv(args.env);
82
+ if (args.scope !== 'project' && env.OPENCODE_CONFIG && env.OPENCODE_CONFIG.length > 0)
83
+ return env.OPENCODE_CONFIG;
84
+ if (args.scope === 'project') {
85
+ return firstExistingOrDefault(resolveOpenCodeProjectConfigCandidates(args));
86
+ }
87
+ return firstExistingOrDefault(resolveOpenCodeGlobalConfigCandidates(args));
88
+ }
89
+ export function pathSeparatorForPlatform(platform = runtimePlatform()) {
90
+ return platform === 'win32' ? ';' : ':';
91
+ }
92
+ export function normalizePathForCompare(input, platform = runtimePlatform()) {
93
+ const normalized = pathForPlatform(platform).resolve(input).replace(/[\\/]+$/, '');
94
+ return platform === 'win32' ? normalized.toLowerCase() : normalized;
95
+ }
96
+ export function isDirectoryOnPath(binDir, envPath = runtimeEnv().PATH ?? runtimeEnv().Path ?? '', platform = runtimePlatform()) {
97
+ const expected = normalizePathForCompare(binDir, platform);
98
+ return envPath
99
+ .split(pathSeparatorForPlatform(platform))
100
+ .filter((item) => item.trim().length > 0)
101
+ .some((item) => normalizePathForCompare(item.trim(), platform) === expected);
102
+ }
103
+ export function shellQuote(value) {
104
+ return `'${value.replace(/'/g, "'\\''")}'`;
105
+ }
106
+ export function powershellSingleQuote(value) {
107
+ return `'${value.replace(/'/g, "''")}'`;
108
+ }
109
+ export function pathHintForPlatform(binDir, platform = runtimePlatform()) {
110
+ if (platform === 'win32') {
111
+ return `[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${binDir}", "User")`;
112
+ }
113
+ return `export PATH=${shellQuote(binDir)}:"$PATH"`;
114
+ }
115
+ export function shellProfilePath(args) {
116
+ if (args.pathConfigPath)
117
+ return args.pathConfigPath;
118
+ const shell = args.shell ?? runtimeEnv(args.env).SHELL ?? '';
119
+ const shellName = basename(shell);
120
+ if (shellName === 'zsh')
121
+ return join(args.homeDir, '.zshrc');
122
+ if (shellName === 'fish')
123
+ return join(args.homeDir, '.config', 'fish', 'config.fish');
124
+ if (shellName === 'bash')
125
+ return join(args.homeDir, '.bashrc');
126
+ return join(args.homeDir, '.profile');
127
+ }
@@ -0,0 +1,145 @@
1
+ export declare const WAVEXZORE_SANDBOX_PACKAGE = "@wavexzore/sandbox";
2
+ export declare const WAVEXZORE_SANDBOX_PLUGIN_ID = "wavexzore-sandbox";
3
+ export declare const WAVEXZORE_SANDBOX_CLI = "wavexzore-sandbox";
4
+ export declare const OPENCODE_HELPER_CLI = "wavexzore-sandbox-helper";
5
+ export declare const WAVEXZORE_SANDBOX_LOADER_PACKAGE = "wavexzore-sandbox-local-loader";
6
+ export declare const HOST_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
7
+ export type InstallStatus = 'created' | 'existing' | 'updated';
8
+ export type BinStatus = InstallStatus | 'skipped';
9
+ export type UninstallStatus = 'removed' | 'updated' | 'missing' | 'kept' | 'skipped' | 'unchanged';
10
+ export type PathStatus = 'present' | 'missing' | 'updated' | 'removed' | 'skipped' | 'unchanged' | 'kept';
11
+ export type StoreScope = 'user' | 'project';
12
+ export type DoctorStatus = 'passed' | 'failed';
13
+ export type RuntimeEnv = Record<string, string | undefined>;
14
+ export type BaseOptions = {
15
+ root?: string;
16
+ scope?: StoreScope;
17
+ homeDir?: string;
18
+ storeDir?: string;
19
+ binDir?: string;
20
+ configDir?: string;
21
+ configPath?: string;
22
+ dryRun?: boolean;
23
+ env?: RuntimeEnv;
24
+ platform?: string;
25
+ };
26
+ export type WavexzoreSandboxManifest = {
27
+ schemaVersion: 1;
28
+ pluginId: typeof WAVEXZORE_SANDBOX_PLUGIN_ID;
29
+ packageName: typeof WAVEXZORE_SANDBOX_PACKAGE;
30
+ activeVersion: string;
31
+ installedVersions: string[];
32
+ channel: string;
33
+ installedAt: string;
34
+ updatedAt: string;
35
+ opencodeConfigPath: string;
36
+ opencodePluginSpec: string;
37
+ cliPath: string;
38
+ };
39
+ export type HostConfigResult = {
40
+ configPath: string;
41
+ configStatus: InstallStatus;
42
+ pluginEntry: string;
43
+ pluginStatus: InstallStatus;
44
+ removedPluginEntries: string[];
45
+ };
46
+ export type HostConfigUninstallResult = {
47
+ configPath: string;
48
+ configStatus: UninstallStatus;
49
+ pluginEntry: string;
50
+ removedPluginEntries: string[];
51
+ };
52
+ export type WavexzoreSandboxInstallOptions = BaseOptions & {
53
+ packageRoot?: string;
54
+ installBin?: boolean;
55
+ setupPath?: boolean;
56
+ noPathHint?: boolean;
57
+ pathConfigPath?: string;
58
+ envPath?: string;
59
+ shell?: string;
60
+ channel?: string;
61
+ };
62
+ export type WavexzoreSandboxInstallResult = {
63
+ root: string;
64
+ dryRun: boolean;
65
+ scope: StoreScope;
66
+ homeDir: string;
67
+ storeDir: string;
68
+ binDir: string;
69
+ cliPath: string;
70
+ binStatus: BinStatus;
71
+ pathStatus: PathStatus;
72
+ pathConfigPath?: string;
73
+ pathHint: string;
74
+ manifestPath: string;
75
+ manifestStatus: InstallStatus;
76
+ loaderDir: string;
77
+ loaderStatus: InstallStatus;
78
+ activeVersion: string;
79
+ versionPath: string;
80
+ versionStatus: InstallStatus;
81
+ configPath: string;
82
+ configStatus: InstallStatus;
83
+ pluginStatus: InstallStatus;
84
+ removedPluginEntries: string[];
85
+ pluginEntry: string;
86
+ };
87
+ export type WavexzoreSandboxUninstallOptions = BaseOptions & {
88
+ keepConfig?: boolean;
89
+ keepStore?: boolean;
90
+ keepBin?: boolean;
91
+ removePath?: boolean;
92
+ pathConfigPath?: string;
93
+ envPath?: string;
94
+ shell?: string;
95
+ };
96
+ export type WavexzoreSandboxUninstallResult = {
97
+ root: string;
98
+ dryRun: boolean;
99
+ scope: StoreScope;
100
+ homeDir: string;
101
+ storeDir: string;
102
+ binDir: string;
103
+ binStatus: UninstallStatus;
104
+ pathStatus: PathStatus;
105
+ pathConfigPath?: string;
106
+ storeStatus: UninstallStatus;
107
+ manifestPath: string;
108
+ configPath: string;
109
+ configStatus: UninstallStatus;
110
+ pluginEntry: string;
111
+ removedPluginEntries: string[];
112
+ };
113
+ export type DoctorCheck = {
114
+ name: string;
115
+ status: DoctorStatus;
116
+ message: string;
117
+ path?: string;
118
+ };
119
+ export type WavexzoreSandboxDoctorOptions = Omit<BaseOptions, 'dryRun'> & {
120
+ projectRoot?: string;
121
+ envPath?: string;
122
+ };
123
+ export type WavexzoreSandboxDoctorResult = {
124
+ status: DoctorStatus;
125
+ homeDir: string;
126
+ storeDir: string;
127
+ binDir: string;
128
+ cliPath: string;
129
+ configPath: string;
130
+ pluginEntry: string;
131
+ activeVersion?: string;
132
+ checks: DoctorCheck[];
133
+ };
134
+ export type WavexzoreSandboxPathOptions = BaseOptions & {
135
+ pathConfigPath?: string;
136
+ envPath?: string;
137
+ shell?: string;
138
+ };
139
+ export type WavexzoreSandboxPathResult = {
140
+ homeDir: string;
141
+ binDir: string;
142
+ pathStatus: PathStatus;
143
+ pathConfigPath?: string;
144
+ pathHint: string;
145
+ };
@@ -0,0 +1,6 @@
1
+ export const WAVEXZORE_SANDBOX_PACKAGE = '@wavexzore/sandbox';
2
+ export const WAVEXZORE_SANDBOX_PLUGIN_ID = 'wavexzore-sandbox';
3
+ export const WAVEXZORE_SANDBOX_CLI = 'wavexzore-sandbox';
4
+ export const OPENCODE_HELPER_CLI = 'wavexzore-sandbox-helper';
5
+ export const WAVEXZORE_SANDBOX_LOADER_PACKAGE = 'wavexzore-sandbox-local-loader';
6
+ export const HOST_CONFIG_SCHEMA_URL = 'https://opencode.ai/config.json';
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import type { StoreScope } from './types.js';
3
+ type StreamWriter = {
4
+ write: (chunk: string) => unknown;
5
+ };
6
+ type CliOptions = {
7
+ cwd?: string;
8
+ stdout?: StreamWriter;
9
+ stderr?: StreamWriter;
10
+ };
11
+ type BaseArgs = {
12
+ root: string;
13
+ configDir?: string;
14
+ configPath?: string;
15
+ homeDir?: string;
16
+ binDir?: string;
17
+ storeDir?: string;
18
+ scope: StoreScope;
19
+ json: boolean;
20
+ dryRun: boolean;
21
+ help: boolean;
22
+ error?: string;
23
+ };
24
+ type InstallArgs = BaseArgs & {
25
+ noBin: boolean;
26
+ setupPath: boolean;
27
+ noPathHint: boolean;
28
+ packageRoot?: string;
29
+ };
30
+ type UninstallArgs = BaseArgs & {
31
+ keepConfig: boolean;
32
+ keepStore: boolean;
33
+ keepBin: boolean;
34
+ removePath: boolean;
35
+ };
36
+ type DoctorArgs = {
37
+ homeDir?: string;
38
+ binDir?: string;
39
+ storeDir?: string;
40
+ configDir?: string;
41
+ configPath?: string;
42
+ projectRoot?: string;
43
+ scope: StoreScope;
44
+ json: boolean;
45
+ help: boolean;
46
+ error?: string;
47
+ };
48
+ type SetupPathArgs = {
49
+ homeDir?: string;
50
+ binDir?: string;
51
+ storeDir?: string;
52
+ scope: StoreScope;
53
+ json: boolean;
54
+ dryRun: boolean;
55
+ help: boolean;
56
+ error?: string;
57
+ };
58
+ export declare const WAVEXZORE_SANDBOX_CLI_HELP = "wavexzore-sandbox CLI\n\nUsage:\n wavexzore-sandbox install [path] [--root <path>] [--scope <user|project>] [--home-dir <path>] [--store-dir <path>] [--bin-dir <path>] [--config-path <file>] [--config-dir <path>] [--package-root <path>] [--json] [--dry-run] [--no-bin] [--setup-path]\n wavexzore-sandbox uninstall [path] [--root <path>] [--scope <user|project>] [--home-dir <path>] [--store-dir <path>] [--bin-dir <path>] [--config-path <file>] [--config-dir <path>] [--json] [--dry-run] [--keep-config] [--keep-store] [--keep-bin] [--remove-path]\n wavexzore-sandbox doctor [--scope <user|project>] [--home-dir <path>] [--store-dir <path>] [--bin-dir <path>] [--config-path <file>] [--config-dir <path>] [--project-root <path>] [--json]\n wavexzore-sandbox setup-path [--scope <user|project>] [--home-dir <path>] [--store-dir <path>] [--bin-dir <path>] [--json] [--dry-run]\n wavexzore-sandbox help\n\nCommands:\n install Deploy @wavexzore/sandbox into a durable local store and configure OpenCode with a file:// plugin loader\n uninstall Remove Wavexzore Sandbox from OpenCode config and delete the local store by default\n doctor Validate OpenCode config, local loader, manifest, active version, plugin import, and OpenCode helper\n setup-path Explicitly add the Wavexzore Sandbox bin directory to PATH/profile\n help Show this help message\n\nOptions:\n --root <path> Invocation workspace. Used for --scope project and relative paths.\n --scope <value> user (default) or project. Project scope writes <root>/.wavexzore/opencode/sandbox.\n --home-dir <path> Product home. Default: ~/.wavexzore/opencode or WAVEXZORE_SANDBOX_HOME.\n --store-dir <path> Explicit local store directory. Default: ~/.wavexzore/opencode/sandbox.\n --bin-dir <path> Internal helper directory. Default: ~/.wavexzore/opencode/bin.\n --config-path <file> Exact OpenCode config file. Otherwise honors OPENCODE_CONFIG for user scope.\n --config-dir <path> OpenCode config directory for user scope.\n --package-root <path> Install only: package root override for development/testing.\n --no-bin Install only: do not create or repair the internal helper/shims under --bin-dir.\n --setup-path Install only: explicitly add --bin-dir to PATH/profile.\n --no-path-hint Install only: suppress PATH hint output after an explicit --setup-path attempt.\n --dry-run Report changes without writing files where supported.\n --keep-config Uninstall only: keep OpenCode config unchanged.\n --keep-store Uninstall only: keep ~/.wavexzore/opencode/sandbox or --store-dir.\n --keep-bin Uninstall only: keep ~/.wavexzore/opencode/bin helper/shims or --bin-dir.\n --remove-path Uninstall only: remove the managed PATH/profile marker or Windows user Path entry.\n --json Print machine-readable output.\n -h, --help Show command help.\n";
59
+ export declare function parseInstallArgs(args: string[]): InstallArgs;
60
+ export declare function parseUninstallArgs(args: string[]): UninstallArgs;
61
+ export declare function parseDoctorArgs(args: string[]): DoctorArgs;
62
+ export declare function parseSetupPathArgs(args: string[]): SetupPathArgs;
63
+ export declare function runWavexzoreSandboxCli(argv: string[], options?: CliOptions): Promise<number>;
64
+ export declare function isMainModule(metaUrl?: string, argv1?: string): boolean;
65
+ export {};