@pixelbyte-software/pixcode 1.49.9 → 1.49.10
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-BT6txdBK.css +32 -0
- package/dist/assets/{index-DzGkH0cd.js → index-DVpGrdpT.js} +94 -94
- package/dist/index.html +2 -2
- package/dist-server/server/services/hermes-gateway.js +87 -3
- package/dist-server/server/services/hermes-gateway.js.map +1 -1
- package/dist-server/server/services/hermes-install-jobs.js +51 -5
- package/dist-server/server/services/hermes-install-jobs.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/hermes-api-install.mjs +2 -0
- package/scripts/smoke/hermes-rest-chat-api.mjs +24 -2
- package/scripts/smoke/hermes-rest-gateway.mjs +4 -1
- package/scripts/smoke/hermes-smoke-launcher-guard.mjs +34 -0
- package/scripts/smoke/pixcode-workbench-1-48.mjs +3 -0
- package/server/services/hermes-gateway.js +91 -3
- package/server/services/hermes-install-jobs.js +48 -5
- package/dist/assets/index-Bw6PxVkB.css +0 -32
|
@@ -173,6 +173,29 @@ function extractRunOutput(body) {
|
|
|
173
173
|
return null;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
function extractResponsesOutput(body) {
|
|
177
|
+
if (!body || typeof body !== 'object') return null;
|
|
178
|
+
|
|
179
|
+
const output = Array.isArray(body.output) ? body.output : [];
|
|
180
|
+
for (const item of output) {
|
|
181
|
+
if (!item || typeof item !== 'object') continue;
|
|
182
|
+
if (item.type === 'message' || item.role === 'assistant') {
|
|
183
|
+
const text = extractTextFromValue(item.content);
|
|
184
|
+
if (text) return text;
|
|
185
|
+
}
|
|
186
|
+
const text = extractTextFromValue(item.output_text)
|
|
187
|
+
|| extractTextFromValue(item.text)
|
|
188
|
+
|| extractTextFromValue(item.message)
|
|
189
|
+
|| extractTextFromValue(item.output);
|
|
190
|
+
if (text) return text;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return extractTextFromValue(body.output_text)
|
|
194
|
+
|| extractTextFromValue(body.message)
|
|
195
|
+
|| extractTextFromValue(body.response)
|
|
196
|
+
|| null;
|
|
197
|
+
}
|
|
198
|
+
|
|
176
199
|
function extractChatCompletionOutput(body) {
|
|
177
200
|
if (!body || typeof body !== 'object') return null;
|
|
178
201
|
const choices = Array.isArray(body.choices) ? body.choices : [];
|
|
@@ -244,6 +267,21 @@ function makeChatCompletionRequest(options) {
|
|
|
244
267
|
};
|
|
245
268
|
}
|
|
246
269
|
|
|
270
|
+
function makeResponsesRequest(options) {
|
|
271
|
+
const input = String(options.input || '').trim();
|
|
272
|
+
return {
|
|
273
|
+
model: options.model || 'hermes-agent',
|
|
274
|
+
input,
|
|
275
|
+
instructions: options.instructions || [
|
|
276
|
+
'You are Hermes Agent running inside Pixcode.',
|
|
277
|
+
'Use Pixcode MCP tools when they help inspect projects, launch CLIs, or perform workspace actions.',
|
|
278
|
+
'Keep answers concise and include concrete next steps when work is blocked.',
|
|
279
|
+
].join(' '),
|
|
280
|
+
conversation: options.sessionId || undefined,
|
|
281
|
+
store: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
247
285
|
async function waitForGatewayReady(gateway) {
|
|
248
286
|
const started = Date.now();
|
|
249
287
|
let lastError = null;
|
|
@@ -377,7 +415,10 @@ export async function ensureHermesGateway(options = {}) {
|
|
|
377
415
|
apiServerKey,
|
|
378
416
|
appRoot,
|
|
379
417
|
});
|
|
380
|
-
const installStatus = readHermesInstallStatus(env
|
|
418
|
+
const installStatus = readHermesInstallStatus(env, {
|
|
419
|
+
allowSmokeHermes: options.allowSmokeHermes === true,
|
|
420
|
+
repairLaunchers: options.repairLaunchers !== false,
|
|
421
|
+
});
|
|
381
422
|
if (!installStatus.installed || !installStatus.command) {
|
|
382
423
|
throw new Error(installStatus.error || 'Hermes Agent CLI is not installed.');
|
|
383
424
|
}
|
|
@@ -403,14 +444,15 @@ export async function ensureHermesGateway(options = {}) {
|
|
|
403
444
|
|
|
404
445
|
await configurePixcodeMcp({ appRoot, env, gateway });
|
|
405
446
|
|
|
406
|
-
const
|
|
447
|
+
const gatewayArgs = options.gatewayArgs || ['gateway', 'run', '--replace'];
|
|
448
|
+
const child = spawn(installStatus.command, gatewayArgs, {
|
|
407
449
|
cwd: projectPath,
|
|
408
450
|
env,
|
|
409
451
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
410
452
|
windowsHide: true,
|
|
411
453
|
});
|
|
412
454
|
gateway.child = child;
|
|
413
|
-
appendGatewayLog(gateway, 'meta', `$ ${installStatus.command}
|
|
455
|
+
appendGatewayLog(gateway, 'meta', `$ ${installStatus.command} ${gatewayArgs.join(' ')}\n`);
|
|
414
456
|
|
|
415
457
|
child.stdout?.on('data', (buf) => appendGatewayLog(gateway, 'stdout', buf.toString()));
|
|
416
458
|
child.stderr?.on('data', (buf) => appendGatewayLog(gateway, 'stderr', buf.toString()));
|
|
@@ -516,6 +558,46 @@ export async function runHermesGatewayPrompt(projectPath, options = {}) {
|
|
|
516
558
|
throw new Error('Hermes prompt is required.');
|
|
517
559
|
}
|
|
518
560
|
|
|
561
|
+
const responsesRequest = makeResponsesRequest({ ...options, input });
|
|
562
|
+
const responseRun = await callGateway(gateway, '/v1/responses', {
|
|
563
|
+
method: 'POST',
|
|
564
|
+
body: JSON.stringify(responsesRequest),
|
|
565
|
+
timeoutMs: options.responsesTimeoutMs || options.timeoutMs || RUN_TIMEOUT_MS,
|
|
566
|
+
}).catch((error) => {
|
|
567
|
+
if (!isGatewayRunning(gateway)) {
|
|
568
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
569
|
+
}
|
|
570
|
+
throw error;
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
if (!isGatewayRunning(gateway)) {
|
|
574
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (responseRun.ok) {
|
|
578
|
+
const status = extractRunStatus(responseRun.body) || 'completed';
|
|
579
|
+
const message = extractResponsesOutput(responseRun.body);
|
|
580
|
+
return {
|
|
581
|
+
ok: status === 'completed' || status === 'succeeded',
|
|
582
|
+
projectPath: gateway.projectPath,
|
|
583
|
+
baseUrl: gateway.baseUrl,
|
|
584
|
+
sessionId: options.sessionId || responsesRequest.conversation || null,
|
|
585
|
+
runId: null,
|
|
586
|
+
responseId: responseRun.body?.id || null,
|
|
587
|
+
status,
|
|
588
|
+
message,
|
|
589
|
+
error: (status === 'completed' || status === 'succeeded') ? null : extractTextFromValue(responseRun.body?.error) || message || 'Hermes response failed.',
|
|
590
|
+
raw: responseRun.body,
|
|
591
|
+
transport: 'responses',
|
|
592
|
+
endpoint: '/v1/responses',
|
|
593
|
+
httpStatus: responseRun.status,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (responseRun.status && responseRun.status !== 404 && responseRun.status !== 405) {
|
|
598
|
+
throw new Error(`Hermes /v1/responses failed with HTTP ${responseRun.status}: ${JSON.stringify(responseRun.body)}`);
|
|
599
|
+
}
|
|
600
|
+
|
|
519
601
|
const chatRequest = makeChatCompletionRequest({ ...options, input });
|
|
520
602
|
const chat = await callGateway(gateway, '/v1/chat/completions', {
|
|
521
603
|
method: 'POST',
|
|
@@ -544,6 +626,8 @@ export async function runHermesGatewayPrompt(projectPath, options = {}) {
|
|
|
544
626
|
message,
|
|
545
627
|
raw: chat.body,
|
|
546
628
|
transport: 'chat.completions',
|
|
629
|
+
endpoint: '/v1/chat/completions',
|
|
630
|
+
httpStatus: chat.status,
|
|
547
631
|
};
|
|
548
632
|
}
|
|
549
633
|
|
|
@@ -584,6 +668,8 @@ export async function runHermesGatewayPrompt(projectPath, options = {}) {
|
|
|
584
668
|
message: extractRunOutput(create.body),
|
|
585
669
|
raw: create.body,
|
|
586
670
|
transport: 'runs',
|
|
671
|
+
endpoint: '/v1/runs',
|
|
672
|
+
httpStatus: create.status,
|
|
587
673
|
};
|
|
588
674
|
}
|
|
589
675
|
|
|
@@ -623,6 +709,8 @@ export async function runHermesGatewayPrompt(projectPath, options = {}) {
|
|
|
623
709
|
error: status === 'completed' ? null : extractTextFromValue(latest?.error) || message || 'Hermes run failed.',
|
|
624
710
|
raw: latest,
|
|
625
711
|
transport: 'runs',
|
|
712
|
+
endpoint: '/v1/runs',
|
|
713
|
+
httpStatus: create.status,
|
|
626
714
|
};
|
|
627
715
|
}
|
|
628
716
|
|
|
@@ -144,8 +144,40 @@ function runHermesVersion(candidate, env) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
return
|
|
147
|
+
function isHermesSmokeCommandOutput(output) {
|
|
148
|
+
return /Hermes Agent v0\.0\.0\s+smoke/i.test(String(output || ''))
|
|
149
|
+
|| /pixcode-hermes-(?:chat-api|smoke)/i.test(String(output || ''));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isExplicitHermesCliPath(candidate, env = process.env) {
|
|
153
|
+
if (!candidate || !env.HERMES_CLI_PATH || !path.isAbsolute(candidate)) return false;
|
|
154
|
+
try {
|
|
155
|
+
return path.resolve(candidate) === path.resolve(env.HERMES_CLI_PATH);
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isTemporaryHermesLauncher(candidate) {
|
|
162
|
+
if (!candidate || !path.isAbsolute(candidate)) return false;
|
|
163
|
+
const normalized = path.resolve(candidate);
|
|
164
|
+
const tempRoot = path.resolve(os.tmpdir());
|
|
165
|
+
return normalized === tempRoot || normalized.startsWith(`${tempRoot}${path.sep}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function shouldRepairHermesLauncher(command, env = process.env, options = {}) {
|
|
169
|
+
if (options.repairLaunchers === false) return false;
|
|
170
|
+
if (!command || command === 'hermes') return false;
|
|
171
|
+
if (isExplicitHermesCliPath(command, env)) return false;
|
|
172
|
+
if (isTemporaryHermesLauncher(command)) return false;
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function isUsableHermesCommand(candidate, env = process.env, options = {}) {
|
|
177
|
+
const result = runHermesVersion(candidate, buildHermesEnv(env));
|
|
178
|
+
if (!result.ok) return false;
|
|
179
|
+
if (!options.allowSmokeHermes && isHermesSmokeCommandOutput(result.output)) return false;
|
|
180
|
+
return true;
|
|
149
181
|
}
|
|
150
182
|
|
|
151
183
|
function isHermesPythonLauncher(candidate) {
|
|
@@ -313,8 +345,9 @@ export function hermesCommandCandidates(env = process.env) {
|
|
|
313
345
|
return [...new Set(candidates.filter(Boolean))];
|
|
314
346
|
}
|
|
315
347
|
|
|
316
|
-
export function readHermesInstallStatus(env = process.env) {
|
|
348
|
+
export function readHermesInstallStatus(env = process.env, options = {}) {
|
|
317
349
|
const hermesEnv = buildHermesEnv(env);
|
|
350
|
+
const rejected = [];
|
|
318
351
|
|
|
319
352
|
for (const candidate of hermesCommandCandidates(hermesEnv)) {
|
|
320
353
|
const isBareCommand = candidate === 'hermes';
|
|
@@ -324,8 +357,16 @@ export function readHermesInstallStatus(env = process.env) {
|
|
|
324
357
|
|
|
325
358
|
const result = runHermesVersion(candidate, hermesEnv);
|
|
326
359
|
if (result.ok) {
|
|
327
|
-
repairHermesCommandLaunchers(candidate, hermesEnv);
|
|
328
360
|
const version = formatHermesVersionOutput(result.output);
|
|
361
|
+
if (!options.allowSmokeHermes && isHermesSmokeCommandOutput(result.output)) {
|
|
362
|
+
rejected.push(`${candidate} (${version || 'smoke-test Hermes launcher'})`);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (shouldRepairHermesLauncher(candidate, hermesEnv, options)) {
|
|
367
|
+
repairHermesCommandLaunchers(candidate, hermesEnv);
|
|
368
|
+
}
|
|
369
|
+
|
|
329
370
|
return {
|
|
330
371
|
installed: true,
|
|
331
372
|
command: candidate,
|
|
@@ -339,7 +380,9 @@ export function readHermesInstallStatus(env = process.env) {
|
|
|
339
380
|
installed: false,
|
|
340
381
|
command: null,
|
|
341
382
|
version: null,
|
|
342
|
-
error:
|
|
383
|
+
error: rejected.length > 0
|
|
384
|
+
? `Only smoke-test Hermes launchers were found and rejected: ${rejected.join(', ')}. Install or repair Hermes Agent.`
|
|
385
|
+
: 'Hermes Agent CLI is not installed or is not on PATH.',
|
|
343
386
|
};
|
|
344
387
|
}
|
|
345
388
|
|