@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.
Files changed (34) hide show
  1. package/dist/assets/{MarkdownRendererImpl-2INOOMmV.js → MarkdownRendererImpl-C2snGm6y.js} +1 -1
  2. package/dist/assets/{MultiRunWindow-BovvaCrr.js → MultiRunWindow-DXXoYCBh.js} +1 -1
  3. package/dist/assets/{NewWorktreeDialog-6y6rHfOQ.js → NewWorktreeDialog-C6394QE9.js} +1 -1
  4. package/dist/assets/OnboardingScreen-CLXmHHBW.js +1 -0
  5. package/dist/assets/{SessionSwitcherDropdown-DSXsR7zB.js → SessionSwitcherDropdown-BMN9gaXo.js} +1 -1
  6. package/dist/assets/{SettingsWindow-Dll7W_Kd.js → SettingsWindow-DXEjjH2f.js} +1 -1
  7. package/dist/assets/{TerminalView-D_ktT_L5.js → TerminalView-CePdcScA.js} +1 -1
  8. package/dist/assets/{es-9Cy-lgBh.js → es-rCA6AcBs.js} +1 -1
  9. package/dist/assets/{ko-BxmPoeb2.js → ko-CTiirGAp.js} +1 -1
  10. package/dist/assets/{main-BpZQobwD.js → main-D1IN8sAs.js} +2 -2
  11. package/dist/assets/{main-D8KFwoUt.js → main-DIwL7PaU.js} +46 -46
  12. package/dist/assets/{miniChat-BdEth0Oc.js → miniChat-DXW_W4_n.js} +2 -2
  13. package/dist/assets/{mobile-BxWzNO1C.js → mobile-DWyy0wq-.js} +2 -2
  14. package/dist/assets/{pl-J7oKZUR2.js → pl-CwJAYYhT.js} +1 -1
  15. package/dist/assets/{pt-BR-p6gRuJwx.js → pt-BR-xndordtt.js} +1 -1
  16. package/dist/assets/{renderElectronMiniChatApp-MNKYhIaT.js → renderElectronMiniChatApp-BmvjGDb_.js} +2 -2
  17. package/dist/assets/{renderMobileApp-DpYY7X9v.js → renderMobileApp-1BEmeUF9.js} +3 -3
  18. package/dist/assets/{uk-BOvMb8eG.js → uk-C52rE_cP.js} +1 -1
  19. package/dist/assets/{useAppFontEffects-DEZpweZZ.js → useAppFontEffects-hYm-4PtP.js} +4 -4
  20. package/dist/assets/{zh-CN-Cv0tBxN1.js → zh-CN-B7IfGwyh.js} +1 -1
  21. package/dist/assets/{zh-TW-CS7R6Ak3.js → zh-TW-DiO_1_-_.js} +1 -1
  22. package/dist/index.html +1 -1
  23. package/dist/mini-chat.html +1 -1
  24. package/dist/mobile.html +1 -1
  25. package/package.json +1 -1
  26. package/server/index.js +0 -15
  27. package/server/lib/fs/routes.js +28 -2
  28. package/server/lib/opencode/env-runtime.js +20 -158
  29. package/server/lib/opencode/env-runtime.test.js +38 -11
  30. package/server/lib/opencode/lifecycle.js +5 -24
  31. package/server/lib/opencode/lifecycle.test.js +0 -2
  32. package/server/lib/opencode/network-runtime.js +1 -1
  33. package/server/lib/opencode/routes.js +1 -1
  34. 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 = spawnSync(shellPath, ['-NoLogo', '-Command', psScript], {
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 = spawnSync(comspec, ['/d', '/s', '/c', 'set'], {
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 = spawnSync(shellPath, ['-lic', 'env -0'], {
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 = spawnSync('where', ['opencode'], {
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
- const wsl = probeWslForOpencode();
473
- if (wsl) {
474
- return applyWslOpencodeResolution({
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 = spawnSync(shell, ['-lic', 'command -v opencode'], {
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 = spawnSync('where', ['node'], {
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 = spawnSync(shell, ['-lic', 'command -v node'], {
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 = spawnSync('where', ['bun'], {
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 = spawnSync(shell, ['-lic', 'command -v bun'], {
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
- const probe = probeWslForOpencode();
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
- const probe = probeWslForOpencode();
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
- ENV_CONFIGURED_OPENCODE_WSL_DISTRO: null,
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('does not classify WSL settings as a native invalid configured binary in strict mode', async () => {
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
- const rejection = runtime.applyOpencodeBinaryFromSettings({ strict: true });
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
- try {
153
- await rejection;
154
- expect(runtime.resolveManagedOpenCodeLaunchSpec('opencode').wrapperType).not.toBe('cmd-wrapper');
155
- } catch (error) {
156
- expect(error.message).toContain('uses WSL');
157
- expect(error.code).toBeUndefined();
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
- const wslBinary = state.resolvedWslBinary || resolveWslExecutablePath();
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('/api/health', ''), {
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}/api/health`, {
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('/api/health', ''), {
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(/\/+$/, '')}/api/health`, {
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('/api/health', ''), {
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};