@pixelbyte-software/pixcode 1.49.7 → 1.49.9
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-Bw6PxVkB.css +32 -0
- package/dist/assets/{index-BqgTbW4j.js → index-DzGkH0cd.js} +138 -138
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +5 -124
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +128 -7
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -1
- package/dist-server/server/services/hermes-gateway.js +226 -1
- package/dist-server/server/services/hermes-gateway.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/hermes-api-install.mjs +5 -13
- package/scripts/smoke/hermes-rest-chat-api.mjs +109 -0
- package/scripts/smoke/hermes-rest-chat-live.mjs +41 -0
- package/scripts/smoke/hermes-rest-gateway.mjs +5 -0
- package/scripts/smoke/pixcode-workbench-1-48.mjs +15 -9
- package/scripts/smoke/vscode-workbench-polish.mjs +8 -2
- package/server/index.js +5 -129
- package/server/modules/orchestration/hermes/hermes.routes.ts +128 -6
- package/server/services/hermes-gateway.js +245 -1
- package/dist/assets/index-DjKDBqln.css +0 -32
|
@@ -15,6 +15,8 @@ const DEFAULT_PORT = 8642;
|
|
|
15
15
|
const PORT_SCAN_LIMIT = 80;
|
|
16
16
|
const STARTUP_TIMEOUT_MS = 30000;
|
|
17
17
|
const FETCH_TIMEOUT_MS = 5000;
|
|
18
|
+
const RUN_TIMEOUT_MS = 120000;
|
|
19
|
+
const RUN_POLL_INTERVAL_MS = 1000;
|
|
18
20
|
const LOG_LIMIT = 800;
|
|
19
21
|
|
|
20
22
|
const gateways = new Map();
|
|
@@ -47,6 +49,10 @@ function makeApiServerKey() {
|
|
|
47
49
|
return `pixcode-hermes-${randomBytes(24).toString('hex')}`;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function sleep(ms) {
|
|
53
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
export function buildHermesGatewayEnv(baseEnv = process.env, options = {}) {
|
|
51
57
|
const host = options.host || DEFAULT_HOST;
|
|
52
58
|
const port = String(options.port || DEFAULT_PORT);
|
|
@@ -124,13 +130,127 @@ async function callGateway(gateway, endpoint, options = {}) {
|
|
|
124
130
|
});
|
|
125
131
|
}
|
|
126
132
|
|
|
133
|
+
function extractRunId(body) {
|
|
134
|
+
if (!body || typeof body !== 'object') return null;
|
|
135
|
+
return body.run_id || body.runId || body.id || body.run?.id || null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractRunStatus(body) {
|
|
139
|
+
if (!body || typeof body !== 'object') return null;
|
|
140
|
+
return body.status || body.state || body.run?.status || body.run?.state || null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function extractTextFromValue(value) {
|
|
144
|
+
if (typeof value === 'string') return value;
|
|
145
|
+
if (!value) return null;
|
|
146
|
+
|
|
147
|
+
if (Array.isArray(value)) {
|
|
148
|
+
return value
|
|
149
|
+
.map(extractTextFromValue)
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.join('\n')
|
|
152
|
+
.trim() || null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (typeof value === 'object') {
|
|
156
|
+
for (const key of ['text', 'content', 'message', 'output', 'response', 'result', 'final']) {
|
|
157
|
+
const text = extractTextFromValue(value[key]);
|
|
158
|
+
if (text) return text;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function extractRunOutput(body) {
|
|
166
|
+
if (!body || typeof body !== 'object') return null;
|
|
167
|
+
|
|
168
|
+
for (const key of ['output_text', 'output', 'response', 'result', 'message', 'messages', 'events', 'final']) {
|
|
169
|
+
const text = extractTextFromValue(body[key]);
|
|
170
|
+
if (text) return text;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function extractChatCompletionOutput(body) {
|
|
177
|
+
if (!body || typeof body !== 'object') return null;
|
|
178
|
+
const choices = Array.isArray(body.choices) ? body.choices : [];
|
|
179
|
+
for (const choice of choices) {
|
|
180
|
+
const text = extractTextFromValue(choice?.message?.content)
|
|
181
|
+
|| extractTextFromValue(choice?.delta?.content)
|
|
182
|
+
|| extractTextFromValue(choice?.text);
|
|
183
|
+
if (text) return text;
|
|
184
|
+
}
|
|
185
|
+
return extractTextFromValue(body.output_text)
|
|
186
|
+
|| extractTextFromValue(body.output)
|
|
187
|
+
|| extractTextFromValue(body.message)
|
|
188
|
+
|| extractTextFromValue(body.response)
|
|
189
|
+
|| null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function recentGatewayLogText(gateway) {
|
|
193
|
+
if (!gateway?.logs?.length) return '';
|
|
194
|
+
return gateway.logs
|
|
195
|
+
.slice(-16)
|
|
196
|
+
.map((entry) => String(entry.chunk || '').trim())
|
|
197
|
+
.filter(Boolean)
|
|
198
|
+
.join('\n')
|
|
199
|
+
.trim();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function gatewayExitMessage(gateway, fallback = 'Hermes gateway is not running.') {
|
|
203
|
+
if (!gateway) return fallback;
|
|
204
|
+
const exit = gateway.exitSignal
|
|
205
|
+
? `Hermes gateway exited with signal ${gateway.exitSignal}.`
|
|
206
|
+
: `Hermes gateway exited with code ${gateway.exitCode ?? 'unknown'}.`;
|
|
207
|
+
const logs = recentGatewayLogText(gateway);
|
|
208
|
+
return logs ? `${exit}\n${logs}` : (gateway.error || exit);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function makeRunRequest(options) {
|
|
212
|
+
const input = String(options.input || '').trim();
|
|
213
|
+
return {
|
|
214
|
+
session_id: options.sessionId || `pixcode-hermes-chat-${Date.now()}-${randomBytes(4).toString('hex')}`,
|
|
215
|
+
input,
|
|
216
|
+
instructions: options.instructions || [
|
|
217
|
+
'You are Hermes Agent running inside Pixcode.',
|
|
218
|
+
'Use Pixcode MCP tools when they help inspect projects, launch CLIs, or perform workspace actions.',
|
|
219
|
+
'Keep answers concise and include concrete next steps when work is blocked.',
|
|
220
|
+
].join(' '),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function makeChatCompletionRequest(options) {
|
|
225
|
+
const input = String(options.input || '').trim();
|
|
226
|
+
const messages = Array.isArray(options.messages) ? options.messages : [
|
|
227
|
+
{
|
|
228
|
+
role: 'system',
|
|
229
|
+
content: options.instructions || [
|
|
230
|
+
'You are Hermes Agent running inside Pixcode.',
|
|
231
|
+
'Use Pixcode MCP tools when they help inspect projects, launch CLIs, or perform workspace actions.',
|
|
232
|
+
'Keep answers concise and include concrete next steps when work is blocked.',
|
|
233
|
+
].join(' '),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
role: 'user',
|
|
237
|
+
content: input,
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
return {
|
|
241
|
+
model: options.model || 'hermes-agent',
|
|
242
|
+
messages,
|
|
243
|
+
stream: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
127
247
|
async function waitForGatewayReady(gateway) {
|
|
128
248
|
const started = Date.now();
|
|
129
249
|
let lastError = null;
|
|
130
250
|
|
|
131
251
|
while (Date.now() - started < STARTUP_TIMEOUT_MS) {
|
|
132
252
|
if (!isGatewayRunning(gateway)) {
|
|
133
|
-
throw new Error(gateway
|
|
253
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
134
254
|
}
|
|
135
255
|
|
|
136
256
|
try {
|
|
@@ -382,6 +502,130 @@ export async function probeHermesGateway(projectPath, options = {}) {
|
|
|
382
502
|
return result;
|
|
383
503
|
}
|
|
384
504
|
|
|
505
|
+
export async function runHermesGatewayPrompt(projectPath, options = {}) {
|
|
506
|
+
const gateway = projectPath
|
|
507
|
+
? gateways.get(normalizeProjectPath(projectPath))
|
|
508
|
+
: Array.from(gateways.values()).find(isGatewayRunning);
|
|
509
|
+
|
|
510
|
+
if (!isGatewayRunning(gateway)) {
|
|
511
|
+
throw new Error('Hermes gateway is not running.');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const input = String(options.input || '').trim();
|
|
515
|
+
if (!input) {
|
|
516
|
+
throw new Error('Hermes prompt is required.');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const chatRequest = makeChatCompletionRequest({ ...options, input });
|
|
520
|
+
const chat = await callGateway(gateway, '/v1/chat/completions', {
|
|
521
|
+
method: 'POST',
|
|
522
|
+
body: JSON.stringify(chatRequest),
|
|
523
|
+
timeoutMs: options.chatTimeoutMs || options.timeoutMs || RUN_TIMEOUT_MS,
|
|
524
|
+
}).catch((error) => {
|
|
525
|
+
if (!isGatewayRunning(gateway)) {
|
|
526
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
527
|
+
}
|
|
528
|
+
throw error;
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
if (!isGatewayRunning(gateway)) {
|
|
532
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (chat.ok) {
|
|
536
|
+
const message = extractChatCompletionOutput(chat.body);
|
|
537
|
+
return {
|
|
538
|
+
ok: true,
|
|
539
|
+
projectPath: gateway.projectPath,
|
|
540
|
+
baseUrl: gateway.baseUrl,
|
|
541
|
+
sessionId: options.sessionId || null,
|
|
542
|
+
runId: null,
|
|
543
|
+
status: 'completed',
|
|
544
|
+
message,
|
|
545
|
+
raw: chat.body,
|
|
546
|
+
transport: 'chat.completions',
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (chat.status && chat.status !== 404 && chat.status !== 405) {
|
|
551
|
+
throw new Error(`Hermes /v1/chat/completions failed with HTTP ${chat.status}: ${JSON.stringify(chat.body)}`);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const request = makeRunRequest({ ...options, input });
|
|
555
|
+
const create = await callGateway(gateway, '/v1/runs', {
|
|
556
|
+
method: 'POST',
|
|
557
|
+
body: JSON.stringify(request),
|
|
558
|
+
timeoutMs: options.createTimeoutMs || 15000,
|
|
559
|
+
}).catch((error) => {
|
|
560
|
+
if (!isGatewayRunning(gateway)) {
|
|
561
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
562
|
+
}
|
|
563
|
+
throw error;
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
if (!isGatewayRunning(gateway)) {
|
|
567
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (!create.ok) {
|
|
571
|
+
throw new Error(`Hermes /v1/runs failed with HTTP ${create.status}: ${JSON.stringify(create.body)}`);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const runId = extractRunId(create.body);
|
|
575
|
+
const initialStatus = extractRunStatus(create.body);
|
|
576
|
+
if (!runId) {
|
|
577
|
+
return {
|
|
578
|
+
ok: true,
|
|
579
|
+
projectPath: gateway.projectPath,
|
|
580
|
+
baseUrl: gateway.baseUrl,
|
|
581
|
+
sessionId: request.session_id,
|
|
582
|
+
runId: null,
|
|
583
|
+
status: initialStatus || 'completed',
|
|
584
|
+
message: extractRunOutput(create.body),
|
|
585
|
+
raw: create.body,
|
|
586
|
+
transport: 'runs',
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const terminalStatuses = new Set(['completed', 'failed', 'cancelled', 'canceled']);
|
|
591
|
+
const started = Date.now();
|
|
592
|
+
let latest = create.body;
|
|
593
|
+
let status = initialStatus || 'queued';
|
|
594
|
+
|
|
595
|
+
while (!terminalStatuses.has(String(status)) && Date.now() - started < (options.timeoutMs || RUN_TIMEOUT_MS)) {
|
|
596
|
+
await sleep(options.pollIntervalMs || RUN_POLL_INTERVAL_MS);
|
|
597
|
+
const poll = await callGateway(gateway, `/v1/runs/${encodeURIComponent(runId)}`, {
|
|
598
|
+
timeoutMs: options.pollTimeoutMs || 15000,
|
|
599
|
+
});
|
|
600
|
+
if (!poll.ok) {
|
|
601
|
+
throw new Error(`Hermes /v1/runs/${runId} failed with HTTP ${poll.status}: ${JSON.stringify(poll.body)}`);
|
|
602
|
+
}
|
|
603
|
+
if (!isGatewayRunning(gateway)) {
|
|
604
|
+
throw new Error(gatewayExitMessage(gateway));
|
|
605
|
+
}
|
|
606
|
+
latest = poll.body;
|
|
607
|
+
status = extractRunStatus(latest) || status;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (!terminalStatuses.has(String(status))) {
|
|
611
|
+
throw new Error(`Hermes run did not finish within ${Math.round((options.timeoutMs || RUN_TIMEOUT_MS) / 1000)}s: ${runId}`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const message = extractRunOutput(latest);
|
|
615
|
+
return {
|
|
616
|
+
ok: status === 'completed',
|
|
617
|
+
projectPath: gateway.projectPath,
|
|
618
|
+
baseUrl: gateway.baseUrl,
|
|
619
|
+
sessionId: request.session_id,
|
|
620
|
+
runId,
|
|
621
|
+
status,
|
|
622
|
+
message,
|
|
623
|
+
error: status === 'completed' ? null : extractTextFromValue(latest?.error) || message || 'Hermes run failed.',
|
|
624
|
+
raw: latest,
|
|
625
|
+
transport: 'runs',
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
385
629
|
export function stopHermesGateway(projectPath) {
|
|
386
630
|
const targets = projectPath
|
|
387
631
|
? [gateways.get(normalizeProjectPath(projectPath))].filter(Boolean)
|