Refactor web CLI into focused modules (#1837)

This commit is contained in:
Bohdan Triapitsyn
2026-06-26 19:28:44 +03:00
committed by GitHub
parent 00821700de
commit 45df19c3b2
25 changed files with 6714 additions and 5814 deletions
+139
View File
@@ -8,12 +8,16 @@ import { spawn } from 'child_process';
import { pathToFileURL } from 'url';
import { isModuleCliExecution, normalizeCliEntryPath } from './cli-entry.js';
import { requestJson } from './lib/cli-http.js';
import { inspectTunnelAttachability } from './lib/cli-lifecycle.js';
import {
assertAuthenticatedNetworkExposure,
commands,
discoverOpenChamberInstanceOnPort,
discoverLifecycleInstances,
discoverRunningInstances,
discoverUnconfirmedRegistryInstanceOnPort,
ensureTunnelProfilesMigrated,
getInstanceFilePath,
getPidFilePath,
isOpenchamberCmdline,
@@ -298,6 +302,98 @@ describe('serve host resolution', () => {
});
});
describe('compatibility exports', () => {
it('allows tunnel profile migration before command options are initialized', async () => {
await withTempOpenChamberDataDir(async () => {
const store = ensureTunnelProfilesMigrated();
expect(store).toEqual({ version: 1, profiles: [] });
});
});
it('includes ngrok in fallback tunnel providers when no server is reachable', async () => {
await withTempOpenChamberDataDir(async () => {
const output = await captureStdout(async () => {
await commands.tunnel({ json: true }, 'providers');
});
const body = JSON.parse(output);
expect(body.source).toBe('fallback');
expect(body.providers.map((entry) => entry.provider)).toContain('ngrok');
});
});
it('supports ngrok quick dry-run with an explicit port', async () => {
await withTempOpenChamberDataDir(async () => {
const output = await captureStdout(async () => {
await commands.tunnel({
json: true,
dryRun: true,
explicitPort: true,
port: 3003,
provider: 'ngrok',
mode: 'quick',
}, 'start');
});
const body = JSON.parse(output);
expect(body).toEqual(expect.objectContaining({
ok: true,
dryRun: true,
provider: 'ngrok',
mode: 'quick',
}));
});
});
});
describe('CLI HTTP helpers', () => {
it('retries UI-authenticated API requests with the stored instance password', async () => {
await withTempOpenChamberDataDir(async () => {
const port = 45678;
fs.writeFileSync(await getInstanceFilePath(port), JSON.stringify({ port, uiPassword: 'secret' }, null, 2));
const originalFetch = globalThis.fetch;
const calls = [];
globalThis.fetch = async (url, options = {}) => {
calls.push({ url: String(url), options });
if (String(url).endsWith('/auth/session')) {
expect(JSON.parse(options.body)).toEqual({ password: 'secret' });
return {
ok: true,
headers: { get: (name) => name.toLowerCase() === 'set-cookie' ? 'oc_ui_session=session-token; Path=/; HttpOnly' : null },
json: async () => ({ authenticated: true }),
};
}
if (options.headers?.Cookie === 'oc_ui_session=session-token') {
return createMockJsonResponse({ ok: true });
}
return {
ok: false,
status: 401,
json: async () => ({ error: 'UI authentication required', locked: true }),
};
};
try {
const { response, body } = await requestJson(port, '/api/openchamber/tunnel/start', {
method: 'POST',
body: JSON.stringify({ provider: 'ngrok', mode: 'quick' }),
});
expect(response.ok).toBe(true);
expect(body).toEqual({ ok: true });
expect(calls.map((call) => new URL(call.url).pathname)).toEqual([
'/api/openchamber/tunnel/start',
'/auth/session',
'/api/openchamber/tunnel/start',
]);
} finally {
globalThis.fetch = originalFetch;
}
});
});
});
describe('cli entry detection', () => {
const modulePath = '/tmp/openchamber/bin/cli.js';
const moduleUrl = pathToFileURL(modulePath).href;
@@ -387,6 +483,49 @@ describe('isOpenchamberProcessRunning', () => {
});
describe('lifecycle instance discovery', () => {
it('does not attribute a desktop runtime response to a different explicit port', async () => {
await withTempOpenChamberDataDir(async (dir) => {
fs.writeFileSync(path.join(dir, 'settings.json'), JSON.stringify({ desktopLocalPort: 57123 }, null, 2));
const instance = await discoverOpenChamberInstanceOnPort(3003, {
fetchImpl: async () => createMockJsonResponse({ runtime: 'desktop', pid: 934 }),
});
expect(instance).toBeNull();
});
});
it('attributes a desktop runtime response to its configured desktop port', async () => {
await withTempOpenChamberDataDir(async (dir) => {
fs.writeFileSync(path.join(dir, 'settings.json'), JSON.stringify({ desktopLocalPort: 57123 }, null, 2));
const instance = await discoverOpenChamberInstanceOnPort(57123, {
fetchImpl: async () => createMockJsonResponse({ runtime: 'desktop', pid: 934 }),
});
expect(instance).toEqual(expect.objectContaining({
port: 57123,
pid: 934,
runtime: 'desktop',
}));
});
});
it('does not mark tunnel attachability as desktop for a different explicit port', async () => {
await withTempOpenChamberDataDir(async (dir) => {
fs.writeFileSync(path.join(dir, 'settings.json'), JSON.stringify({ desktopLocalPort: 57123 }, null, 2));
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => createMockJsonResponse({ runtime: 'desktop', pid: 934 });
try {
const attachability = await inspectTunnelAttachability(3004, { requireHealthy: false });
expect(attachability.reason).not.toBe('desktop');
} finally {
globalThis.fetch = originalFetch;
}
});
});
it('keeps pid and instance files when live port probe confirms a cmdline mismatch', async () => {
await withTempOpenChamberDataDir(async () => {
const port = 45123;