@pixelbyte-software/pixcode 1.49.2 → 1.49.3

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.
@@ -92,6 +92,177 @@ function buildHermesEnv(baseEnv = process.env, extras = {}) {
92
92
  return env;
93
93
  }
94
94
 
95
+ const ANSI_ESCAPE_REGEX =
96
+ /(?:\u001B\[[0-?]*[ -/]*[@-~]|\u009B[0-?]*[ -/]*[@-~]|\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)|\u009D[^\u0007\u009C]*(?:\u0007|\u009C)|\u001B[PX^_][^\u001B]*\u001B\\|[\u0090\u0098\u009E\u009F][^\u009C]*\u009C|\u001B[@-Z\\-_])/g;
97
+
98
+ export function formatHermesVersionOutput(output) {
99
+ const firstLine = String(output || '')
100
+ .replace(ANSI_ESCAPE_REGEX, '')
101
+ .split(/\r?\n/)
102
+ .map((line) => line.trim())
103
+ .find(Boolean);
104
+ if (!firstLine) return null;
105
+
106
+ const versionMatch = firstLine.match(/Hermes Agent v[^\s]+(?:\s+\([^)]+\))?/i);
107
+ return versionMatch?.[0] || firstLine;
108
+ }
109
+
110
+ function hermesExecutableNames() {
111
+ return process.platform === 'win32'
112
+ ? ['hermes.cmd', 'hermes.bat', 'hermes.exe', 'hermes']
113
+ : ['hermes'];
114
+ }
115
+
116
+ function pushHermesCommandFiles(candidates, dir) {
117
+ if (!dir) return;
118
+ for (const name of hermesExecutableNames()) {
119
+ candidates.push(path.join(dir, name));
120
+ }
121
+ }
122
+
123
+ function runHermesVersion(candidate, env) {
124
+ try {
125
+ const result = spawn.sync(candidate, ['--version'], {
126
+ encoding: 'utf8',
127
+ env,
128
+ stdio: ['ignore', 'pipe', 'pipe'],
129
+ timeout: 5000,
130
+ windowsHide: true,
131
+ });
132
+ if (result.error || result.status !== 0) {
133
+ return { ok: false, output: '', error: result.error?.message || result.stderr || result.stdout || null };
134
+ }
135
+
136
+ return { ok: true, output: `${result.stdout || result.stderr || ''}` };
137
+ } catch (error) {
138
+ return { ok: false, output: '', error: error instanceof Error ? error.message : String(error) };
139
+ }
140
+ }
141
+
142
+ export function isUsableHermesCommand(candidate, env = process.env) {
143
+ return runHermesVersion(candidate, buildHermesEnv(env)).ok;
144
+ }
145
+
146
+ function isHermesPythonLauncher(candidate) {
147
+ if (!candidate || process.platform !== 'win32') return false;
148
+ const extension = path.extname(candidate).toLowerCase();
149
+ if (extension && extension !== '.py') return false;
150
+
151
+ try {
152
+ const source = fs.readFileSync(candidate, 'utf8').slice(0, 1000);
153
+ return source.includes('Hermes Agent CLI launcher') || source.includes('hermes_cli.main');
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ function windowsPythonCandidates(candidate, env = process.env) {
160
+ const localAppData = env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
161
+ const candidateDir = candidate ? path.dirname(candidate) : '';
162
+ const installDir = env.HERMES_INSTALL_DIR || path.join(localAppData, 'hermes', 'hermes-agent');
163
+ return [
164
+ candidateDir ? path.join(candidateDir, 'python.exe') : null,
165
+ path.join(installDir, 'venv', 'Scripts', 'python.exe'),
166
+ path.join(installDir, '.venv', 'Scripts', 'python.exe'),
167
+ env.HERMES_HOME ? path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'Scripts', 'python.exe') : null,
168
+ env.HERMES_HOME ? path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'Scripts', 'python.exe') : null,
169
+ ].filter(Boolean);
170
+ }
171
+
172
+ function windowsHermesCmdShimBody(command, env = process.env) {
173
+ const hermesHome = env.HERMES_HOME || path.join(env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'hermes');
174
+ const normalizedCommand = command.replace(/"/g, '""');
175
+ const extension = path.extname(command).toLowerCase();
176
+ const lines = [
177
+ '@echo off',
178
+ 'setlocal',
179
+ 'set PYTHONPATH=',
180
+ 'set PYTHONHOME=',
181
+ `set "HERMES_HOME=${hermesHome.replace(/"/g, '""')}"`,
182
+ ];
183
+
184
+ if (isHermesPythonLauncher(command)) {
185
+ const python = windowsPythonCandidates(command, env).find((candidate) => {
186
+ try {
187
+ return fs.existsSync(candidate);
188
+ } catch {
189
+ return false;
190
+ }
191
+ });
192
+ if (python) {
193
+ lines.push(`"${python.replace(/"/g, '""')}" "${normalizedCommand}" %*`);
194
+ lines.push('exit /b %ERRORLEVEL%');
195
+ return `${lines.join('\r\n')}\r\n`;
196
+ }
197
+ }
198
+
199
+ if (extension === '.ps1') {
200
+ lines.push(`powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${normalizedCommand}" %*`);
201
+ } else if (extension === '.cmd' || extension === '.bat') {
202
+ lines.push(`call "${normalizedCommand}" %*`);
203
+ } else {
204
+ lines.push(`"${normalizedCommand}" %*`);
205
+ }
206
+ lines.push('exit /b %ERRORLEVEL%');
207
+ return `${lines.join('\r\n')}\r\n`;
208
+ }
209
+
210
+ function repairWindowsHermesShim(command, env, appendLog) {
211
+ if (!path.isAbsolute(command) || !fs.existsSync(command)) return null;
212
+ const localAppData = env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
213
+ const binDir = path.join(localAppData, 'hermes', 'bin');
214
+ const shimPath = path.join(binDir, 'hermes.cmd');
215
+ if (path.resolve(command).toLowerCase() === path.resolve(shimPath).toLowerCase()) return shimPath;
216
+ fs.mkdirSync(binDir, { recursive: true });
217
+ const body = windowsHermesCmdShimBody(command, env);
218
+ const previous = fs.existsSync(shimPath) ? fs.readFileSync(shimPath, 'utf8') : '';
219
+ if (previous !== body) {
220
+ fs.writeFileSync(shimPath, body);
221
+ appendLog?.('meta', `Repaired Windows Hermes command shim: ${shimPath}\n`);
222
+ }
223
+ return shimPath;
224
+ }
225
+
226
+ function repairPosixHermesShim(command, env, appendLog) {
227
+ void env;
228
+ if (!path.isAbsolute(command) || !fs.existsSync(command)) return null;
229
+ const localBin = path.join(os.homedir(), '.local', 'bin');
230
+ const shimPath = path.join(localBin, 'hermes');
231
+ const resolvedCommand = fs.realpathSync.native?.(command) || fs.realpathSync(command);
232
+ const resolvedShim = fs.existsSync(shimPath)
233
+ ? (fs.realpathSync.native?.(shimPath) || fs.realpathSync(shimPath))
234
+ : null;
235
+ if (resolvedShim && resolvedShim === resolvedCommand) return shimPath;
236
+
237
+ fs.mkdirSync(localBin, { recursive: true });
238
+ const body = [
239
+ '#!/usr/bin/env bash',
240
+ 'unset PYTHONPATH',
241
+ 'unset PYTHONHOME',
242
+ `exec "${resolvedCommand.replace(/"/g, '\\"')}" "$@"`,
243
+ '',
244
+ ].join('\n');
245
+ const previous = fs.existsSync(shimPath) ? fs.readFileSync(shimPath, 'utf8') : '';
246
+ if (previous !== body) {
247
+ fs.writeFileSync(shimPath, body, { mode: 0o755 });
248
+ fs.chmodSync(shimPath, 0o755);
249
+ appendLog?.('meta', `Repaired Hermes command shim: ${shimPath}\n`);
250
+ }
251
+ return shimPath;
252
+ }
253
+
254
+ export function repairHermesCommandLaunchers(command, env = process.env, appendLog = null) {
255
+ if (!command || command === 'hermes') return null;
256
+ try {
257
+ return process.platform === 'win32'
258
+ ? repairWindowsHermesShim(command, env, appendLog)
259
+ : repairPosixHermesShim(command, env, appendLog);
260
+ } catch (error) {
261
+ appendLog?.('stderr', `Hermes command shim repair skipped: ${error instanceof Error ? error.message : String(error)}\n`);
262
+ return null;
263
+ }
264
+ }
265
+
95
266
  export function primeHermesPath(env = process.env) {
96
267
  const next = buildHermesEnv(env);
97
268
  env.PATH = next.PATH;
@@ -104,26 +275,15 @@ export function hermesCommandCandidates(env = process.env) {
104
275
  const resolved = findExecutableOnPath('hermes', hermesEnv);
105
276
 
106
277
  if (env.HERMES_CLI_PATH) candidates.push(env.HERMES_CLI_PATH);
107
- if (resolved) candidates.push(resolved);
108
- candidates.push('hermes');
278
+ for (const dir of knownHermesBinDirs(env)) {
279
+ pushHermesCommandFiles(candidates, dir);
280
+ }
109
281
 
110
282
  if (process.platform === 'win32') {
111
283
  const localAppData = env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
112
- candidates.push(
113
- ...(env.HERMES_INSTALL_DIR ? [
114
- path.join(env.HERMES_INSTALL_DIR, 'venv', 'Scripts', 'hermes.exe'),
115
- path.join(env.HERMES_INSTALL_DIR, '.venv', 'Scripts', 'hermes.exe'),
116
- path.join(env.HERMES_INSTALL_DIR, 'hermes.exe'),
117
- ] : []),
118
- ...(env.HERMES_HOME ? [
119
- path.join(env.HERMES_HOME, 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'),
120
- path.join(env.HERMES_HOME, 'hermes-agent', '.venv', 'Scripts', 'hermes.exe'),
121
- path.join(env.HERMES_HOME, 'hermes-agent', 'hermes.exe'),
122
- ] : []),
123
- path.join(localAppData, 'hermes', 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'),
124
- path.join(localAppData, 'hermes', 'hermes-agent', '.venv', 'Scripts', 'hermes.exe'),
125
- path.join(localAppData, 'hermes', 'hermes-agent', 'hermes.exe'),
126
- );
284
+ pushHermesCommandFiles(candidates, env.HERMES_INSTALL_DIR);
285
+ pushHermesCommandFiles(candidates, env.HERMES_HOME ? path.join(env.HERMES_HOME, 'hermes-agent') : null);
286
+ pushHermesCommandFiles(candidates, path.join(localAppData, 'hermes', 'hermes-agent'));
127
287
  } else {
128
288
  candidates.push(
129
289
  ...(env.HERMES_INSTALL_DIR ? [
@@ -142,6 +302,9 @@ export function hermesCommandCandidates(env = process.env) {
142
302
  );
143
303
  }
144
304
 
305
+ if (resolved) candidates.push(resolved);
306
+ candidates.push('hermes');
307
+
145
308
  return [...new Set(candidates.filter(Boolean))];
146
309
  }
147
310
 
@@ -154,15 +317,10 @@ export function readHermesInstallStatus(env = process.env) {
154
317
  continue;
155
318
  }
156
319
 
157
- const result = spawn.sync(candidate, ['--version'], {
158
- encoding: 'utf8',
159
- env: hermesEnv,
160
- stdio: ['ignore', 'pipe', 'pipe'],
161
- timeout: 5000,
162
- windowsHide: true,
163
- });
164
- if (!result.error && result.status === 0) {
165
- const version = `${result.stdout || result.stderr || ''}`.trim() || null;
320
+ const result = runHermesVersion(candidate, hermesEnv);
321
+ if (result.ok) {
322
+ repairHermesCommandLaunchers(candidate, hermesEnv);
323
+ const version = formatHermesVersionOutput(result.output);
166
324
  return {
167
325
  installed: true,
168
326
  command: candidate,