@pixelbyte-software/pixcode 1.49.1 → 1.49.2

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.
@@ -1,57 +1,20 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
1
  import os from 'node:os';
4
- import path from 'node:path';
5
2
  import express from 'express';
6
3
  import { adapterRegistry } from '../../../modules/orchestration/a2a/adapter-registry.js';
7
4
  import { a2aTaskStore as hermesTaskStore } from '../../../modules/orchestration/a2a/task-store.js';
5
+ import { cancelHermesInstallJob, createHermesInstallJob, getHermesInstallJob, readHermesInstallStatus, snapshotHermesInstallDonePayload, } from '../../../services/hermes-install-jobs.js';
8
6
  const HERMES_TERMINAL_LAUNCH_LIMIT = 100;
9
7
  const HERMES_TERMINAL_LAUNCH_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
10
8
  let nextHermesTerminalLaunchId = 1;
11
9
  const hermesTerminalLaunches = [];
12
- function hermesCommandCandidates() {
13
- const candidates = [process.env.HERMES_CLI_PATH, 'hermes'].filter((candidate) => (typeof candidate === 'string' && candidate.trim().length > 0));
14
- if (process.platform === 'win32') {
15
- const localAppData = process.env.LOCALAPPDATA;
16
- if (localAppData) {
17
- candidates.push(path.join(localAppData, 'hermes', 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'), path.join(localAppData, 'hermes', 'hermes-agent', '.venv', 'Scripts', 'hermes.exe'), path.join(localAppData, 'hermes', 'hermes-agent', 'hermes.exe'));
18
- }
19
- }
20
- else {
21
- candidates.push(path.join(os.homedir(), '.local', 'bin', 'hermes'), path.join(os.homedir(), '.hermes', 'hermes-agent', 'venv', 'bin', 'hermes'), '/usr/local/bin/hermes');
22
- }
23
- return [...new Set(candidates)];
10
+ function writeSse(res, event, payload) {
11
+ res.write(`event: ${event}\n`);
12
+ res.write(`data: ${JSON.stringify(payload)}\n\n`);
24
13
  }
25
- function readHermesInstallStatus() {
26
- for (const candidate of hermesCommandCandidates()) {
27
- const isBareCommand = candidate === 'hermes';
28
- if (!isBareCommand && !existsSync(candidate)) {
29
- continue;
30
- }
31
- const result = spawnSync(candidate, ['--version'], {
32
- encoding: 'utf8',
33
- stdio: ['ignore', 'pipe', 'pipe'],
34
- timeout: 5000,
35
- shell: false,
36
- });
37
- if (!result.error && result.status === 0) {
38
- const version = `${result.stdout || result.stderr || ''}`.trim() || null;
39
- return {
40
- installed: true,
41
- command: candidate,
42
- version,
43
- error: null,
44
- };
45
- }
46
- }
47
- return {
48
- installed: false,
49
- command: null,
50
- version: null,
51
- error: 'Hermes Agent CLI is not installed or is not on PATH.',
52
- };
14
+ function readUserId(req) {
15
+ return req.user?.id ?? req.user?.userId ?? null;
53
16
  }
54
- export function createHermesRouter() {
17
+ export function createHermesRouter(options = {}) {
55
18
  const router = express.Router();
56
19
  router.get('/status', (_req, res) => {
57
20
  res.json({
@@ -89,6 +52,122 @@ export function createHermesRouter() {
89
52
  router.get('/install-status', (_req, res) => {
90
53
  res.json(readHermesInstallStatus());
91
54
  });
55
+ router.post('/install', (req, res) => {
56
+ const apiKey = options.createHermesApiKey?.(readUserId(req)) ?? null;
57
+ if (!apiKey) {
58
+ res.status(500).json({
59
+ error: {
60
+ code: 'HERMES_API_KEY_UNAVAILABLE',
61
+ message: 'Pixcode could not create a Hermes MCP API key for this user.',
62
+ },
63
+ });
64
+ return;
65
+ }
66
+ const body = (req.body ?? {});
67
+ const job = createHermesInstallJob({
68
+ appRoot: options.appRoot ?? process.cwd(),
69
+ force: Boolean(body.force),
70
+ pixcodeApiKey: apiKey,
71
+ pixcodeBaseUrl: options.resolvePublicBaseUrl?.(req) ?? `http://127.0.0.1:${process.env.SERVER_PORT ?? process.env.PORT ?? '3001'}`,
72
+ skipBrowser: body.skipBrowser !== false,
73
+ });
74
+ res.status(202).json({
75
+ jobId: job.id,
76
+ provider: 'hermes',
77
+ status: job.status,
78
+ startedAt: job.startedAt,
79
+ });
80
+ });
81
+ router.get('/install/:jobId/stream', (req, res) => {
82
+ const job = getHermesInstallJob(req.params.jobId);
83
+ if (!job) {
84
+ res.status(404).json({
85
+ error: {
86
+ code: 'HERMES_INSTALL_JOB_NOT_FOUND',
87
+ message: 'Hermes install job not found or already expired.',
88
+ },
89
+ });
90
+ return;
91
+ }
92
+ res.setHeader('Content-Type', 'text/event-stream');
93
+ res.setHeader('Cache-Control', 'no-cache, no-transform');
94
+ res.setHeader('Connection', 'keep-alive');
95
+ res.setHeader('X-Accel-Buffering', 'no');
96
+ if (typeof res.flushHeaders === 'function')
97
+ res.flushHeaders();
98
+ try {
99
+ res.socket?.setNoDelay?.(true);
100
+ }
101
+ catch { /* noop */ }
102
+ let closed = false;
103
+ const safeWrite = (event, payload) => {
104
+ if (closed)
105
+ return;
106
+ try {
107
+ writeSse(res, event, payload);
108
+ }
109
+ catch { /* socket gone */ }
110
+ };
111
+ try {
112
+ res.write(': start\n\n');
113
+ }
114
+ catch { /* noop */ }
115
+ const heartbeat = setInterval(() => {
116
+ if (closed)
117
+ return;
118
+ try {
119
+ res.write(': ping\n\n');
120
+ }
121
+ catch { /* noop */ }
122
+ }, 5000);
123
+ for (const entry of job.logs) {
124
+ safeWrite('log', { stream: entry.stream, chunk: entry.chunk });
125
+ }
126
+ const onLog = (entry) => {
127
+ safeWrite('log', { stream: entry.stream, chunk: entry.chunk });
128
+ };
129
+ const onDone = (payload) => {
130
+ safeWrite('done', payload);
131
+ cleanup();
132
+ try {
133
+ res.end();
134
+ }
135
+ catch { /* noop */ }
136
+ };
137
+ function cleanup() {
138
+ if (closed)
139
+ return;
140
+ closed = true;
141
+ clearInterval(heartbeat);
142
+ job.emitter.off('log', onLog);
143
+ job.emitter.off('done', onDone);
144
+ }
145
+ if (job.status !== 'running') {
146
+ safeWrite('done', snapshotHermesInstallDonePayload(job));
147
+ cleanup();
148
+ try {
149
+ res.end();
150
+ }
151
+ catch { /* noop */ }
152
+ return;
153
+ }
154
+ job.emitter.on('log', onLog);
155
+ job.emitter.once('done', onDone);
156
+ req.on('close', cleanup);
157
+ });
158
+ router.delete('/install/:jobId', (req, res) => {
159
+ const job = getHermesInstallJob(req.params.jobId);
160
+ if (!job) {
161
+ res.status(404).json({
162
+ error: {
163
+ code: 'HERMES_INSTALL_JOB_NOT_FOUND',
164
+ message: 'Hermes install job not found.',
165
+ },
166
+ });
167
+ return;
168
+ }
169
+ res.json({ cancelled: cancelHermesInstallJob(req.params.jobId) });
170
+ });
92
171
  router.get('/agents', (_req, res) => {
93
172
  res.json({
94
173
  agent: 'hermes',
@@ -1 +1 @@
1
- {"version":3,"file":"hermes.routes.js","sourceRoot":"","sources":["../../../../../server/modules/orchestration/hermes/hermes.routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hermes.routes.js","sourceRoot":"","sources":["../../../../../server/modules/orchestration/hermes/hermes.routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,417 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import spawn from 'cross-spawn';
7
+ import { buildCliSpawnEnv, findExecutableOnPath } from './install-jobs.js';
8
+ const POSIX_INSTALLER_URL = 'https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh';
9
+ const WINDOWS_INSTALLER_URL = 'https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1';
10
+ const FINISHED_TTL_MS = 10 * 60 * 1000;
11
+ const HARD_TIMEOUT_MS = 20 * 60 * 1000;
12
+ const jobs = new Map();
13
+ function pathSeparator() {
14
+ return process.platform === 'win32' ? ';' : ':';
15
+ }
16
+ function splitPathList(value) {
17
+ return String(value || '').split(pathSeparator()).map((entry) => entry.trim()).filter(Boolean);
18
+ }
19
+ function mergePathEntries(env, preferredEntries) {
20
+ const existing = splitPathList(env.PATH || env.Path || '');
21
+ const seen = new Set();
22
+ const merged = [];
23
+ for (const entry of [...preferredEntries, ...existing]) {
24
+ if (!entry)
25
+ continue;
26
+ const key = path.resolve(entry);
27
+ if (seen.has(key))
28
+ continue;
29
+ seen.add(key);
30
+ merged.push(entry);
31
+ }
32
+ const nextPath = merged.join(pathSeparator());
33
+ env.PATH = nextPath;
34
+ if ('Path' in env || process.platform === 'win32')
35
+ env.Path = nextPath;
36
+ return env;
37
+ }
38
+ function knownHermesBinDirs(env = process.env) {
39
+ const home = os.homedir();
40
+ if (process.platform === 'win32') {
41
+ const localAppData = env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
42
+ return [
43
+ ...(env.HERMES_INSTALL_DIR ? [
44
+ path.join(env.HERMES_INSTALL_DIR, 'venv', 'Scripts'),
45
+ path.join(env.HERMES_INSTALL_DIR, '.venv', 'Scripts'),
46
+ env.HERMES_INSTALL_DIR,
47
+ ] : []),
48
+ ...(env.HERMES_HOME ? [
49
+ path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'Scripts'),
50
+ path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'Scripts'),
51
+ path.join(env.HERMES_HOME, 'hermes-agent'),
52
+ ] : []),
53
+ path.join(localAppData, 'hermes', 'bin'),
54
+ path.join(localAppData, 'hermes', 'hermes-agent'),
55
+ path.join(localAppData, 'hermes', 'hermes-agent', 'venv', 'Scripts'),
56
+ path.join(localAppData, 'hermes', 'hermes-agent', '.venv', 'Scripts'),
57
+ ];
58
+ }
59
+ return [
60
+ ...(env.HERMES_INSTALL_DIR ? [
61
+ path.join(env.HERMES_INSTALL_DIR, 'venv', 'bin'),
62
+ path.join(env.HERMES_INSTALL_DIR, '.venv', 'bin'),
63
+ ] : []),
64
+ ...(env.HERMES_HOME ? [
65
+ path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'bin'),
66
+ path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'bin'),
67
+ ] : []),
68
+ path.join(home, '.local', 'bin'),
69
+ path.join(home, '.hermes', 'hermes-agent', 'venv', 'bin'),
70
+ path.join(home, '.hermes', 'hermes-agent', '.venv', 'bin'),
71
+ '/usr/local/bin',
72
+ '/usr/local/lib/hermes-agent/venv/bin',
73
+ ];
74
+ }
75
+ function buildHermesEnv(baseEnv = process.env, extras = {}) {
76
+ const env = mergePathEntries(buildCliSpawnEnv(baseEnv), knownHermesBinDirs(baseEnv));
77
+ for (const [key, value] of Object.entries(extras)) {
78
+ if (typeof value === 'string' && value.length > 0) {
79
+ env[key] = value;
80
+ }
81
+ }
82
+ delete env.PYTHONPATH;
83
+ delete env.PYTHONHOME;
84
+ return env;
85
+ }
86
+ export function primeHermesPath(env = process.env) {
87
+ const next = buildHermesEnv(env);
88
+ env.PATH = next.PATH;
89
+ if ('Path' in env || next.Path)
90
+ env.Path = next.Path || next.PATH;
91
+ }
92
+ export function hermesCommandCandidates(env = process.env) {
93
+ const candidates = [];
94
+ const hermesEnv = buildHermesEnv(env);
95
+ const resolved = findExecutableOnPath('hermes', hermesEnv);
96
+ if (env.HERMES_CLI_PATH)
97
+ candidates.push(env.HERMES_CLI_PATH);
98
+ if (resolved)
99
+ candidates.push(resolved);
100
+ candidates.push('hermes');
101
+ if (process.platform === 'win32') {
102
+ const localAppData = env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
103
+ candidates.push(...(env.HERMES_INSTALL_DIR ? [
104
+ path.join(env.HERMES_INSTALL_DIR, 'venv', 'Scripts', 'hermes.exe'),
105
+ path.join(env.HERMES_INSTALL_DIR, '.venv', 'Scripts', 'hermes.exe'),
106
+ path.join(env.HERMES_INSTALL_DIR, 'hermes.exe'),
107
+ ] : []), ...(env.HERMES_HOME ? [
108
+ path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'),
109
+ path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'Scripts', 'hermes.exe'),
110
+ path.join(env.HERMES_HOME, 'hermes-agent', 'hermes.exe'),
111
+ ] : []), path.join(localAppData, 'hermes', 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'), path.join(localAppData, 'hermes', 'hermes-agent', '.venv', 'Scripts', 'hermes.exe'), path.join(localAppData, 'hermes', 'hermes-agent', 'hermes.exe'));
112
+ }
113
+ else {
114
+ candidates.push(...(env.HERMES_INSTALL_DIR ? [
115
+ path.join(env.HERMES_INSTALL_DIR, 'venv', 'bin', 'hermes'),
116
+ path.join(env.HERMES_INSTALL_DIR, '.venv', 'bin', 'hermes'),
117
+ ] : []), ...(env.HERMES_HOME ? [
118
+ path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'bin', 'hermes'),
119
+ path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'bin', 'hermes'),
120
+ ] : []), path.join(os.homedir(), '.local', 'bin', 'hermes'), path.join(os.homedir(), '.hermes', 'hermes-agent', 'venv', 'bin', 'hermes'), path.join(os.homedir(), '.hermes', 'hermes-agent', '.venv', 'bin', 'hermes'), '/usr/local/bin/hermes', '/usr/local/lib/hermes-agent/venv/bin/hermes');
121
+ }
122
+ return [...new Set(candidates.filter(Boolean))];
123
+ }
124
+ export function readHermesInstallStatus(env = process.env) {
125
+ const hermesEnv = buildHermesEnv(env);
126
+ for (const candidate of hermesCommandCandidates(hermesEnv)) {
127
+ const isBareCommand = candidate === 'hermes';
128
+ if (!isBareCommand && !fs.existsSync(candidate)) {
129
+ continue;
130
+ }
131
+ const result = spawn.sync(candidate, ['--version'], {
132
+ encoding: 'utf8',
133
+ env: hermesEnv,
134
+ stdio: ['ignore', 'pipe', 'pipe'],
135
+ timeout: 5000,
136
+ windowsHide: true,
137
+ });
138
+ if (!result.error && result.status === 0) {
139
+ const version = `${result.stdout || result.stderr || ''}`.trim() || null;
140
+ return {
141
+ installed: true,
142
+ command: candidate,
143
+ version,
144
+ error: null,
145
+ };
146
+ }
147
+ }
148
+ return {
149
+ installed: false,
150
+ command: null,
151
+ version: null,
152
+ error: 'Hermes Agent CLI is not installed or is not on PATH.',
153
+ };
154
+ }
155
+ async function downloadHermesInstaller(url, targetPath, appendLog) {
156
+ appendLog('meta', `Downloading ${url}\n`);
157
+ try {
158
+ const response = await fetch(url);
159
+ if (!response.ok) {
160
+ throw new Error(`HTTP ${response.status}`);
161
+ }
162
+ const bytes = Buffer.from(await response.arrayBuffer());
163
+ await fs.promises.writeFile(targetPath, bytes, { mode: 0o700 });
164
+ appendLog('meta', `Downloaded installer to ${targetPath}\n`);
165
+ return;
166
+ }
167
+ catch (error) {
168
+ appendLog('stderr', `Node download failed: ${error instanceof Error ? error.message : String(error)}\n`);
169
+ appendLog('meta', 'Retrying installer download with the system download tool...\n');
170
+ }
171
+ await downloadHermesInstallerWithNativeTool(url, targetPath, appendLog);
172
+ if (process.platform !== 'win32') {
173
+ await fs.promises.chmod(targetPath, 0o700);
174
+ }
175
+ appendLog('meta', `Downloaded installer to ${targetPath}\n`);
176
+ }
177
+ function downloadHermesInstallerWithNativeTool(url, targetPath, appendLog) {
178
+ const isWindows = process.platform === 'win32';
179
+ const command = isWindows ? 'powershell.exe' : 'curl';
180
+ const args = isWindows
181
+ ? [
182
+ '-NoProfile',
183
+ '-ExecutionPolicy',
184
+ 'Bypass',
185
+ '-Command',
186
+ `$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri ${JSON.stringify(url)} -UseBasicParsing -OutFile ${JSON.stringify(targetPath)}`,
187
+ ]
188
+ : ['-fL', '--retry', '2', '--connect-timeout', '20', '-o', targetPath, url];
189
+ appendLog('meta', `$ ${command} ${args.join(' ')}\n`);
190
+ return new Promise((resolve, reject) => {
191
+ const child = spawn(command, args, {
192
+ env: buildHermesEnv(process.env),
193
+ stdio: ['ignore', 'pipe', 'pipe'],
194
+ windowsHide: true,
195
+ });
196
+ child.stdout.on('data', (buf) => appendLog('stdout', buf.toString()));
197
+ child.stderr.on('data', (buf) => appendLog('stderr', buf.toString()));
198
+ child.on('error', reject);
199
+ child.on('close', (code, signal) => {
200
+ if (signal) {
201
+ reject(new Error(`${command} killed by ${signal}`));
202
+ return;
203
+ }
204
+ if (code !== 0) {
205
+ reject(new Error(`${command} exited with code ${code}`));
206
+ return;
207
+ }
208
+ resolve();
209
+ });
210
+ });
211
+ }
212
+ function createJob(options) {
213
+ const id = randomUUID();
214
+ const emitter = new EventEmitter();
215
+ emitter.setMaxListeners(20);
216
+ return {
217
+ id,
218
+ provider: 'hermes',
219
+ status: 'running',
220
+ startedAt: new Date().toISOString(),
221
+ finishedAt: null,
222
+ exitCode: null,
223
+ error: null,
224
+ logs: [],
225
+ emitter,
226
+ child: null,
227
+ timer: null,
228
+ binaryPath: null,
229
+ options,
230
+ };
231
+ }
232
+ function appendLog(job, stream, chunk) {
233
+ const entry = { stream, chunk, at: Date.now() };
234
+ job.logs.push(entry);
235
+ if (job.logs.length > 2500) {
236
+ job.logs.splice(0, job.logs.length - 2500);
237
+ }
238
+ job.emitter.emit('log', entry);
239
+ }
240
+ function finishJob(job, status, payload = {}) {
241
+ if (job.status !== 'running')
242
+ return;
243
+ job.status = status;
244
+ job.finishedAt = new Date().toISOString();
245
+ if (typeof payload.exitCode === 'number')
246
+ job.exitCode = payload.exitCode;
247
+ if (payload.error)
248
+ job.error = payload.error;
249
+ if (payload.binaryPath)
250
+ job.binaryPath = payload.binaryPath;
251
+ job.emitter.emit('done', snapshotHermesInstallDonePayload(job));
252
+ scheduleCleanup(job);
253
+ }
254
+ function scheduleCleanup(job) {
255
+ if (job.timer) {
256
+ clearTimeout(job.timer);
257
+ job.timer = null;
258
+ }
259
+ setTimeout(() => {
260
+ jobs.delete(job.id);
261
+ }, FINISHED_TTL_MS);
262
+ }
263
+ function spawnLogged(job, command, args, options) {
264
+ appendLog(job, 'meta', `$ ${command} ${args.join(' ')}\n`);
265
+ const child = spawn(command, args, {
266
+ ...options,
267
+ stdio: ['ignore', 'pipe', 'pipe'],
268
+ windowsHide: true,
269
+ });
270
+ job.child = child;
271
+ child.stdout.on('data', (buf) => appendLog(job, 'stdout', buf.toString()));
272
+ child.stderr.on('data', (buf) => appendLog(job, 'stderr', buf.toString()));
273
+ return new Promise((resolve, reject) => {
274
+ child.on('error', reject);
275
+ child.on('close', (code, signal) => {
276
+ if (signal) {
277
+ reject(new Error(`${command} killed by ${signal}`));
278
+ return;
279
+ }
280
+ resolve(code ?? 0);
281
+ });
282
+ });
283
+ }
284
+ async function runConfigureScript(job, env, appRoot) {
285
+ const configureScript = path.join(appRoot, 'scripts', 'hermes', 'configure-pixcode-mcp.mjs');
286
+ if (!fs.existsSync(configureScript)) {
287
+ appendLog(job, 'stderr', `Pixcode MCP configure script was not found: ${configureScript}\n`);
288
+ return;
289
+ }
290
+ const code = await spawnLogged(job, process.execPath, [configureScript], {
291
+ cwd: appRoot,
292
+ env,
293
+ });
294
+ if (code !== 0) {
295
+ throw new Error(`Pixcode MCP configuration exited with code ${code}`);
296
+ }
297
+ }
298
+ function installerCommand(installerPath, { hermesHome, installDir, skipBrowser }) {
299
+ if (process.platform === 'win32') {
300
+ return {
301
+ command: 'powershell.exe',
302
+ args: [
303
+ '-NoProfile',
304
+ '-ExecutionPolicy',
305
+ 'Bypass',
306
+ '-File',
307
+ installerPath,
308
+ '-SkipSetup',
309
+ '-NonInteractive',
310
+ '-Branch',
311
+ 'main',
312
+ ...(hermesHome ? ['-HermesHome', hermesHome] : []),
313
+ ...(installDir ? ['-InstallDir', installDir] : []),
314
+ ],
315
+ };
316
+ }
317
+ return {
318
+ command: 'bash',
319
+ args: [
320
+ installerPath,
321
+ '--skip-setup',
322
+ '--branch',
323
+ 'main',
324
+ ...(installDir ? ['--dir', installDir] : []),
325
+ ...(hermesHome ? ['--hermes-home', hermesHome] : []),
326
+ ...(skipBrowser ? ['--skip-browser'] : []),
327
+ ],
328
+ };
329
+ }
330
+ async function runHermesInstall(job) {
331
+ const { appRoot, force = false, hermesHome, installDir, pixcodeApiKey, pixcodeBaseUrl, skipBrowser = true, } = job.options;
332
+ const env = buildHermesEnv(process.env, {
333
+ PIXCODE_API_KEY: pixcodeApiKey,
334
+ PIXCODE_BASE_URL: pixcodeBaseUrl,
335
+ PIXCODE_APP_ROOT: appRoot,
336
+ HERMES_HOME: hermesHome,
337
+ HERMES_INSTALL_DIR: installDir,
338
+ });
339
+ appendLog(job, 'meta', `Hermes install mode: ${process.platform}\n`);
340
+ appendLog(job, 'meta', `Pixcode base URL: ${pixcodeBaseUrl}\n`);
341
+ const before = readHermesInstallStatus(env);
342
+ if (before.installed && !force) {
343
+ appendLog(job, 'meta', `Hermes already installed: ${before.version || before.command}\n`);
344
+ await runConfigureScript(job, env, appRoot);
345
+ finishJob(job, 'done', { binaryPath: before.command });
346
+ return;
347
+ }
348
+ const installerPath = path.join(os.tmpdir(), `pixcode-hermes-install-${job.id}${process.platform === 'win32' ? '.ps1' : '.sh'}`);
349
+ await downloadHermesInstaller(process.platform === 'win32' ? WINDOWS_INSTALLER_URL : POSIX_INSTALLER_URL, installerPath, (stream, chunk) => {
350
+ appendLog(job, stream, chunk);
351
+ });
352
+ const install = installerCommand(installerPath, { hermesHome, installDir, skipBrowser });
353
+ const installCode = await spawnLogged(job, install.command, install.args, {
354
+ cwd: os.homedir(),
355
+ env,
356
+ });
357
+ if (installCode !== 0) {
358
+ throw new Error(`Hermes installer exited with code ${installCode}`);
359
+ }
360
+ primeHermesPath();
361
+ const after = readHermesInstallStatus(env);
362
+ if (!after.installed) {
363
+ throw new Error(after.error || 'Hermes installer finished but hermes was not found.');
364
+ }
365
+ appendLog(job, 'meta', `Hermes ready: ${after.version || after.command}\n`);
366
+ await runConfigureScript(job, env, appRoot);
367
+ finishJob(job, 'done', { binaryPath: after.command });
368
+ }
369
+ export function createHermesInstallJob(options) {
370
+ const job = createJob(options);
371
+ jobs.set(job.id, job);
372
+ job.timer = setTimeout(() => {
373
+ if (job.status !== 'running')
374
+ return;
375
+ try {
376
+ job.child?.kill('SIGKILL');
377
+ }
378
+ catch { /* noop */ }
379
+ finishJob(job, 'error', { error: 'Hermes install timed out after 20 minutes' });
380
+ }, HARD_TIMEOUT_MS);
381
+ runHermesInstall(job).catch((error) => {
382
+ const message = error instanceof Error ? error.message : String(error);
383
+ appendLog(job, 'stderr', `${message}\n`);
384
+ finishJob(job, 'error', { error: message });
385
+ });
386
+ return job;
387
+ }
388
+ export function getHermesInstallJob(id) {
389
+ return jobs.get(id) || null;
390
+ }
391
+ export function cancelHermesInstallJob(id) {
392
+ const job = jobs.get(id);
393
+ if (!job || job.status !== 'running')
394
+ return false;
395
+ try {
396
+ job.child?.kill();
397
+ }
398
+ catch { /* noop */ }
399
+ finishJob(job, 'error', { error: 'Hermes install cancelled' });
400
+ return true;
401
+ }
402
+ export function snapshotHermesInstallDonePayload(job) {
403
+ if (job.status === 'done') {
404
+ return {
405
+ success: true,
406
+ exitCode: job.exitCode,
407
+ binaryPath: job.binaryPath,
408
+ message: 'Hermes Agent is installed and Pixcode MCP is configured.',
409
+ };
410
+ }
411
+ return {
412
+ success: false,
413
+ exitCode: job.exitCode,
414
+ error: job.error || 'Hermes install failed',
415
+ };
416
+ }
417
+ //# sourceMappingURL=hermes-install-jobs.js.map