Fix subagent crash and add external OpenCode server support (#188)
* 🐛 Fix UI crash when subagent is active Remove sessions dependency from hooks to prevent cascading re-renders. Use getState() instead and switch to getGlobalSessionStatus(). * ✨ Add support for connecting to external OpenCode server Add OPENCODE_SKIP_START env var to skip starting embedded server. Use OPENCODE_PORT to connect to existing OpenCode instance. Update help text to document the new environment variables. * 📝 Document external OpenCode server support Add OPENCODE_PORT and OPENCODE_SKIP_START to READMEs. Update AGENTS.md with external server integration notes. * ✨ Add URL-based routing for shareable session links - Add react-router-dom dependency - Create URL store for bi-directional sync with Zustand - Add WebRouter/DesktopRouter context for runtime-aware routing - Add useURLSync hook to sync URL with session/tab state - Add useNavigation hook with copySessionLink utility - Add share button in Header for copying session links - Update App.tsx to use router wrappers URL structure: /session/:sessionId?tab={chat|git|diff|terminal|files}&directory=/path /settings This enables shareable links and deep-linking to specific sessions. * Revert "✨ Add URL-based routing for shareable session links" This reverts commit b53ee304950a789538fa3d4a236d6361e634ea61.
This commit is contained in:
@@ -31,10 +31,16 @@ openchamber # Start on port 3000
|
||||
openchamber --port 8080 # Custom port
|
||||
openchamber --daemon # Background mode
|
||||
openchamber --ui-password secret # Password-protect UI
|
||||
OPENCODE_PORT=4096 OPENCODE_SKIP_START=true openchamber # Connect to external OpenCode server
|
||||
openchamber stop # Stop server
|
||||
openchamber update # Update to latest version
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `OPENCODE_PORT` - Port of external OpenCode server to connect to (instead of starting embedded server)
|
||||
- `OPENCODE_SKIP_START` - Skip starting embedded OpenCode server (use with `OPENCODE_PORT` to connect to external instance)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [OpenCode CLI](https://opencode.ai) installed (`opencode`)
|
||||
|
||||
@@ -153,7 +153,7 @@ function parseArgs() {
|
||||
|
||||
function showHelp() {
|
||||
console.log(`
|
||||
OpenChamber - Web interface for the OpenCode AI coding agent
|
||||
OpenChamber - Web interface for the OpenCode AI coding agent
|
||||
|
||||
USAGE:
|
||||
openchamber [COMMAND] [OPTIONS]
|
||||
@@ -176,7 +176,9 @@ OPTIONS:
|
||||
-v, --version Show version
|
||||
|
||||
ENVIRONMENT:
|
||||
OPENCHAMBER_UI_PASSWORD Alternative to --ui-password flag
|
||||
OPENCHAMBER_UI_PASSWORD Alternative to --ui-password flag
|
||||
OPENCODE_PORT Port of external OpenCode server to connect to
|
||||
OPENCODE_SKIP_START Skip starting OpenCode, use external server
|
||||
|
||||
EXAMPLES:
|
||||
openchamber # Start on default port 3000 (or a free port)
|
||||
@@ -506,6 +508,7 @@ const commands = {
|
||||
OPENCODE_BINARY: opencodeBinary,
|
||||
...(typeof effectiveUiPassword === 'string' ? { OPENCHAMBER_UI_PASSWORD: effectiveUiPassword } : {}),
|
||||
OPENCHAMBER_TRY_CF_TUNNEL: options.tryCfTunnel ? 'true' : 'false',
|
||||
...(process.env.OPENCODE_SKIP_START ? { OPENCHAMBER_SKIP_OPENCODE_START: process.env.OPENCODE_SKIP_START } : {}),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -574,6 +577,9 @@ const commands = {
|
||||
if (typeof effectiveUiPassword === 'string') {
|
||||
process.env.OPENCHAMBER_UI_PASSWORD = effectiveUiPassword;
|
||||
}
|
||||
if (process.env.OPENCODE_SKIP_START) {
|
||||
process.env.OPENCHAMBER_SKIP_OPENCODE_START = process.env.OPENCODE_SKIP_START;
|
||||
}
|
||||
if (showAutoGeneratedPassword) {
|
||||
console.log(`\n🔐 Auto-generated password: \x1b[92m${effectiveUiPassword}\x1b[0m`);
|
||||
console.log('⚠️ Save this password - it won\'t be shown again!\n');
|
||||
@@ -590,6 +596,7 @@ const commands = {
|
||||
OPENCODE_BINARY: opencodeBinary,
|
||||
...(typeof effectiveUiPassword === 'string' ? { OPENCHAMBER_UI_PASSWORD: effectiveUiPassword } : {}),
|
||||
OPENCHAMBER_TRY_CF_TUNNEL: options.tryCfTunnel ? 'true' : 'false',
|
||||
...(process.env.OPENCODE_SKIP_START ? { OPENCHAMBER_SKIP_OPENCODE_START: process.env.OPENCODE_SKIP_START } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1340,6 +1340,9 @@ const ENV_CONFIGURED_OPENCODE_PORT = (() => {
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
})();
|
||||
|
||||
const ENV_SKIP_OPENCODE_START = process.env.OPENCODE_SKIP_START === 'true' ||
|
||||
process.env.OPENCHAMBER_SKIP_OPENCODE_START === 'true';
|
||||
|
||||
const ENV_CONFIGURED_API_PREFIX = normalizeApiPrefix(
|
||||
process.env.OPENCODE_API_PREFIX || process.env.OPENCHAMBER_API_PREFIX || ''
|
||||
);
|
||||
@@ -5664,12 +5667,17 @@ async function main(options = {}) {
|
||||
|
||||
|
||||
try {
|
||||
// Check if we can reuse an existing OpenCode process from a previous HMR cycle
|
||||
syncFromHmrState();
|
||||
if (await isOpenCodeProcessHealthy()) {
|
||||
console.log(`[HMR] Reusing existing OpenCode process on port ${openCodePort}`);
|
||||
} else if (ENV_SKIP_OPENCODE_START && ENV_CONFIGURED_OPENCODE_PORT) {
|
||||
console.log(`Using external OpenCode server on port ${ENV_CONFIGURED_OPENCODE_PORT} (skip-start mode)`);
|
||||
setOpenCodePort(ENV_CONFIGURED_OPENCODE_PORT);
|
||||
isOpenCodeReady = true;
|
||||
lastOpenCodeError = null;
|
||||
openCodeNotReadySince = 0;
|
||||
syncToHmrState();
|
||||
} else {
|
||||
// No healthy process, start fresh
|
||||
if (ENV_CONFIGURED_OPENCODE_PORT) {
|
||||
console.log(`Using OpenCode port from environment: ${ENV_CONFIGURED_OPENCODE_PORT}`);
|
||||
setOpenCodePort(ENV_CONFIGURED_OPENCODE_PORT);
|
||||
|
||||
Reference in New Issue
Block a user