@pixelbyte-software/pixcode 1.48.5 → 1.49.0
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-QNWLsise.js → index-58IIiyST.js} +163 -163
- package/dist/assets/index-BpUexHb8.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +80 -6
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +39 -0
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/hermes/configure-pixcode-mcp.mjs +87 -0
- package/scripts/hermes/pixcode-mcp-server.mjs +216 -0
- package/scripts/smoke/git-install-update.mjs +133 -54
- package/scripts/smoke/pixcode-workbench-1-48.mjs +15 -1
- package/scripts/smoke/vscode-workbench-polish.mjs +21 -2
- package/scripts/update-git-install.mjs +162 -4
- package/server/index.js +88 -6
- package/server/modules/orchestration/hermes/hermes.routes.ts +55 -0
- package/dist/assets/index-B3lN7dBd.css +0 -32
|
@@ -27,6 +27,10 @@ const SERVER_VERSION = (() => {
|
|
|
27
27
|
return '0.0.0';
|
|
28
28
|
}
|
|
29
29
|
})();
|
|
30
|
+
const HERMES_SHELL_COMMANDS = new Set([
|
|
31
|
+
'pixcode:hermes:start',
|
|
32
|
+
'pixcode:hermes:install',
|
|
33
|
+
]);
|
|
30
34
|
const DAEMON_COMMAND_CONTEXT = {
|
|
31
35
|
appRoot: APP_ROOT,
|
|
32
36
|
cliEntry: path.join(APP_ROOT, 'server', 'cli.js'),
|
|
@@ -74,7 +78,7 @@ import { ensurePortOpen } from './utils/port-access.js';
|
|
|
74
78
|
import { applyAllStoredCredentialsToEnv, } from './services/provider-credentials.js';
|
|
75
79
|
import { primeCliBinPath } from './services/install-jobs.js';
|
|
76
80
|
import { startEnabledPluginServers, stopAllPlugins, getPluginPort } from './utils/plugin-process-manager.js';
|
|
77
|
-
import { initializeDatabase, sessionNamesDb, applyCustomSessionNames } from './database/db.js';
|
|
81
|
+
import { initializeDatabase, sessionNamesDb, applyCustomSessionNames, apiKeysDb } from './database/db.js';
|
|
78
82
|
import { setNotificationWebSocketServer } from './services/notification-orchestrator.js';
|
|
79
83
|
import { configureWebPush } from './services/vapid-keys.js';
|
|
80
84
|
import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js';
|
|
@@ -263,6 +267,63 @@ function killProviderPtySessions(projectPath, provider) {
|
|
|
263
267
|
}
|
|
264
268
|
return killed;
|
|
265
269
|
}
|
|
270
|
+
function shellQuotePosix(value) {
|
|
271
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
272
|
+
}
|
|
273
|
+
function shellQuotePowerShell(value) {
|
|
274
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
275
|
+
}
|
|
276
|
+
function resolvePublicBaseUrl(request) {
|
|
277
|
+
const headers = request?.headers || {};
|
|
278
|
+
const forwardedProto = String(headers['x-forwarded-proto'] || '').split(',')[0].trim();
|
|
279
|
+
const proto = forwardedProto || (request?.socket?.encrypted ? 'https' : 'http');
|
|
280
|
+
const host = headers['x-forwarded-host'] || headers.host || `127.0.0.1:${process.env.SERVER_PORT || process.env.PORT || '3001'}`;
|
|
281
|
+
return `${proto}://${String(host).split(',')[0].trim()}`;
|
|
282
|
+
}
|
|
283
|
+
function getOrCreateHermesApiKey(userId) {
|
|
284
|
+
if (!userId)
|
|
285
|
+
return null;
|
|
286
|
+
const existing = apiKeysDb
|
|
287
|
+
.getApiKeys(userId)
|
|
288
|
+
.find((key) => key.key_name === 'Hermes Agent MCP' && key.is_active);
|
|
289
|
+
if (existing?.api_key) {
|
|
290
|
+
return existing.api_key;
|
|
291
|
+
}
|
|
292
|
+
return apiKeysDb.createApiKey(userId, 'Hermes Agent MCP', [
|
|
293
|
+
'hermes:mcp',
|
|
294
|
+
'projects:read',
|
|
295
|
+
'providers:read',
|
|
296
|
+
'terminal:launch',
|
|
297
|
+
]).apiKey;
|
|
298
|
+
}
|
|
299
|
+
function buildHermesShellCommand(kind, env) {
|
|
300
|
+
const configureScript = path.join(APP_ROOT, 'scripts', 'hermes', 'configure-pixcode-mcp.mjs');
|
|
301
|
+
const isWindows = os.platform() === 'win32';
|
|
302
|
+
const quote = isWindows ? shellQuotePowerShell : shellQuotePosix;
|
|
303
|
+
const configure = `node ${quote(configureScript)}`;
|
|
304
|
+
if (isWindows) {
|
|
305
|
+
const setEnv = [
|
|
306
|
+
`$env:PIXCODE_BASE_URL=${quote(env.PIXCODE_BASE_URL)}`,
|
|
307
|
+
`$env:PIXCODE_API_KEY=${quote(env.PIXCODE_API_KEY)}`,
|
|
308
|
+
`$env:PIXCODE_APP_ROOT=${quote(env.PIXCODE_APP_ROOT)}`,
|
|
309
|
+
].join('; ');
|
|
310
|
+
const install = '& ([scriptblock]::Create((irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1))) -SkipSetup -Branch main';
|
|
311
|
+
if (kind === 'pixcode:hermes:install') {
|
|
312
|
+
return `${setEnv}; ${install}; ${configure}`;
|
|
313
|
+
}
|
|
314
|
+
return `${setEnv}; ${configure}; if (-not (Get-Command hermes -ErrorAction SilentlyContinue)) { ${install} }; hermes chat --toolsets "hermes-cli,mcp-pixcode"`;
|
|
315
|
+
}
|
|
316
|
+
const setEnv = [
|
|
317
|
+
`PIXCODE_BASE_URL=${quote(env.PIXCODE_BASE_URL)}`,
|
|
318
|
+
`PIXCODE_API_KEY=${quote(env.PIXCODE_API_KEY)}`,
|
|
319
|
+
`PIXCODE_APP_ROOT=${quote(env.PIXCODE_APP_ROOT)}`,
|
|
320
|
+
].join(' ');
|
|
321
|
+
const install = 'curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash';
|
|
322
|
+
if (kind === 'pixcode:hermes:install') {
|
|
323
|
+
return `${setEnv} sh -lc ${quote(`${install} && node ${shellQuotePosix(configureScript)}`)}`;
|
|
324
|
+
}
|
|
325
|
+
return `${setEnv} sh -lc ${quote(`node ${shellQuotePosix(configureScript)} && if ! command -v hermes >/dev/null 2>&1; then ${install}; fi; hermes chat --toolsets "hermes-cli,mcp-pixcode"`)}`;
|
|
326
|
+
}
|
|
266
327
|
// Single WebSocket server that handles both paths
|
|
267
328
|
const wss = new WebSocketServer({
|
|
268
329
|
server,
|
|
@@ -1690,7 +1751,7 @@ wss.on('connection', (ws, request) => {
|
|
|
1690
1751
|
const urlObj = new URL(url, 'http://localhost');
|
|
1691
1752
|
const pathname = urlObj.pathname;
|
|
1692
1753
|
if (pathname === '/shell') {
|
|
1693
|
-
handleShellConnection(ws);
|
|
1754
|
+
handleShellConnection(ws, request);
|
|
1694
1755
|
}
|
|
1695
1756
|
else if (pathname === '/ws') {
|
|
1696
1757
|
handleChatConnection(ws, request);
|
|
@@ -1905,7 +1966,7 @@ function handleChatConnection(ws, request) {
|
|
|
1905
1966
|
});
|
|
1906
1967
|
}
|
|
1907
1968
|
// Handle shell WebSocket connections
|
|
1908
|
-
function handleShellConnection(ws) {
|
|
1969
|
+
function handleShellConnection(ws, request) {
|
|
1909
1970
|
console.log('🐚 Shell client connected');
|
|
1910
1971
|
let shellProcess = null;
|
|
1911
1972
|
let ptySessionKey = null;
|
|
@@ -1933,7 +1994,20 @@ function handleShellConnection(ws) {
|
|
|
1933
1994
|
const sessionId = data.sessionId;
|
|
1934
1995
|
const hasSession = data.hasSession;
|
|
1935
1996
|
const provider = data.provider || 'claude';
|
|
1936
|
-
|
|
1997
|
+
let initialCommand = data.initialCommand;
|
|
1998
|
+
const hermesCommand = HERMES_SHELL_COMMANDS.has(initialCommand) ? initialCommand : null;
|
|
1999
|
+
if (hermesCommand) {
|
|
2000
|
+
const apiKey = getOrCreateHermesApiKey(request?.user?.id);
|
|
2001
|
+
if (!apiKey) {
|
|
2002
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Hermes MCP could not create a Pixcode API key for this user.' }));
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
initialCommand = buildHermesShellCommand(hermesCommand, {
|
|
2006
|
+
PIXCODE_BASE_URL: resolvePublicBaseUrl(request),
|
|
2007
|
+
PIXCODE_API_KEY: apiKey,
|
|
2008
|
+
PIXCODE_APP_ROOT: APP_ROOT,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
1937
2011
|
const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell';
|
|
1938
2012
|
const forceNewSession = Boolean(data.forceNewSession);
|
|
1939
2013
|
urlDetectionBuffer = '';
|
|
@@ -2006,7 +2080,7 @@ function handleShellConnection(ws) {
|
|
|
2006
2080
|
console.log('📋 Session info:', hasSession ? `Resume session ${sessionId}` : (isPlainShell ? 'Plain shell mode' : 'New session'));
|
|
2007
2081
|
console.log('🤖 Provider:', isPlainShell ? 'plain-shell' : provider);
|
|
2008
2082
|
if (initialCommand) {
|
|
2009
|
-
console.log('⚡ Initial command:', initialCommand);
|
|
2083
|
+
console.log('⚡ Initial command:', hermesCommand ? hermesCommand : initialCommand);
|
|
2010
2084
|
}
|
|
2011
2085
|
// First send a welcome message
|
|
2012
2086
|
let welcomeMsg;
|
|
@@ -2161,7 +2235,7 @@ function handleShellConnection(ws) {
|
|
|
2161
2235
|
shellCommand = command;
|
|
2162
2236
|
}
|
|
2163
2237
|
}
|
|
2164
|
-
console.log('🔧 Executing shell command:', shellCommand || 'interactive shell');
|
|
2238
|
+
console.log('🔧 Executing shell command:', hermesCommand ? hermesCommand : (shellCommand || 'interactive shell'));
|
|
2165
2239
|
// Use appropriate shell based on platform
|
|
2166
2240
|
const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash';
|
|
2167
2241
|
const shellArgs = isPlainShell && !initialCommand
|