Compare commits

...

2 Commits

Author SHA1 Message Date
viper151
c3599cd2c4 chore(release): v1.29.2 2026-04-14 18:16:20 +00:00
simosmik
9b11c034d9 fix(sandbox): use backgrounded sbx run to keep sandbox alive 2026-04-14 18:14:58 +00:00
4 changed files with 31 additions and 27 deletions

View File

@@ -3,6 +3,12 @@
All notable changes to CloudCLI UI will be documented in this file. All notable changes to CloudCLI UI will be documented in this file.
## [1.29.2](https://github.com/siteboon/claudecodeui/compare/v1.29.1...v1.29.2) (2026-04-14)
### Bug Fixes
* **sandbox:** use backgrounded sbx run to keep sandbox alive ([9b11c03](https://github.com/siteboon/claudecodeui/commit/9b11c034d9a19710a23b56c62dcf07c21a17bd97))
## [1.29.1](https://github.com/siteboon/claudecodeui/compare/v1.29.0...v1.29.1) (2026-04-14) ## [1.29.1](https://github.com/siteboon/claudecodeui/compare/v1.29.0...v1.29.1) (2026-04-14)
### Bug Fixes ### Bug Fixes

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@cloudcli-ai/cloudcli", "name": "@cloudcli-ai/cloudcli",
"version": "1.29.1", "version": "1.29.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@cloudcli-ai/cloudcli", "name": "@cloudcli-ai/cloudcli",
"version": "1.29.1", "version": "1.29.2",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@cloudcli-ai/cloudcli", "name": "@cloudcli-ai/cloudcli",
"version": "1.29.1", "version": "1.29.2",
"description": "A web-based UI for Claude Code CLI", "description": "A web-based UI for Claude Code CLI",
"type": "module", "type": "module",
"main": "server/index.js", "main": "server/index.js",

View File

@@ -367,7 +367,7 @@ Advanced usage:
} }
async function sandboxCommand(args) { async function sandboxCommand(args) {
const { execFileSync } = await import('child_process'); const { execFileSync, spawn: spawnProcess } = await import('child_process');
// Safe execution — uses execFileSync (no shell) to prevent injection // Safe execution — uses execFileSync (no shell) to prevent injection
const sbx = (subcmd, opts = {}) => { const sbx = (subcmd, opts = {}) => {
@@ -443,12 +443,15 @@ async function sandboxCommand(args) {
process.exit(1); process.exit(1);
} }
console.log(`\n${c.info('▶')} Starting sandbox ${c.bright(opts.name)}...`); console.log(`\n${c.info('▶')} Starting sandbox ${c.bright(opts.name)}...`);
try { const restartRun = spawnProcess('sbx', ['run', opts.name], {
sbx(['start', opts.name], { inherit: true }); detached: true,
} catch { /* might already be running */ } stdio: ['ignore', 'ignore', 'ignore'],
});
restartRun.unref();
await new Promise(resolve => setTimeout(resolve, 5000));
console.log(`${c.info('▶')} Launching CloudCLI web server...`); console.log(`${c.info('▶')} Launching CloudCLI web server...`);
sbx(['exec', '-d', opts.name, 'cloudcli', 'start', '--port', '3001']); sbx(['exec', opts.name, 'bash', '-c', 'cloudcli start --port 3001 &']);
console.log(`${c.info('▶')} Forwarding port ${opts.port} → 3001...`); console.log(`${c.info('▶')} Forwarding port ${opts.port} → 3001...`);
try { try {
@@ -515,22 +518,19 @@ async function sandboxCommand(args) {
} }
console.log(c.dim('─'.repeat(50))); console.log(c.dim('─'.repeat(50)));
// Step 1: Create sandbox // Step 1: Launch sandbox with sbx run in background.
// sbx run creates the sandbox (or reconnects) AND holds an active session,
// which prevents the sandbox from auto-stopping.
console.log(`\n${c.info('▶')} Creating sandbox ${c.bright(opts.name)}...`); console.log(`\n${c.info('▶')} Creating sandbox ${c.bright(opts.name)}...`);
try { const bgRun = spawnProcess('sbx', [
sbx( 'run', '--template', opts.template, '--name', opts.name, opts.agent, workspace,
['create', '--template', opts.template, '--name', opts.name, opts.agent, workspace], ], {
{ inherit: true } detached: true,
); stdio: ['ignore', 'ignore', 'ignore'],
} catch (e) { });
const msg = e.stdout || e.stderr || e.message || ''; bgRun.unref();
if (msg.includes('already exists')) { // Wait for sandbox to be ready
console.log(`${c.warn('⚠')} Sandbox ${c.bright(opts.name)} already exists. Starting it instead...\n`); await new Promise(resolve => setTimeout(resolve, 5000));
try { sbx(['start', opts.name]); } catch { /* may already be running */ }
} else {
throw e;
}
}
// Step 2: Inject environment variables // Step 2: Inject environment variables
if (opts.env.length > 0) { if (opts.env.length > 0) {
@@ -548,11 +548,9 @@ async function sandboxCommand(args) {
} }
} }
// Step 3: Start CloudCLI as a long-running detached exec session. // Step 3: Start CloudCLI inside the sandbox
// Using -d with a long-running command (cloudcli start never exits)
// keeps the exec session alive, which keeps the sandbox running.
console.log(`${c.info('▶')} Launching CloudCLI web server...`); console.log(`${c.info('▶')} Launching CloudCLI web server...`);
sbx(['exec', '-d', opts.name, 'cloudcli', 'start', '--port', '3001']); sbx(['exec', opts.name, 'bash', '-c', 'cloudcli start --port 3001 &']);
// Step 4: Forward port // Step 4: Forward port
console.log(`${c.info('▶')} Forwarding port ${opts.port} → 3001...`); console.log(`${c.info('▶')} Forwarding port ${opts.port} → 3001...`);