@hermespilot/link 0.4.7 → 0.4.8
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.
|
@@ -4117,7 +4117,8 @@ function isConversationMissingError(error) {
|
|
|
4117
4117
|
|
|
4118
4118
|
// src/hermes/gateway.ts
|
|
4119
4119
|
import { execFile as execFile2, spawn } from "child_process";
|
|
4120
|
-
import {
|
|
4120
|
+
import { constants as fsConstants } from "fs";
|
|
4121
|
+
import { access, readFile as readFile5, realpath, stat as stat4 } from "fs/promises";
|
|
4121
4122
|
import path7 from "path";
|
|
4122
4123
|
import { setTimeout as delay } from "timers/promises";
|
|
4123
4124
|
import { promisify as promisify2 } from "util";
|
|
@@ -4132,7 +4133,7 @@ import os2 from "os";
|
|
|
4132
4133
|
import path5 from "path";
|
|
4133
4134
|
|
|
4134
4135
|
// src/constants.ts
|
|
4135
|
-
var LINK_VERSION = "0.4.
|
|
4136
|
+
var LINK_VERSION = "0.4.8";
|
|
4136
4137
|
var LINK_COMMAND = "hermeslink";
|
|
4137
4138
|
var LINK_DEFAULT_PORT = 52379;
|
|
4138
4139
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -4631,6 +4632,8 @@ var PROFILE_NAME_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
|
4631
4632
|
var DASHBOARD_STATUS_URL = "http://127.0.0.1:9119/api/status";
|
|
4632
4633
|
var DASHBOARD_STATUS_TIMEOUT_MS = 1500;
|
|
4633
4634
|
var DEFAULT_VERSION_CACHE_TTL_MS = 6e4;
|
|
4635
|
+
var VERSION_METADATA_TIMEOUT_MS = 3e3;
|
|
4636
|
+
var VERSION_COMMAND_TIMEOUT_MS = 5e3;
|
|
4634
4637
|
var MAX_VERSION_LOG_OUTPUT_LENGTH = 1200;
|
|
4635
4638
|
var HERMES_GATEWAY_ENV_BLOCKLIST_PREFIXES = ["API_SERVER_"];
|
|
4636
4639
|
var gatewayStartInFlightByProfile = /* @__PURE__ */ new Map();
|
|
@@ -4826,9 +4829,26 @@ async function readHermesVersion(options = {}) {
|
|
|
4826
4829
|
}
|
|
4827
4830
|
async function execHermesVersion(hermesBin, logger) {
|
|
4828
4831
|
const failures = [];
|
|
4832
|
+
try {
|
|
4833
|
+
const metadataVersion = await readHermesPythonMetadataVersion(
|
|
4834
|
+
hermesBin,
|
|
4835
|
+
VERSION_METADATA_TIMEOUT_MS
|
|
4836
|
+
);
|
|
4837
|
+
if (metadataVersion) {
|
|
4838
|
+
return metadataVersion;
|
|
4839
|
+
}
|
|
4840
|
+
} catch (error) {
|
|
4841
|
+
const failure = describeVersionMetadataFailure(hermesBin, error);
|
|
4842
|
+
failures.push(failure);
|
|
4843
|
+
void logger?.debug("hermes_version_metadata_probe_failed", failure.fields);
|
|
4844
|
+
}
|
|
4829
4845
|
for (const args of [["--version"], ["version"]]) {
|
|
4830
4846
|
try {
|
|
4831
|
-
return await spawnHermesVersionCommand(
|
|
4847
|
+
return await spawnHermesVersionCommand(
|
|
4848
|
+
hermesBin,
|
|
4849
|
+
args,
|
|
4850
|
+
VERSION_COMMAND_TIMEOUT_MS
|
|
4851
|
+
);
|
|
4832
4852
|
} catch (error) {
|
|
4833
4853
|
const failure = describeVersionCommandFailure(hermesBin, args, error);
|
|
4834
4854
|
failures.push(failure);
|
|
@@ -4844,6 +4864,109 @@ async function execHermesVersion(hermesBin, logger) {
|
|
|
4844
4864
|
});
|
|
4845
4865
|
throw new Error(summary);
|
|
4846
4866
|
}
|
|
4867
|
+
async function readHermesPythonMetadataVersion(hermesBin, timeoutMs) {
|
|
4868
|
+
const hermesPath = await resolveExecutablePath(hermesBin);
|
|
4869
|
+
if (!hermesPath) {
|
|
4870
|
+
return null;
|
|
4871
|
+
}
|
|
4872
|
+
const firstLine = await readFirstLine(hermesPath);
|
|
4873
|
+
const interpreter = parsePythonShebang(firstLine);
|
|
4874
|
+
if (!interpreter) {
|
|
4875
|
+
return null;
|
|
4876
|
+
}
|
|
4877
|
+
return await spawnHermesMetadataCommand(interpreter, timeoutMs);
|
|
4878
|
+
}
|
|
4879
|
+
async function spawnHermesMetadataCommand(interpreter, timeoutMs) {
|
|
4880
|
+
const script = [
|
|
4881
|
+
"import importlib.metadata as metadata",
|
|
4882
|
+
"try:",
|
|
4883
|
+
' version = metadata.version("hermes-agent")',
|
|
4884
|
+
' release_date = ""',
|
|
4885
|
+
"except Exception:",
|
|
4886
|
+
" import hermes_cli",
|
|
4887
|
+
' version = getattr(hermes_cli, "__version__", "")',
|
|
4888
|
+
' release_date = getattr(hermes_cli, "__release_date__", "")',
|
|
4889
|
+
"if not version:",
|
|
4890
|
+
' raise SystemExit("Hermes Agent version metadata not found")',
|
|
4891
|
+
'suffix = f" ({release_date})" if release_date else ""',
|
|
4892
|
+
'print(f"Hermes Agent v{version}{suffix}")',
|
|
4893
|
+
""
|
|
4894
|
+
].join("\n");
|
|
4895
|
+
return await new Promise((resolve, reject) => {
|
|
4896
|
+
const child = spawn(
|
|
4897
|
+
interpreter.command,
|
|
4898
|
+
[...interpreter.args, "-c", script],
|
|
4899
|
+
{
|
|
4900
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4901
|
+
windowsHide: true,
|
|
4902
|
+
env: { ...process.env, HERMES_NONINTERACTIVE: "1" }
|
|
4903
|
+
}
|
|
4904
|
+
);
|
|
4905
|
+
let stdout = "";
|
|
4906
|
+
let stderr = "";
|
|
4907
|
+
let settled = false;
|
|
4908
|
+
const timer = setTimeout(() => {
|
|
4909
|
+
finish(() => {
|
|
4910
|
+
reject(
|
|
4911
|
+
createVersionCommandError(
|
|
4912
|
+
`Hermes Python metadata probe timed out after ${timeoutMs}ms`,
|
|
4913
|
+
{ stdout, stderr, killed: true }
|
|
4914
|
+
)
|
|
4915
|
+
);
|
|
4916
|
+
}, true);
|
|
4917
|
+
}, timeoutMs);
|
|
4918
|
+
const finish = (callback, kill = false) => {
|
|
4919
|
+
if (settled) {
|
|
4920
|
+
return;
|
|
4921
|
+
}
|
|
4922
|
+
settled = true;
|
|
4923
|
+
clearTimeout(timer);
|
|
4924
|
+
if (kill && child.pid && !child.killed) {
|
|
4925
|
+
child.kill();
|
|
4926
|
+
}
|
|
4927
|
+
callback();
|
|
4928
|
+
};
|
|
4929
|
+
child.stdout?.on("data", (chunk) => {
|
|
4930
|
+
stdout += chunk.toString();
|
|
4931
|
+
});
|
|
4932
|
+
child.stderr?.on("data", (chunk) => {
|
|
4933
|
+
stderr += chunk.toString();
|
|
4934
|
+
});
|
|
4935
|
+
child.once("error", (error) => {
|
|
4936
|
+
finish(() => {
|
|
4937
|
+
reject(
|
|
4938
|
+
createVersionCommandError(error.message, {
|
|
4939
|
+
stdout,
|
|
4940
|
+
stderr,
|
|
4941
|
+
cause: error
|
|
4942
|
+
})
|
|
4943
|
+
);
|
|
4944
|
+
});
|
|
4945
|
+
});
|
|
4946
|
+
child.once("close", (code, signal) => {
|
|
4947
|
+
finish(() => {
|
|
4948
|
+
const raw = `${stdout}
|
|
4949
|
+
${stderr}`.trim();
|
|
4950
|
+
if (code === 0 && parseHermesVersion(raw)) {
|
|
4951
|
+
resolve({ stdout: raw });
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4954
|
+
reject(
|
|
4955
|
+
createVersionCommandError(
|
|
4956
|
+
`Hermes Python metadata probe exited with code ${code ?? "null"}`,
|
|
4957
|
+
{
|
|
4958
|
+
stdout,
|
|
4959
|
+
stderr,
|
|
4960
|
+
code,
|
|
4961
|
+
signal,
|
|
4962
|
+
killed: child.killed
|
|
4963
|
+
}
|
|
4964
|
+
)
|
|
4965
|
+
);
|
|
4966
|
+
});
|
|
4967
|
+
});
|
|
4968
|
+
});
|
|
4969
|
+
}
|
|
4847
4970
|
async function spawnHermesVersionCommand(hermesBin, args, timeoutMs) {
|
|
4848
4971
|
return await new Promise((resolve, reject) => {
|
|
4849
4972
|
const child = spawn(hermesBin, args, {
|
|
@@ -5424,6 +5547,20 @@ function createVersionCommandError(message, details) {
|
|
|
5424
5547
|
}
|
|
5425
5548
|
return error;
|
|
5426
5549
|
}
|
|
5550
|
+
function describeVersionMetadataFailure(hermesBin, error) {
|
|
5551
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5552
|
+
const details = readExecErrorDetails(error, message);
|
|
5553
|
+
return {
|
|
5554
|
+
summary: `${hermesBin} Python metadata probe failed: ${message}`,
|
|
5555
|
+
fields: {
|
|
5556
|
+
hermes_bin: hermesBin,
|
|
5557
|
+
command: "python-metadata",
|
|
5558
|
+
cwd: process.cwd(),
|
|
5559
|
+
error: message,
|
|
5560
|
+
...details
|
|
5561
|
+
}
|
|
5562
|
+
};
|
|
5563
|
+
}
|
|
5427
5564
|
function describeVersionCommandFailure(hermesBin, args, error) {
|
|
5428
5565
|
const message = error instanceof Error ? error.message : String(error);
|
|
5429
5566
|
const details = readExecErrorDetails(error, message);
|
|
@@ -5473,6 +5610,91 @@ function readExecErrorDetails(error, message) {
|
|
|
5473
5610
|
function truncateVersionLogOutput(value) {
|
|
5474
5611
|
return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
|
|
5475
5612
|
}
|
|
5613
|
+
async function resolveExecutablePath(command) {
|
|
5614
|
+
const candidates = executablePathCandidates(command);
|
|
5615
|
+
for (const candidate of candidates) {
|
|
5616
|
+
try {
|
|
5617
|
+
await access(candidate, fsConstants.X_OK);
|
|
5618
|
+
return await realpath(candidate).catch(() => candidate);
|
|
5619
|
+
} catch {
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
return null;
|
|
5623
|
+
}
|
|
5624
|
+
function executablePathCandidates(command) {
|
|
5625
|
+
const normalized = command.trim();
|
|
5626
|
+
if (!normalized) {
|
|
5627
|
+
return [];
|
|
5628
|
+
}
|
|
5629
|
+
if (/[\\/]/u.test(normalized)) {
|
|
5630
|
+
return withWindowsExecutableExtensions(normalized);
|
|
5631
|
+
}
|
|
5632
|
+
const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter(Boolean);
|
|
5633
|
+
return pathEntries.flatMap(
|
|
5634
|
+
(entry) => withWindowsExecutableExtensions(path7.join(entry, normalized))
|
|
5635
|
+
);
|
|
5636
|
+
}
|
|
5637
|
+
function withWindowsExecutableExtensions(filePath) {
|
|
5638
|
+
if (process.platform !== "win32" || path7.extname(filePath)) {
|
|
5639
|
+
return [filePath];
|
|
5640
|
+
}
|
|
5641
|
+
const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
|
|
5642
|
+
return [filePath, ...extensions.map((extension) => `${filePath}${extension}`)];
|
|
5643
|
+
}
|
|
5644
|
+
async function readFirstLine(filePath) {
|
|
5645
|
+
const raw = await readFile5(filePath, "utf8");
|
|
5646
|
+
return raw.split(/\r?\n/u, 1)[0] ?? "";
|
|
5647
|
+
}
|
|
5648
|
+
function parsePythonShebang(line) {
|
|
5649
|
+
if (!line.startsWith("#!")) {
|
|
5650
|
+
return null;
|
|
5651
|
+
}
|
|
5652
|
+
const parts = splitShellWords(line.slice(2).trim());
|
|
5653
|
+
if (parts.length === 0) {
|
|
5654
|
+
return null;
|
|
5655
|
+
}
|
|
5656
|
+
const commandName = path7.basename(parts[0] ?? "").toLowerCase();
|
|
5657
|
+
if (commandName === "env") {
|
|
5658
|
+
const args = expandEnvShebangArgs(parts.slice(1));
|
|
5659
|
+
const pythonIndex = args.findIndex((part) => isPythonCommandName(part));
|
|
5660
|
+
if (pythonIndex < 0) {
|
|
5661
|
+
return null;
|
|
5662
|
+
}
|
|
5663
|
+
return {
|
|
5664
|
+
command: parts[0],
|
|
5665
|
+
args
|
|
5666
|
+
};
|
|
5667
|
+
}
|
|
5668
|
+
if (!isPythonCommandName(commandName)) {
|
|
5669
|
+
return null;
|
|
5670
|
+
}
|
|
5671
|
+
return {
|
|
5672
|
+
command: parts[0],
|
|
5673
|
+
args: parts.slice(1)
|
|
5674
|
+
};
|
|
5675
|
+
}
|
|
5676
|
+
function isPythonCommandName(command) {
|
|
5677
|
+
return /^python(?:\d+(?:\.\d+)?)?$/iu.test(path7.basename(command));
|
|
5678
|
+
}
|
|
5679
|
+
function expandEnvShebangArgs(args) {
|
|
5680
|
+
const expanded = [];
|
|
5681
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
5682
|
+
const arg = args[index];
|
|
5683
|
+
if (arg === "-S" && args[index + 1]) {
|
|
5684
|
+
expanded.push(...splitShellWords(args[index + 1]));
|
|
5685
|
+
index += 1;
|
|
5686
|
+
continue;
|
|
5687
|
+
}
|
|
5688
|
+
if (arg !== "-S") {
|
|
5689
|
+
expanded.push(arg);
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
return expanded;
|
|
5693
|
+
}
|
|
5694
|
+
function splitShellWords(value) {
|
|
5695
|
+
const matches = value.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/gu);
|
|
5696
|
+
return [...matches].map((match) => match[1] ?? match[2] ?? match[3] ?? "");
|
|
5697
|
+
}
|
|
5476
5698
|
function compareSemver(left, right) {
|
|
5477
5699
|
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
|
|
5478
5700
|
const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -12677,7 +12899,7 @@ async function resolveHermesPythonCommand() {
|
|
|
12677
12899
|
if (explicit) {
|
|
12678
12900
|
return { command: explicit, args: [] };
|
|
12679
12901
|
}
|
|
12680
|
-
const hermesBin = await
|
|
12902
|
+
const hermesBin = await resolveExecutablePath2(resolveHermesBin());
|
|
12681
12903
|
if (hermesBin) {
|
|
12682
12904
|
const shebang = await readShebang(hermesBin);
|
|
12683
12905
|
const command = shebangToPythonCommand(shebang);
|
|
@@ -12690,7 +12912,7 @@ async function resolveHermesPythonCommand() {
|
|
|
12690
12912
|
args: []
|
|
12691
12913
|
};
|
|
12692
12914
|
}
|
|
12693
|
-
async function
|
|
12915
|
+
async function resolveExecutablePath2(command) {
|
|
12694
12916
|
if (path17.isAbsolute(command)) {
|
|
12695
12917
|
return await isExecutableFile(command) ? command : null;
|
|
12696
12918
|
}
|
package/dist/cli/index.js
CHANGED
package/dist/http/app.js
CHANGED