@openchamber/web 1.5.0 → 1.5.2

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/index.html CHANGED
@@ -191,10 +191,10 @@
191
191
  pointer-events: none;
192
192
  }
193
193
  </style>
194
- <script type="module" crossorigin src="/assets/index-B5OwFsqC.js"></script>
195
- <link rel="modulepreload" crossorigin href="/assets/vendor-.bun-CqCnPJev.js">
194
+ <script type="module" crossorigin src="/assets/index-BaEcQUOW.js"></script>
195
+ <link rel="modulepreload" crossorigin href="/assets/vendor-.bun-w0O-Y8Mh.js">
196
196
  <link rel="stylesheet" crossorigin href="/assets/vendor--Jn2c0Clh.css">
197
- <link rel="stylesheet" crossorigin href="/assets/index-DXflA-iU.css">
197
+ <link rel="stylesheet" crossorigin href="/assets/index-lcIVG0lU.css">
198
198
  </head>
199
199
  <body class="h-full bg-background text-foreground">
200
200
  <div id="root" class="h-full">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchamber/web",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
package/server/index.js CHANGED
@@ -619,6 +619,9 @@ const sanitizeSettingsUpdate = (payload) => {
619
619
  const trimmed = candidate.commitMessageModel.trim();
620
620
  result.commitMessageModel = trimmed.length > 0 ? trimmed : undefined;
621
621
  }
622
+ if (typeof candidate.gitmojiEnabled === 'boolean') {
623
+ result.gitmojiEnabled = candidate.gitmojiEnabled;
624
+ }
622
625
 
623
626
  const skillCatalogs = sanitizeSkillCatalogs(candidate.skillCatalogs);
624
627
  if (skillCatalogs) {
@@ -875,10 +878,8 @@ let expressApp = null;
875
878
  let currentRestartPromise = null;
876
879
  let isRestartingOpenCode = false;
877
880
  let openCodeApiPrefix = '';
878
- let openCodeApiPrefixDetected = false;
881
+ let openCodeApiPrefixDetected = true;
879
882
  let openCodeApiDetectionTimer = null;
880
- let isDetectingApiPrefix = false;
881
- let openCodeApiDetectionPromise = null;
882
883
  let lastOpenCodeError = null;
883
884
  let isOpenCodeReady = false;
884
885
  let openCodeNotReadySince = 0;
@@ -949,10 +950,8 @@ const ENV_CONFIGURED_API_PREFIX = normalizeApiPrefix(
949
950
  process.env.OPENCODE_API_PREFIX || process.env.OPENCHAMBER_API_PREFIX || ''
950
951
  );
951
952
 
952
- if (ENV_CONFIGURED_API_PREFIX) {
953
- openCodeApiPrefix = ENV_CONFIGURED_API_PREFIX;
954
- openCodeApiPrefixDetected = true;
955
- console.log(`Using OpenCode API prefix from environment: ${openCodeApiPrefix}`);
953
+ if (ENV_CONFIGURED_API_PREFIX && ENV_CONFIGURED_API_PREFIX !== '') {
954
+ console.warn('Ignoring configured OpenCode API prefix; API runs at root.');
956
955
  }
957
956
 
958
957
  function setOpenCodePort(port) {
@@ -1041,7 +1040,7 @@ function buildAugmentedPath() {
1041
1040
  return Array.from(augmented).join(path.delimiter);
1042
1041
  }
1043
1042
 
1044
- const API_PREFIX_CANDIDATES = ['', '/api']; // Simplified - only check root and /api
1043
+ const API_PREFIX_CANDIDATES = [''];
1045
1044
 
1046
1045
  async function waitForReady(url, timeoutMs = 10000) {
1047
1046
  const start = Date.now();
@@ -1086,36 +1085,17 @@ function normalizeApiPrefix(prefix) {
1086
1085
  return withLeading.endsWith('/') ? withLeading.slice(0, -1) : withLeading;
1087
1086
  }
1088
1087
 
1089
- function setDetectedOpenCodeApiPrefix(prefix) {
1090
- const normalized = normalizeApiPrefix(prefix);
1091
- if (!openCodeApiPrefixDetected || openCodeApiPrefix !== normalized) {
1092
- openCodeApiPrefix = normalized;
1093
- openCodeApiPrefixDetected = true;
1094
- if (openCodeApiDetectionTimer) {
1095
- clearTimeout(openCodeApiDetectionTimer);
1096
- openCodeApiDetectionTimer = null;
1097
- }
1098
- console.log(`Detected OpenCode API prefix: ${normalized || '(root)'}`);
1088
+ function setDetectedOpenCodeApiPrefix() {
1089
+ openCodeApiPrefix = '';
1090
+ openCodeApiPrefixDetected = true;
1091
+ if (openCodeApiDetectionTimer) {
1092
+ clearTimeout(openCodeApiDetectionTimer);
1093
+ openCodeApiDetectionTimer = null;
1099
1094
  }
1100
1095
  }
1101
1096
 
1102
1097
  function getCandidateApiPrefixes() {
1103
- if (openCodeApiPrefixDetected) {
1104
- return [openCodeApiPrefix];
1105
- }
1106
-
1107
- const candidates = [];
1108
- if (openCodeApiPrefix && !candidates.includes(openCodeApiPrefix)) {
1109
- candidates.push(openCodeApiPrefix);
1110
- }
1111
-
1112
- for (const candidate of API_PREFIX_CANDIDATES) {
1113
- if (!candidates.includes(candidate)) {
1114
- candidates.push(candidate);
1115
- }
1116
- }
1117
-
1118
- return candidates;
1098
+ return API_PREFIX_CANDIDATES;
1119
1099
  }
1120
1100
 
1121
1101
  function buildOpenCodeUrl(path, prefixOverride) {
@@ -1123,9 +1103,7 @@ function buildOpenCodeUrl(path, prefixOverride) {
1123
1103
  throw new Error('OpenCode port is not available');
1124
1104
  }
1125
1105
  const normalizedPath = path.startsWith('/') ? path : `/${path}`;
1126
- const prefix = normalizeApiPrefix(
1127
- prefixOverride !== undefined ? prefixOverride : openCodeApiPrefixDetected ? openCodeApiPrefix : ''
1128
- );
1106
+ const prefix = normalizeApiPrefix(prefixOverride !== undefined ? prefixOverride : '');
1129
1107
  const fullPath = `${prefix}${normalizedPath}`;
1130
1108
  return `http://localhost:${openCodePort}${fullPath}`;
1131
1109
  }
@@ -1214,165 +1192,22 @@ function writeSseEvent(res, payload) {
1214
1192
  res.write(`data: ${JSON.stringify(payload)}\n\n`);
1215
1193
  }
1216
1194
 
1217
- function extractApiPrefixFromUrl(urlString, expectedSuffix) {
1218
- if (!urlString) {
1219
- return null;
1220
- }
1221
- try {
1222
- const parsed = new URL(urlString);
1223
- const pathname = parsed.pathname || '';
1224
- if (expectedSuffix && pathname.endsWith(expectedSuffix)) {
1225
- const prefix = pathname.slice(0, pathname.length - expectedSuffix.length);
1226
- return normalizeApiPrefix(prefix);
1227
- }
1228
- } catch (error) {
1229
- console.warn(`Failed to parse OpenCode URL "${urlString}": ${error.message}`);
1230
- }
1231
- return null;
1232
- }
1233
-
1234
- async function tryDetectOpenCodeApiPrefix() {
1235
- if (!openCodePort) {
1236
- return false;
1237
- }
1238
-
1239
- const docPrefix = await detectPrefixFromDocumentation();
1240
- if (docPrefix !== null) {
1241
- setDetectedOpenCodeApiPrefix(docPrefix);
1242
- return true;
1243
- }
1244
-
1245
- const candidates = getCandidateApiPrefixes();
1246
-
1247
- for (const candidate of candidates) {
1248
- try {
1249
- const response = await fetch(buildOpenCodeUrl('/config', candidate), {
1250
- method: 'GET',
1251
- headers: { Accept: 'application/json' }
1252
- });
1253
-
1254
- if (response.ok) {
1255
- await response.json().catch(() => null);
1256
- setDetectedOpenCodeApiPrefix(candidate);
1257
- return true;
1258
- }
1259
- } catch (error) {
1260
-
1261
- }
1262
- }
1263
-
1264
- return false;
1265
- }
1266
-
1267
- async function detectOpenCodeApiPrefix() {
1268
- if (openCodeApiPrefixDetected) {
1269
- return true;
1270
- }
1271
-
1272
- if (!openCodePort) {
1273
- return false;
1274
- }
1275
-
1276
- if (isDetectingApiPrefix) {
1277
- try {
1278
- await openCodeApiDetectionPromise;
1279
- } catch (error) {
1280
-
1281
- }
1282
- return openCodeApiPrefixDetected;
1283
- }
1284
-
1285
- isDetectingApiPrefix = true;
1286
- openCodeApiDetectionPromise = (async () => {
1287
- const success = await tryDetectOpenCodeApiPrefix();
1288
- if (!success) {
1289
- console.warn('Failed to detect OpenCode API prefix via documentation or known candidates');
1290
- }
1291
- return success;
1292
- })();
1293
-
1294
- try {
1295
- const result = await openCodeApiDetectionPromise;
1296
- return result;
1297
- } finally {
1298
- isDetectingApiPrefix = false;
1299
- openCodeApiDetectionPromise = null;
1300
- }
1195
+ function extractApiPrefixFromUrl() {
1196
+ return '';
1301
1197
  }
1302
1198
 
1303
- async function ensureOpenCodeApiPrefix() {
1304
- if (openCodeApiPrefixDetected) {
1305
- return true;
1306
- }
1307
-
1308
- const result = await detectOpenCodeApiPrefix();
1309
- if (!result) {
1310
- scheduleOpenCodeApiDetection();
1311
- }
1312
- return result;
1313
- }
1314
-
1315
- function scheduleOpenCodeApiDetection(delayMs = 500) {
1316
- if (openCodeApiPrefixDetected) {
1317
- return;
1318
- }
1319
-
1320
- if (openCodeApiDetectionTimer) {
1321
- clearTimeout(openCodeApiDetectionTimer);
1322
- }
1323
-
1324
- openCodeApiDetectionTimer = setTimeout(async () => {
1325
- openCodeApiDetectionTimer = null;
1326
- const success = await detectOpenCodeApiPrefix();
1327
- if (!success) {
1328
- const nextDelay = Math.min(delayMs * 2, 8000);
1329
- scheduleOpenCodeApiDetection(nextDelay);
1330
- }
1331
- }, delayMs);
1199
+ function detectOpenCodeApiPrefix() {
1200
+ openCodeApiPrefixDetected = true;
1201
+ openCodeApiPrefix = '';
1202
+ return true;
1332
1203
  }
1333
1204
 
1334
- const OPENAPI_DOC_PATHS = ['/doc'];
1335
-
1336
- function extractPrefixFromOpenApiDocument(content) {
1337
-
1338
- const globalMatch = content.match(/__OPENCODE_API_BASE__\s*=\s*['"]([^'"]+)['"]/);
1339
- if (globalMatch && globalMatch[1]) {
1340
- return normalizeApiPrefix(globalMatch[1]);
1341
- }
1342
- return null;
1205
+ function ensureOpenCodeApiPrefix() {
1206
+ return detectOpenCodeApiPrefix();
1343
1207
  }
1344
1208
 
1345
- async function detectPrefixFromDocumentation() {
1346
- if (!openCodePort) {
1347
- return null;
1348
- }
1349
-
1350
- const prefixesToTry = [...new Set(['', ...API_PREFIX_CANDIDATES])];
1351
-
1352
- for (const prefix of prefixesToTry) {
1353
- for (const docPath of OPENAPI_DOC_PATHS) {
1354
- try {
1355
- const response = await fetch(buildOpenCodeUrl(docPath, prefix), {
1356
- method: 'GET',
1357
- headers: { Accept: '*/*' }
1358
- });
1359
-
1360
- if (!response.ok) {
1361
- continue;
1362
- }
1363
-
1364
- const text = await response.text();
1365
- const extracted = extractPrefixFromOpenApiDocument(text);
1366
- if (extracted !== null) {
1367
- return extracted;
1368
- }
1369
- } catch (error) {
1370
-
1371
- }
1372
- }
1373
- }
1374
-
1375
- return null;
1209
+ function scheduleOpenCodeApiDetection() {
1210
+ return;
1376
1211
  }
1377
1212
 
1378
1213
  function parseArgs(argv = process.argv.slice(2)) {
@@ -1434,10 +1269,19 @@ function killProcessOnPort(port) {
1434
1269
  try {
1435
1270
  // SDK's proc.kill() only kills the Node wrapper, not the actual opencode binary.
1436
1271
  // Kill any process listening on our port to clean up orphaned children.
1437
- spawnSync('sh', ['-c', `lsof -ti:${port} | xargs kill -9 2>/dev/null || true`], {
1438
- stdio: 'ignore',
1439
- timeout: 5000
1440
- });
1272
+ const result = spawnSync('lsof', ['-ti', `:${port}`], { encoding: 'utf8', timeout: 5000 });
1273
+ const output = result.stdout || '';
1274
+ const myPid = process.pid;
1275
+ for (const pidStr of output.split(/\s+/)) {
1276
+ const pid = parseInt(pidStr.trim(), 10);
1277
+ if (pid && pid !== myPid) {
1278
+ try {
1279
+ spawnSync('kill', ['-9', String(pid)], { stdio: 'ignore', timeout: 2000 });
1280
+ } catch {
1281
+ // Ignore
1282
+ }
1283
+ }
1284
+ }
1441
1285
  } catch {
1442
1286
  // Ignore - process may already be dead
1443
1287
  }
@@ -1536,13 +1380,12 @@ async function restartOpenCode() {
1536
1380
  syncToHmrState();
1537
1381
  }
1538
1382
 
1539
- // Reset detection state
1540
- openCodeApiPrefixDetected = false;
1383
+ openCodeApiPrefixDetected = true;
1384
+ openCodeApiPrefix = '';
1541
1385
  if (openCodeApiDetectionTimer) {
1542
1386
  clearTimeout(openCodeApiDetectionTimer);
1543
1387
  openCodeApiDetectionTimer = null;
1544
1388
  }
1545
- openCodeApiDetectionPromise = null;
1546
1389
 
1547
1390
  lastOpenCodeError = null;
1548
1391
  openCodeProcess = await startOpenCode();
@@ -1564,7 +1407,8 @@ async function restartOpenCode() {
1564
1407
  openCodePort = null;
1565
1408
  syncToHmrState();
1566
1409
  }
1567
- openCodeApiPrefixDetected = false;
1410
+ openCodeApiPrefixDetected = true;
1411
+ openCodeApiPrefix = '';
1568
1412
  throw error;
1569
1413
  } finally {
1570
1414
  currentRestartPromise = null;
@@ -1581,74 +1425,51 @@ async function waitForOpenCodeReady(timeoutMs = 20000, intervalMs = 400) {
1581
1425
  let lastError = null;
1582
1426
 
1583
1427
  while (Date.now() < deadline) {
1584
- const prefixes = getCandidateApiPrefixes();
1585
-
1586
- for (const prefix of prefixes) {
1587
- try {
1588
- const normalizedPrefix = normalizeApiPrefix(prefix);
1589
-
1590
- const configPromise = fetch(buildOpenCodeUrl('/config', normalizedPrefix), {
1428
+ try {
1429
+ const [configResult, agentResult] = await Promise.all([
1430
+ fetch(buildOpenCodeUrl('/config', ''), {
1591
1431
  method: 'GET',
1592
1432
  headers: { Accept: 'application/json' }
1593
- }).catch((error) => error);
1594
-
1595
- const agentPromise = fetch(buildOpenCodeUrl('/agent', normalizedPrefix), {
1433
+ }).catch((error) => error),
1434
+ fetch(buildOpenCodeUrl('/agent', ''), {
1596
1435
  method: 'GET',
1597
1436
  headers: { Accept: 'application/json' }
1598
- }).catch((error) => error);
1599
-
1600
- const [configResult, agentResult] = await Promise.all([configPromise, agentPromise]);
1601
-
1602
- if (configResult instanceof Error) {
1603
- lastError = configResult;
1604
- continue;
1605
- }
1437
+ }).catch((error) => error)
1438
+ ]);
1606
1439
 
1607
- if (!configResult.ok) {
1608
- if (configResult.status === 404 && !openCodeApiPrefixDetected && normalizedPrefix === '') {
1609
- lastError = new Error('OpenCode config endpoint returned 404 on root prefix');
1610
- } else {
1611
- lastError = new Error(`OpenCode config endpoint responded with status ${configResult.status}`);
1612
- }
1613
- continue;
1614
- }
1440
+ if (configResult instanceof Error) {
1441
+ lastError = configResult;
1442
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
1443
+ continue;
1444
+ }
1615
1445
 
1616
- await configResult.json().catch(() => null);
1617
- const detectedPrefix = extractApiPrefixFromUrl(configResult.url, '/config');
1618
- if (detectedPrefix !== null) {
1619
- setDetectedOpenCodeApiPrefix(detectedPrefix);
1620
- } else if (normalizedPrefix) {
1621
- setDetectedOpenCodeApiPrefix(normalizedPrefix);
1622
- }
1446
+ if (!configResult.ok) {
1447
+ lastError = new Error(`OpenCode config endpoint responded with status ${configResult.status}`);
1448
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
1449
+ continue;
1450
+ }
1623
1451
 
1624
- if (agentResult instanceof Error) {
1625
- lastError = agentResult;
1626
- continue;
1627
- }
1452
+ await configResult.json().catch(() => null);
1628
1453
 
1629
- if (!agentResult.ok) {
1630
- lastError = new Error(`Agent endpoint responded with status ${agentResult.status}`);
1631
- continue;
1632
- }
1454
+ if (agentResult instanceof Error) {
1455
+ lastError = agentResult;
1456
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
1457
+ continue;
1458
+ }
1633
1459
 
1634
- await agentResult.json().catch(() => []);
1460
+ if (!agentResult.ok) {
1461
+ lastError = new Error(`Agent endpoint responded with status ${agentResult.status}`);
1462
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
1463
+ continue;
1464
+ }
1635
1465
 
1636
- const effectivePrefix = detectedPrefix !== null ? detectedPrefix : normalizedPrefix;
1637
- if (detectedPrefix === null) {
1638
- const agentPrefix = extractApiPrefixFromUrl(agentResult.url, '/agent');
1639
- if (agentPrefix !== null) {
1640
- setDetectedOpenCodeApiPrefix(agentPrefix);
1641
- } else if (normalizedPrefix) {
1642
- setDetectedOpenCodeApiPrefix(normalizedPrefix);
1643
- }
1644
- }
1466
+ await agentResult.json().catch(() => []);
1645
1467
 
1646
- isOpenCodeReady = true;
1647
- lastOpenCodeError = null;
1648
- return;
1649
- } catch (error) {
1650
- lastError = error;
1651
- }
1468
+ isOpenCodeReady = true;
1469
+ lastOpenCodeError = null;
1470
+ return;
1471
+ } catch (error) {
1472
+ lastError = error;
1652
1473
  }
1653
1474
 
1654
1475
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
@@ -1820,12 +1641,8 @@ function setupProxy(app) {
1820
1641
  next();
1821
1642
  });
1822
1643
 
1823
- app.use('/api', async (req, res, next) => {
1824
- try {
1825
- await ensureOpenCodeApiPrefix();
1826
- } catch (error) {
1827
- console.warn(`OpenCode API prefix detection failed for ${req.method} ${req.path}: ${error.message}`);
1828
- }
1644
+ app.use('/api', (_req, _res, next) => {
1645
+ ensureOpenCodeApiPrefix();
1829
1646
  next();
1830
1647
  });
1831
1648
 
@@ -1858,11 +1675,7 @@ function setupProxy(app) {
1858
1675
 
1859
1676
  const suffix = path.slice(4) || '/';
1860
1677
 
1861
- if (!openCodeApiPrefixDetected || openCodeApiPrefix === '') {
1862
- return suffix;
1863
- }
1864
-
1865
- return `${openCodeApiPrefix}${suffix}`;
1678
+ return suffix;
1866
1679
  },
1867
1680
  ws: true,
1868
1681
  onError: (err, req, res) => {
@@ -1894,9 +1707,7 @@ function setupProxy(app) {
1894
1707
  proxyRes.headers['X-Content-Type-Options'] = 'nosniff';
1895
1708
  }
1896
1709
 
1897
- if (proxyRes.statusCode === 404 && !openCodeApiPrefixDetected) {
1898
- scheduleOpenCodeApiDetection();
1899
- }
1710
+
1900
1711
  }
1901
1712
  });
1902
1713
 
@@ -2004,8 +1815,8 @@ async function main(options = {}) {
2004
1815
  timestamp: new Date().toISOString(),
2005
1816
  openCodePort: openCodePort,
2006
1817
  openCodeRunning: Boolean(openCodePort && isOpenCodeReady && !isRestartingOpenCode),
2007
- openCodeApiPrefix,
2008
- openCodeApiPrefixDetected,
1818
+ openCodeApiPrefix: '',
1819
+ openCodeApiPrefixDetected: true,
2009
1820
  isOpenCodeReady,
2010
1821
  lastOpenCodeError
2011
1822
  });
@@ -2204,18 +2015,9 @@ async function main(options = {}) {
2204
2015
  });
2205
2016
 
2206
2017
  app.get('/api/global/event', async (req, res) => {
2207
- if (!openCodeApiPrefixDetected) {
2208
- try {
2209
- await detectOpenCodeApiPrefix();
2210
- } catch {
2211
- // ignore detection failures
2212
- }
2213
- }
2214
-
2215
2018
  let targetUrl;
2216
2019
  try {
2217
- const prefix = openCodeApiPrefixDetected ? openCodeApiPrefix : '';
2218
- targetUrl = new URL(buildOpenCodeUrl('/global/event', prefix));
2020
+ targetUrl = new URL(buildOpenCodeUrl('/global/event', ''));
2219
2021
  } catch (error) {
2220
2022
  return res.status(503).json({ error: 'OpenCode service unavailable' });
2221
2023
  }
@@ -2321,18 +2123,9 @@ async function main(options = {}) {
2321
2123
  });
2322
2124
 
2323
2125
  app.get('/api/event', async (req, res) => {
2324
- if (!openCodeApiPrefixDetected) {
2325
- try {
2326
- await detectOpenCodeApiPrefix();
2327
- } catch {
2328
- // ignore detection failures
2329
- }
2330
- }
2331
-
2332
2126
  let targetUrl;
2333
2127
  try {
2334
- const prefix = openCodeApiPrefixDetected ? openCodeApiPrefix : '';
2335
- targetUrl = new URL(buildOpenCodeUrl('/event', prefix));
2128
+ targetUrl = new URL(buildOpenCodeUrl('/event', ''));
2336
2129
  } catch (error) {
2337
2130
  return res.status(503).json({ error: 'OpenCode service unavailable' });
2338
2131
  }