@pixelbyte-software/pixcode 1.48.3 → 1.48.4
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/assets/{index-CzFGzjCx.js → index--CoGCNtl.js} +167 -167
- package/dist/assets/index-ipxWr2Zo.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +51 -8
- package/dist-server/server/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/pixcode-workbench-1-48.mjs +8 -2
- package/scripts/smoke/vscode-workbench-polish.mjs +32 -8
- package/server/index.js +59 -6
- package/dist/assets/index-CqqruMJK.css +0 -32
|
@@ -40,9 +40,13 @@ assert.match(workbench, /openEditorTabs|activeEditorPath/, 'Workbench editor sho
|
|
|
40
40
|
assert.doesNotMatch(workbench, /<ChatInterface/, 'Right workbench panel should not render the chat composer.');
|
|
41
41
|
assert.match(workbench, /WorkbenchCliPanel/, 'Right workbench panel should render the CLI terminal panel.');
|
|
42
42
|
assert.match(workbench, /setIsTerminalOpen\(true\)/, 'CLI picker should give way to a full-height terminal after the user starts a provider.');
|
|
43
|
-
assert.match(workbench, /onClose=\{\
|
|
43
|
+
assert.match(workbench, /onClose=\{closeTerminal\}/, 'Closing the workbench terminal should return to the CLI picker.');
|
|
44
44
|
assert.match(workbench, /WorkbenchCliPanelToolbar/, 'CLI terminal should keep history and new-session actions visible.');
|
|
45
|
-
assert.match(workbench, /
|
|
45
|
+
assert.match(workbench, /WORKBENCH_CLI_STATE_STORAGE_KEY/, 'CLI terminal should remember per-project open state across workspace switches.');
|
|
46
|
+
assert.match(workbench, /openNewCliSessionPicker/, 'CLI terminal plus should return to provider selection before starting a fresh session.');
|
|
47
|
+
assert.match(workbench, /terminateCurrentCliSession\(selectedProvider\)/, 'CLI terminal plus should terminate the existing provider PTY before showing selection.');
|
|
48
|
+
assert.match(workbench, /forceNewSession=\{terminalLaunch\.forceNewSession\}/, 'Fresh CLI sessions should bypass the cached default PTY.');
|
|
49
|
+
assert.match(serverIndex, /\/api\/shell\/sessions\/terminate/, 'Backend should expose an authenticated endpoint to terminate cached provider PTYs immediately.');
|
|
46
50
|
assert.doesNotMatch(shellTerminal, /new WebglAddon\(\)/, 'Workbench terminal should use the stable xterm renderer.');
|
|
47
51
|
assert.match(workbench, /setActivityPanel\('explorer'\)/, 'Selecting a project should return the side panel to Explorer.');
|
|
48
52
|
assert.match(gitPanelHeader, /compact/, 'Workbench Source Control should have compact icon-only controls.');
|
|
@@ -64,5 +68,7 @@ assert.doesNotMatch(orchestration, /A2A|a2a/, 'Orchestration page should not pre
|
|
|
64
68
|
assert.match(serverIndex, /app\.use\('\/hermes', createHermesTaskRouter\(\)\)/, 'Internal task router should be mounted behind Hermes.');
|
|
65
69
|
assert.doesNotMatch(serverIndex, /app\.use\('\/a2a'/, 'Server should not expose the old A2A route.');
|
|
66
70
|
assert.match(hermesRoutes, /createHermesRouter/, 'Hermes should have a dedicated orchestration API router.');
|
|
71
|
+
assert.match(serverIndex, /forceNewSession/, 'Shell backend should support explicit fresh-session launches from the workbench.');
|
|
72
|
+
assert.match(serverIndex, /killProviderPtySessions/, 'Shell backend should terminate old provider PTYs when a fresh CLI session is requested.');
|
|
67
73
|
|
|
68
74
|
console.log('pixcode workbench 1.48 smoke passed');
|
|
@@ -98,8 +98,14 @@ for (const token of ['closeOtherWorkspaces', 'closeAllWorkspaces']) {
|
|
|
98
98
|
|
|
99
99
|
assert.match(
|
|
100
100
|
workspaceTabsSource,
|
|
101
|
-
/
|
|
102
|
-
'Workspace add button should
|
|
101
|
+
/items-center justify-center/,
|
|
102
|
+
'Workspace add button should center its plus icon instead of rendering an off-center bare icon.',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
assert.match(
|
|
106
|
+
workspaceTabsSource,
|
|
107
|
+
/border-r border-border/,
|
|
108
|
+
'Workspace add button should read as part of the tab strip instead of a floating bare button.',
|
|
103
109
|
);
|
|
104
110
|
|
|
105
111
|
assert.match(
|
|
@@ -156,8 +162,8 @@ assert.match(
|
|
|
156
162
|
|
|
157
163
|
assert.match(
|
|
158
164
|
workbench,
|
|
159
|
-
/onClose=\{\
|
|
160
|
-
'Right CLI terminal close button should return to the picker
|
|
165
|
+
/onClose=\{closeTerminal\}/,
|
|
166
|
+
'Right CLI terminal close button should return to the picker through the persisted close flow.',
|
|
161
167
|
);
|
|
162
168
|
|
|
163
169
|
assert.match(
|
|
@@ -174,8 +180,26 @@ assert.match(
|
|
|
174
180
|
|
|
175
181
|
assert.match(
|
|
176
182
|
workbench,
|
|
177
|
-
/
|
|
178
|
-
'Right CLI panel should
|
|
183
|
+
/WORKBENCH_CLI_STATE_STORAGE_KEY/,
|
|
184
|
+
'Right CLI panel should persist per-project terminal state when switching workspaces.',
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
assert.match(
|
|
188
|
+
workbench,
|
|
189
|
+
/openNewCliSessionPicker/,
|
|
190
|
+
'Right CLI panel toolbar plus should stop the current terminal view and return to CLI selection.',
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
assert.match(
|
|
194
|
+
workbench,
|
|
195
|
+
/terminateCurrentCliSession\(selectedProvider\)/,
|
|
196
|
+
'Right CLI panel toolbar plus should explicitly terminate the current provider PTY before showing the picker.',
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
assert.match(
|
|
200
|
+
workbench,
|
|
201
|
+
/forceNewSession=\{terminalLaunch\.forceNewSession\}/,
|
|
202
|
+
'Right CLI panel should mark explicitly started new sessions so the backend does not reconnect the old PTY.',
|
|
179
203
|
);
|
|
180
204
|
|
|
181
205
|
assert.match(
|
|
@@ -186,8 +210,8 @@ assert.match(
|
|
|
186
210
|
|
|
187
211
|
assert.match(
|
|
188
212
|
workbench,
|
|
189
|
-
/onNewSession=\{
|
|
190
|
-
'Right CLI terminal toolbar should wire the plus button to
|
|
213
|
+
/onNewSession=\{openNewCliSessionPicker\}/,
|
|
214
|
+
'Right CLI terminal toolbar should wire the plus button to the new-session picker flow.',
|
|
191
215
|
);
|
|
192
216
|
|
|
193
217
|
assert.doesNotMatch(
|
package/server/index.js
CHANGED
|
@@ -285,8 +285,44 @@ const server = http.createServer(app);
|
|
|
285
285
|
const ptySessionsMap = new Map();
|
|
286
286
|
const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
287
287
|
const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
|
|
288
|
+
const SHELL_CLI_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
|
|
288
289
|
import { stripAnsiSequences, normalizeDetectedUrl, extractUrlsFromText, shouldAutoOpenUrlFromOutput } from './utils/url-detection.js';
|
|
289
290
|
|
|
291
|
+
function terminatePtySession(sessionKey, session, reason) {
|
|
292
|
+
if (!session) return false;
|
|
293
|
+
|
|
294
|
+
console.log(`🧹 Terminating PTY session (${reason}):`, sessionKey);
|
|
295
|
+
if (session.timeoutId) {
|
|
296
|
+
clearTimeout(session.timeoutId);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
if (session.pty && session.pty.kill) {
|
|
301
|
+
session.pty.kill();
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.warn('Failed to kill PTY session:', error.message);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
ptySessionsMap.delete(sessionKey);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function killProviderPtySessions(projectPath, provider) {
|
|
312
|
+
let killed = 0;
|
|
313
|
+
for (const [sessionKey, session] of ptySessionsMap.entries()) {
|
|
314
|
+
if (
|
|
315
|
+
session?.projectPath === projectPath &&
|
|
316
|
+
session?.provider === provider &&
|
|
317
|
+
!session?.isPlainShell
|
|
318
|
+
) {
|
|
319
|
+
killed += terminatePtySession(sessionKey, session, 'fresh provider session') ? 1 : 0;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return killed;
|
|
324
|
+
}
|
|
325
|
+
|
|
290
326
|
// Single WebSocket server that handles both paths
|
|
291
327
|
const wss = new WebSocketServer({
|
|
292
328
|
server,
|
|
@@ -358,6 +394,18 @@ app.get('/health', (req, res) => {
|
|
|
358
394
|
// Optional API key validation (if configured)
|
|
359
395
|
app.use('/api', validateApiKey);
|
|
360
396
|
|
|
397
|
+
app.post('/api/shell/sessions/terminate', authenticateToken, (req, res) => {
|
|
398
|
+
const provider = req.body?.provider || 'claude';
|
|
399
|
+
const projectPath = req.body?.projectPath || os.homedir();
|
|
400
|
+
|
|
401
|
+
if (!SHELL_CLI_PROVIDERS.has(provider)) {
|
|
402
|
+
return res.status(400).json({ error: 'Unsupported provider' });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const killedSessions = killProviderPtySessions(projectPath, provider);
|
|
406
|
+
res.json({ success: true, killedSessions });
|
|
407
|
+
});
|
|
408
|
+
|
|
361
409
|
// Authentication routes (public)
|
|
362
410
|
app.use('/api/auth', authRoutes);
|
|
363
411
|
|
|
@@ -2044,6 +2092,7 @@ function handleShellConnection(ws) {
|
|
|
2044
2092
|
const provider = data.provider || 'claude';
|
|
2045
2093
|
const initialCommand = data.initialCommand;
|
|
2046
2094
|
const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell';
|
|
2095
|
+
const forceNewSession = Boolean(data.forceNewSession && !isPlainShell);
|
|
2047
2096
|
urlDetectionBuffer = '';
|
|
2048
2097
|
announcedAuthUrls.clear();
|
|
2049
2098
|
|
|
@@ -2077,14 +2126,16 @@ function handleShellConnection(ws) {
|
|
|
2077
2126
|
if (isLoginCommand) {
|
|
2078
2127
|
const oldSession = ptySessionsMap.get(ptySessionKey);
|
|
2079
2128
|
if (oldSession) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2129
|
+
terminatePtySession(ptySessionKey, oldSession, 'fresh login');
|
|
2130
|
+
}
|
|
2131
|
+
} else if (forceNewSession) {
|
|
2132
|
+
const killedSessions = killProviderPtySessions(projectPath, provider);
|
|
2133
|
+
if (killedSessions > 0) {
|
|
2134
|
+
console.log(`🧹 Fresh ${provider} session requested; terminated ${killedSessions} cached PTY session(s).`);
|
|
2084
2135
|
}
|
|
2085
2136
|
}
|
|
2086
2137
|
|
|
2087
|
-
const existingSession = isLoginCommand ? null : ptySessionsMap.get(ptySessionKey);
|
|
2138
|
+
const existingSession = (isLoginCommand || forceNewSession) ? null : ptySessionsMap.get(ptySessionKey);
|
|
2088
2139
|
if (existingSession) {
|
|
2089
2140
|
console.log('♻️ Reconnecting to existing PTY session:', ptySessionKey);
|
|
2090
2141
|
shellProcess = existingSession.pty;
|
|
@@ -2292,7 +2343,9 @@ function handleShellConnection(ws) {
|
|
|
2292
2343
|
buffer: [],
|
|
2293
2344
|
timeoutId: null,
|
|
2294
2345
|
projectPath,
|
|
2295
|
-
sessionId
|
|
2346
|
+
sessionId,
|
|
2347
|
+
provider,
|
|
2348
|
+
isPlainShell
|
|
2296
2349
|
});
|
|
2297
2350
|
|
|
2298
2351
|
// Handle data output
|