feat(vscode): redesign control center (#236)

* feat(vscode): redesign control center

* fix(vscode): keep launch target messaging honest
This commit is contained in:
Vasanth T
2026-04-03 19:28:25 +05:30
committed by GitHub
parent fb32e3f829
commit 6987a54a71
8 changed files with 2408 additions and 232 deletions

View File

@@ -1,15 +1,29 @@
# OpenClaude VS Code Extension
A sleek VS Code companion for OpenClaude with a visual **Control Center** plus terminal-first workflows.
A practical VS Code companion for OpenClaude with a project-aware **Control Center**, predictable terminal launch behavior, and quick access to useful OpenClaude workflows.
## Features
- **Control Center sidebar UI** in the Activity Bar:
- **Real Control Center status** in the Activity Bar:
- whether the configured `openclaude` command is installed
- the launch command being used
- whether the launch shim injects `CLAUDE_CODE_USE_OPENAI=1`
- the current workspace folder
- the launch cwd that will be used for terminal sessions
- whether `.openclaude-profile.json` exists in the current workspace root
- a conservative provider summary derived from the workspace profile or known environment flags
- **Project-aware launch behavior**:
- `Launch OpenClaude` launches from the active editor's workspace when possible
- falls back to the first workspace folder when needed
- avoids launching from an arbitrary default cwd when a project is open
- **Practical sidebar actions**:
- Launch OpenClaude
- Open repository/docs
- Open VS Code theme picker
- **Terminal launch command**: `OpenClaude: Launch in Terminal`
- **Built-in dark theme**: `OpenClaude Terminal Black` (terminal-inspired, low-glare, neon accents)
- Launch in Workspace Root
- Open Workspace Profile
- Open Repository
- Open Setup Guide
- Open Command Palette
- **Built-in dark theme**: `OpenClaude Terminal Black`
## Requirements
@@ -20,19 +34,31 @@ A sleek VS Code companion for OpenClaude with a visual **Control Center** plus t
- `OpenClaude: Open Control Center`
- `OpenClaude: Launch in Terminal`
- `OpenClaude: Launch in Workspace Root`
- `OpenClaude: Open Repository`
- `OpenClaude: Open Setup Guide`
- `OpenClaude: Open Workspace Profile`
## Settings
- `openclaude.launchCommand` (default: `openclaude`)
- `openclaude.terminalName` (default: `OpenClaude`)
- `openclaude.useOpenAIShim` (default: `true`)
- `openclaude.useOpenAIShim` (default: `false`)
`openclaude.useOpenAIShim` only injects `CLAUDE_CODE_USE_OPENAI=1` into terminals launched by the extension. It does not guess or configure a provider by itself.
## Notes on Status Detection
- Provider status prefers the real workspace `.openclaude-profile.json` file when present.
- If no saved profile exists, the extension falls back to known environment flags available to the VS Code extension host.
- If the source of truth is unclear, the extension shows `unknown` instead of guessing.
## Development
From this folder:
```bash
npm run test
npm run lint
```

View File

@@ -1,7 +1,7 @@
{
"name": "openclaude-vscode",
"displayName": "OpenClaude",
"description": "Sleek VS Code extension for OpenClaude with a visual Control Center and terminal-aligned theme.",
"description": "Practical VS Code companion for OpenClaude with project-aware launch behavior and a real Control Center.",
"version": "0.1.1",
"publisher": "devnull-bootloader",
"engines": {
@@ -14,11 +14,22 @@
"activationEvents": [
"onStartupFinished",
"onCommand:openclaude.start",
"onCommand:openclaude.startInWorkspaceRoot",
"onCommand:openclaude.openDocs",
"onCommand:openclaude.openSetupDocs",
"onCommand:openclaude.openWorkspaceProfile",
"onCommand:openclaude.openControlCenter",
"onView:openclaude.controlCenter"
],
"main": "./src/extension.js",
"files": [
"README.md",
"media/**",
"src/extension.js",
"src/presentation.js",
"src/state.js",
"themes/**"
],
"contributes": {
"commands": [
{
@@ -26,11 +37,26 @@
"title": "OpenClaude: Launch in Terminal",
"category": "OpenClaude"
},
{
"command": "openclaude.startInWorkspaceRoot",
"title": "OpenClaude: Launch in Workspace Root",
"category": "OpenClaude"
},
{
"command": "openclaude.openDocs",
"title": "OpenClaude: Open Repository",
"category": "OpenClaude"
},
{
"command": "openclaude.openSetupDocs",
"title": "OpenClaude: Open Setup Guide",
"category": "OpenClaude"
},
{
"command": "openclaude.openWorkspaceProfile",
"title": "OpenClaude: Open Workspace Profile",
"category": "OpenClaude"
},
{
"command": "openclaude.openControlCenter",
"title": "OpenClaude: Open Control Center",
@@ -84,7 +110,8 @@
]
},
"scripts": {
"lint": "node --check ./src/extension.js",
"test": "node --test ./src/*.test.js",
"lint": "node -e \"for (const file of require('node:fs').readdirSync('./src')) { if (file.endsWith('.js')) { require('node:child_process').execFileSync(process.execPath, ['--check', require('node:path').join('src', file)], { stdio: 'inherit' }); } }\"",
"package": "npx @vscode/vsce package --no-dependencies"
},
"keywords": [

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,249 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const Module = require('node:module');
function createStatus(overrides = {}) {
return {
installed: true,
executable: 'openclaude',
launchCommand: 'openclaude --project-aware',
terminalName: 'OpenClaude',
shimEnabled: false,
workspaceFolder: '/workspace/openclaude/very/long/path/example-project',
workspaceSourceLabel: 'active editor workspace',
launchCwd: '/workspace/openclaude/very/long/path/example-project',
launchCwdLabel: '/workspace/openclaude/very/long/path/example-project',
canLaunchInWorkspaceRoot: true,
profileStatusLabel: 'Found',
profileStatusHint: '/workspace/openclaude/very/long/path/example-project/.openclaude-profile.json',
workspaceProfilePath: '/workspace/openclaude/very/long/path/example-project/.openclaude-profile.json',
providerState: {
label: 'Codex',
detail: 'gpt-5.4',
source: 'profile',
},
providerSourceLabel: 'saved profile',
...overrides,
};
}
function loadExtension() {
const extensionPath = require.resolve('./extension');
delete require.cache[extensionPath];
const originalLoad = Module._load;
Module._load = function patchedLoad(request, parent, isMain) {
if (request === 'vscode') {
return {
workspace: {},
window: {},
env: {},
commands: {},
Uri: { parse: value => value, file: value => value },
};
}
return originalLoad.call(this, request, parent, isMain);
};
try {
return require('./extension');
} finally {
Module._load = originalLoad;
}
}
test('renderControlCenterHtml uses the OpenClaude wordmark, status rail, and warm action hierarchy', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(createStatus(), { nonce: 'test-nonce', platform: 'win32' });
assert.match(html, /Open<span class="wordmark-accent">Claude<\/span>/);
assert.match(html, /class="status-rail"/);
assert.match(html, /\.sunset-gradient\s*\{/);
assert.match(html, /class="action-button primary" id="launch"/);
assert.match(html, /class="action-button secondary" id="launchRoot"/);
assert.match(
html,
/title="\/workspace\/openclaude\/very\/long\/path\/example-project"[^>]*>\/workspace\/openclaude\/very\/long\/path\/example-project<\//,
);
});
test('renderControlCenterHtml shows explicit disabled and empty states when workspace data is missing', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(
createStatus({
workspaceFolder: null,
workspaceSourceLabel: 'no workspace open',
launchCwd: null,
launchCwdLabel: 'VS Code default terminal cwd',
canLaunchInWorkspaceRoot: false,
profileStatusLabel: 'No workspace',
profileStatusHint: 'Open a workspace folder to detect a saved profile',
workspaceProfilePath: null,
}),
{ nonce: 'test-nonce', platform: 'linux' },
);
assert.match(
html,
/class="action-button secondary" id="launchRoot"[^>]*disabled[^>]*>[\s\S]*Open a workspace folder to enable workspace-root launch/,
);
assert.match(html, /No workspace profile yet/);
assert.match(html, /Open a workspace folder to detect a saved profile/);
assert.doesNotMatch(html, /id="openProfile"/);
});
test('OpenClaudeControlCenterProvider.getHtml supplies a nonce to the redesigned renderer', () => {
const { OpenClaudeControlCenterProvider } = loadExtension();
const provider = new OpenClaudeControlCenterProvider();
assert.doesNotThrow(() => provider.getHtml(createStatus()));
const html = provider.getHtml(createStatus());
assert.match(html, /script-src 'nonce-[^']+'/);
assert.match(html, /<script nonce="[^"]+">/);
assert.doesNotMatch(html, /nonce-undefined/);
assert.doesNotMatch(html, /<script nonce="undefined">/);
});
test('resolveLaunchTargets distinguishes project-aware launch from workspace-root launch', () => {
const { resolveLaunchTargets } = loadExtension();
assert.deepEqual(
resolveLaunchTargets({
activeFilePath: '/workspace/openclaude/src/panels/control-center.js',
workspacePath: '/workspace/openclaude',
workspaceSourceLabel: 'active editor workspace',
}),
{
projectAwareCwd: '/workspace/openclaude/src/panels',
projectAwareCwdLabel: '/workspace/openclaude/src/panels',
projectAwareSourceLabel: 'active file directory',
workspaceRootCwd: '/workspace/openclaude',
workspaceRootCwdLabel: '/workspace/openclaude',
launchActionsShareTarget: false,
launchActionsShareTargetReason: null,
},
);
});
test('resolveLaunchTargets anchors relative launch commands to the workspace root', () => {
const { resolveLaunchTargets } = loadExtension();
assert.deepEqual(
resolveLaunchTargets({
executable: './node_modules/.bin/openclaude',
activeFilePath: '/workspace/openclaude/src/panels/control-center.js',
workspacePath: '/workspace/openclaude',
workspaceSourceLabel: 'active editor workspace',
}),
{
projectAwareCwd: '/workspace/openclaude',
projectAwareCwdLabel: '/workspace/openclaude',
projectAwareSourceLabel: 'workspace root (required by relative launch command)',
workspaceRootCwd: '/workspace/openclaude',
workspaceRootCwdLabel: '/workspace/openclaude',
launchActionsShareTarget: true,
launchActionsShareTargetReason: 'relative-launch-command',
},
);
});
test('resolveLaunchTargets ignores active files outside the selected workspace', () => {
const { resolveLaunchTargets } = loadExtension();
assert.deepEqual(
resolveLaunchTargets({
executable: 'openclaude',
activeFilePath: '/tmp/notes/scratch.js',
workspacePath: '/workspace/openclaude',
workspaceSourceLabel: 'first workspace folder',
}),
{
projectAwareCwd: '/workspace/openclaude',
projectAwareCwdLabel: '/workspace/openclaude',
projectAwareSourceLabel: 'first workspace folder',
workspaceRootCwd: '/workspace/openclaude',
workspaceRootCwdLabel: '/workspace/openclaude',
launchActionsShareTarget: true,
launchActionsShareTargetReason: null,
},
);
});
test('renderControlCenterHtml restores landmark and heading semantics', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(createStatus(), { nonce: 'test-nonce', platform: 'win32' });
assert.match(html, /<main class="shell" aria-labelledby="control-center-title">/);
assert.match(html, /<header class="hero">/);
assert.match(html, /<h1 class="headline-title" id="control-center-title">/);
assert.match(html, /<section class="modules" aria-label="Control center details">/);
assert.match(html, /<h2 class="module-title" id="section-project">Project<\/h2>/);
assert.match(html, /<section class="actions-layout" aria-label="Control center actions">/);
});
test('renderControlCenterHtml explains distinct launch targets when an active file directory is available', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(
createStatus({
launchCwd: '/workspace/openclaude/src/panels',
launchCwdLabel: '/workspace/openclaude/src/panels',
launchCwdSourceLabel: 'active file directory',
workspaceRootCwd: '/workspace/openclaude',
workspaceRootCwdLabel: '/workspace/openclaude',
}),
{ nonce: 'test-nonce', platform: 'linux' },
);
assert.match(html, /Starts beside the active file · \/workspace\/openclaude\/src\/panels/);
assert.match(html, /Always starts at the workspace root · \/workspace\/openclaude/);
});
test('renderControlCenterHtml makes shared workspace-root launches explicit for relative commands', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(
createStatus({
launchCwd: '/workspace/openclaude',
launchCwdLabel: '/workspace/openclaude',
launchCwdSourceLabel: 'workspace root (required by relative launch command)',
workspaceRootCwd: '/workspace/openclaude',
workspaceRootCwdLabel: '/workspace/openclaude',
launchActionsShareTarget: true,
launchActionsShareTargetReason: 'relative-launch-command',
}),
{ nonce: 'test-nonce', platform: 'linux' },
);
assert.match(html, /Project-aware launch is anchored to the workspace root by the relative command · \/workspace\/openclaude/);
assert.match(html, /Same workspace-root target as Launch OpenClaude because the relative command resolves from the workspace root · \/workspace\/openclaude/);
});
test('renderControlCenterHtml escapes hostile text and title values', () => {
const { renderControlCenterHtml } = loadExtension();
const html = renderControlCenterHtml(
createStatus({
launchCommand: '<img src=x onerror="boom()">',
workspaceFolder: '"/><script>workspace()</script>',
workspaceSourceLabel: 'active <b>workspace</b>',
launchCwdLabel: '"><script>cwd()</script>',
profileStatusHint: '<svg onload="profile()">',
workspaceProfilePath: '"/><script>profile-path()</script>',
providerState: {
label: 'Provider "><img src=x onerror="label()">',
detail: '<script>provider-detail()</script>',
source: 'profile',
},
}),
{ nonce: 'test-nonce', platform: 'linux' },
);
assert.match(html, /&lt;img src=x onerror=&quot;boom\(\)&quot;&gt;/);
assert.match(html, /&quot;\/&gt;&lt;script&gt;workspace\(\)&lt;\/script&gt;/);
assert.match(html, /active &lt;b&gt;workspace&lt;\/b&gt;/);
assert.match(html, /&lt;svg onload=&quot;profile\(\)&quot;&gt;/);
assert.match(html, /Provider &quot;&gt;&lt;img src=x onerror=&quot;label\(\)&quot;&gt;/);
assert.match(html, /&lt;script&gt;provider-detail\(\)&lt;\/script&gt; · saved profile/);
assert.doesNotMatch(html, /<script>workspace\(\)<\/script>/);
assert.doesNotMatch(html, /<img src=x onerror="boom\(\)">/);
});

View File

@@ -0,0 +1,202 @@
function truncateMiddle(value, maxLength) {
const text = String(value || '');
if (!text || text.length <= maxLength) {
return text;
}
const basename = text.split(/[\\/]/).filter(Boolean).pop() || '';
if (basename && basename.length + 4 <= maxLength) {
const separator = text.includes('\\') ? '\\' : '/';
return `...${separator}${basename}`;
}
if (maxLength <= 3) {
return '.'.repeat(Math.max(maxLength, 0));
}
const available = maxLength - 3;
const startLength = Math.ceil(available / 2);
const endLength = Math.floor(available / 2);
return `${text.slice(0, startLength)}...${text.slice(text.length - endLength)}`;
}
function getPathTail(value) {
const text = String(value || '');
if (!text) {
return '';
}
return text.split(/[\\/]/).filter(Boolean).pop() || text;
}
function buildActionModel({ canLaunchInWorkspaceRoot, workspaceProfilePath } = {}) {
return {
primary: {
id: 'launch',
label: 'Launch OpenClaude',
detail: 'Use the resolved project-aware launch directory',
tone: 'accent',
disabled: false,
},
launchRoot: {
id: 'launchRoot',
label: 'Launch in Workspace Root',
detail: canLaunchInWorkspaceRoot
? 'Launch directly from the resolved workspace root'
: 'Open a workspace folder to enable workspace-root launch',
tone: 'neutral',
disabled: !canLaunchInWorkspaceRoot,
},
openProfile: workspaceProfilePath
? {
id: 'openProfile',
label: 'Open Workspace Profile',
detail: `Inspect ${truncateMiddle(workspaceProfilePath, 40)}`,
tone: 'neutral',
disabled: false,
}
: null,
};
}
function getRuntimeTone(installed) {
return installed ? 'positive' : 'critical';
}
function getProfileTone(profileStatusLabel) {
return profileStatusLabel === 'Invalid' || profileStatusLabel === 'Unreadable'
? 'warning'
: 'neutral';
}
function getProviderTone(providerState) {
return providerState?.source === 'shim' || providerState?.source === 'unknown'
? 'warning'
: 'neutral';
}
function getProviderDetail(providerState, providerSourceLabel) {
const detail = providerState?.detail || '';
if (!detail) {
return providerSourceLabel || '';
}
switch (providerState?.source) {
case 'profile':
return [detail, providerSourceLabel].filter(Boolean).join(' · ');
case 'env':
return /^from environment$/i.test(detail)
? detail
: [detail, providerSourceLabel].filter(Boolean).join(' · ');
case 'shim':
case 'unknown':
return detail;
default:
return [detail, providerSourceLabel].filter(Boolean).join(' · ');
}
}
function buildControlCenterViewModel(status = {}) {
const runtimeSummary = status.installed ? 'Installed' : 'Missing';
const runtimeDetail = status.executable || 'Unknown command';
const providerDetail = getProviderDetail(status.providerState, status.providerSourceLabel);
const providerTone = getProviderTone(status.providerState);
const workspaceSummary = status.workspaceFolder ? getPathTail(status.workspaceFolder) : 'No workspace open';
const workspaceDetail = [status.workspaceFolder, status.workspaceSourceLabel]
.filter(Boolean)
.join(' · ') || 'no workspace open';
return {
header: {
eyebrow: 'OpenClaude Control Center',
title: 'Project-aware OpenClaude companion',
subtitle:
'Useful local status, predictable launch behavior, and quick access to the workflows you actually use.',
},
headerBadges: [
{
key: 'runtime',
label: 'Runtime',
value: runtimeSummary,
tone: getRuntimeTone(status.installed),
},
{
key: 'provider',
label: 'Provider',
value: status.providerState?.label || 'Unknown',
tone: providerTone,
},
{
key: 'profileStatus',
label: 'Profile',
value: status.profileStatusLabel || 'Unknown',
tone: getProfileTone(status.profileStatusLabel),
},
],
summaryCards: [
{
key: 'workspace',
label: 'Workspace',
value: status.workspaceFolder || 'No workspace open',
detail: status.workspaceSourceLabel || 'no workspace open',
},
{
key: 'launchCwd',
label: 'Launch cwd',
value: status.launchCwdLabel || 'VS Code default terminal cwd',
},
{
key: 'launchCommand',
label: 'Launch command',
value: status.launchCommand || '',
detail: status.terminalName ? `Integrated terminal: ${status.terminalName}` : '',
},
],
detailSections: [
{
title: 'Project',
rows: [
{
key: 'workspace',
label: 'Workspace folder',
summary: workspaceSummary,
detail: workspaceDetail,
},
{
key: 'profileStatus',
label: 'Workspace profile',
summary: status.profileStatusLabel || 'Unknown',
detail: status.profileStatusHint || '',
tone: getProfileTone(status.profileStatusLabel),
},
],
},
{
title: 'Runtime',
rows: [
{
key: 'runtime',
label: 'OpenClaude executable',
summary: runtimeSummary,
detail: runtimeDetail,
tone: getRuntimeTone(status.installed),
},
{
key: 'provider',
label: 'Detected provider',
summary: status.providerState?.label || 'Unknown',
detail: providerDetail,
tone: providerTone,
},
],
},
],
actions: buildActionModel(status),
};
}
module.exports = {
truncateMiddle,
buildActionModel,
buildControlCenterViewModel,
};

View File

@@ -0,0 +1,291 @@
const test = require('node:test');
const assert = require('node:assert/strict');
function loadPresentation() {
return require('./presentation');
}
test('truncateMiddle keeps the profile filename visible', () => {
const { truncateMiddle } = loadPresentation();
assert.equal(
truncateMiddle('/Users/example/projects/openclaude/workspace/.openclaude-profile.json', 30),
'.../.openclaude-profile.json',
);
});
test('truncateMiddle keeps the filename visible for Windows-style paths', () => {
const { truncateMiddle } = loadPresentation();
assert.equal(
truncateMiddle('C:\\Users\\example\\openclaude\\workspace\\.openclaude-profile.json', 30),
'...\\.openclaude-profile.json',
);
});
test('buildActionModel disables workspace-root launch without a workspace', () => {
const { buildActionModel } = loadPresentation();
const model = buildActionModel({
canLaunchInWorkspaceRoot: false,
workspaceProfilePath: null,
});
assert.deepEqual(model.launchRoot, {
id: 'launchRoot',
label: 'Launch in Workspace Root',
detail: 'Open a workspace folder to enable workspace-root launch',
tone: 'neutral',
disabled: true,
});
});
test('buildActionModel hides workspace-profile action when no profile exists', () => {
const { buildActionModel } = loadPresentation();
const model = buildActionModel({
canLaunchInWorkspaceRoot: true,
workspaceProfilePath: null,
});
assert.deepEqual(model.primary, {
id: 'launch',
label: 'Launch OpenClaude',
detail: 'Use the resolved project-aware launch directory',
tone: 'accent',
disabled: false,
});
assert.equal(model.openProfile, null);
});
test('buildActionModel includes workspace-profile action when a profile exists', () => {
const { buildActionModel } = loadPresentation();
const model = buildActionModel({
canLaunchInWorkspaceRoot: true,
workspaceProfilePath: 'C:\\Users\\example\\openclaude\\workspace\\.openclaude-profile.json',
});
assert.deepEqual(model.openProfile, {
id: 'openProfile',
label: 'Open Workspace Profile',
detail: 'Inspect ...\\.openclaude-profile.json',
tone: 'neutral',
disabled: false,
});
});
function createStatus(overrides = {}) {
return {
installed: true,
executable: 'openclaude',
launchCommand: 'openclaude --project-aware',
terminalName: 'OpenClaude',
shimEnabled: false,
workspaceFolder: '/workspace/openclaude',
workspaceSourceLabel: 'active editor workspace',
launchCwd: '/workspace/openclaude',
launchCwdLabel: '/workspace/openclaude',
canLaunchInWorkspaceRoot: true,
profileStatusLabel: 'Found',
profileStatusHint: '/workspace/openclaude/.openclaude-profile.json',
workspaceProfilePath: '/workspace/openclaude/.openclaude-profile.json',
providerState: {
label: 'Codex',
detail: 'gpt-5.4',
source: 'profile',
},
providerSourceLabel: 'saved profile',
...overrides,
};
}
test('buildControlCenterViewModel keeps header badges and summary cards non-redundant', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus());
const headerKeys = new Set(viewModel.headerBadges.map(badge => badge.key));
const summaryKeys = new Set(viewModel.summaryCards.map(card => card.key));
assert.deepEqual([...headerKeys].sort(), ['profileStatus', 'provider', 'runtime']);
assert.deepEqual([...summaryKeys].sort(), ['launchCommand', 'launchCwd', 'workspace']);
for (const key of headerKeys) {
assert.equal(summaryKeys.has(key), false);
}
});
test('buildControlCenterViewModel uses stable semantic tones for badges and actions', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus({
installed: false,
profileStatusLabel: 'Invalid',
providerState: {
label: 'OpenAI-compatible (provider unknown)',
detail: 'launch shim enabled',
source: 'shim',
},
providerSourceLabel: 'launch setting',
}));
assert.deepEqual(viewModel.headerBadges, [
{
key: 'runtime',
label: 'Runtime',
value: 'Missing',
tone: 'critical',
},
{
key: 'provider',
label: 'Provider',
value: 'OpenAI-compatible (provider unknown)',
tone: 'warning',
},
{
key: 'profileStatus',
label: 'Profile',
value: 'Invalid',
tone: 'warning',
},
]);
assert.equal(viewModel.actions.primary.tone, 'accent');
assert.equal(viewModel.actions.launchRoot.tone, 'neutral');
});
test('buildControlCenterViewModel uses a concise project summary before full path detail', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus());
assert.deepEqual(viewModel.detailSections, [
{
title: 'Project',
rows: [
{
key: 'workspace',
label: 'Workspace folder',
summary: 'openclaude',
detail: '/workspace/openclaude · active editor workspace',
},
{
key: 'profileStatus',
label: 'Workspace profile',
summary: 'Found',
detail: '/workspace/openclaude/.openclaude-profile.json',
tone: 'neutral',
},
],
},
{
title: 'Runtime',
rows: [
{
key: 'runtime',
label: 'OpenClaude executable',
summary: 'Installed',
detail: 'openclaude',
tone: 'positive',
},
{
key: 'provider',
label: 'Detected provider',
summary: 'Codex',
detail: 'gpt-5.4 · saved profile',
tone: 'neutral',
},
],
},
]);
});
test('buildControlCenterViewModel keeps launch command only in summary cards', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus());
assert.deepEqual(viewModel.summaryCards.find(card => card.key === 'launchCommand'), {
key: 'launchCommand',
label: 'Launch command',
value: 'openclaude --project-aware',
detail: 'Integrated terminal: OpenClaude',
});
assert.equal(
viewModel.detailSections.some(section => section.rows.some(row => row.key === 'launchCommand')),
false,
);
});
test('buildControlCenterViewModel keeps env-backed provider detail non-redundant', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus({
providerState: {
label: 'Gemini',
detail: 'from environment',
source: 'env',
},
providerSourceLabel: 'environment',
}));
assert.deepEqual(viewModel.detailSections[1].rows.find(row => row.key === 'provider'), {
key: 'provider',
label: 'Detected provider',
summary: 'Gemini',
detail: 'from environment',
tone: 'neutral',
});
});
test('buildControlCenterViewModel keeps shim-backed provider detail honest', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus({
providerState: {
label: 'OpenAI-compatible (provider unknown)',
detail: 'launch shim enabled',
source: 'shim',
},
providerSourceLabel: 'launch setting',
}));
assert.deepEqual(viewModel.detailSections[1].rows.find(row => row.key === 'provider'), {
key: 'provider',
label: 'Detected provider',
summary: 'OpenAI-compatible (provider unknown)',
detail: 'launch shim enabled',
tone: 'warning',
});
});
test('buildControlCenterViewModel keeps unknown provider detail honest', () => {
const { buildControlCenterViewModel } = loadPresentation();
const viewModel = buildControlCenterViewModel(createStatus({
providerState: {
label: 'Unknown',
detail: 'no saved profile or provider env detected',
source: 'unknown',
},
providerSourceLabel: 'unknown',
}));
assert.deepEqual(viewModel.detailSections[1].rows.find(row => row.key === 'provider'), {
key: 'provider',
label: 'Detected provider',
summary: 'Unknown',
detail: 'no saved profile or provider env detected',
tone: 'warning',
});
});
test('buildControlCenterViewModel carries forward the existing action model', () => {
const { buildControlCenterViewModel, buildActionModel } = loadPresentation();
const status = createStatus();
const viewModel = buildControlCenterViewModel(status);
assert.deepEqual(viewModel.actions, buildActionModel(status));
});

View File

@@ -0,0 +1,389 @@
const fs = require('fs');
const path = require('path');
const SAVED_PROFILES = new Set([
'openai',
'ollama',
'codex',
'gemini',
'atomic-chat',
]);
const CODEX_ALIAS_MODELS = new Set([
'codexplan',
'codexspark',
'gpt-5.4',
'gpt-5.4-mini',
'gpt-5.3-codex',
'gpt-5.3-codex-spark',
'gpt-5.2',
'gpt-5.2-codex',
'gpt-5.1-codex-max',
'gpt-5.1-codex-mini',
]);
function asNonEmptyString(value) {
return typeof value === 'string' && value.trim() ? value.trim() : null;
}
function isEnvTruthy(value) {
const normalized = asNonEmptyString(value);
if (!normalized) {
return false;
}
const lowered = normalized.toLowerCase();
return lowered !== '0' && lowered !== 'false' && lowered !== 'no';
}
function chooseLaunchWorkspace({ activeWorkspacePath, workspacePaths }) {
const activePath = asNonEmptyString(activeWorkspacePath);
if (activePath) {
return { workspacePath: activePath, source: 'active-workspace' };
}
const firstWorkspacePath = Array.isArray(workspacePaths)
? asNonEmptyString(workspacePaths[0])
: null;
if (firstWorkspacePath) {
return { workspacePath: firstWorkspacePath, source: 'first-workspace' };
}
return { workspacePath: null, source: 'none' };
}
function sanitizeProfileEnv(env) {
if (!env || typeof env !== 'object' || Array.isArray(env)) {
return {};
}
return Object.fromEntries(
Object.entries(env).filter(([, value]) => typeof value === 'string' && value.trim()),
);
}
function parseProfileFile(raw) {
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
return null;
}
const profile = asNonEmptyString(parsed.profile);
if (!profile || !SAVED_PROFILES.has(profile)) {
return null;
}
if (!parsed.env || typeof parsed.env !== 'object' || Array.isArray(parsed.env)) {
return null;
}
return {
profile,
env: sanitizeProfileEnv(parsed.env),
createdAt: asNonEmptyString(parsed.createdAt),
};
} catch {
return null;
}
}
function isLocalBaseUrl(baseUrl) {
const normalized = asNonEmptyString(baseUrl);
if (!normalized) {
return false;
}
try {
const hostname = new URL(normalized).hostname.toLowerCase();
return (
hostname === 'localhost' ||
hostname === '127.0.0.1' ||
hostname === '0.0.0.0' ||
hostname === '::1' ||
hostname.endsWith('.local')
);
} catch {
return false;
}
}
function resolveCommandCheckPath(command, workspacePath) {
const normalized = asNonEmptyString(command);
if (!normalized) {
return null;
}
if (!normalized.includes(path.sep) && !normalized.includes('/')) {
return null;
}
if (path.isAbsolute(normalized)) {
return normalized;
}
return workspacePath
? path.resolve(workspacePath, normalized)
: path.resolve(normalized);
}
function getEnvValue(env, key) {
if (!env || typeof env !== 'object') {
return '';
}
const matchedKey = Object.keys(env).find(candidate => candidate.toUpperCase() === key);
return matchedKey ? env[matchedKey] : '';
}
function canAccessExecutable(filePath, platform) {
try {
fs.accessSync(filePath, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
return true;
} catch {
return false;
}
}
function findCommandPath(command, options = {}) {
const normalized = asNonEmptyString(command);
if (!normalized) {
return null;
}
const cwd = asNonEmptyString(options.cwd);
const env = options.env || process.env;
const platform = options.platform || process.platform;
const hasPathSeparators = normalized.includes(path.sep) || normalized.includes('/');
if (hasPathSeparators) {
if (!path.isAbsolute(normalized) && !cwd) {
return null;
}
const directPath = resolveCommandCheckPath(normalized, cwd);
return directPath && canAccessExecutable(directPath, platform) ? directPath : null;
}
const pathValue = getEnvValue(env, 'PATH');
if (!pathValue) {
return null;
}
const pathExtValue = getEnvValue(env, 'PATHEXT');
const hasExplicitExtension = Boolean(path.extname(normalized));
const extensions = platform === 'win32'
? (hasExplicitExtension
? ['']
: (pathExtValue || '.COM;.EXE;.BAT;.CMD')
.split(';')
.map(extension => extension.trim())
.filter(Boolean))
: [''];
for (const directory of pathValue.split(path.delimiter)) {
const baseDirectory = asNonEmptyString(directory);
if (!baseDirectory) {
continue;
}
for (const extension of extensions) {
const candidatePath = path.join(baseDirectory, `${normalized}${extension}`);
if (canAccessExecutable(candidatePath, platform)) {
return candidatePath;
}
}
}
return null;
}
function isPathInsideWorkspace(filePath, workspacePath) {
const normalizedFilePath = asNonEmptyString(filePath);
const normalizedWorkspacePath = asNonEmptyString(workspacePath);
if (!normalizedFilePath || !normalizedWorkspacePath) {
return false;
}
const resolvedFilePath = path.resolve(normalizedFilePath);
const resolvedWorkspacePath = path.resolve(normalizedWorkspacePath);
const comparableFilePath = process.platform === 'win32'
? resolvedFilePath.toLowerCase()
: resolvedFilePath;
const comparableWorkspacePath = process.platform === 'win32'
? resolvedWorkspacePath.toLowerCase()
: resolvedWorkspacePath;
const relativePath = path.relative(comparableWorkspacePath, comparableFilePath);
return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
}
function hasCodexBaseUrl(baseUrl) {
const normalized = asNonEmptyString(baseUrl);
if (!normalized) {
return false;
}
return /chatgpt\.com\/backend-api\/codex/i.test(normalized);
}
function hasCodexAlias(model) {
const normalized = asNonEmptyString(model);
if (!normalized) {
return false;
}
const baseModel = normalized.toLowerCase().split('?', 1)[0] || normalized.toLowerCase();
return CODEX_ALIAS_MODELS.has(baseModel);
}
function getOpenAICompatibleLabel(baseUrl, model) {
const normalizedBaseUrl = (asNonEmptyString(baseUrl) || '').toLowerCase();
const normalizedModel = (asNonEmptyString(model) || '').toLowerCase();
if (hasCodexBaseUrl(baseUrl) || (!baseUrl && hasCodexAlias(model))) {
return 'Codex';
}
if (/localhost:11434|127\.0\.0\.1:11434|0\.0\.0\.0:11434/i.test(normalizedBaseUrl)) {
return 'Ollama';
}
if (/localhost:1234|127\.0\.0\.1:1234|0\.0\.0\.0:1234/i.test(normalizedBaseUrl)) {
return 'LM Studio';
}
if (normalizedBaseUrl.includes('deepseek') || normalizedModel.includes('deepseek')) {
return 'DeepSeek';
}
if (normalizedBaseUrl.includes('openrouter')) {
return 'OpenRouter';
}
if (normalizedBaseUrl.includes('together')) {
return 'Together AI';
}
if (normalizedBaseUrl.includes('groq')) {
return 'Groq';
}
if (normalizedBaseUrl.includes('mistral') || normalizedModel.includes('mistral')) {
return 'Mistral';
}
if (normalizedBaseUrl.includes('azure')) {
return 'Azure OpenAI';
}
if (normalizedBaseUrl.includes('api.openai.com') || !normalizedBaseUrl) {
return 'OpenAI';
}
if (isLocalBaseUrl(normalizedBaseUrl)) {
return 'Local OpenAI-compatible';
}
return 'OpenAI-compatible';
}
function buildProviderState(label, detail, source) {
return {
label,
detail,
source,
};
}
function getDetail(env, fallback) {
return (
asNonEmptyString(env.OPENAI_MODEL) ||
asNonEmptyString(env.GEMINI_MODEL) ||
asNonEmptyString(env.OPENAI_BASE_URL) ||
asNonEmptyString(env.GEMINI_BASE_URL) ||
fallback
);
}
function describeOpenAICompatible(env, source) {
const baseUrl = asNonEmptyString(env.OPENAI_BASE_URL) || asNonEmptyString(env.OPENAI_API_BASE);
const model = asNonEmptyString(env.OPENAI_MODEL);
const label = getOpenAICompatibleLabel(baseUrl, model);
if (label === 'Codex') {
return buildProviderState('Codex', model || 'ChatGPT Codex', source);
}
return buildProviderState(label, model || baseUrl || 'OpenAI-compatible runtime', source);
}
function describeSavedProfile(profile) {
switch (profile.profile) {
case 'ollama':
return buildProviderState('Ollama', getDetail(profile.env, 'saved profile'), 'profile');
case 'gemini':
return buildProviderState('Gemini', getDetail(profile.env, 'saved profile'), 'profile');
case 'codex':
return buildProviderState('Codex', getDetail(profile.env, 'saved profile'), 'profile');
case 'atomic-chat':
return buildProviderState('Atomic Chat', getDetail(profile.env, 'saved profile'), 'profile');
case 'openai':
default:
return describeOpenAICompatible(profile.env, 'profile');
}
}
function describeProviderState({ shimEnabled, env, profile }) {
if (profile) {
return describeSavedProfile(profile);
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_GEMINI)) {
return buildProviderState('Gemini', getDetail(env, 'from environment'), 'env');
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_GITHUB)) {
return buildProviderState('GitHub Models', getDetail(env, 'from environment'), 'env');
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_BEDROCK)) {
return buildProviderState('Bedrock', 'from environment', 'env');
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_VERTEX)) {
return buildProviderState('Vertex AI', 'from environment', 'env');
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_FOUNDRY)) {
return buildProviderState('Foundry', 'from environment', 'env');
}
if (isEnvTruthy(env.CLAUDE_CODE_USE_OPENAI)) {
return describeOpenAICompatible(env, 'env');
}
if (shimEnabled) {
return buildProviderState(
'OpenAI-compatible (provider unknown)',
'launch shim enabled',
'shim',
);
}
return buildProviderState(
'Unknown',
'no saved profile or provider env detected',
'unknown',
);
}
module.exports = {
chooseLaunchWorkspace,
describeProviderState,
findCommandPath,
isPathInsideWorkspace,
parseProfileFile,
resolveCommandCheckPath,
};

View File

@@ -0,0 +1,208 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const {
chooseLaunchWorkspace,
describeProviderState,
findCommandPath,
parseProfileFile,
resolveCommandCheckPath,
} = require('./state');
test('chooseLaunchWorkspace prefers the active workspace folder', () => {
assert.deepEqual(
chooseLaunchWorkspace({
activeWorkspacePath: '/repo-b',
workspacePaths: ['/repo-a', '/repo-b'],
}),
{ workspacePath: '/repo-b', source: 'active-workspace' },
);
});
test('chooseLaunchWorkspace falls back to the first workspace folder', () => {
assert.deepEqual(
chooseLaunchWorkspace({
activeWorkspacePath: null,
workspacePaths: ['/repo-a', '/repo-b'],
}),
{ workspacePath: '/repo-a', source: 'first-workspace' },
);
});
test('parseProfileFile returns null for invalid JSON', () => {
assert.equal(parseProfileFile('{bad json}'), null);
});
test('parseProfileFile returns null for unsupported profiles', () => {
assert.equal(
parseProfileFile(
JSON.stringify({
profile: 'lmstudio',
env: {},
createdAt: '2026-04-03T00:00:00.000Z',
}),
),
null,
);
});
test('parseProfileFile returns null when env is missing', () => {
assert.equal(
parseProfileFile(
JSON.stringify({
profile: 'openai',
createdAt: '2026-04-03T00:00:00.000Z',
}),
),
null,
);
});
test('parseProfileFile returns null when env is not an object', () => {
assert.equal(
parseProfileFile(
JSON.stringify({
profile: 'openai',
env: ['OPENAI_MODEL=gpt-4o'],
createdAt: '2026-04-03T00:00:00.000Z',
}),
),
null,
);
});
test('resolveCommandCheckPath resolves workspace-relative executables', () => {
assert.equal(
resolveCommandCheckPath('./node_modules/.bin/openclaude', '/repo'),
require('node:path').resolve('/repo', './node_modules/.bin/openclaude'),
);
});
test('resolveCommandCheckPath leaves bare commands alone', () => {
assert.equal(resolveCommandCheckPath('openclaude', '/repo'), null);
});
test('findCommandPath treats shell-like input as a literal executable name', t => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openclaude-command-'));
t.after(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
const commandName = process.platform === 'win32'
? 'openclaude & whoami'
: 'openclaude && whoami';
const executableName = process.platform === 'win32'
? `${commandName}.cmd`
: commandName;
const executablePath = path.join(tempDir, executableName);
fs.writeFileSync(executablePath, process.platform === 'win32' ? '@echo off\r\n' : '#!/bin/sh\n');
if (process.platform !== 'win32') {
fs.chmodSync(executablePath, 0o755);
}
const resolvedPath = findCommandPath(commandName, {
cwd: null,
env: {
PATH: tempDir,
PATHEXT: '.CMD;.EXE',
},
platform: process.platform,
});
assert.ok(resolvedPath);
assert.equal(resolvedPath.toLowerCase(), executablePath.toLowerCase());
});
test('describeProviderState uses saved profile when present', () => {
assert.deepEqual(
describeProviderState({
shimEnabled: false,
env: {},
profile: {
profile: 'ollama',
env: { OPENAI_MODEL: 'llama3.2' },
createdAt: '2026-04-03T00:00:00.000Z',
},
}),
{
label: 'Ollama',
detail: 'llama3.2',
source: 'profile',
},
);
});
test('describeProviderState reports LM Studio from openai profile base url', () => {
assert.deepEqual(
describeProviderState({
shimEnabled: false,
env: {},
profile: {
profile: 'openai',
env: {
OPENAI_BASE_URL: 'http://localhost:1234/v1',
OPENAI_MODEL: 'qwen2.5-coder',
},
createdAt: '2026-04-03T00:00:00.000Z',
},
}),
{
label: 'LM Studio',
detail: 'qwen2.5-coder',
source: 'profile',
},
);
});
test('describeProviderState reports environment-backed provider details', () => {
assert.deepEqual(
describeProviderState({
shimEnabled: false,
env: {
CLAUDE_CODE_USE_OPENAI: '1',
OPENAI_BASE_URL: 'http://localhost:11434/v1',
OPENAI_MODEL: 'llama3.2:3b',
},
profile: null,
}),
{
label: 'Ollama',
detail: 'llama3.2:3b',
source: 'env',
},
);
});
test('describeProviderState reports unknown when only the shim is enabled', () => {
assert.deepEqual(
describeProviderState({
shimEnabled: true,
env: {},
profile: null,
}),
{
label: 'OpenAI-compatible (provider unknown)',
detail: 'launch shim enabled',
source: 'shim',
},
);
});
test('describeProviderState stays honest when nothing is configured', () => {
assert.deepEqual(
describeProviderState({
shimEnabled: false,
env: {},
profile: null,
}),
{
label: 'Unknown',
detail: 'no saved profile or provider env detected',
source: 'unknown',
},
);
});