@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.
- package/dist/assets/{index-BsQAPOnf.js → index-DexGC0hs.js} +125 -125
- package/dist/assets/index-ntmmehps.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +29 -12
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/services/hermes-install-jobs.js +180 -21
- package/dist-server/server/services/hermes-install-jobs.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/hermes-api-install.mjs +11 -0
- package/scripts/smoke/pixcode-workbench-1-48.mjs +6 -2
- package/server/index.js +29 -12
- package/server/services/hermes-install-jobs.js +184 -26
- package/dist/assets/index-B8TbMftk.css +0 -32
|
@@ -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
|
-
|
|
108
|
-
|
|
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.
|
|
113
|
-
|
|
114
|
-
|
|
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 =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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,
|