@openchamber/web 1.12.2 → 1.12.3
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/dist/assets/{MarkdownRendererImpl-2INOOMmV.js → MarkdownRendererImpl-C2snGm6y.js} +1 -1
- package/dist/assets/{MultiRunWindow-BovvaCrr.js → MultiRunWindow-DXXoYCBh.js} +1 -1
- package/dist/assets/{NewWorktreeDialog-6y6rHfOQ.js → NewWorktreeDialog-C6394QE9.js} +1 -1
- package/dist/assets/OnboardingScreen-CLXmHHBW.js +1 -0
- package/dist/assets/{SessionSwitcherDropdown-DSXsR7zB.js → SessionSwitcherDropdown-BMN9gaXo.js} +1 -1
- package/dist/assets/{SettingsWindow-Dll7W_Kd.js → SettingsWindow-DXEjjH2f.js} +1 -1
- package/dist/assets/{TerminalView-D_ktT_L5.js → TerminalView-CePdcScA.js} +1 -1
- package/dist/assets/{es-9Cy-lgBh.js → es-rCA6AcBs.js} +1 -1
- package/dist/assets/{ko-BxmPoeb2.js → ko-CTiirGAp.js} +1 -1
- package/dist/assets/{main-BpZQobwD.js → main-D1IN8sAs.js} +2 -2
- package/dist/assets/{main-D8KFwoUt.js → main-DIwL7PaU.js} +46 -46
- package/dist/assets/{miniChat-BdEth0Oc.js → miniChat-DXW_W4_n.js} +2 -2
- package/dist/assets/{mobile-BxWzNO1C.js → mobile-DWyy0wq-.js} +2 -2
- package/dist/assets/{pl-J7oKZUR2.js → pl-CwJAYYhT.js} +1 -1
- package/dist/assets/{pt-BR-p6gRuJwx.js → pt-BR-xndordtt.js} +1 -1
- package/dist/assets/{renderElectronMiniChatApp-MNKYhIaT.js → renderElectronMiniChatApp-BmvjGDb_.js} +2 -2
- package/dist/assets/{renderMobileApp-DpYY7X9v.js → renderMobileApp-1BEmeUF9.js} +3 -3
- package/dist/assets/{uk-BOvMb8eG.js → uk-C52rE_cP.js} +1 -1
- package/dist/assets/{useAppFontEffects-DEZpweZZ.js → useAppFontEffects-hYm-4PtP.js} +4 -4
- package/dist/assets/{zh-CN-Cv0tBxN1.js → zh-CN-B7IfGwyh.js} +1 -1
- package/dist/assets/{zh-TW-CS7R6Ak3.js → zh-TW-DiO_1_-_.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/mini-chat.html +1 -1
- package/dist/mobile.html +1 -1
- package/package.json +1 -1
- package/server/index.js +0 -15
- package/server/lib/fs/routes.js +28 -2
- package/server/lib/opencode/env-runtime.js +20 -158
- package/server/lib/opencode/env-runtime.test.js +38 -11
- package/server/lib/opencode/lifecycle.js +5 -24
- package/server/lib/opencode/lifecycle.test.js +0 -2
- package/server/lib/opencode/network-runtime.js +1 -1
- package/server/lib/opencode/routes.js +1 -1
- package/dist/assets/OnboardingScreen-B1Ft9kmW.js +0 -1
|
@@ -9,8 +9,8 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
9
9
|
state,
|
|
10
10
|
normalizeDirectoryPath,
|
|
11
11
|
readSettingsFromDiskMigrated,
|
|
12
|
-
ENV_CONFIGURED_OPENCODE_WSL_DISTRO,
|
|
13
12
|
} = deps;
|
|
13
|
+
const runSpawnSync = typeof deps.spawnSync === 'function' ? deps.spawnSync : spawnSync;
|
|
14
14
|
|
|
15
15
|
const parseNullSeparatedEnvSnapshot = (raw) => {
|
|
16
16
|
if (typeof raw !== 'string' || raw.length === 0) {
|
|
@@ -149,7 +149,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
149
149
|
|
|
150
150
|
for (const shellPath of powershellCandidates) {
|
|
151
151
|
try {
|
|
152
|
-
const result =
|
|
152
|
+
const result = runSpawnSync(shellPath, ['-NoLogo', '-Command', psScript], {
|
|
153
153
|
encoding: 'utf8',
|
|
154
154
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
155
155
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -168,7 +168,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
168
168
|
|
|
169
169
|
const comspec = process.env.ComSpec || 'cmd.exe';
|
|
170
170
|
try {
|
|
171
|
-
const result =
|
|
171
|
+
const result = runSpawnSync(comspec, ['/d', '/s', '/c', 'set'], {
|
|
172
172
|
encoding: 'utf8',
|
|
173
173
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
174
174
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -202,7 +202,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
try {
|
|
205
|
-
const result =
|
|
205
|
+
const result = runSpawnSync(shellPath, ['-lic', 'env -0'], {
|
|
206
206
|
encoding: 'utf8',
|
|
207
207
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
208
208
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -267,122 +267,6 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
267
267
|
state.resolvedWslDistro = null;
|
|
268
268
|
};
|
|
269
269
|
|
|
270
|
-
const resolveWslExecutablePath = () => {
|
|
271
|
-
if (process.platform !== 'win32') {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const explicit = [process.env.WSL_BINARY, process.env.OPENCHAMBER_WSL_BINARY]
|
|
276
|
-
.map((v) => (typeof v === 'string' ? v.trim() : ''))
|
|
277
|
-
.filter(Boolean);
|
|
278
|
-
|
|
279
|
-
for (const candidate of explicit) {
|
|
280
|
-
if (isExecutable(candidate)) {
|
|
281
|
-
return candidate;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
const result = spawnSync('where', ['wsl'], {
|
|
287
|
-
encoding: 'utf8',
|
|
288
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
289
|
-
windowsHide: true,
|
|
290
|
-
});
|
|
291
|
-
if (result.status === 0) {
|
|
292
|
-
const lines = (result.stdout || '')
|
|
293
|
-
.split(/\r?\n/)
|
|
294
|
-
.map((line) => line.trim())
|
|
295
|
-
.filter(Boolean);
|
|
296
|
-
const found = lines.find((line) => isExecutable(line));
|
|
297
|
-
if (found) {
|
|
298
|
-
return found;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
} catch {
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const systemRoot = process.env.SystemRoot || 'C:\\Windows';
|
|
305
|
-
const fallback = path.join(systemRoot, 'System32', 'wsl.exe');
|
|
306
|
-
if (isExecutable(fallback)) {
|
|
307
|
-
return fallback;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return null;
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
const buildWslExecArgs = (execArgs, distroOverride = null) => {
|
|
314
|
-
const distro = typeof distroOverride === 'string' && distroOverride.trim().length > 0
|
|
315
|
-
? distroOverride.trim()
|
|
316
|
-
: ENV_CONFIGURED_OPENCODE_WSL_DISTRO;
|
|
317
|
-
|
|
318
|
-
const prefix = distro ? ['-d', distro] : [];
|
|
319
|
-
return [...prefix, '--exec', ...execArgs];
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const probeWslForOpencode = () => {
|
|
323
|
-
if (process.platform !== 'win32') {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const wslBinary = resolveWslExecutablePath();
|
|
328
|
-
if (!wslBinary) {
|
|
329
|
-
return null;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
const result = spawnSync(
|
|
334
|
-
wslBinary,
|
|
335
|
-
buildWslExecArgs(['sh', '-lc', 'command -v opencode']),
|
|
336
|
-
{
|
|
337
|
-
encoding: 'utf8',
|
|
338
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
339
|
-
timeout: 6000,
|
|
340
|
-
windowsHide: true,
|
|
341
|
-
}
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
if (result.status !== 0) {
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const lines = (result.stdout || '')
|
|
349
|
-
.split(/\r?\n/)
|
|
350
|
-
.map((line) => line.trim())
|
|
351
|
-
.filter(Boolean);
|
|
352
|
-
const found = lines[0] || '';
|
|
353
|
-
if (!found) {
|
|
354
|
-
return null;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
return {
|
|
358
|
-
wslBinary,
|
|
359
|
-
opencodePath: found,
|
|
360
|
-
distro: ENV_CONFIGURED_OPENCODE_WSL_DISTRO,
|
|
361
|
-
};
|
|
362
|
-
} catch {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
const applyWslOpencodeResolution = ({ wslBinary, opencodePath, source = 'wsl', distro = null } = {}) => {
|
|
368
|
-
const resolvedWsl = wslBinary || resolveWslExecutablePath();
|
|
369
|
-
if (!resolvedWsl) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
state.useWslForOpencode = true;
|
|
374
|
-
state.resolvedWslBinary = resolvedWsl;
|
|
375
|
-
state.resolvedWslOpencodePath = typeof opencodePath === 'string' && opencodePath.trim().length > 0
|
|
376
|
-
? opencodePath.trim()
|
|
377
|
-
: 'opencode';
|
|
378
|
-
state.resolvedWslDistro = typeof distro === 'string' && distro.trim().length > 0 ? distro.trim() : ENV_CONFIGURED_OPENCODE_WSL_DISTRO;
|
|
379
|
-
state.resolvedOpencodeBinary = `wsl:${state.resolvedWslOpencodePath}`;
|
|
380
|
-
state.resolvedOpencodeBinarySource = source;
|
|
381
|
-
|
|
382
|
-
delete process.env.OPENCODE_BINARY;
|
|
383
|
-
return state.resolvedOpencodeBinary;
|
|
384
|
-
};
|
|
385
|
-
|
|
386
270
|
const resolveOpencodeCliPath = () => {
|
|
387
271
|
const explicit = [
|
|
388
272
|
process.env.OPENCODE_BINARY,
|
|
@@ -450,7 +334,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
450
334
|
|
|
451
335
|
if (process.platform === 'win32') {
|
|
452
336
|
try {
|
|
453
|
-
const result =
|
|
337
|
+
const result = runSpawnSync('where', ['opencode'], {
|
|
454
338
|
encoding: 'utf8',
|
|
455
339
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
456
340
|
windowsHide: true,
|
|
@@ -469,15 +353,9 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
469
353
|
}
|
|
470
354
|
} catch {
|
|
471
355
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
wslBinary: wsl.wslBinary,
|
|
476
|
-
opencodePath: wsl.opencodePath,
|
|
477
|
-
source: 'wsl',
|
|
478
|
-
distro: wsl.distro,
|
|
479
|
-
});
|
|
480
|
-
}
|
|
356
|
+
// Do not auto-detect OpenCode from WSL. OpenCode sessions are keyed by
|
|
357
|
+
// server-visible directories, and mixing Windows paths with WSL paths
|
|
358
|
+
// creates duplicate/missing project state in the desktop app.
|
|
481
359
|
return null;
|
|
482
360
|
}
|
|
483
361
|
|
|
@@ -485,7 +363,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
485
363
|
for (const shell of shells) {
|
|
486
364
|
if (!isExecutable(shell)) continue;
|
|
487
365
|
try {
|
|
488
|
-
const result =
|
|
366
|
+
const result = runSpawnSync(shell, ['-lic', 'command -v opencode'], {
|
|
489
367
|
encoding: 'utf8',
|
|
490
368
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
491
369
|
windowsHide: true,
|
|
@@ -530,7 +408,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
530
408
|
|
|
531
409
|
if (process.platform === 'win32') {
|
|
532
410
|
try {
|
|
533
|
-
const result =
|
|
411
|
+
const result = runSpawnSync('where', ['node'], {
|
|
534
412
|
encoding: 'utf8',
|
|
535
413
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
536
414
|
windowsHide: true,
|
|
@@ -552,7 +430,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
552
430
|
for (const shell of shells) {
|
|
553
431
|
if (!isExecutable(shell)) continue;
|
|
554
432
|
try {
|
|
555
|
-
const result =
|
|
433
|
+
const result = runSpawnSync(shell, ['-lic', 'command -v node'], {
|
|
556
434
|
encoding: 'utf8',
|
|
557
435
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
558
436
|
windowsHide: true,
|
|
@@ -611,7 +489,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
611
489
|
}
|
|
612
490
|
|
|
613
491
|
try {
|
|
614
|
-
const result =
|
|
492
|
+
const result = runSpawnSync('where', ['bun'], {
|
|
615
493
|
encoding: 'utf8',
|
|
616
494
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
617
495
|
windowsHide: true,
|
|
@@ -633,7 +511,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
633
511
|
for (const shell of shells) {
|
|
634
512
|
if (!isExecutable(shell)) continue;
|
|
635
513
|
try {
|
|
636
|
-
const result =
|
|
514
|
+
const result = runSpawnSync(shell, ['-lic', 'command -v bun'], {
|
|
637
515
|
encoding: 'utf8',
|
|
638
516
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
639
517
|
windowsHide: true,
|
|
@@ -764,7 +642,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
764
642
|
return null;
|
|
765
643
|
}
|
|
766
644
|
|
|
767
|
-
const launcherPath = path.resolve(path.dirname(wrapperPath), launcherMatch[0]);
|
|
645
|
+
const launcherPath = path.resolve(path.dirname(wrapperPath), launcherMatch[0].replace(/[\\/]+/g, path.sep));
|
|
768
646
|
return path.dirname(path.dirname(path.dirname(launcherPath)));
|
|
769
647
|
} catch {
|
|
770
648
|
return null;
|
|
@@ -1032,35 +910,21 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
1032
910
|
: null;
|
|
1033
911
|
|
|
1034
912
|
if (explicitWslPath && explicitWslPath[1] && explicitWslPath[1].trim().length > 0) {
|
|
1035
|
-
|
|
1036
|
-
const applied = applyWslOpencodeResolution({
|
|
1037
|
-
wslBinary: probe?.wslBinary || resolveWslExecutablePath(),
|
|
1038
|
-
opencodePath: explicitWslPath[1].trim(),
|
|
1039
|
-
source: 'settings-wsl-path',
|
|
1040
|
-
distro: probe?.distro || ENV_CONFIGURED_OPENCODE_WSL_DISTRO,
|
|
1041
|
-
});
|
|
1042
|
-
if (applied) {
|
|
1043
|
-
return applied;
|
|
1044
|
-
}
|
|
913
|
+
clearWslOpencodeResolution();
|
|
1045
914
|
if (strict) {
|
|
1046
915
|
throw createConfiguredWslOpencodeError(raw);
|
|
1047
916
|
}
|
|
917
|
+
console.warn(`Configured settings.opencodeBinary uses WSL, which is no longer supported by OpenChamber desktop: ${raw}`);
|
|
918
|
+
return null;
|
|
1048
919
|
}
|
|
1049
920
|
|
|
1050
921
|
if (process.platform === 'win32' && (isWslExecutableValue(raw) || isWslExecutableValue(normalized || ''))) {
|
|
1051
|
-
|
|
1052
|
-
const applied = applyWslOpencodeResolution({
|
|
1053
|
-
wslBinary: probe?.wslBinary || normalized || raw || null,
|
|
1054
|
-
opencodePath: probe?.opencodePath || 'opencode',
|
|
1055
|
-
source: 'settings-wsl',
|
|
1056
|
-
distro: probe?.distro || ENV_CONFIGURED_OPENCODE_WSL_DISTRO,
|
|
1057
|
-
});
|
|
1058
|
-
if (applied) {
|
|
1059
|
-
return applied;
|
|
1060
|
-
}
|
|
922
|
+
clearWslOpencodeResolution();
|
|
1061
923
|
if (strict) {
|
|
1062
924
|
throw createConfiguredWslOpencodeError(raw);
|
|
1063
925
|
}
|
|
926
|
+
console.warn(`Configured settings.opencodeBinary points to WSL, which is no longer supported by OpenChamber desktop: ${raw}`);
|
|
927
|
+
return null;
|
|
1064
928
|
}
|
|
1065
929
|
|
|
1066
930
|
if (normalized && isExecutable(normalized) && !isMacOpenCodeAppBundlePath(normalized)) {
|
|
@@ -1219,8 +1083,6 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
|
|
1219
1083
|
isExecutable,
|
|
1220
1084
|
searchPathFor,
|
|
1221
1085
|
resolveGitBinaryForSpawn,
|
|
1222
|
-
resolveWslExecutablePath,
|
|
1223
|
-
buildWslExecArgs,
|
|
1224
1086
|
clearResolvedOpenCodeBinary,
|
|
1225
1087
|
};
|
|
1226
1088
|
};
|
|
@@ -72,7 +72,7 @@ afterEach(() => {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
const createRuntime = (settings) => {
|
|
75
|
+
const createRuntime = (settings, options = {}) => {
|
|
76
76
|
const state = {
|
|
77
77
|
cachedLoginShellEnvSnapshot: null,
|
|
78
78
|
resolvedOpencodeBinary: null,
|
|
@@ -90,7 +90,7 @@ const createRuntime = (settings) => {
|
|
|
90
90
|
state,
|
|
91
91
|
normalizeDirectoryPath: (value) => value,
|
|
92
92
|
readSettingsFromDiskMigrated: async () => settings,
|
|
93
|
-
|
|
93
|
+
spawnSync: options.spawnSync,
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
return { runtime, state };
|
|
@@ -138,7 +138,7 @@ describe('OpenCode env runtime', () => {
|
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
it('
|
|
141
|
+
it('rejects WSL settings in strict mode', async () => {
|
|
142
142
|
setPlatform('win32');
|
|
143
143
|
const dir = createTempDir('openchamber-no-wsl-');
|
|
144
144
|
process.env.PATH = dir;
|
|
@@ -147,15 +147,42 @@ describe('OpenCode env runtime', () => {
|
|
|
147
147
|
process.env.OPENCHAMBER_WSL_BINARY = path.join(dir, 'missing-openchamber-wsl.exe');
|
|
148
148
|
const { runtime } = createRuntime({ opencodeBinary: 'wsl:/usr/local/bin/opencode' });
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
await expect(runtime.applyOpencodeBinaryFromSettings({ strict: true })).rejects.toMatchObject({
|
|
151
|
+
message: expect.stringContaining('uses WSL'),
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('does not auto-detect OpenCode from WSL fallback paths', () => {
|
|
156
|
+
setPlatform('win32');
|
|
157
|
+
const dir = createTempDir('openchamber-wsl-opencode-');
|
|
158
|
+
const wslBinary = path.join(dir, 'wsl.exe');
|
|
159
|
+
fs.writeFileSync(wslBinary, '');
|
|
160
|
+
process.env.PATH = dir;
|
|
161
|
+
process.env.SystemRoot = dir;
|
|
162
|
+
process.env.WSL_BINARY = wslBinary;
|
|
163
|
+
delete process.env.OPENCODE_BINARY;
|
|
151
164
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
const calls = [];
|
|
166
|
+
const spawnSyncMock = (command, args) => {
|
|
167
|
+
calls.push({ command, args });
|
|
168
|
+
if (command === 'where') {
|
|
169
|
+
return { status: 1, stdout: '', stderr: '' };
|
|
170
|
+
}
|
|
171
|
+
if (command === wslBinary) {
|
|
172
|
+
return { status: 0, stdout: '/home/alice/.opencode/bin/opencode\n', stderr: '' };
|
|
173
|
+
}
|
|
174
|
+
return { status: 1, stdout: '', stderr: '' };
|
|
175
|
+
};
|
|
176
|
+
const { runtime, state } = createRuntime({}, { spawnSync: spawnSyncMock });
|
|
177
|
+
|
|
178
|
+
expect(runtime.resolveOpencodeCliPath()).toBeNull();
|
|
179
|
+
expect(state.useWslForOpencode).toBe(false);
|
|
180
|
+
expect(state.resolvedWslBinary).toBeNull();
|
|
181
|
+
expect(state.resolvedWslOpencodePath).toBeNull();
|
|
182
|
+
expect(state.resolvedOpencodeBinarySource).toBeNull();
|
|
183
|
+
|
|
184
|
+
const wslCall = calls.find((call) => call.command === wslBinary);
|
|
185
|
+
expect(wslCall).toBeUndefined();
|
|
159
186
|
});
|
|
160
187
|
|
|
161
188
|
it('launches Windows cmd shims through cmd call without embedded quotes', () => {
|
|
@@ -13,6 +13,7 @@ const HEALTH_CHECK_MAX_CONSECUTIVE_FAILURES = parsePositiveInt(
|
|
|
13
13
|
);
|
|
14
14
|
const HEALTH_CHECK_INTERVAL_OVERRIDE_MS = parsePositiveInt(process.env.OPENCHAMBER_OPENCODE_HEALTH_INTERVAL_MS, 0);
|
|
15
15
|
const HEALTH_CHECK_RESULT_CACHE_MS = parsePositiveInt(process.env.OPENCHAMBER_OPENCODE_HEALTH_CACHE_MS, 750);
|
|
16
|
+
const OPENCODE_HEALTH_PATH = '/global/health';
|
|
16
17
|
|
|
17
18
|
export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
18
19
|
const {
|
|
@@ -27,8 +28,6 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
27
28
|
applyOpencodeBinaryFromSettings,
|
|
28
29
|
ensureOpencodeCliEnv,
|
|
29
30
|
ensureLocalOpenCodeServerPassword,
|
|
30
|
-
buildWslExecArgs,
|
|
31
|
-
resolveWslExecutablePath,
|
|
32
31
|
resolveManagedOpenCodeLaunchSpec,
|
|
33
32
|
setOpenCodePort,
|
|
34
33
|
setDetectedOpenCodeApiPrefix,
|
|
@@ -230,25 +229,7 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
230
229
|
let launchWrapperType = null;
|
|
231
230
|
|
|
232
231
|
if (process.platform === 'win32' && state.useWslForOpencode) {
|
|
233
|
-
|
|
234
|
-
if (!wslBinary) {
|
|
235
|
-
throw new Error('WSL executable not found while attempting to launch OpenCode from WSL');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const wslOpencode = state.resolvedWslOpencodePath && state.resolvedWslOpencodePath.trim().length > 0
|
|
239
|
-
? state.resolvedWslOpencodePath.trim()
|
|
240
|
-
: 'opencode';
|
|
241
|
-
const serveHost = hostname === '127.0.0.1' ? '0.0.0.0' : hostname;
|
|
242
|
-
|
|
243
|
-
binary = wslBinary;
|
|
244
|
-
args = buildWslExecArgs([
|
|
245
|
-
wslOpencode,
|
|
246
|
-
'serve',
|
|
247
|
-
'--hostname',
|
|
248
|
-
serveHost,
|
|
249
|
-
'--port',
|
|
250
|
-
String(port),
|
|
251
|
-
], state.resolvedWslDistro);
|
|
232
|
+
throw new Error('Launching OpenCode through WSL is no longer supported. Install OpenCode natively on Windows and configure opencode.cmd or opencode.exe.');
|
|
252
233
|
}
|
|
253
234
|
|
|
254
235
|
if (process.platform === 'win32' && !state.useWslForOpencode) {
|
|
@@ -392,7 +373,7 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
392
373
|
}
|
|
393
374
|
|
|
394
375
|
try {
|
|
395
|
-
const response = await fetch(buildOpenCodeUrl(
|
|
376
|
+
const response = await fetch(buildOpenCodeUrl(OPENCODE_HEALTH_PATH, ''), {
|
|
396
377
|
method: 'GET',
|
|
397
378
|
headers: {
|
|
398
379
|
Accept: 'application/json',
|
|
@@ -417,7 +398,7 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
417
398
|
const controller = new AbortController();
|
|
418
399
|
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
419
400
|
const base = origin ?? `http://127.0.0.1:${port}`;
|
|
420
|
-
const response = await fetch(`${base}
|
|
401
|
+
const response = await fetch(`${base}${OPENCODE_HEALTH_PATH}`, {
|
|
421
402
|
method: 'GET',
|
|
422
403
|
headers: {
|
|
423
404
|
Accept: 'application/json',
|
|
@@ -663,7 +644,7 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
663
644
|
try {
|
|
664
645
|
const controller = new AbortController();
|
|
665
646
|
timeout = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);
|
|
666
|
-
const response = await fetch(buildOpenCodeUrl(
|
|
647
|
+
const response = await fetch(buildOpenCodeUrl(OPENCODE_HEALTH_PATH, ''), {
|
|
667
648
|
method: 'GET',
|
|
668
649
|
headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
|
|
669
650
|
signal: controller.signal,
|
|
@@ -85,8 +85,6 @@ const createRuntime = (overrides = {}) => {
|
|
|
85
85
|
applyOpencodeBinaryFromSettings: vi.fn(async () => null),
|
|
86
86
|
ensureOpencodeCliEnv: vi.fn(),
|
|
87
87
|
ensureLocalOpenCodeServerPassword: vi.fn(async () => 'password'),
|
|
88
|
-
buildWslExecArgs: vi.fn((args) => args),
|
|
89
|
-
resolveWslExecutablePath: vi.fn(),
|
|
90
88
|
resolveManagedOpenCodeLaunchSpec: vi.fn((binary) => ({ binary, args: [], wrapperType: null })),
|
|
91
89
|
setOpenCodePort: vi.fn((port) => {
|
|
92
90
|
state.openCodePort = port;
|
|
@@ -33,7 +33,7 @@ export const createOpenCodeNetworkRuntime = (deps) => {
|
|
|
33
33
|
try {
|
|
34
34
|
const controller = new AbortController();
|
|
35
35
|
timeout = setTimeout(() => controller.abort(), 3000);
|
|
36
|
-
const response = await fetch(`${url.replace(/\/+$/, '')}/
|
|
36
|
+
const response = await fetch(`${url.replace(/\/+$/, '')}/global/health`, {
|
|
37
37
|
method: 'GET',
|
|
38
38
|
headers: {
|
|
39
39
|
Accept: 'application/json',
|
|
@@ -214,7 +214,7 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
|
|
|
214
214
|
|
|
215
215
|
app.get('/api/opencode/health', async (_req, res) => {
|
|
216
216
|
try {
|
|
217
|
-
const healthResponse = await fetch(buildOpenCodeUrl('/
|
|
217
|
+
const healthResponse = await fetch(buildOpenCodeUrl('/global/health', ''), {
|
|
218
218
|
method: 'GET',
|
|
219
219
|
headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
|
|
220
220
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{H as E,j as t,R as a,c as z}from"./vendor-.bun-TPgXvEwq.js";import{u as V,en as me,fv as oe,aJ as Z,em as ee,am as F,dN as B,B as U,I as $,aT as re,be as ae,al as le,o as ie,m as J,H as T,aM as Q}from"./useAppFontEffects-DEZpweZZ.js";import{b as M}from"./index-D9t9eCUC.js";function pe(r){switch(r){case"ok":return null;case"auth":return"onboarding.remoteConnection.probe.authMessage";case"update-recommended":return"onboarding.remoteConnection.probe.updateRecommendedMessage";case"incompatible":return"onboarding.remoteConnection.probe.incompatibleMessage";case"wrong-service":return"onboarding.remoteConnection.probe.wrongServiceMessage";case"unreachable":return"onboarding.remoteConnection.probe.unreachableMessage";default:return null}}function q(r){return r==="wrong-service"||r==="unreachable"||r==="incompatible"}function ce({onBack:r,showBackButton:e=!0,initialUrl:i="",initialLabel:n="",isRecoveryMode:o=!1,onConnect:b,onSwitchToLocal:c}){const{t:s}=V(),[h,y]=E.useState(i),[x,R]=E.useState(n),[N,f]=E.useState("idle"),[p,v]=E.useState(null),[S,C]=E.useState(""),j=me(h),m=j?.persistedUrl??null,w=E.useCallback(d=>{y(d.target.value),f("idle"),v(null),C("")},[]),g=E.useCallback(d=>{R(d.target.value)},[]),k=E.useCallback(async()=>{if(m){f("testing"),v(null),C("");try{const d=await oe(m);v(d),f(d.status==="ok"||d.status==="update-recommended"?"success":"error")}catch(d){C(d instanceof Error?d.message:s("onboarding.remoteConnection.errors.connectionTestFailed")),f("error")}}},[m,s]),K=E.useCallback(async()=>{if(!j)return;const d=j.persistedUrl;f("testing"),v(null),C("");try{const D=await oe(d);if(v(D),q(D.status)){f("error");return}const P=await Z(),de=x.trim()||d,G=P.hosts.find(W=>W.url===d),Y=G?G.id:`host-${Date.now().toString(16)}`,te={id:Y,label:de,url:d,apiUrl:d},ue=G?P.hosts.map(W=>W.id===Y?te:W):[...P.hosts,te];if(await ee({hosts:ue,defaultHostId:Y,initialHostChoiceCompleted:!0}),b?.(),j.redeemUrl){window.location.assign(j.redeemUrl);return}F()&&await B()}catch(D){C(D instanceof Error?D.message:s("onboarding.remoteConnection.errors.failedToSaveConnection")),f("error")}},[j,x,b,s]),L=N==="testing",I=m!==null&&!L,_=m!==null&&!L&&!q(p?.status??null),O=pe(p?.status??null),H=p?.status==="ok",u=p?.status==="update-recommended",A=p?.status==="auth",l=q(p?.status??null);return t.jsx("div",{className:"flex flex-col items-center justify-center h-full p-8",children:t.jsxs("div",{className:"w-full max-w-md space-y-6",children:[e&&t.jsx("div",{className:"flex items-center",children:t.jsx(U,{variant:"ghost",onClick:r,className:"p-0 text-muted-foreground hover:text-foreground",children:s("onboarding.common.actions.back")})}),t.jsxs("div",{className:"space-y-2 text-center",children:[t.jsx("h1",{className:"typography-ui-header text-xl font-semibold text-foreground",children:s(o?"onboarding.remoteConnection.titleRecovery":"onboarding.remoteConnection.title")}),t.jsx("p",{className:"text-muted-foreground text-sm",children:s("onboarding.remoteConnection.description")})]}),t.jsxs("div",{className:"space-y-4",children:[t.jsxs("div",{className:"space-y-2",children:[t.jsx("label",{htmlFor:"remote-url",className:"text-sm text-foreground",children:s("onboarding.remoteConnection.field.serverAddress")}),t.jsx($,{id:"remote-url",type:"url",value:h,onChange:w,placeholder:s("onboarding.remoteConnection.field.serverAddressPlaceholder"),disabled:L})]}),t.jsxs("div",{className:"space-y-2",children:[t.jsx("label",{htmlFor:"remote-label",className:"text-sm text-foreground",children:s("onboarding.remoteConnection.field.nameOptional")}),t.jsx($,{id:"remote-label",type:"text",value:x,onChange:g,placeholder:s("onboarding.remoteConnection.field.namePlaceholder"),disabled:L})]})]}),p&&H&&t.jsx("div",{className:"rounded-lg border p-3 text-sm",style:{borderColor:"var(--status-success)",color:"var(--status-success)"},children:s("onboarding.remoteConnection.status.connectedSuccessfully",{latencyMs:p.latencyMs})}),p&&A&&t.jsx("div",{className:"rounded-lg border p-3 text-sm",style:{borderColor:"var(--status-warning)",color:"var(--status-warning)"},children:s("onboarding.remoteConnection.status.authWarning")}),p&&u&&t.jsx("div",{className:"rounded-lg border p-3 text-sm",style:{borderColor:"var(--status-warning)",color:"var(--status-warning)"},children:O?s(O):null}),p&&l&&t.jsxs("div",{className:"rounded-lg border p-3 text-sm space-y-3",style:{borderColor:"var(--status-error)",color:"var(--status-error)"},children:[t.jsxs("div",{children:[t.jsx("div",{className:"font-semibold mb-1",children:s("onboarding.remoteConnection.status.connectionFailed")}),t.jsx("div",{className:"opacity-90",children:O?s(O):null})]}),t.jsx("div",{className:"text-xs opacity-80",children:p.status==="unreachable"?s("onboarding.remoteConnection.status.suggestionsUnreachable"):s("onboarding.remoteConnection.status.suggestionsWrongService")})]}),S&&t.jsx("div",{className:"rounded-lg border p-3 text-sm",style:{borderColor:"var(--status-error)",color:"var(--status-error)"},children:S}),t.jsxs("div",{className:"flex gap-3",children:[t.jsx(U,{variant:"outline",onClick:k,disabled:!I,children:s(L?"onboarding.remoteConnection.actions.testing":"onboarding.remoteConnection.actions.testConnection")}),t.jsx(U,{onClick:K,disabled:!_,children:s("onboarding.remoteConnection.actions.connectAndRestart")})]}),l&&t.jsxs("div",{className:"flex flex-col gap-2 pt-2 border-t border-border",children:[t.jsx("div",{className:"text-xs text-muted-foreground text-center",children:s("onboarding.remoteConnection.actions.whatToDo")}),t.jsxs("div",{className:"flex gap-2",children:[t.jsx(U,{variant:"outline",size:"sm",onClick:r,className:"flex-1",children:s("onboarding.remoteConnection.actions.chooseDifferentServer")}),!o&&c&&t.jsx(U,{variant:"outline",size:"sm",onClick:c,className:"flex-1",children:s("onboarding.remoteConnection.actions.useLocalInstead")})]})]})]})})}const he="curl -fsSL https://opencode.ai/install | bash",fe="https://opencode.ai/docs",be="https://opencode.ai/docs/windows-wsl",se=2500;function xe(r){const e=z.c(5),{onCopy:i,copyTitle:n}=r;let o;e[0]===Symbol.for("react.memo_cache_sentinel")?(o=t.jsxs("code",{className:"flex-1 text-left overflow-x-auto whitespace-nowrap",children:[t.jsx("span",{style:{color:"var(--syntax-keyword)"},children:"curl"}),t.jsx("span",{className:"text-muted-foreground",children:" -fsSL "}),t.jsx("span",{style:{color:"var(--syntax-string)"},children:"https://opencode.ai/install"}),t.jsx("span",{className:"text-muted-foreground",children:" | "}),t.jsx("span",{style:{color:"var(--syntax-keyword)"},children:"bash"})]}),e[0]=o):o=e[0];let b;e[1]===Symbol.for("react.memo_cache_sentinel")?(b=t.jsx(T,{name:"file-copy",className:"h-4 w-4"}),e[1]=b):b=e[1];let c;return e[2]!==n||e[3]!==i?(c=t.jsxs("div",{className:"flex items-center justify-between gap-3 w-full",children:[o,t.jsx("button",{onClick:i,className:"inline-flex items-center text-muted-foreground hover:text-foreground transition-colors shrink-0",title:n,"aria-label":n,children:b})]}),e[2]=n,e[3]=i,e[4]=c):c=e[4],c}function ge({onCliAvailable:r}){const{t:e}=V(),[i,n]=a.useState(!1),[o,b]=a.useState(!1),[c,s]=a.useState(!1),[h,y]=a.useState(!1),[x,R]=a.useState(""),[N,f]=a.useState("unknown"),[p,v]=a.useState("local"),[S,C]=a.useState(!1),[j,m]=a.useState(!1);a.useEffect(()=>{b(F())},[]),a.useEffect(()=>{if(typeof navigator>"u"){f("unknown");return}const l=navigator.userAgent||"";/Windows/i.test(l)?f("windows"):/Macintosh|Mac OS X/i.test(l)?f("macos"):/Linux/i.test(l)?f("linux"):f("unknown")},[]),a.useEffect(()=>{let l=!1;return(async()=>{try{const d=await M("/api/config/settings",{method:"GET",headers:{Accept:"application/json"}});if(!d.ok)return;const D=await d.json().catch(()=>null);if(!D||l)return;const P=typeof D.opencodeBinary=="string"?D.opencodeBinary.trim():"";P&&R(P)}catch{}})(),()=>{l=!0}},[]);const w=a.useCallback(async l=>{const d=l.target;d.closest(".app-region-no-drag")||d.closest("button, a, input, select, textarea, code, summary, details")||l.button===0&&o&&await re()},[o]),g=a.useCallback(async()=>{try{const l=await M("/health");if(!l.ok)return!1;const d=await l.json();return d.openCodeRunning===!0||d.isOpenCodeReady===!0}catch{return!1}},[]),k=a.useCallback(async l=>{if(!o)return;const d=await Z();await ee({...d,...l==="local"?{defaultHostId:"local"}:{},initialHostChoiceCompleted:!0})},[o]),K=a.useCallback(async()=>{o&&await k("local"),r?.()},[o,r,k]);a.useEffect(()=>{if(p!=="local")return;let l=!1,d=null;const D=async()=>{if(!l){try{const P=await g();if(l)return;if(P){await K();return}}catch{}l||(d=setTimeout(D,se))}};return d=setTimeout(D,se),()=>{l=!0,d&&clearTimeout(d)}},[p,g,K]);const L=a.useCallback(async()=>{y(!0);try{await g()&&await K()}finally{y(!1)}},[g,K]),I=a.useCallback(async()=>{if(!(typeof window>"u")&&o)try{const l=await ae();l.success&&l.path&&l.path.trim().length>0&&R(l.path.trim())}catch{}},[o]),_=a.useCallback(async()=>{s(!0);try{if(await le({opencodeBinary:x.trim()}),o){await k("local"),await B();return}await M("/api/config/reload",{method:"POST"})}finally{setTimeout(()=>s(!1),1e3)}},[o,x,k]),O=a.useCallback(async()=>{const l=await ie(he);l.ok?(n(!0),setTimeout(()=>n(!1),2e3)):console.error("Failed to copy:",l.error)},[]),H=N==="windows"?be:fe,u=N==="windows"?"C:\\Users\\you\\AppData\\Roaming\\npm\\opencode.cmd":N==="linux"?"/home/you/.bun/bin/opencode":"/Users/you/.bun/bin/opencode",A=!o||p==="local";return t.jsx("div",{className:"app-region-drag h-full flex items-center justify-center bg-transparent p-8 cursor-default select-none overflow-y-auto",onMouseDown:w,children:t.jsxs("div",{className:"w-full max-w-md space-y-7",children:[t.jsxs("header",{className:"text-center space-y-1.5",children:[t.jsx("h1",{className:"text-2xl font-semibold tracking-tight text-foreground",children:e("onboarding.chooser.title")}),t.jsx("p",{className:"text-sm text-muted-foreground",children:e("onboarding.chooser.description")})]}),o&&t.jsxs("div",{className:"app-region-no-drag flex gap-1.5",children:[t.jsx("button",{type:"button",className:J("flex-1 px-4 py-2 rounded-lg border transition-colors text-sm",p==="local"?"border-[var(--interactive-selection)] text-foreground bg-[var(--interactive-selection)]/10":"border-border text-muted-foreground hover:text-foreground hover:border-muted-foreground"),onClick:()=>v("local"),children:e("onboarding.chooser.tabs.localInstall")}),t.jsx("button",{type:"button",className:J("flex-1 px-4 py-2 rounded-lg border transition-colors text-sm",p==="remote"?"border-[var(--interactive-selection)] text-foreground bg-[var(--interactive-selection)]/10":"border-border text-muted-foreground hover:text-foreground hover:border-muted-foreground"),onClick:()=>v("remote"),children:e("onboarding.chooser.tabs.connectRemote")})]}),o&&p==="remote"?t.jsx("div",{className:"app-region-no-drag",children:t.jsx(ce,{onBack:()=>v("local"),showBackButton:!1,onSwitchToLocal:()=>v("local")})}):null,A&&t.jsxs("div",{className:"space-y-4",children:[N==="windows"&&t.jsxs("div",{className:"rounded-lg border border-border bg-background/50 p-4",children:[t.jsx("div",{className:"text-sm text-foreground",children:e("onboarding.localSetup.windows.title")}),t.jsxs("ol",{className:"mt-2 list-decimal space-y-1 pl-5 text-sm text-muted-foreground",children:[t.jsxs("li",{children:[e("onboarding.localSetup.windows.stepInstallWsl")," ",t.jsx("code",{className:"text-foreground/80",children:"wsl --install"})," ",e("onboarding.localSetup.windows.stepInstallWslSuffix")]}),t.jsx("li",{children:e("onboarding.localSetup.windows.stepRunInstallInWsl")}),t.jsx("li",{children:e("onboarding.localSetup.windows.stepSetBinaryPath")})]})]}),t.jsx("p",{className:"text-sm text-muted-foreground text-center leading-relaxed",children:e("onboarding.localSetup.intro")}),t.jsx("div",{className:"app-region-no-drag rounded-lg border border-border bg-background/60 backdrop-blur-sm px-4 py-3 font-mono text-sm",children:i?t.jsxs("div",{className:"flex items-center gap-2",style:{color:"var(--status-success)"},children:[t.jsx(T,{name:"check",className:"h-4 w-4"}),e("onboarding.common.status.copiedToClipboard")]}):t.jsx(xe,{onCopy:O,copyTitle:e("onboarding.common.copyToClipboard")})}),t.jsxs("div",{className:"app-region-no-drag flex items-center justify-between",children:[t.jsxs("a",{href:H,target:"_blank",rel:"noopener noreferrer",className:"text-xs text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1",children:[e(N==="windows"?"onboarding.localSetup.docs.windows":"onboarding.localSetup.docs.default"),t.jsx(T,{name:"external-link",className:"h-3 w-3"})]}),t.jsx("button",{type:"button",onClick:L,disabled:h,className:"text-xs text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50",children:e(h?"onboarding.localSetup.actions.checking":"onboarding.localSetup.actions.checkNow")})]}),t.jsxs("div",{className:"rounded-lg border px-4 py-3 flex items-center gap-3",style:{borderColor:"color-mix(in srgb, var(--primary-base) 20%, transparent)",backgroundColor:"color-mix(in srgb, var(--primary-base) 6%, transparent)"},role:"status","aria-live":"polite",children:[t.jsxs("span",{className:"relative inline-flex h-2.5 w-2.5 shrink-0","aria-hidden":!0,children:[t.jsx("span",{className:"absolute inset-0 rounded-full",style:{backgroundColor:"var(--primary-base)",animation:"pulse-opacity 1.6s ease-in-out infinite"}}),t.jsx("span",{className:"absolute inset-[-4px] rounded-full",style:{backgroundColor:"var(--primary-base)",animation:"pulse-opacity-dim 1.6s ease-in-out infinite",opacity:0}})]}),t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsx("div",{className:"text-sm text-foreground leading-tight",children:e("onboarding.localSetup.status.watching")}),t.jsx("div",{className:"text-xs text-muted-foreground leading-tight mt-0.5",children:e("onboarding.localSetup.status.autoContinue")})]})]}),t.jsxs("details",{className:"app-region-no-drag group rounded-lg border border-border/60 px-4 open:bg-background/40 transition-colors",open:S,onToggle:l=>C(l.currentTarget.open),children:[t.jsxs("summary",{className:"flex items-center justify-between cursor-pointer py-2.5 text-sm text-muted-foreground hover:text-foreground transition-colors list-none [&::-webkit-details-marker]:hidden",children:[t.jsx("span",{children:e("onboarding.localSetup.advanced.title")}),t.jsx(T,{name:"arrow-down-s",className:"h-4 w-4 transition-transform group-open:rotate-180"})]}),t.jsxs("div",{className:"pb-4 space-y-2",children:[t.jsxs("div",{className:"flex gap-2",children:[t.jsx($,{value:x,onChange:l=>R(l.target.value),placeholder:u,disabled:c,className:"flex-1 font-mono text-xs"}),t.jsx(U,{type:"button",variant:"secondary",size:"sm",onClick:I,disabled:c||!o,children:e("onboarding.localSetup.actions.browse")}),t.jsx(U,{type:"button",size:"sm",onClick:_,disabled:c||!x.trim(),children:e("onboarding.localSetup.actions.apply")})]}),t.jsx("p",{className:"text-xs text-muted-foreground/70",children:e("onboarding.localSetup.helper.saveAndReload")})]})]}),t.jsxs("details",{className:"app-region-no-drag group rounded-lg border border-border/60 px-4 open:bg-background/40 transition-colors",open:j,onToggle:l=>m(l.currentTarget.open),children:[t.jsxs("summary",{className:"flex items-center justify-between cursor-pointer py-2.5 text-sm text-muted-foreground hover:text-foreground transition-colors list-none [&::-webkit-details-marker]:hidden",children:[t.jsx("span",{children:e("onboarding.localSetup.troubleshoot.title")}),t.jsx(T,{name:"arrow-down-s",className:"h-4 w-4 transition-transform group-open:rotate-180"})]}),t.jsx("ul",{className:"pb-4 space-y-1.5 text-xs text-muted-foreground list-disc pl-4",children:N==="windows"?t.jsxs(t.Fragment,{children:[t.jsx("li",{children:e("onboarding.localSetup.windows.hintInstallInWsl")}),t.jsx("li",{children:e("onboarding.localSetup.windows.hintDetectionFailed")})]}):t.jsxs(t.Fragment,{children:[t.jsx("li",{children:e("onboarding.localSetup.hint.ensurePath")}),t.jsx("li",{children:e("onboarding.localSetup.hint.setEnv")}),t.jsx("li",{children:e("onboarding.localSetup.hint.missingRuntime")})]})})]})]})]})})}const ye="curl -fsSL https://opencode.ai/install | bash",ve="https://opencode.ai/docs",we="https://opencode.ai/docs/windows-wsl";function je(r){const e=z.c(5),{onCopy:i,copyTitle:n}=r;let o;e[0]===Symbol.for("react.memo_cache_sentinel")?(o=t.jsxs("code",{children:[t.jsx("span",{style:{color:"var(--syntax-keyword)"},children:"curl"}),t.jsx("span",{className:"text-muted-foreground",children:" -fsSL "}),t.jsx("span",{style:{color:"var(--syntax-string)"},children:"https://opencode.ai/install"}),t.jsx("span",{className:"text-muted-foreground",children:" | "}),t.jsx("span",{style:{color:"var(--syntax-keyword)"},children:"bash"})]}),e[0]=o):o=e[0];let b;e[1]===Symbol.for("react.memo_cache_sentinel")?(b=t.jsx(T,{name:"file-copy",className:"h-4 w-4"}),e[1]=b):b=e[1];let c;return e[2]!==n||e[3]!==i?(c=t.jsxs("div",{className:"flex items-center justify-center gap-3",children:[o,t.jsx("button",{onClick:i,className:"inline-flex items-center text-muted-foreground hover:text-foreground transition-colors",title:n,children:b})]}),e[2]=n,e[3]=i,e[4]=c):c=e[4],c}const ke=3e4;function Ce({onBack:r,onCliAvailable:e,isFromRecovery:i=!1,onSwitchToRemote:n}){const{t:o}=V(),[b,c]=a.useState(!1),[s,h]=a.useState(!1),[y,x]=a.useState(!1),[R,N]=a.useState(!1),[f,p]=a.useState(!1),[v,S]=a.useState(null),[C,j]=a.useState(""),[m,w]=a.useState("unknown");a.useEffect(()=>{const u=setTimeout(()=>h(!0),ke);return()=>clearTimeout(u)},[]),a.useEffect(()=>{x(F())},[]),a.useEffect(()=>{if(typeof navigator>"u"){w("unknown");return}const u=navigator.userAgent||"";if(/Windows/i.test(u)){w("windows");return}if(/Macintosh|Mac OS X/i.test(u)){w("macos");return}if(/Linux/i.test(u)){w("linux");return}w("unknown")},[]),a.useEffect(()=>{let u=!1;return(async()=>{try{const A=await M("/api/config/settings",{method:"GET",headers:{Accept:"application/json"}});if(!A.ok)return;const l=await A.json().catch(()=>null);if(!l||u)return;const d=typeof l.opencodeBinary=="string"?l.opencodeBinary.trim():"";d&&j(d)}catch{}})(),()=>{u=!0}},[]);const g=a.useCallback(async u=>{u.target.closest("button, a, input, select, textarea, code")||u.button===0&&y&&await re()},[y]),k=a.useCallback(async()=>{try{const u=await M("/health");if(!u.ok)return!1;const A=await u.json();return A.openCodeRunning===!0||A.isOpenCodeReady===!0}catch{return!1}},[]),K=a.useCallback(async()=>{if(!(typeof window>"u")&&y)try{const u=await ae();u.success&&u.path&&u.path.trim().length>0&&j(u.path.trim())}catch{}},[y]),L=a.useCallback(async()=>{N(!0);try{if(await le({opencodeBinary:C.trim()}),y){await B();return}await M("/api/config/reload",{method:"POST"})}finally{setTimeout(()=>N(!1),1e3)}},[y,C]),I=a.useCallback(async()=>{const u=await ie(ye);u.ok?(c(!0),setTimeout(()=>c(!1),2e3)):console.error("Failed to copy:",u.error)},[]),_=a.useCallback(async()=>{p(!0),S(null);try{await k()?e?.():S(o("onboarding.localSetup.errors.cliNotReady"))}catch(u){S(u instanceof Error?u.message:o("onboarding.localSetup.errors.detectionFailed"))}finally{p(!1)}},[k,e,o]),O=m==="windows"?we:ve,H=m==="windows"?"C:\\Users\\you\\AppData\\Roaming\\npm\\opencode.cmd":m==="linux"?"/home/you/.bun/bin/opencode":"/Users/you/.bun/bin/opencode";return t.jsxs("div",{className:"h-full flex items-center justify-center bg-transparent p-8 relative cursor-default select-none",onMouseDown:g,children:[t.jsxs("div",{className:"w-full max-w-lg space-y-4 text-center",children:[t.jsx("div",{className:"flex items-center",children:t.jsx(U,{variant:"ghost",onClick:r,className:"p-0 text-muted-foreground hover:text-foreground",children:o("onboarding.common.actions.back")})}),t.jsxs("div",{className:"space-y-4",children:[t.jsx("h1",{className:"text-3xl font-semibold tracking-tight text-foreground",children:o("onboarding.localSetup.title")}),t.jsx("p",{className:"text-muted-foreground",children:o("onboarding.localSetup.description")})]}),m==="windows"&&t.jsxs("div",{className:"mx-auto max-w-2xl rounded-lg border border-border bg-background/50 p-4 text-left",children:[t.jsx("div",{className:"text-sm text-foreground",children:o("onboarding.localSetup.windows.title")}),t.jsxs("ol",{className:"mt-2 list-decimal space-y-1 pl-5 text-sm text-muted-foreground",children:[t.jsxs("li",{children:[o("onboarding.localSetup.windows.stepInstallWsl")," ",t.jsx("code",{className:"text-foreground/80",children:"wsl --install"})," ",o("onboarding.localSetup.windows.stepInstallWslSuffix")]}),t.jsx("li",{children:o("onboarding.localSetup.windows.stepRunInstallInWsl")}),t.jsx("li",{children:o("onboarding.localSetup.windows.stepSetBinaryPath")})]})]}),t.jsx("div",{className:"flex justify-center",children:t.jsx("div",{className:"bg-background/60 backdrop-blur-sm border border-border rounded-lg px-5 py-3 font-mono text-sm w-fit",children:b?t.jsxs("div",{className:"flex items-center justify-center gap-2",style:{color:"var(--status-success)"},children:[t.jsx(T,{name:"check",className:"h-4 w-4"}),o("onboarding.common.status.copiedToClipboard")]}):t.jsx(je,{onCopy:I,copyTitle:o("onboarding.common.copyToClipboard")})})}),t.jsxs("a",{href:O,target:"_blank",rel:"noopener noreferrer",className:"text-sm text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1 justify-center",children:[o(m==="windows"?"onboarding.localSetup.docs.windows":"onboarding.localSetup.docs.default"),t.jsx(T,{name:"external-link",className:"h-3 w-3"})]}),v&&t.jsx("div",{className:"mx-auto max-w-md rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive",children:v}),t.jsxs("div",{className:"space-y-3",children:[t.jsx(U,{type:"button",onClick:_,disabled:f,className:"w-full max-w-xs",size:"lg",children:o(f?"onboarding.localSetup.actions.checking":"onboarding.localSetup.actions.checkAndContinue")}),t.jsx("p",{className:"text-xs text-muted-foreground",children:o("onboarding.localSetup.helper.checkAndContinue")})]}),t.jsx("div",{className:"mx-auto w-full max-w-xl pt-4",children:t.jsxs("div",{className:"space-y-2",children:[t.jsx("div",{className:"text-sm text-muted-foreground",children:o("onboarding.localSetup.field.alreadyInstalled")}),t.jsxs("div",{className:"flex gap-2",children:[t.jsx($,{value:C,onChange:u=>j(u.target.value),placeholder:H,disabled:R,className:"flex-1 font-mono text-xs"}),t.jsx(U,{type:"button",variant:"secondary",onClick:K,disabled:R||!y,children:o("onboarding.localSetup.actions.browse")}),t.jsx(U,{type:"button",onClick:L,disabled:R,children:o("onboarding.localSetup.actions.apply")})]}),t.jsx("div",{className:"text-xs text-muted-foreground/70",children:o("onboarding.localSetup.helper.saveAndReload")})]})}),i&&n&&t.jsxs("div",{className:"text-center pt-4",children:[t.jsx("p",{className:"text-sm text-muted-foreground mb-2",children:o("onboarding.localSetup.remotePreference")}),t.jsx(U,{variant:"link",onClick:n,children:o("onboarding.localSetup.actions.connectRemoteServer")})]})]}),s&&t.jsx("div",{className:"absolute bottom-8 left-0 right-0 text-center space-y-1",children:m==="windows"?t.jsxs(t.Fragment,{children:[t.jsx("p",{className:"text-sm text-muted-foreground/70",children:o("onboarding.localSetup.windows.hintInstallInWsl")}),t.jsx("p",{className:"text-sm text-muted-foreground/70",children:o("onboarding.localSetup.windows.hintDetectionFailed")})]}):t.jsxs(t.Fragment,{children:[t.jsx("p",{className:"text-sm text-muted-foreground/70",children:o("onboarding.localSetup.hint.ensurePath")}),t.jsx("p",{className:"text-sm text-muted-foreground/70",children:o("onboarding.localSetup.hint.setEnv")}),t.jsx("p",{className:"text-sm text-muted-foreground/70",children:o("onboarding.localSetup.hint.missingRuntime")})]})})]})}function X(r,e){if(r?.trim())return Q(r.trim());if(e)return Q(e)}function Ne(r,e,i){switch(r){case"local-unavailable":return{title:"Local OpenCode Unavailable",description:"OpenCode CLI could not be started or is not installed. Install OpenCode or connect to a remote server instead.",titleKey:"onboarding.desktopRecovery.localUnavailable.title",descriptionKey:"onboarding.desktopRecovery.localUnavailable.description",iconKey:"local",showRetry:!0,retryLabel:"Retry Local",retryLabelKey:"onboarding.desktopRecovery.localUnavailable.retry",showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Set Up Local",useLocalLabelKey:"onboarding.desktopRecovery.localUnavailable.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"};case"remote-missing":return{title:"No Default Connection",description:"Your saved default connection could not be found. Choose how you want to connect.",titleKey:"onboarding.desktopRecovery.noDefaultConnection.title",descriptionKey:"onboarding.desktopRecovery.noDefaultConnection.description",iconKey:"local",showRetry:!1,showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Use Local",useLocalLabelKey:"onboarding.desktopRecovery.common.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"};case"remote-unreachable":{const n=X(e,i);return{title:"Remote Server Unreachable",description:`Could not connect to "${n||"the remote server"}". Check your network connection and verify the server address.`,titleKey:"onboarding.desktopRecovery.remoteUnreachable.title",descriptionKey:"onboarding.desktopRecovery.remoteUnreachable.description",descriptionParams:n?{host:n}:void 0,iconKey:"remote",showRetry:!0,retryLabel:"Retry Connection",retryLabelKey:"onboarding.desktopRecovery.remoteUnreachable.retry",showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Use Local",useLocalLabelKey:"onboarding.desktopRecovery.common.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"}}case"remote-wrong-service":{const n=X(e,i);return{title:"Incompatible Server",description:`The server at "${n||"unknown"}" is not running OpenChamber. Verify the address points to an OpenChamber server.`,titleKey:"onboarding.desktopRecovery.incompatibleServer.title",descriptionKey:"onboarding.desktopRecovery.incompatibleServer.description",descriptionParams:n?{host:n}:void 0,iconKey:"remote",showRetry:!1,showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Use Local",useLocalLabelKey:"onboarding.desktopRecovery.common.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"}}case"remote-incompatible":{const n=X(e,i);return{title:"Server Update Required",description:`The OpenChamber server at "${n||"unknown"}" is not compatible with this app version. Update OpenChamber on the server, then try again.`,titleKey:"onboarding.desktopRecovery.remoteIncompatible.title",descriptionKey:"onboarding.desktopRecovery.remoteIncompatible.description",descriptionParams:n?{host:n}:void 0,iconKey:"remote",showRetry:!0,retryLabel:"Retry Connection",retryLabelKey:"onboarding.desktopRecovery.remoteUnreachable.retry",showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Use Local",useLocalLabelKey:"onboarding.desktopRecovery.common.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"}}case"missing-default-host":return{title:"No Default Connection",description:"Your saved default connection could not be found. Choose how you want to connect.",titleKey:"onboarding.desktopRecovery.noDefaultConnection.title",descriptionKey:"onboarding.desktopRecovery.noDefaultConnection.description",iconKey:"local",showRetry:!1,showUseLocal:!0,showUseRemote:!0,useLocalLabel:"Use Local",useLocalLabelKey:"onboarding.desktopRecovery.common.useLocal",useRemoteLabel:"Use Remote",useRemoteLabelKey:"onboarding.desktopRecovery.common.useRemote"};default:{const n=r;throw new Error(`Unknown recovery variant: ${n}`)}}}function Se(r){switch(r){case"local":return t.jsx(T,{name:"macbook",className:"h-8 w-8"});case"remote":return t.jsx(T,{name:"server",className:"h-8 w-8"})}}function Re({variant:r,hostLabel:e,hostUrl:i,onRetry:n,onUseLocal:o,onUseRemote:b,isRetrying:c=!1}){const{t:s}=V(),h=Ne(r,e,i),y=h.retryLabelKey??"onboarding.desktopRecovery.actions.retryConnection",x=a.useMemo(()=>{if(h.descriptionParams?.host)return h.descriptionParams;if(r==="remote-unreachable")return{host:s("onboarding.desktopRecovery.placeholders.remoteServer")};if(r==="remote-wrong-service"||r==="remote-incompatible")return{host:s("onboarding.desktopRecovery.placeholders.unknownServer")}},[h.descriptionParams,s,r]);return t.jsx("div",{className:"flex flex-col items-center justify-center h-full p-8",children:t.jsxs("div",{className:"w-full max-w-md space-y-6",children:[t.jsxs("div",{className:"flex flex-col items-center space-y-3 text-center",children:[t.jsx("div",{className:"p-3 rounded-full",style:{backgroundColor:"var(--status-warning)",opacity:.15},children:t.jsx("div",{style:{color:"var(--status-warning)"},children:Se(h.iconKey)})}),t.jsx("h1",{className:"typography-ui-header text-xl font-semibold text-foreground",children:s(h.titleKey)}),t.jsx("p",{className:"text-muted-foreground text-sm max-w-sm",children:s(h.descriptionKey,x)})]}),i&&(r==="remote-unreachable"||r==="remote-wrong-service"||r==="remote-incompatible")&&t.jsxs("div",{className:"rounded-lg border border-border bg-background/50 p-3",children:[t.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:s("onboarding.remoteConnection.field.serverAddress")}),t.jsx("div",{className:"font-mono text-sm text-foreground truncate",children:Q(i)})]}),t.jsxs("div",{className:"flex flex-col gap-2",children:[h.showRetry&&n&&t.jsxs(U,{onClick:n,disabled:c,className:"w-full",children:[t.jsx(T,{name:"refresh",className:J("h-4 w-4",c&&"animate-spin")}),s(c?"onboarding.desktopRecovery.actions.retrying":y)]}),t.jsxs("div",{className:"flex gap-2",children:[h.showUseLocal&&o&&t.jsxs(U,{variant:"outline",onClick:o,disabled:c,className:"flex-1",children:[t.jsx(T,{name:"macbook",className:"h-4 w-4"}),s(h.useLocalLabelKey)]}),h.showUseRemote&&b&&t.jsxs(U,{variant:"outline",onClick:b,disabled:c,className:"flex-1",children:[t.jsx(T,{name:"server",className:"h-4 w-4"}),s(h.useRemoteLabelKey)]})]})]})]})})}function ne(r,e){if(e==="use-remote")return{kind:"remote-form"};switch(r){case"local-unavailable":return{kind:"local-setup"};case"remote-unreachable":case"remote-incompatible":case"remote-wrong-service":case"remote-missing":case"missing-default-host":return{kind:"switch-default-to-local"};default:{const i=r;throw new Error(`Unhandled RecoveryVariant: ${i}`)}}}function Le(r){const e=z.c(27),{variant:i,hostUrl:n,hostLabel:o,onRetry:b,onChooseRemote:c,showRemoteForm:s,onCloseRemoteForm:h,onSwitchToLocalFromRemote:y,onEnterLocalSetup:x,isRetrying:R}=r,N=s===void 0?!1:s,f=R===void 0?!1:R,p=Ue;let v;e[0]!==b?(v=async()=>{if(F()){await B();return}await M("/api/config/reload",{method:"POST"}),b?.()},e[0]=b,e[1]=v):v=e[1];const S=v;let C;e[2]!==x||e[3]!==i?(C=async()=>{if(ne(i,"use-local").kind==="local-setup"){x?.();return}if(await p("local"),F()){await B();return}window.location.reload()},e[2]=x,e[3]=i,e[4]=C):C=e[4];const j=C;let m;e[5]!==c||e[6]!==i?(m=()=>{ne(i,"use-remote").kind==="remote-form"&&c?.()},e[5]=c,e[6]=i,e[7]=m):m=e[7];const w=m;if(N){const k=i==="remote-wrong-service"?"":n||"",K=i==="remote-wrong-service"?"":o||"";let L;e[8]!==c||e[9]!==h?(L=h||(()=>c?.()),e[8]=c,e[9]=h,e[10]=L):L=e[10];let I;e[11]!==x||e[12]!==y?(I=y||(()=>{p("local").then(()=>{F()?B():x?.()})}),e[11]=x,e[12]=y,e[13]=I):I=e[13];let _;return e[14]!==K||e[15]!==k||e[16]!==L||e[17]!==I?(_=t.jsx(ce,{onBack:L,initialUrl:k,initialLabel:K,isRecoveryMode:!0,onSwitchToLocal:I}),e[14]=K,e[15]=k,e[16]=L,e[17]=I,e[18]=_):_=e[18],_}let g;return e[19]!==S||e[20]!==j||e[21]!==w||e[22]!==o||e[23]!==n||e[24]!==f||e[25]!==i?(g=t.jsx(Re,{variant:i,hostLabel:o,hostUrl:n,onRetry:S,onUseLocal:j,onUseRemote:w,isRetrying:f}),e[19]=S,e[20]=j,e[21]=w,e[22]=o,e[23]=n,e[24]=f,e[25]=i,e[26]=g):g=e[26],g}async function Ue(r){if(!F())return;const e=await Z();await ee({...e,...r==="local"?{defaultHostId:"local"}:{},initialHostChoiceCompleted:!0})}function De(r){const e=z.c(26),{onBack:i,onCliAvailable:n,mode:o,recoveryVariant:b,recoveryHostUrl:c,recoveryHostLabel:s,onEnterLocalSetup:h}=r,y=o===void 0?"first-launch":o,x=b===void 0?"missing-default-host":b,[R,N]=a.useState(!1),[f,p]=a.useState(!1);let v;e[0]===Symbol.for("react.memo_cache_sentinel")?(v=()=>{p(!1),N(!1)},e[0]=v):v=e[0];let S;e[1]!==y||e[2]!==s||e[3]!==c||e[4]!==x?(S=[y,x,c,s],e[1]=y,e[2]=s,e[3]=c,e[4]=x,e[5]=S):S=e[5],a.useEffect(v,S);const C=f?"local-setup":y;if(C==="recovery"){let m,w;e[6]===Symbol.for("react.memo_cache_sentinel")?(m=()=>N(!1),w=()=>{N(!1),p(!0)},e[6]=m,e[7]=w):(m=e[6],w=e[7]);let g;e[8]!==h?(g=()=>{p(!0),h?.()},e[8]=h,e[9]=g):g=e[9];let k;return e[10]!==s||e[11]!==c||e[12]!==x||e[13]!==R||e[14]!==g?(k=t.jsx(Le,{variant:x,hostUrl:c,hostLabel:s,showRemoteForm:R,onCloseRemoteForm:m,onSwitchToLocalFromRemote:w,onEnterLocalSetup:g}),e[10]=s,e[11]=c,e[12]=x,e[13]=R,e[14]=g,e[15]=k):k=e[15],k}if(C==="local-setup"){let m;e[16]!==i||e[17]!==f?(m=()=>{f?p(!1):i?.()},e[16]=i,e[17]=f,e[18]=m):m=e[18];let w;e[19]===Symbol.for("react.memo_cache_sentinel")?(w=()=>N(!0),e[19]=w):w=e[19];let g;return e[20]!==n||e[21]!==f||e[22]!==m?(g=t.jsx(Ce,{onBack:m,onCliAvailable:n,isFromRecovery:f,onSwitchToRemote:w}),e[20]=n,e[21]=f,e[22]=m,e[23]=g):g=e[23],g}let j;return e[24]!==n?(j=t.jsx(ge,{onCliAvailable:n}),e[24]=n,e[25]=j):j=e[25],j}export{De as OnboardingScreen};
|