@hermespilot/link 0.7.8-beta.0 → 0.7.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.
- package/dist/{chunk-A3USRRZU.js → chunk-7JPQICDK.js} +494 -269
- package/dist/cli/index.js +5 -3
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import Router from "@koa/router";
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { createHash as createHash6, randomUUID as randomUUID11 } from "crypto";
|
|
8
|
-
import
|
|
8
|
+
import path25 from "path";
|
|
9
9
|
|
|
10
10
|
// src/database/link-database.ts
|
|
11
11
|
import { mkdir } from "fs/promises";
|
|
@@ -2032,6 +2032,13 @@ function translateChineseKnownErrorToEnglish(message) {
|
|
|
2032
2032
|
if (restartFailed?.groups) {
|
|
2033
2033
|
return `Hermes Gateway restart failed.${restartFailed.groups.detail}`;
|
|
2034
2034
|
}
|
|
2035
|
+
if (message.startsWith("Hermes Link \u5DF2\u8FDE\u63A5\uFF0C\u4F46\u672C\u673A\u6267\u884C\u9762\u4E0D\u53EF\u7528\uFF1A")) {
|
|
2036
|
+
return [
|
|
2037
|
+
"Hermes Link is reachable, but the local Hermes execution backend is unavailable because the background service cannot find the `hermes` command.",
|
|
2038
|
+
"First run `hermeslink restart` on this computer to try to recover. If that fixes the App, run `hermeslink autostart on` afterwards to refresh the boot autostart environment so this does not happen again after the next reboot.",
|
|
2039
|
+
"If it still fails, run `hermeslink doctor` for diagnostics. If Hermes Agent is installed in a custom location, set HERMES_BIN to the full path of the `hermes` executable."
|
|
2040
|
+
].join("\n");
|
|
2041
|
+
}
|
|
2035
2042
|
const cliMissing = /^没有找到可用的 Hermes Agent CLI。请先安装 Hermes Agent,或设置 HERMES_BIN。(?<detail>.*)$/u.exec(message);
|
|
2036
2043
|
if (cliMissing?.groups) {
|
|
2037
2044
|
return `No usable Hermes Agent CLI was found. Install Hermes Agent or set HERMES_BIN.${cliMissing.groups.detail}`;
|
|
@@ -6547,7 +6554,7 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
6547
6554
|
orderTimeMs: fileStat.mtimeMs
|
|
6548
6555
|
});
|
|
6549
6556
|
}
|
|
6550
|
-
return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path:
|
|
6557
|
+
return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path: path35, mtime }) => ({ path: path35, mtime }));
|
|
6551
6558
|
}
|
|
6552
6559
|
function readCronOutputTimestamp(fileName) {
|
|
6553
6560
|
const match = fileName.match(
|
|
@@ -6636,7 +6643,7 @@ function isConversationMissingError(error) {
|
|
|
6636
6643
|
}
|
|
6637
6644
|
|
|
6638
6645
|
// src/constants.ts
|
|
6639
|
-
var LINK_VERSION = "0.7.8
|
|
6646
|
+
var LINK_VERSION = "0.7.8";
|
|
6640
6647
|
var LINK_COMMAND = "hermeslink";
|
|
6641
6648
|
var LINK_DEFAULT_PORT = 52379;
|
|
6642
6649
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -6669,6 +6676,7 @@ function resolveRuntimePaths(homeDir = resolveRuntimeHome()) {
|
|
|
6669
6676
|
var defaultLinkConfig = {
|
|
6670
6677
|
port: LINK_DEFAULT_PORT,
|
|
6671
6678
|
lanHost: null,
|
|
6679
|
+
hermesBin: null,
|
|
6672
6680
|
serverBaseUrl: "https://hermes-server.clawpilot.me",
|
|
6673
6681
|
relayBaseUrl: "https://hermes-relay.clawpilot.me",
|
|
6674
6682
|
appConnectTokenIssuer: "https://hermes-server.clawpilot.me",
|
|
@@ -6680,6 +6688,7 @@ async function loadConfig(paths = resolveRuntimePaths()) {
|
|
|
6680
6688
|
const existing = await readJsonFile(paths.configFile);
|
|
6681
6689
|
const language = normalizeConfiguredLanguage(existing?.language);
|
|
6682
6690
|
const lanHost = normalizeLanHost(existing?.lanHost);
|
|
6691
|
+
const hermesBin = normalizeHermesBin(existing?.hermesBin);
|
|
6683
6692
|
const logLevel = normalizeLogLevel(
|
|
6684
6693
|
existing?.logLevel ?? process.env.HERMESLINK_LOG_LEVEL
|
|
6685
6694
|
);
|
|
@@ -6688,6 +6697,7 @@ async function loadConfig(paths = resolveRuntimePaths()) {
|
|
|
6688
6697
|
...existing ?? {},
|
|
6689
6698
|
language,
|
|
6690
6699
|
lanHost,
|
|
6700
|
+
hermesBin,
|
|
6691
6701
|
logLevel
|
|
6692
6702
|
};
|
|
6693
6703
|
}
|
|
@@ -6696,6 +6706,7 @@ async function saveConfig(patch, paths = resolveRuntimePaths()) {
|
|
|
6696
6706
|
const next = {
|
|
6697
6707
|
...current,
|
|
6698
6708
|
...patch,
|
|
6709
|
+
hermesBin: patch.hermesBin === void 0 ? current.hermesBin : normalizeHermesBin(patch.hermesBin),
|
|
6699
6710
|
logLevel: patch.logLevel === void 0 ? current.logLevel : normalizeLogLevel(patch.logLevel)
|
|
6700
6711
|
};
|
|
6701
6712
|
await writeJsonFile(paths.configFile, next);
|
|
@@ -6735,6 +6746,16 @@ function normalizeLanHost(value) {
|
|
|
6735
6746
|
}
|
|
6736
6747
|
return host;
|
|
6737
6748
|
}
|
|
6749
|
+
function normalizeHermesBin(value) {
|
|
6750
|
+
if (value === null || value === void 0) {
|
|
6751
|
+
return null;
|
|
6752
|
+
}
|
|
6753
|
+
if (typeof value !== "string") {
|
|
6754
|
+
return null;
|
|
6755
|
+
}
|
|
6756
|
+
const trimmed = value.trim();
|
|
6757
|
+
return trimmed || null;
|
|
6758
|
+
}
|
|
6738
6759
|
function isUsableLanIpv4(value) {
|
|
6739
6760
|
const parts = value.split(".").map((part) => Number.parseInt(part, 10));
|
|
6740
6761
|
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
@@ -6904,9 +6925,8 @@ function readErrorMessage(payload) {
|
|
|
6904
6925
|
|
|
6905
6926
|
// src/hermes/gateway.ts
|
|
6906
6927
|
import { execFile as execFile2, spawn } from "child_process";
|
|
6907
|
-
import {
|
|
6908
|
-
import
|
|
6909
|
-
import path7 from "path";
|
|
6928
|
+
import { access, readFile as readFile5, stat as stat4 } from "fs/promises";
|
|
6929
|
+
import path8 from "path";
|
|
6910
6930
|
import { setTimeout as delay2 } from "timers/promises";
|
|
6911
6931
|
import { promisify as promisify2 } from "util";
|
|
6912
6932
|
|
|
@@ -7287,6 +7307,15 @@ function rotatedLogFile(filePath, index) {
|
|
|
7287
7307
|
|
|
7288
7308
|
// src/hermes/cli.ts
|
|
7289
7309
|
import { execFile } from "child_process";
|
|
7310
|
+
import {
|
|
7311
|
+
accessSync,
|
|
7312
|
+
constants as fsConstants,
|
|
7313
|
+
readFileSync,
|
|
7314
|
+
realpathSync,
|
|
7315
|
+
statSync
|
|
7316
|
+
} from "fs";
|
|
7317
|
+
import os4 from "os";
|
|
7318
|
+
import path7 from "path";
|
|
7290
7319
|
import { promisify } from "util";
|
|
7291
7320
|
var execFileAsync = promisify(execFile);
|
|
7292
7321
|
async function deleteHermesSession(sessionId, profileName = "default") {
|
|
@@ -7379,7 +7408,158 @@ async function renameHermesSession(sessionId, title, profileName = "default") {
|
|
|
7379
7408
|
}
|
|
7380
7409
|
}
|
|
7381
7410
|
function resolveHermesBin() {
|
|
7382
|
-
return
|
|
7411
|
+
return resolveHermesBinResolution().bin;
|
|
7412
|
+
}
|
|
7413
|
+
function resolveHermesBinResolution(paths = resolveRuntimePaths()) {
|
|
7414
|
+
const envHermesBin = process.env.HERMES_BIN?.trim();
|
|
7415
|
+
if (envHermesBin) {
|
|
7416
|
+
return { bin: envHermesBin, source: "env" };
|
|
7417
|
+
}
|
|
7418
|
+
const configuredHermesBin = readConfiguredHermesBin(paths);
|
|
7419
|
+
if (configuredHermesBin) {
|
|
7420
|
+
const resolved = resolveExecutablePathSync(configuredHermesBin);
|
|
7421
|
+
if (resolved) {
|
|
7422
|
+
return { bin: resolved, source: "config" };
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
const pathHermesBin = resolveExecutablePathSync("hermes", { pathOnly: true });
|
|
7426
|
+
if (pathHermesBin) {
|
|
7427
|
+
return { bin: pathHermesBin, source: "path" };
|
|
7428
|
+
}
|
|
7429
|
+
const commonHermesBin = commonHermesBinCandidates().map((candidate) => resolveExecutablePathSync(candidate)).find((candidate) => Boolean(candidate));
|
|
7430
|
+
if (commonHermesBin) {
|
|
7431
|
+
return { bin: commonHermesBin, source: "common" };
|
|
7432
|
+
}
|
|
7433
|
+
return { bin: "hermes", source: "fallback" };
|
|
7434
|
+
}
|
|
7435
|
+
async function rememberResolvedHermesBin(hermesBin, paths = resolveRuntimePaths()) {
|
|
7436
|
+
const resolved = resolveExecutablePathSync(hermesBin);
|
|
7437
|
+
if (!resolved || !path7.isAbsolute(resolved)) {
|
|
7438
|
+
return null;
|
|
7439
|
+
}
|
|
7440
|
+
if (readConfiguredHermesBin(paths) === resolved) {
|
|
7441
|
+
return resolved;
|
|
7442
|
+
}
|
|
7443
|
+
await saveConfig({ hermesBin: resolved }, paths);
|
|
7444
|
+
return resolved;
|
|
7445
|
+
}
|
|
7446
|
+
function resolveExecutablePathSync(command, options = {}) {
|
|
7447
|
+
const candidates = executablePathCandidates(command, options);
|
|
7448
|
+
for (const candidate of candidates) {
|
|
7449
|
+
if (!isExecutableFile(candidate)) {
|
|
7450
|
+
continue;
|
|
7451
|
+
}
|
|
7452
|
+
try {
|
|
7453
|
+
return realpathSync(candidate);
|
|
7454
|
+
} catch {
|
|
7455
|
+
return candidate;
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
return null;
|
|
7459
|
+
}
|
|
7460
|
+
function isHermesExecutableMissingError(error) {
|
|
7461
|
+
if (typeof error === "object" && error !== null && "code" in error) {
|
|
7462
|
+
const code = error.code;
|
|
7463
|
+
if (code === "ENOENT") {
|
|
7464
|
+
return true;
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7468
|
+
return /\bspawn\b[\s\S]*\bENOENT\b/u.test(message);
|
|
7469
|
+
}
|
|
7470
|
+
function createHermesCliMissingError(error, language = "zh-CN") {
|
|
7471
|
+
return new LinkHttpError(
|
|
7472
|
+
503,
|
|
7473
|
+
"hermes_agent_cli_missing",
|
|
7474
|
+
buildHermesCliMissingMessage(resolveHermesBin(), error, language)
|
|
7475
|
+
);
|
|
7476
|
+
}
|
|
7477
|
+
function buildHermesCliMissingMessage(hermesBin, error, language = "zh-CN") {
|
|
7478
|
+
if (language === "en") {
|
|
7479
|
+
return buildEnglishHermesCliMissingMessage(hermesBin, error);
|
|
7480
|
+
}
|
|
7481
|
+
const detail = path7.isAbsolute(hermesBin) ? `\u914D\u7F6E\u7684 Hermes CLI \u8DEF\u5F84\u4E0D\u53EF\u7528\uFF1A${hermesBin}` : `\u540E\u53F0\u73AF\u5883\u627E\u4E0D\u5230 Hermes Agent \u7684 \`${hermesBin}\` \u547D\u4EE4`;
|
|
7482
|
+
const originalHint = isHermesExecutableMissingError(error) ? "" : formatHermesCliErrorHint(error);
|
|
7483
|
+
return [
|
|
7484
|
+
`Hermes Link \u5DF2\u8FDE\u63A5\uFF0C\u4F46\u672C\u673A\u6267\u884C\u9762\u4E0D\u53EF\u7528\uFF1A${detail}\uFF0C\u56E0\u6B64\u65E0\u6CD5\u542F\u52A8 Hermes Agent \u804A\u5929\u8FDB\u7A0B\u3002`,
|
|
7485
|
+
"\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `hermeslink restart` \u5C1D\u8BD5\u6062\u590D\u3002\u82E5\u91CD\u542F\u540E\u6062\u590D\u6B63\u5E38\uFF0C\u8BF7\u518D\u8FD0\u884C `hermeslink autostart on` \u5237\u65B0\u5F00\u673A\u81EA\u542F\u73AF\u5883\uFF0C\u907F\u514D\u4E0B\u6B21\u91CD\u542F\u7535\u8111\u540E\u518D\u6B21\u53D1\u751F\u3002",
|
|
7486
|
+
"\u5982\u679C\u4ECD\u7136\u5931\u8D25\uFF0C\u8BF7\u8FD0\u884C `hermeslink doctor` \u67E5\u770B\u8BCA\u65AD\uFF1B\u82E5 Hermes Agent \u5B89\u88C5\u5728\u81EA\u5B9A\u4E49\u8DEF\u5F84\uFF0C\u8BF7\u5C06 HERMES_BIN \u8BBE\u7F6E\u4E3A `hermes` \u53EF\u6267\u884C\u6587\u4EF6\u7684\u5B8C\u6574\u8DEF\u5F84\u3002",
|
|
7487
|
+
originalHint
|
|
7488
|
+
].filter(Boolean).join("\n");
|
|
7489
|
+
}
|
|
7490
|
+
function buildEnglishHermesCliMissingMessage(hermesBin, error) {
|
|
7491
|
+
const detail = path7.isAbsolute(hermesBin) ? `the configured Hermes CLI path is not usable: ${hermesBin}` : `the background environment cannot find the \`${hermesBin}\` command`;
|
|
7492
|
+
const originalHint = isHermesExecutableMissingError(error) ? "" : formatHermesCliErrorHint(error, "en");
|
|
7493
|
+
return [
|
|
7494
|
+
`Hermes Link is reachable, but the local Hermes execution backend is unavailable because ${detail}. Hermes Link cannot start the Hermes Agent chat process.`,
|
|
7495
|
+
"First run `hermeslink restart` on this computer to try to recover. If that fixes the App, run `hermeslink autostart on` afterwards to refresh the boot autostart environment so this does not happen again after the next reboot.",
|
|
7496
|
+
"If it still fails, run `hermeslink doctor` for diagnostics. If Hermes Agent is installed in a custom location, set HERMES_BIN to the full path of the `hermes` executable.",
|
|
7497
|
+
originalHint
|
|
7498
|
+
].filter(Boolean).join("\n");
|
|
7499
|
+
}
|
|
7500
|
+
function formatHermesCliErrorHint(error, language = "zh-CN") {
|
|
7501
|
+
if (error === void 0 || error === null) {
|
|
7502
|
+
return "";
|
|
7503
|
+
}
|
|
7504
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7505
|
+
if (!message.trim()) {
|
|
7506
|
+
return "";
|
|
7507
|
+
}
|
|
7508
|
+
return language === "en" ? `Raw diagnostic: ${message}` : `\u539F\u59CB\u8BCA\u65AD\uFF1A${message}`;
|
|
7509
|
+
}
|
|
7510
|
+
function readConfiguredHermesBin(paths) {
|
|
7511
|
+
try {
|
|
7512
|
+
const raw = readFileSync(paths.configFile, "utf8");
|
|
7513
|
+
const payload = JSON.parse(raw);
|
|
7514
|
+
return normalizeHermesBin(payload.hermesBin);
|
|
7515
|
+
} catch {
|
|
7516
|
+
return null;
|
|
7517
|
+
}
|
|
7518
|
+
}
|
|
7519
|
+
function executablePathCandidates(command, options) {
|
|
7520
|
+
const normalized = command.trim();
|
|
7521
|
+
if (!normalized) {
|
|
7522
|
+
return [];
|
|
7523
|
+
}
|
|
7524
|
+
if (/[\\/]/u.test(normalized)) {
|
|
7525
|
+
return options.pathOnly ? [] : withWindowsExecutableExtensions(normalized);
|
|
7526
|
+
}
|
|
7527
|
+
const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter(Boolean);
|
|
7528
|
+
return pathEntries.flatMap(
|
|
7529
|
+
(entry) => withWindowsExecutableExtensions(path7.join(entry, normalized))
|
|
7530
|
+
);
|
|
7531
|
+
}
|
|
7532
|
+
function commonHermesBinCandidates() {
|
|
7533
|
+
const home = os4.homedir();
|
|
7534
|
+
return [
|
|
7535
|
+
path7.join(home, ".hermes", "hermes-agent", "venv", "bin", "hermes"),
|
|
7536
|
+
path7.join(home, ".local", "bin", "hermes"),
|
|
7537
|
+
...process.platform === "win32" ? [] : [
|
|
7538
|
+
"/opt/homebrew/bin/hermes",
|
|
7539
|
+
"/usr/local/bin/hermes",
|
|
7540
|
+
"/usr/bin/hermes",
|
|
7541
|
+
"/bin/hermes"
|
|
7542
|
+
]
|
|
7543
|
+
];
|
|
7544
|
+
}
|
|
7545
|
+
function withWindowsExecutableExtensions(filePath) {
|
|
7546
|
+
if (process.platform !== "win32" || path7.extname(filePath)) {
|
|
7547
|
+
return [filePath];
|
|
7548
|
+
}
|
|
7549
|
+
const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
|
|
7550
|
+
return [filePath, ...extensions.map((extension) => `${filePath}${extension}`)];
|
|
7551
|
+
}
|
|
7552
|
+
function isExecutableFile(filePath) {
|
|
7553
|
+
try {
|
|
7554
|
+
const stats = statSync(filePath);
|
|
7555
|
+
if (!stats.isFile()) {
|
|
7556
|
+
return false;
|
|
7557
|
+
}
|
|
7558
|
+
accessSync(filePath, fsConstants.X_OK);
|
|
7559
|
+
return true;
|
|
7560
|
+
} catch {
|
|
7561
|
+
return false;
|
|
7562
|
+
}
|
|
7383
7563
|
}
|
|
7384
7564
|
function readSessionDeleteStatus(stdout, stderr) {
|
|
7385
7565
|
const output = `${stdout.toString()}
|
|
@@ -7486,7 +7666,8 @@ async function ensureHermesApiServerAvailable(options = {}) {
|
|
|
7486
7666
|
const start = await startHermesGatewayOnce(
|
|
7487
7667
|
options.paths ?? resolveRuntimePaths(),
|
|
7488
7668
|
profileName,
|
|
7489
|
-
options.logger
|
|
7669
|
+
options.logger,
|
|
7670
|
+
options.language
|
|
7490
7671
|
);
|
|
7491
7672
|
health = await waitForHermesApiHealth(
|
|
7492
7673
|
configResult.apiServer,
|
|
@@ -7512,7 +7693,8 @@ async function ensureHermesApiServerAvailable(options = {}) {
|
|
|
7512
7693
|
const repairedStart = await startHermesGatewayOnce(
|
|
7513
7694
|
options.paths ?? resolveRuntimePaths(),
|
|
7514
7695
|
profileName,
|
|
7515
|
-
options.logger
|
|
7696
|
+
options.logger,
|
|
7697
|
+
options.language
|
|
7516
7698
|
);
|
|
7517
7699
|
health = await waitForHermesApiHealth(
|
|
7518
7700
|
repairedConfigResult.apiServer,
|
|
@@ -7673,7 +7855,7 @@ async function execHermesVersion(hermesBin, logger) {
|
|
|
7673
7855
|
throw new Error(summary);
|
|
7674
7856
|
}
|
|
7675
7857
|
async function readHermesPythonMetadataVersion(hermesBin, timeoutMs) {
|
|
7676
|
-
const hermesPath =
|
|
7858
|
+
const hermesPath = resolveExecutablePathSync(hermesBin);
|
|
7677
7859
|
if (!hermesPath) {
|
|
7678
7860
|
return null;
|
|
7679
7861
|
}
|
|
@@ -7865,22 +8047,25 @@ function assertHermesRunsApiSupported(version, status, endpoint = "/v1/runs", la
|
|
|
7865
8047
|
language === "en" ? `${versionText}The current Hermes Agent API Server does not support the ${endpoint} endpoint required by HermesPilot. Run \`hermes update\` to upgrade Hermes Agent.` : `${versionText}\u5F53\u524D Hermes Agent API Server \u4E0D\u652F\u6301 HermesPilot \u9700\u8981\u7684 ${endpoint} \u63A5\u53E3\uFF0C\u8BF7\u5148\u8FD0\u884C \`hermes update\` \u5347\u7EA7 Hermes Agent\u3002`
|
|
7866
8048
|
);
|
|
7867
8049
|
}
|
|
7868
|
-
async function startHermesGatewayOnce(paths, profileName, logger) {
|
|
8050
|
+
async function startHermesGatewayOnce(paths, profileName, logger, language = "zh-CN") {
|
|
7869
8051
|
if (!gatewayStartInFlightByProfile.has(profileName)) {
|
|
7870
8052
|
gatewayStartInFlightByProfile.set(
|
|
7871
8053
|
profileName,
|
|
7872
|
-
startHermesGateway(paths, profileName, logger).finally(() => {
|
|
8054
|
+
startHermesGateway(paths, profileName, logger, language).finally(() => {
|
|
7873
8055
|
gatewayStartInFlightByProfile.delete(profileName);
|
|
7874
8056
|
})
|
|
7875
8057
|
);
|
|
7876
8058
|
}
|
|
7877
8059
|
return await gatewayStartInFlightByProfile.get(profileName);
|
|
7878
8060
|
}
|
|
7879
|
-
async function startHermesGateway(paths, profileName, logger) {
|
|
8061
|
+
async function startHermesGateway(paths, profileName, logger, language = "zh-CN") {
|
|
7880
8062
|
const version = await readHermesVersion({ logger }).catch((error) => {
|
|
7881
8063
|
void logger?.error("gateway_hermes_cli_unavailable", {
|
|
7882
8064
|
error: error instanceof Error ? error.message : String(error)
|
|
7883
8065
|
});
|
|
8066
|
+
if (isHermesExecutableMissingError(error)) {
|
|
8067
|
+
throw createHermesCliMissingError(error, language);
|
|
8068
|
+
}
|
|
7884
8069
|
throw new LinkHttpError(
|
|
7885
8070
|
503,
|
|
7886
8071
|
"hermes_agent_not_available",
|
|
@@ -7899,11 +8084,12 @@ async function startHermesGateway(paths, profileName, logger) {
|
|
|
7899
8084
|
}
|
|
7900
8085
|
const gatewayLog = createRotatingTextLogWriter({
|
|
7901
8086
|
paths,
|
|
7902
|
-
fileName:
|
|
8087
|
+
fileName: path8.basename(getGatewayRuntimeLogFile(paths))
|
|
7903
8088
|
});
|
|
7904
8089
|
const logPath = gatewayLog.filePath;
|
|
7905
8090
|
try {
|
|
7906
|
-
const
|
|
8091
|
+
const hermesBin = resolveHermesBin();
|
|
8092
|
+
const child = spawn(hermesBin, gatewayRunArgs(profileName), {
|
|
7907
8093
|
detached: true,
|
|
7908
8094
|
env: buildHermesGatewayChildEnv(),
|
|
7909
8095
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7934,7 +8120,20 @@ async function startHermesGateway(paths, profileName, logger) {
|
|
|
7934
8120
|
callback();
|
|
7935
8121
|
};
|
|
7936
8122
|
child.once("spawn", () => finish(resolve));
|
|
7937
|
-
child.once(
|
|
8123
|
+
child.once(
|
|
8124
|
+
"error",
|
|
8125
|
+
(error) => finish(() => {
|
|
8126
|
+
if (isHermesExecutableMissingError(error)) {
|
|
8127
|
+
void logger?.error("gateway_hermes_cli_spawn_missing", {
|
|
8128
|
+
hermes_bin: hermesBin,
|
|
8129
|
+
error: error.message
|
|
8130
|
+
});
|
|
8131
|
+
reject(createHermesCliMissingError(error, language));
|
|
8132
|
+
return;
|
|
8133
|
+
}
|
|
8134
|
+
reject(error);
|
|
8135
|
+
})
|
|
8136
|
+
);
|
|
7938
8137
|
});
|
|
7939
8138
|
unrefDetachedGatewayProcess(child);
|
|
7940
8139
|
void logger?.info("gateway_process_spawned", {
|
|
@@ -7969,7 +8168,7 @@ async function restartHermesGatewayServiceIfAvailable(options) {
|
|
|
7969
8168
|
if (!home) {
|
|
7970
8169
|
return null;
|
|
7971
8170
|
}
|
|
7972
|
-
const launchdPlistPath =
|
|
8171
|
+
const launchdPlistPath = path8.join(
|
|
7973
8172
|
home,
|
|
7974
8173
|
"Library",
|
|
7975
8174
|
"LaunchAgents",
|
|
@@ -7988,13 +8187,16 @@ async function restartHermesGatewayServiceIfAvailable(options) {
|
|
|
7988
8187
|
void options.logger?.error("gateway_service_restart_failed", {
|
|
7989
8188
|
error: error instanceof Error ? error.message : String(error)
|
|
7990
8189
|
});
|
|
8190
|
+
if (isHermesExecutableMissingError(error)) {
|
|
8191
|
+
throw createHermesCliMissingError(error, options.language);
|
|
8192
|
+
}
|
|
7991
8193
|
throw new LinkHttpError(
|
|
7992
8194
|
503,
|
|
7993
8195
|
"hermes_gateway_restart_failed",
|
|
7994
8196
|
`Hermes Gateway \u670D\u52A1\u91CD\u542F\u5931\u8D25\u3002${formatErrorSuffix(error)}`
|
|
7995
8197
|
);
|
|
7996
8198
|
}
|
|
7997
|
-
const logPath =
|
|
8199
|
+
const logPath = path8.join(home, ".hermes", "logs", "gateway.error.log");
|
|
7998
8200
|
const logHint = await readRecentGatewayLogHint(logPath);
|
|
7999
8201
|
return {
|
|
8000
8202
|
pid: null,
|
|
@@ -8414,6 +8616,12 @@ async function readHermesDashboardVersion(options = {}) {
|
|
|
8414
8616
|
async function probeHermesVersion(hermesBin, options) {
|
|
8415
8617
|
try {
|
|
8416
8618
|
const { stdout } = await execHermesVersion(hermesBin, options.logger);
|
|
8619
|
+
await rememberResolvedHermesBin(hermesBin).catch((error) => {
|
|
8620
|
+
void options.logger?.debug("hermes_bin_remember_failed", {
|
|
8621
|
+
hermes_bin: hermesBin,
|
|
8622
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8623
|
+
});
|
|
8624
|
+
});
|
|
8417
8625
|
const raw = stdout.trim();
|
|
8418
8626
|
const version = parseHermesVersion(raw);
|
|
8419
8627
|
return buildHermesVersionInfo(raw, version);
|
|
@@ -8523,37 +8731,6 @@ function readExecErrorDetails(error, message) {
|
|
|
8523
8731
|
function truncateVersionLogOutput(value) {
|
|
8524
8732
|
return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
|
|
8525
8733
|
}
|
|
8526
|
-
async function resolveExecutablePath(command) {
|
|
8527
|
-
const candidates = executablePathCandidates(command);
|
|
8528
|
-
for (const candidate of candidates) {
|
|
8529
|
-
try {
|
|
8530
|
-
await access(candidate, fsConstants.X_OK);
|
|
8531
|
-
return await realpath(candidate).catch(() => candidate);
|
|
8532
|
-
} catch {
|
|
8533
|
-
}
|
|
8534
|
-
}
|
|
8535
|
-
return null;
|
|
8536
|
-
}
|
|
8537
|
-
function executablePathCandidates(command) {
|
|
8538
|
-
const normalized = command.trim();
|
|
8539
|
-
if (!normalized) {
|
|
8540
|
-
return [];
|
|
8541
|
-
}
|
|
8542
|
-
if (/[\\/]/u.test(normalized)) {
|
|
8543
|
-
return withWindowsExecutableExtensions(normalized);
|
|
8544
|
-
}
|
|
8545
|
-
const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter(Boolean);
|
|
8546
|
-
return pathEntries.flatMap(
|
|
8547
|
-
(entry) => withWindowsExecutableExtensions(path7.join(entry, normalized))
|
|
8548
|
-
);
|
|
8549
|
-
}
|
|
8550
|
-
function withWindowsExecutableExtensions(filePath) {
|
|
8551
|
-
if (process.platform !== "win32" || path7.extname(filePath)) {
|
|
8552
|
-
return [filePath];
|
|
8553
|
-
}
|
|
8554
|
-
const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
|
|
8555
|
-
return [filePath, ...extensions.map((extension) => `${filePath}${extension}`)];
|
|
8556
|
-
}
|
|
8557
8734
|
async function readFirstLine(filePath) {
|
|
8558
8735
|
const raw = await readFile5(filePath, "utf8");
|
|
8559
8736
|
return raw.split(/\r?\n/u, 1)[0] ?? "";
|
|
@@ -8566,7 +8743,7 @@ function parsePythonShebang(line) {
|
|
|
8566
8743
|
if (parts.length === 0) {
|
|
8567
8744
|
return null;
|
|
8568
8745
|
}
|
|
8569
|
-
const commandName =
|
|
8746
|
+
const commandName = path8.basename(parts[0] ?? "").toLowerCase();
|
|
8570
8747
|
if (commandName === "env") {
|
|
8571
8748
|
const args = expandEnvShebangArgs(parts.slice(1));
|
|
8572
8749
|
const pythonIndex = args.findIndex((part) => isPythonCommandName(part));
|
|
@@ -8587,7 +8764,7 @@ function parsePythonShebang(line) {
|
|
|
8587
8764
|
};
|
|
8588
8765
|
}
|
|
8589
8766
|
function isPythonCommandName(command) {
|
|
8590
|
-
return /^python(?:\d+(?:\.\d+)?)?$/iu.test(
|
|
8767
|
+
return /^python(?:\d+(?:\.\d+)?)?$/iu.test(path8.basename(command));
|
|
8591
8768
|
}
|
|
8592
8769
|
function expandEnvShebangArgs(args) {
|
|
8593
8770
|
const expanded = [];
|
|
@@ -8631,7 +8808,7 @@ import { spawn as spawn2 } from "child_process";
|
|
|
8631
8808
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8632
8809
|
import { readFile as readFile6, stat as stat5 } from "fs/promises";
|
|
8633
8810
|
import net2 from "net";
|
|
8634
|
-
import
|
|
8811
|
+
import path9 from "path";
|
|
8635
8812
|
import { setTimeout as delay3 } from "timers/promises";
|
|
8636
8813
|
import WebSocket from "ws";
|
|
8637
8814
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
@@ -8657,7 +8834,8 @@ async function streamTuiGatewayRun(input) {
|
|
|
8657
8834
|
profileName,
|
|
8658
8835
|
paths: input.paths,
|
|
8659
8836
|
logger: input.logger,
|
|
8660
|
-
signal: input.signal
|
|
8837
|
+
signal: input.signal,
|
|
8838
|
+
language: input.language
|
|
8661
8839
|
});
|
|
8662
8840
|
const started = await ensureRpcSession(client, {
|
|
8663
8841
|
...input,
|
|
@@ -8961,9 +9139,10 @@ async function startTuiGatewayBackend(input) {
|
|
|
8961
9139
|
];
|
|
8962
9140
|
const logWriter = createRotatingTextLogWriter({
|
|
8963
9141
|
paths,
|
|
8964
|
-
fileName:
|
|
9142
|
+
fileName: path9.basename(getGatewayRuntimeLogFile(paths))
|
|
8965
9143
|
});
|
|
8966
|
-
const
|
|
9144
|
+
const hermesBin = resolveHermesBin();
|
|
9145
|
+
const child = spawn2(hermesBin, args, {
|
|
8967
9146
|
env: {
|
|
8968
9147
|
...process.env,
|
|
8969
9148
|
HERMES_DASHBOARD_SESSION_TOKEN: token
|
|
@@ -8988,7 +9167,13 @@ async function startTuiGatewayBackend(input) {
|
|
|
8988
9167
|
signal
|
|
8989
9168
|
});
|
|
8990
9169
|
});
|
|
8991
|
-
await waitForChildSpawn(
|
|
9170
|
+
await waitForChildSpawn(
|
|
9171
|
+
child,
|
|
9172
|
+
input.signal,
|
|
9173
|
+
input.logger,
|
|
9174
|
+
hermesBin,
|
|
9175
|
+
input.language
|
|
9176
|
+
);
|
|
8992
9177
|
const baseUrl = `http://127.0.0.1:${port}`;
|
|
8993
9178
|
const wsUrl = `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(token)}`;
|
|
8994
9179
|
try {
|
|
@@ -9764,7 +9949,7 @@ async function readLogTailSummary(logPath) {
|
|
|
9764
9949
|
return "";
|
|
9765
9950
|
}
|
|
9766
9951
|
}
|
|
9767
|
-
async function waitForChildSpawn(child, signal) {
|
|
9952
|
+
async function waitForChildSpawn(child, signal, logger, hermesBin = resolveHermesBin(), language = "zh-CN") {
|
|
9768
9953
|
await new Promise((resolve, reject) => {
|
|
9769
9954
|
const abort = () => {
|
|
9770
9955
|
child.kill();
|
|
@@ -9787,6 +9972,14 @@ async function waitForChildSpawn(child, signal) {
|
|
|
9787
9972
|
};
|
|
9788
9973
|
const onError = (error) => {
|
|
9789
9974
|
cleanup();
|
|
9975
|
+
if (isHermesExecutableMissingError(error)) {
|
|
9976
|
+
void logger?.error("tui_gateway_hermes_cli_spawn_missing", {
|
|
9977
|
+
hermes_bin: hermesBin,
|
|
9978
|
+
error: error.message
|
|
9979
|
+
});
|
|
9980
|
+
reject(createHermesCliMissingError(error, language));
|
|
9981
|
+
return;
|
|
9982
|
+
}
|
|
9790
9983
|
reject(error);
|
|
9791
9984
|
};
|
|
9792
9985
|
signal?.addEventListener("abort", abort, { once: true });
|
|
@@ -10452,11 +10645,11 @@ function workspaceNotFound() {
|
|
|
10452
10645
|
// src/conversations/blob-store.ts
|
|
10453
10646
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
10454
10647
|
import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3, stat as stat6, writeFile } from "fs/promises";
|
|
10455
|
-
import
|
|
10648
|
+
import path11 from "path";
|
|
10456
10649
|
|
|
10457
10650
|
// src/conversations/media.ts
|
|
10458
10651
|
import { createHash as createHash2 } from "crypto";
|
|
10459
|
-
import
|
|
10652
|
+
import path10 from "path";
|
|
10460
10653
|
|
|
10461
10654
|
// src/conversations/delivery-contract.ts
|
|
10462
10655
|
var HERMES_LINK_DELIVERY_TAG_PATTERN = /<hermes_link_delivery>\s*(\{[\s\S]*?\})\s*<\/hermes_link_delivery>/giu;
|
|
@@ -10546,7 +10739,7 @@ function cleanMessageTextParts(message) {
|
|
|
10546
10739
|
}
|
|
10547
10740
|
}
|
|
10548
10741
|
function inferMimeType(filePath) {
|
|
10549
|
-
const extension =
|
|
10742
|
+
const extension = path10.extname(filePath).toLowerCase();
|
|
10550
10743
|
return {
|
|
10551
10744
|
".png": "image/png",
|
|
10552
10745
|
".jpg": "image/jpeg",
|
|
@@ -10670,7 +10863,7 @@ function mediaSourceKey(sourcePath) {
|
|
|
10670
10863
|
return createHash2("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
|
|
10671
10864
|
}
|
|
10672
10865
|
function sanitizeFilename(value, fallback) {
|
|
10673
|
-
const base =
|
|
10866
|
+
const base = path10.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
|
|
10674
10867
|
const safe = base.replace(/[/:\\]/gu, "_").slice(0, 200).trim();
|
|
10675
10868
|
return safe || fallback;
|
|
10676
10869
|
}
|
|
@@ -10699,9 +10892,9 @@ function isBlobReferencedByMessages(snapshot, blobId) {
|
|
|
10699
10892
|
}
|
|
10700
10893
|
function resolveMediaSourcePath(sourcePath) {
|
|
10701
10894
|
const trimmed = sourcePath.trim();
|
|
10702
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
10703
|
-
const resolved =
|
|
10704
|
-
if (!
|
|
10895
|
+
const expanded = trimmed.startsWith("~/") ? path10.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
|
|
10896
|
+
const resolved = path10.resolve(expanded);
|
|
10897
|
+
if (!path10.isAbsolute(expanded)) {
|
|
10705
10898
|
throw new LinkHttpError(
|
|
10706
10899
|
400,
|
|
10707
10900
|
"media_source_path_not_absolute",
|
|
@@ -10896,7 +11089,7 @@ function normalizeConversationIds(conversationIds) {
|
|
|
10896
11089
|
}
|
|
10897
11090
|
function blobPath(paths, blobId) {
|
|
10898
11091
|
assertValidBlobId(blobId);
|
|
10899
|
-
return
|
|
11092
|
+
return path11.join(paths.blobsDir, `${blobId}.bin`);
|
|
10900
11093
|
}
|
|
10901
11094
|
async function writeConversationBlob(paths, conversationId, input, options) {
|
|
10902
11095
|
assertValidConversationId(conversationId);
|
|
@@ -10905,7 +11098,7 @@ async function writeConversationBlob(paths, conversationId, input, options) {
|
|
|
10905
11098
|
}
|
|
10906
11099
|
const id = `blob_${randomUUID5().replaceAll("-", "")}`;
|
|
10907
11100
|
const filePath = blobPath(paths, id);
|
|
10908
|
-
await mkdir6(
|
|
11101
|
+
await mkdir6(path11.dirname(filePath), { recursive: true, mode: 448 });
|
|
10909
11102
|
await writeFile(filePath, input.bytes, { mode: 384 });
|
|
10910
11103
|
const blob = {
|
|
10911
11104
|
id,
|
|
@@ -10981,7 +11174,7 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
|
|
|
10981
11174
|
}
|
|
10982
11175
|
}
|
|
10983
11176
|
const targetDir = conversationAttachmentsDir(paths, conversationId);
|
|
10984
|
-
const targetPath =
|
|
11177
|
+
const targetPath = path11.join(
|
|
10985
11178
|
targetDir,
|
|
10986
11179
|
materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
|
|
10987
11180
|
);
|
|
@@ -11034,7 +11227,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
11034
11227
|
continue;
|
|
11035
11228
|
}
|
|
11036
11229
|
const manifest = await readJsonFile(
|
|
11037
|
-
|
|
11230
|
+
path11.join(paths.blobsDir, entry.name)
|
|
11038
11231
|
).catch(() => null);
|
|
11039
11232
|
if (manifest?.conversation_ids?.includes(conversationId)) {
|
|
11040
11233
|
blobIds.push(blobId);
|
|
@@ -11044,7 +11237,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
11044
11237
|
}
|
|
11045
11238
|
function conversationAttachmentsDir(paths, conversationId) {
|
|
11046
11239
|
assertValidConversationId(conversationId);
|
|
11047
|
-
return
|
|
11240
|
+
return path11.join(paths.conversationsDir, conversationId, "attachments");
|
|
11048
11241
|
}
|
|
11049
11242
|
function isNodeError6(error, code) {
|
|
11050
11243
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
@@ -12079,7 +12272,7 @@ function commandText(language, zh, en) {
|
|
|
12079
12272
|
|
|
12080
12273
|
// src/conversations/delivery-staging.ts
|
|
12081
12274
|
import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
|
|
12082
|
-
import
|
|
12275
|
+
import path12 from "path";
|
|
12083
12276
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
12084
12277
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
12085
12278
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
@@ -12092,14 +12285,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
|
12092
12285
|
});
|
|
12093
12286
|
}
|
|
12094
12287
|
function deliveryStagingRunDir(paths, conversationId, runId) {
|
|
12095
|
-
return
|
|
12288
|
+
return path12.join(
|
|
12096
12289
|
deliveryStagingConversationDir(paths, conversationId),
|
|
12097
12290
|
safePathSegment(runId, "run")
|
|
12098
12291
|
);
|
|
12099
12292
|
}
|
|
12100
12293
|
function deliveryStagingConversationDir(paths, conversationId) {
|
|
12101
12294
|
assertValidConversationId(conversationId);
|
|
12102
|
-
return
|
|
12295
|
+
return path12.join(paths.conversationsDir, conversationId, "delivery-staging");
|
|
12103
12296
|
}
|
|
12104
12297
|
function safePathSegment(value, fallback) {
|
|
12105
12298
|
const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
|
|
@@ -12109,7 +12302,7 @@ function safePathSegment(value, fallback) {
|
|
|
12109
12302
|
// src/conversations/conversation-archive-plans.ts
|
|
12110
12303
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
12111
12304
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
12112
|
-
import
|
|
12305
|
+
import path13 from "path";
|
|
12113
12306
|
var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
|
|
12114
12307
|
var ConversationArchivePlanStore = class {
|
|
12115
12308
|
constructor(paths) {
|
|
@@ -12152,10 +12345,10 @@ var ConversationArchivePlanStore = class {
|
|
|
12152
12345
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
12153
12346
|
}
|
|
12154
12347
|
plansDir() {
|
|
12155
|
-
return
|
|
12348
|
+
return path13.join(this.paths.indexesDir, "conversation-archive-plans");
|
|
12156
12349
|
}
|
|
12157
12350
|
planPath(planId) {
|
|
12158
|
-
return
|
|
12351
|
+
return path13.join(this.plansDir(), `${planId}.json`);
|
|
12159
12352
|
}
|
|
12160
12353
|
};
|
|
12161
12354
|
function normalizePlanId(planId) {
|
|
@@ -12173,7 +12366,7 @@ function normalizePlanId(planId) {
|
|
|
12173
12366
|
// src/conversations/conversation-clear-plans.ts
|
|
12174
12367
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
12175
12368
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
12176
|
-
import
|
|
12369
|
+
import path14 from "path";
|
|
12177
12370
|
var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
|
|
12178
12371
|
var ConversationClearPlanStore = class {
|
|
12179
12372
|
constructor(paths) {
|
|
@@ -12217,10 +12410,10 @@ var ConversationClearPlanStore = class {
|
|
|
12217
12410
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
12218
12411
|
}
|
|
12219
12412
|
plansDir() {
|
|
12220
|
-
return
|
|
12413
|
+
return path14.join(this.paths.indexesDir, "conversation-clear-plans");
|
|
12221
12414
|
}
|
|
12222
12415
|
planPath(planId) {
|
|
12223
|
-
return
|
|
12416
|
+
return path14.join(this.plansDir(), `${planId}.json`);
|
|
12224
12417
|
}
|
|
12225
12418
|
};
|
|
12226
12419
|
function normalizePlanId2(planId) {
|
|
@@ -13059,14 +13252,14 @@ function readAttachmentWaveform(attachment) {
|
|
|
13059
13252
|
|
|
13060
13253
|
// src/hermes/session-title.ts
|
|
13061
13254
|
import { stat as stat8 } from "fs/promises";
|
|
13062
|
-
import
|
|
13255
|
+
import path15 from "path";
|
|
13063
13256
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
13064
13257
|
const trimmedSessionId = sessionId.trim();
|
|
13065
13258
|
if (!trimmedSessionId) {
|
|
13066
13259
|
return void 0;
|
|
13067
13260
|
}
|
|
13068
13261
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
13069
|
-
const dbPath =
|
|
13262
|
+
const dbPath = path15.join(
|
|
13070
13263
|
resolveHermesProfileDir(resolvedProfileName),
|
|
13071
13264
|
"state.db"
|
|
13072
13265
|
);
|
|
@@ -13087,7 +13280,7 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
13087
13280
|
return void 0;
|
|
13088
13281
|
}
|
|
13089
13282
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
13090
|
-
const dbPath =
|
|
13283
|
+
const dbPath = path15.join(
|
|
13091
13284
|
resolveHermesProfileDir(resolvedProfileName),
|
|
13092
13285
|
"state.db"
|
|
13093
13286
|
);
|
|
@@ -13863,10 +14056,15 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
13863
14056
|
return;
|
|
13864
14057
|
}
|
|
13865
14058
|
try {
|
|
14059
|
+
const failureMessage = await this.formatRunWorkerFailureMessage(
|
|
14060
|
+
conversationId,
|
|
14061
|
+
runId,
|
|
14062
|
+
error
|
|
14063
|
+
);
|
|
13866
14064
|
await this.deps.runLifecycle.failRun(
|
|
13867
14065
|
conversationId,
|
|
13868
14066
|
runId,
|
|
13869
|
-
|
|
14067
|
+
failureMessage
|
|
13870
14068
|
);
|
|
13871
14069
|
} catch (failError) {
|
|
13872
14070
|
if (isConversationNotFoundError(failError)) {
|
|
@@ -13893,6 +14091,26 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
13893
14091
|
});
|
|
13894
14092
|
});
|
|
13895
14093
|
}
|
|
14094
|
+
async formatRunWorkerFailureMessage(conversationId, runId, error) {
|
|
14095
|
+
if (error instanceof LinkHttpError && error.code === "hermes_agent_cli_missing") {
|
|
14096
|
+
return createHermesCliMissingError(
|
|
14097
|
+
void 0,
|
|
14098
|
+
await this.readRunLanguage(conversationId, runId)
|
|
14099
|
+
).message;
|
|
14100
|
+
}
|
|
14101
|
+
if (isHermesExecutableMissingError(error)) {
|
|
14102
|
+
return createHermesCliMissingError(
|
|
14103
|
+
error,
|
|
14104
|
+
await this.readRunLanguage(conversationId, runId)
|
|
14105
|
+
).message;
|
|
14106
|
+
}
|
|
14107
|
+
return error instanceof Error ? error.message : String(error);
|
|
14108
|
+
}
|
|
14109
|
+
async readRunLanguage(conversationId, runId) {
|
|
14110
|
+
const snapshot = await this.deps.store.readSnapshot(conversationId).catch(() => null);
|
|
14111
|
+
const run = snapshot?.runs.find((item) => item.id === runId);
|
|
14112
|
+
return normalizeLanguage(run?.language);
|
|
14113
|
+
}
|
|
13896
14114
|
async startNextQueuedRunLocked(conversationId) {
|
|
13897
14115
|
const manifest = await this.deps.store.readRunnableManifest(conversationId);
|
|
13898
14116
|
const snapshot = await this.deps.store.readSnapshot(conversationId);
|
|
@@ -15430,7 +15648,7 @@ import {
|
|
|
15430
15648
|
rm as rm5,
|
|
15431
15649
|
writeFile as writeFile2
|
|
15432
15650
|
} from "fs/promises";
|
|
15433
|
-
import
|
|
15651
|
+
import path16 from "path";
|
|
15434
15652
|
var ConversationStore = class {
|
|
15435
15653
|
constructor(paths) {
|
|
15436
15654
|
this.paths = paths;
|
|
@@ -15561,23 +15779,23 @@ var ConversationStore = class {
|
|
|
15561
15779
|
return manifest != null && manifest.status !== "deleted_soft";
|
|
15562
15780
|
}
|
|
15563
15781
|
removeConversationAttachments(conversationId) {
|
|
15564
|
-
return rm5(
|
|
15782
|
+
return rm5(path16.join(this.conversationDir(conversationId), "attachments"), {
|
|
15565
15783
|
recursive: true,
|
|
15566
15784
|
force: true
|
|
15567
15785
|
});
|
|
15568
15786
|
}
|
|
15569
15787
|
conversationDir(conversationId) {
|
|
15570
15788
|
assertValidConversationId(conversationId);
|
|
15571
|
-
return
|
|
15789
|
+
return path16.join(this.paths.conversationsDir, conversationId);
|
|
15572
15790
|
}
|
|
15573
15791
|
manifestPath(conversationId) {
|
|
15574
|
-
return
|
|
15792
|
+
return path16.join(this.conversationDir(conversationId), "manifest.json");
|
|
15575
15793
|
}
|
|
15576
15794
|
snapshotPath(conversationId) {
|
|
15577
|
-
return
|
|
15795
|
+
return path16.join(this.conversationDir(conversationId), "snapshot.json");
|
|
15578
15796
|
}
|
|
15579
15797
|
eventsPath(conversationId) {
|
|
15580
|
-
return
|
|
15798
|
+
return path16.join(this.conversationDir(conversationId), "events.ndjson");
|
|
15581
15799
|
}
|
|
15582
15800
|
};
|
|
15583
15801
|
function createEmptySnapshot2() {
|
|
@@ -15590,11 +15808,11 @@ function isNodeError9(error, code) {
|
|
|
15590
15808
|
// src/conversations/hermes-session-sync.ts
|
|
15591
15809
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
15592
15810
|
import { readdir as readdir7, readFile as readFile10, stat as stat10 } from "fs/promises";
|
|
15593
|
-
import
|
|
15811
|
+
import path18 from "path";
|
|
15594
15812
|
|
|
15595
15813
|
// src/conversations/delivery-import.ts
|
|
15596
15814
|
import { lstat as lstat2, readFile as readFile9, readdir as readdir6, stat as stat9 } from "fs/promises";
|
|
15597
|
-
import
|
|
15815
|
+
import path17 from "path";
|
|
15598
15816
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
15599
15817
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
15600
15818
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -15665,16 +15883,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
15665
15883
|
".m4a"
|
|
15666
15884
|
]);
|
|
15667
15885
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
15668
|
-
const resolvedDir =
|
|
15669
|
-
const relative =
|
|
15670
|
-
if (!relative || relative.startsWith("..") ||
|
|
15886
|
+
const resolvedDir = path17.resolve(stagingDir);
|
|
15887
|
+
const relative = path17.relative(path17.resolve(paths.conversationsDir), resolvedDir);
|
|
15888
|
+
if (!relative || relative.startsWith("..") || path17.isAbsolute(relative)) {
|
|
15671
15889
|
throw new LinkHttpError(
|
|
15672
15890
|
400,
|
|
15673
15891
|
"delivery_staging_invalid",
|
|
15674
15892
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
15675
15893
|
);
|
|
15676
15894
|
}
|
|
15677
|
-
const segments = relative.split(
|
|
15895
|
+
const segments = relative.split(path17.sep);
|
|
15678
15896
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
15679
15897
|
throw new LinkHttpError(
|
|
15680
15898
|
400,
|
|
@@ -15710,7 +15928,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
15710
15928
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
15711
15929
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
15712
15930
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
15713
|
-
const sourcePath =
|
|
15931
|
+
const sourcePath = path17.join(stagingDir, entry.name);
|
|
15714
15932
|
const mime = inferMimeType(sourcePath);
|
|
15715
15933
|
return {
|
|
15716
15934
|
path: sourcePath,
|
|
@@ -15896,7 +16114,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
15896
16114
|
}
|
|
15897
16115
|
return deps.writeBlob(conversationId, {
|
|
15898
16116
|
bytes: await readFile9(sourcePath),
|
|
15899
|
-
filename:
|
|
16117
|
+
filename: path17.basename(sourcePath),
|
|
15900
16118
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
15901
16119
|
});
|
|
15902
16120
|
}
|
|
@@ -15909,7 +16127,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
15909
16127
|
};
|
|
15910
16128
|
}
|
|
15911
16129
|
function isSupportedDeliveryFilename(filename) {
|
|
15912
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
16130
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path17.extname(filename).toLowerCase());
|
|
15913
16131
|
}
|
|
15914
16132
|
function readString9(payload, key) {
|
|
15915
16133
|
const value = payload[key];
|
|
@@ -15967,7 +16185,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
15967
16185
|
const candidates = [];
|
|
15968
16186
|
for (const profileName of profileNames) {
|
|
15969
16187
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
15970
|
-
const dbPath =
|
|
16188
|
+
const dbPath = path18.join(profileDir, "state.db");
|
|
15971
16189
|
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
15972
16190
|
result.errors.push({
|
|
15973
16191
|
profile: profileName,
|
|
@@ -16083,7 +16301,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
16083
16301
|
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
16084
16302
|
const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
16085
16303
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
16086
|
-
const dbPath =
|
|
16304
|
+
const dbPath = path18.join(profileDir, "state.db");
|
|
16087
16305
|
const sessions = await listProfileSessionsByIdPrefix(
|
|
16088
16306
|
dbPath,
|
|
16089
16307
|
`cron_${jobId}_`
|
|
@@ -17394,8 +17612,8 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
17394
17612
|
return [];
|
|
17395
17613
|
}
|
|
17396
17614
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
17397
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
17398
|
-
const transcriptPath =
|
|
17615
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path18.join(profileDir, "sessions"));
|
|
17616
|
+
const transcriptPath = path18.join(sessionsDir, `${sessionId}.jsonl`);
|
|
17399
17617
|
const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
|
|
17400
17618
|
if (isNodeError11(error, "ENOENT")) {
|
|
17401
17619
|
return "";
|
|
@@ -17765,7 +17983,7 @@ function isNodeError11(error, code) {
|
|
|
17765
17983
|
|
|
17766
17984
|
// src/conversations/delivery-context.ts
|
|
17767
17985
|
import { copyFile, mkdir as mkdir11, stat as stat11 } from "fs/promises";
|
|
17768
|
-
import
|
|
17986
|
+
import path19 from "path";
|
|
17769
17987
|
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
17770
17988
|
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
17771
17989
|
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
@@ -17837,10 +18055,10 @@ function findDeliveryContext(input) {
|
|
|
17837
18055
|
);
|
|
17838
18056
|
}
|
|
17839
18057
|
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
17840
|
-
const stagingDir =
|
|
17841
|
-
const conversationsRoot =
|
|
17842
|
-
const relative =
|
|
17843
|
-
if (!relative || relative.startsWith("..") ||
|
|
18058
|
+
const stagingDir = path19.resolve(context.stagingDir);
|
|
18059
|
+
const conversationsRoot = path19.resolve(paths.conversationsDir);
|
|
18060
|
+
const relative = path19.relative(conversationsRoot, stagingDir);
|
|
18061
|
+
if (!relative || relative.startsWith("..") || path19.isAbsolute(relative)) {
|
|
17844
18062
|
throw new LinkHttpError(
|
|
17845
18063
|
400,
|
|
17846
18064
|
"delivery_staging_invalid",
|
|
@@ -17851,13 +18069,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
17851
18069
|
const result = { staged: [], skipped: [] };
|
|
17852
18070
|
const usedNames = /* @__PURE__ */ new Set();
|
|
17853
18071
|
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
17854
|
-
const sourcePath =
|
|
18072
|
+
const sourcePath = path19.resolve(file.path);
|
|
17855
18073
|
let baseName = sanitizeFilename(
|
|
17856
|
-
file.caption ||
|
|
18074
|
+
file.caption || path19.basename(sourcePath),
|
|
17857
18075
|
`attachment-${index + 1}`
|
|
17858
18076
|
);
|
|
17859
|
-
const sourceExtension =
|
|
17860
|
-
if (!
|
|
18077
|
+
const sourceExtension = path19.extname(sourcePath);
|
|
18078
|
+
if (!path19.extname(baseName) && sourceExtension) {
|
|
17861
18079
|
baseName = `${baseName}${sourceExtension}`;
|
|
17862
18080
|
}
|
|
17863
18081
|
const filename = uniqueStagingFilename(
|
|
@@ -17884,7 +18102,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
17884
18102
|
});
|
|
17885
18103
|
continue;
|
|
17886
18104
|
}
|
|
17887
|
-
const targetPath =
|
|
18105
|
+
const targetPath = path19.join(stagingDir, filename);
|
|
17888
18106
|
await copyFile(sourcePath, targetPath);
|
|
17889
18107
|
result.staged.push({
|
|
17890
18108
|
source_path: sourcePath,
|
|
@@ -17927,7 +18145,7 @@ function normalizeDeliveryFileInputs(value) {
|
|
|
17927
18145
|
});
|
|
17928
18146
|
}
|
|
17929
18147
|
function uniqueStagingFilename(filename, usedNames) {
|
|
17930
|
-
const extension =
|
|
18148
|
+
const extension = path19.extname(filename);
|
|
17931
18149
|
const stem = filename.slice(0, filename.length - extension.length);
|
|
17932
18150
|
let candidate = filename;
|
|
17933
18151
|
let suffix = 2;
|
|
@@ -18424,10 +18642,10 @@ function parseHermesApiCapabilities(payload) {
|
|
|
18424
18642
|
sessionKeyHeader: readString12(features, "session_key_header")
|
|
18425
18643
|
};
|
|
18426
18644
|
}
|
|
18427
|
-
async function callHermesApi(
|
|
18645
|
+
async function callHermesApi(path35, init, options) {
|
|
18428
18646
|
const method = init.method ?? "GET";
|
|
18429
18647
|
const startedAt = Date.now();
|
|
18430
|
-
void options.logger?.debug("hermes_api_request_started", { method, path:
|
|
18648
|
+
void options.logger?.debug("hermes_api_request_started", { method, path: path35 });
|
|
18431
18649
|
const availability = await ensureHermesApiServerAvailable({
|
|
18432
18650
|
fetchImpl: options.fetchImpl,
|
|
18433
18651
|
logger: options.logger,
|
|
@@ -18436,7 +18654,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18436
18654
|
});
|
|
18437
18655
|
let config = availability.configResult.apiServer;
|
|
18438
18656
|
const fetcher = options.fetchImpl ?? fetch;
|
|
18439
|
-
const request = () => fetchHermesApi(fetcher, config,
|
|
18657
|
+
const request = () => fetchHermesApi(fetcher, config, path35, init, options);
|
|
18440
18658
|
let response;
|
|
18441
18659
|
try {
|
|
18442
18660
|
response = await request();
|
|
@@ -18444,7 +18662,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18444
18662
|
logHermesApiError(
|
|
18445
18663
|
options.logger,
|
|
18446
18664
|
method,
|
|
18447
|
-
|
|
18665
|
+
path35,
|
|
18448
18666
|
options.profileName,
|
|
18449
18667
|
startedAt,
|
|
18450
18668
|
error
|
|
@@ -18455,7 +18673,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18455
18673
|
logHermesApiResponse(
|
|
18456
18674
|
options.logger,
|
|
18457
18675
|
method,
|
|
18458
|
-
|
|
18676
|
+
path35,
|
|
18459
18677
|
options.profileName,
|
|
18460
18678
|
startedAt,
|
|
18461
18679
|
response
|
|
@@ -18464,7 +18682,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18464
18682
|
}
|
|
18465
18683
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
18466
18684
|
method,
|
|
18467
|
-
path:
|
|
18685
|
+
path: path35,
|
|
18468
18686
|
profile: options.profileName ?? "default",
|
|
18469
18687
|
port: config.port ?? null,
|
|
18470
18688
|
duration_ms: Date.now() - startedAt
|
|
@@ -18483,7 +18701,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18483
18701
|
logHermesApiError(
|
|
18484
18702
|
options.logger,
|
|
18485
18703
|
method,
|
|
18486
|
-
|
|
18704
|
+
path35,
|
|
18487
18705
|
options.profileName,
|
|
18488
18706
|
startedAt,
|
|
18489
18707
|
error
|
|
@@ -18493,7 +18711,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18493
18711
|
logHermesApiResponse(
|
|
18494
18712
|
options.logger,
|
|
18495
18713
|
method,
|
|
18496
|
-
|
|
18714
|
+
path35,
|
|
18497
18715
|
options.profileName,
|
|
18498
18716
|
startedAt,
|
|
18499
18717
|
response
|
|
@@ -18503,7 +18721,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18503
18721
|
}
|
|
18504
18722
|
void options.logger?.warn("hermes_api_request_repairing_after_401", {
|
|
18505
18723
|
method,
|
|
18506
|
-
path:
|
|
18724
|
+
path: path35,
|
|
18507
18725
|
profile: options.profileName ?? "default",
|
|
18508
18726
|
port: config.port ?? null,
|
|
18509
18727
|
duration_ms: Date.now() - startedAt
|
|
@@ -18524,7 +18742,7 @@ async function callHermesApi(path34, init, options) {
|
|
|
18524
18742
|
logHermesApiError(
|
|
18525
18743
|
options.logger,
|
|
18526
18744
|
method,
|
|
18527
|
-
|
|
18745
|
+
path35,
|
|
18528
18746
|
options.profileName,
|
|
18529
18747
|
startedAt,
|
|
18530
18748
|
error
|
|
@@ -18534,21 +18752,21 @@ async function callHermesApi(path34, init, options) {
|
|
|
18534
18752
|
logHermesApiResponse(
|
|
18535
18753
|
options.logger,
|
|
18536
18754
|
method,
|
|
18537
|
-
|
|
18755
|
+
path35,
|
|
18538
18756
|
options.profileName,
|
|
18539
18757
|
startedAt,
|
|
18540
18758
|
response
|
|
18541
18759
|
);
|
|
18542
18760
|
return response;
|
|
18543
18761
|
}
|
|
18544
|
-
async function fetchHermesApi(fetcher, config,
|
|
18762
|
+
async function fetchHermesApi(fetcher, config, path35, init, options) {
|
|
18545
18763
|
const headers = new Headers(init.headers);
|
|
18546
18764
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
18547
18765
|
if (config.key) {
|
|
18548
18766
|
headers.set("x-api-key", config.key);
|
|
18549
18767
|
headers.set("authorization", `Bearer ${config.key}`);
|
|
18550
18768
|
}
|
|
18551
|
-
return await fetcher(`http://127.0.0.1:${config.port}${
|
|
18769
|
+
return await fetcher(`http://127.0.0.1:${config.port}${path35}`, {
|
|
18552
18770
|
...init,
|
|
18553
18771
|
headers
|
|
18554
18772
|
}).catch((error) => {
|
|
@@ -18557,10 +18775,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
|
|
|
18557
18775
|
}
|
|
18558
18776
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
18559
18777
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
18560
|
-
path:
|
|
18778
|
+
path: path35,
|
|
18561
18779
|
profile: options.profileName ?? "default",
|
|
18562
18780
|
port: config.port ?? null,
|
|
18563
|
-
url: `http://127.0.0.1:${config.port}${
|
|
18781
|
+
url: `http://127.0.0.1:${config.port}${path35}`,
|
|
18564
18782
|
error: error instanceof Error ? error.message : String(error)
|
|
18565
18783
|
});
|
|
18566
18784
|
throw new LinkHttpError(
|
|
@@ -18570,10 +18788,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
|
|
|
18570
18788
|
);
|
|
18571
18789
|
});
|
|
18572
18790
|
}
|
|
18573
|
-
function logHermesApiResponse(logger, method,
|
|
18791
|
+
function logHermesApiResponse(logger, method, path35, profileName, startedAt, response) {
|
|
18574
18792
|
const fields = {
|
|
18575
18793
|
method,
|
|
18576
|
-
path:
|
|
18794
|
+
path: path35,
|
|
18577
18795
|
profile: profileName ?? "default",
|
|
18578
18796
|
status: response.status,
|
|
18579
18797
|
duration_ms: Date.now() - startedAt
|
|
@@ -18594,10 +18812,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
18594
18812
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
18595
18813
|
});
|
|
18596
18814
|
}
|
|
18597
|
-
function logHermesApiError(logger, method,
|
|
18815
|
+
function logHermesApiError(logger, method, path35, profileName, startedAt, error) {
|
|
18598
18816
|
void logger?.warn("hermes_api_request_failed", {
|
|
18599
18817
|
method,
|
|
18600
|
-
path:
|
|
18818
|
+
path: path35,
|
|
18601
18819
|
profile: profileName ?? "default",
|
|
18602
18820
|
duration_ms: Date.now() - startedAt,
|
|
18603
18821
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
@@ -18665,7 +18883,7 @@ function readBoolean2(payload, key) {
|
|
|
18665
18883
|
|
|
18666
18884
|
// src/conversations/history-builder.ts
|
|
18667
18885
|
import { readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
18668
|
-
import
|
|
18886
|
+
import path20 from "path";
|
|
18669
18887
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
18670
18888
|
var HERMES_HISTORY_COLUMNS = [
|
|
18671
18889
|
"role",
|
|
@@ -18737,13 +18955,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
18737
18955
|
}
|
|
18738
18956
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
18739
18957
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
18740
|
-
const dbPath =
|
|
18958
|
+
const dbPath = path20.join(profileDir, "state.db");
|
|
18741
18959
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
18742
18960
|
sessionsDir: value.sessionsDir,
|
|
18743
18961
|
configured: value.configured,
|
|
18744
18962
|
configError: false
|
|
18745
18963
|
})).catch(() => ({
|
|
18746
|
-
sessionsDir:
|
|
18964
|
+
sessionsDir: path20.join(profileDir, "sessions"),
|
|
18747
18965
|
configured: false,
|
|
18748
18966
|
configError: true
|
|
18749
18967
|
}));
|
|
@@ -18919,8 +19137,8 @@ async function readFirstExistingFile(paths) {
|
|
|
18919
19137
|
}
|
|
18920
19138
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
18921
19139
|
return [
|
|
18922
|
-
|
|
18923
|
-
|
|
19140
|
+
path20.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
19141
|
+
path20.join(sessionsDir, `${sessionId}.${extension}`)
|
|
18924
19142
|
];
|
|
18925
19143
|
}
|
|
18926
19144
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -19139,8 +19357,8 @@ function normalizeProfileForCompare(value) {
|
|
|
19139
19357
|
// src/hermes/stt.ts
|
|
19140
19358
|
import { execFile as execFile3 } from "child_process";
|
|
19141
19359
|
import { access as access2, readFile as readFile12, stat as stat13 } from "fs/promises";
|
|
19142
|
-
import
|
|
19143
|
-
import
|
|
19360
|
+
import os5 from "os";
|
|
19361
|
+
import path21 from "path";
|
|
19144
19362
|
import { promisify as promisify3 } from "util";
|
|
19145
19363
|
var execFileAsync3 = promisify3(execFile3);
|
|
19146
19364
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -19235,7 +19453,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
19235
19453
|
};
|
|
19236
19454
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
19237
19455
|
if (sourceRoot) {
|
|
19238
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
19456
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
|
|
19239
19457
|
}
|
|
19240
19458
|
return env;
|
|
19241
19459
|
}
|
|
@@ -19272,7 +19490,7 @@ async function resolveHermesPythonRuntime() {
|
|
|
19272
19490
|
sourceRoot: await findExplicitHermesSourceRoot() ?? await findHermesSourceRoot(explicit)
|
|
19273
19491
|
};
|
|
19274
19492
|
}
|
|
19275
|
-
const hermesBin = await
|
|
19493
|
+
const hermesBin = await resolveExecutablePath(resolveHermesBin());
|
|
19276
19494
|
let shebangRuntime = null;
|
|
19277
19495
|
if (hermesBin) {
|
|
19278
19496
|
const launcherTarget = await readHermesLauncherTarget(hermesBin);
|
|
@@ -19329,23 +19547,23 @@ async function resolveHermesPythonRuntime() {
|
|
|
19329
19547
|
sourceRoot: await findExplicitHermesSourceRoot() ?? await findDevHermesAgentSource()
|
|
19330
19548
|
};
|
|
19331
19549
|
}
|
|
19332
|
-
async function
|
|
19333
|
-
if (
|
|
19334
|
-
return await
|
|
19550
|
+
async function resolveExecutablePath(command) {
|
|
19551
|
+
if (path21.isAbsolute(command)) {
|
|
19552
|
+
return await isExecutableFile2(command) ? command : null;
|
|
19335
19553
|
}
|
|
19336
19554
|
const pathEnv = process.env.PATH ?? "";
|
|
19337
19555
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
19338
|
-
for (const dir of pathEnv.split(
|
|
19556
|
+
for (const dir of pathEnv.split(path21.delimiter)) {
|
|
19339
19557
|
for (const extension of extensions) {
|
|
19340
|
-
const candidate =
|
|
19341
|
-
if (await
|
|
19558
|
+
const candidate = path21.join(dir, `${command}${extension}`);
|
|
19559
|
+
if (await isExecutableFile2(candidate)) {
|
|
19342
19560
|
return candidate;
|
|
19343
19561
|
}
|
|
19344
19562
|
}
|
|
19345
19563
|
}
|
|
19346
19564
|
return null;
|
|
19347
19565
|
}
|
|
19348
|
-
async function
|
|
19566
|
+
async function isExecutableFile2(filePath) {
|
|
19349
19567
|
try {
|
|
19350
19568
|
const info = await stat13(filePath);
|
|
19351
19569
|
if (!info.isFile()) {
|
|
@@ -19359,14 +19577,14 @@ async function isExecutableFile(filePath) {
|
|
|
19359
19577
|
}
|
|
19360
19578
|
async function findHermesVenvPython(sourceRoot) {
|
|
19361
19579
|
const candidates = process.platform === "win32" ? [
|
|
19362
|
-
|
|
19363
|
-
|
|
19580
|
+
path21.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
19581
|
+
path21.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
19364
19582
|
] : [
|
|
19365
|
-
|
|
19366
|
-
|
|
19583
|
+
path21.join(sourceRoot, "venv", "bin", "python"),
|
|
19584
|
+
path21.join(sourceRoot, ".venv", "bin", "python")
|
|
19367
19585
|
];
|
|
19368
19586
|
for (const candidate of candidates) {
|
|
19369
|
-
if (await
|
|
19587
|
+
if (await isExecutableFile2(candidate)) {
|
|
19370
19588
|
return candidate;
|
|
19371
19589
|
}
|
|
19372
19590
|
}
|
|
@@ -19392,8 +19610,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
19392
19610
|
}
|
|
19393
19611
|
async function findDevHermesAgentSource() {
|
|
19394
19612
|
const candidates = [
|
|
19395
|
-
|
|
19396
|
-
|
|
19613
|
+
path21.resolve(process.cwd(), "reference/hermes-agent"),
|
|
19614
|
+
path21.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
19397
19615
|
];
|
|
19398
19616
|
for (const candidate of candidates) {
|
|
19399
19617
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -19409,10 +19627,10 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
19409
19627
|
line
|
|
19410
19628
|
);
|
|
19411
19629
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
19412
|
-
if (!rawTarget || !
|
|
19630
|
+
if (!rawTarget || !path21.isAbsolute(rawTarget)) {
|
|
19413
19631
|
continue;
|
|
19414
19632
|
}
|
|
19415
|
-
if (await
|
|
19633
|
+
if (await isExecutableFile2(rawTarget)) {
|
|
19416
19634
|
return rawTarget;
|
|
19417
19635
|
}
|
|
19418
19636
|
}
|
|
@@ -19439,12 +19657,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
19439
19657
|
return null;
|
|
19440
19658
|
}
|
|
19441
19659
|
async function findHermesSourceRoot(executablePath) {
|
|
19442
|
-
let cursor =
|
|
19660
|
+
let cursor = path21.dirname(path21.resolve(executablePath));
|
|
19443
19661
|
for (let index = 0; index < 6; index += 1) {
|
|
19444
19662
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
19445
19663
|
return cursor;
|
|
19446
19664
|
}
|
|
19447
|
-
const parent =
|
|
19665
|
+
const parent = path21.dirname(cursor);
|
|
19448
19666
|
if (parent === cursor) {
|
|
19449
19667
|
break;
|
|
19450
19668
|
}
|
|
@@ -19455,14 +19673,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
19455
19673
|
function hermesSourceRootCandidates() {
|
|
19456
19674
|
const candidates = [
|
|
19457
19675
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
19458
|
-
|
|
19676
|
+
path21.join(os5.homedir(), ".hermes", "hermes-agent"),
|
|
19459
19677
|
"/usr/local/lib/hermes-agent",
|
|
19460
19678
|
"/opt/hermes"
|
|
19461
19679
|
].filter((candidate) => Boolean(candidate));
|
|
19462
19680
|
if (process.platform === "win32") {
|
|
19463
19681
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
19464
19682
|
if (localAppData) {
|
|
19465
|
-
candidates.unshift(
|
|
19683
|
+
candidates.unshift(path21.join(localAppData, "hermes", "hermes-agent"));
|
|
19466
19684
|
}
|
|
19467
19685
|
}
|
|
19468
19686
|
return candidates;
|
|
@@ -19472,7 +19690,7 @@ async function findExplicitHermesSourceRoot() {
|
|
|
19472
19690
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
19473
19691
|
}
|
|
19474
19692
|
async function isHermesAgentSourceRoot(candidate) {
|
|
19475
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
19693
|
+
return await isDirectory(candidate) && await isDirectory(path21.join(candidate, "tools")) && await isDirectory(path21.join(candidate, "hermes_cli"));
|
|
19476
19694
|
}
|
|
19477
19695
|
async function isDirectory(candidate) {
|
|
19478
19696
|
return stat13(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
@@ -19484,13 +19702,13 @@ function compactProcessOutput(value) {
|
|
|
19484
19702
|
|
|
19485
19703
|
// src/hermes/usage-probe.ts
|
|
19486
19704
|
import { open as open3, readFile as readFile14, rm as rm6, stat as stat15 } from "fs/promises";
|
|
19487
|
-
import
|
|
19705
|
+
import path23 from "path";
|
|
19488
19706
|
import YAML3 from "yaml";
|
|
19489
19707
|
|
|
19490
19708
|
// src/hermes/profiles.ts
|
|
19491
19709
|
import { execFile as execFile4 } from "child_process";
|
|
19492
19710
|
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat14 } from "fs/promises";
|
|
19493
|
-
import
|
|
19711
|
+
import path22 from "path";
|
|
19494
19712
|
import { setTimeout as delay4 } from "timers/promises";
|
|
19495
19713
|
import { promisify as promisify4 } from "util";
|
|
19496
19714
|
import YAML2 from "yaml";
|
|
@@ -19633,7 +19851,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
19633
19851
|
return {
|
|
19634
19852
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
19635
19853
|
modelCount: listedModels?.models.length ?? 0,
|
|
19636
|
-
skillCount: await countSkills(
|
|
19854
|
+
skillCount: await countSkills(path22.join(profileDir, "skills")).catch(
|
|
19637
19855
|
() => 0
|
|
19638
19856
|
),
|
|
19639
19857
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -19694,7 +19912,7 @@ async function hasDefaultProfileConfigSource() {
|
|
|
19694
19912
|
if (!profileStat?.isDirectory()) {
|
|
19695
19913
|
return false;
|
|
19696
19914
|
}
|
|
19697
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
19915
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path22.join(profilePath, ".env"));
|
|
19698
19916
|
}
|
|
19699
19917
|
async function deleteHermesProfileWithCli(name) {
|
|
19700
19918
|
try {
|
|
@@ -19864,7 +20082,7 @@ async function countSkills(root) {
|
|
|
19864
20082
|
);
|
|
19865
20083
|
let count = 0;
|
|
19866
20084
|
for (const entry of entries) {
|
|
19867
|
-
const entryPath =
|
|
20085
|
+
const entryPath = path22.join(root, entry.name);
|
|
19868
20086
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
19869
20087
|
continue;
|
|
19870
20088
|
}
|
|
@@ -19957,7 +20175,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
19957
20175
|
const normalizedProfile = normalizeProfileName4(profileName);
|
|
19958
20176
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
19959
20177
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
19960
|
-
const pluginPath =
|
|
20178
|
+
const pluginPath = path23.join(
|
|
19961
20179
|
profilePath,
|
|
19962
20180
|
"plugins",
|
|
19963
20181
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -19983,7 +20201,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
19983
20201
|
return { ...base(), skipped: true };
|
|
19984
20202
|
}
|
|
19985
20203
|
try {
|
|
19986
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
20204
|
+
await ensureDirectoryWithInheritedMetadata(path23.dirname(eventsPath), 448);
|
|
19987
20205
|
const state = await readUsageProbeState(paths);
|
|
19988
20206
|
const pluginState = state.profiles[normalizedProfile];
|
|
19989
20207
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -20116,7 +20334,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
20116
20334
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
20117
20335
|
}
|
|
20118
20336
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
20119
|
-
return
|
|
20337
|
+
return path23.join(
|
|
20120
20338
|
paths.homeDir,
|
|
20121
20339
|
USAGE_PROBE_DIR,
|
|
20122
20340
|
safeProfileSegment(profileName),
|
|
@@ -20140,8 +20358,8 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
20140
20358
|
};
|
|
20141
20359
|
}
|
|
20142
20360
|
async function writeUsageProbePlugin(input) {
|
|
20143
|
-
const manifestPath =
|
|
20144
|
-
const initPath =
|
|
20361
|
+
const manifestPath = path23.join(input.pluginPath, "plugin.yaml");
|
|
20362
|
+
const initPath = path23.join(input.pluginPath, "__init__.py");
|
|
20145
20363
|
const manifest = usageProbeManifest();
|
|
20146
20364
|
const source = usageProbePythonSource(
|
|
20147
20365
|
input.profileName,
|
|
@@ -20488,9 +20706,9 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
20488
20706
|
});
|
|
20489
20707
|
}
|
|
20490
20708
|
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
20491
|
-
const legacyPath =
|
|
20709
|
+
const legacyPath = path23.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
20492
20710
|
const [manifest, init] = await Promise.all([
|
|
20493
|
-
readFile14(
|
|
20711
|
+
readFile14(path23.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
20494
20712
|
(error) => {
|
|
20495
20713
|
if (isNodeError15(error, "ENOENT")) {
|
|
20496
20714
|
return null;
|
|
@@ -20498,7 +20716,7 @@ async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
|
20498
20716
|
throw error;
|
|
20499
20717
|
}
|
|
20500
20718
|
),
|
|
20501
|
-
readFile14(
|
|
20719
|
+
readFile14(path23.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
20502
20720
|
(error) => {
|
|
20503
20721
|
if (isNodeError15(error, "ENOENT")) {
|
|
20504
20722
|
return null;
|
|
@@ -20547,7 +20765,7 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
20547
20765
|
}));
|
|
20548
20766
|
}
|
|
20549
20767
|
function usageProbeStatePath(paths) {
|
|
20550
|
-
return
|
|
20768
|
+
return path23.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
20551
20769
|
}
|
|
20552
20770
|
async function readHermesConfigDocument2(configPath, language) {
|
|
20553
20771
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
@@ -21150,7 +21368,7 @@ function isRunToolResultCompensationEnabled(env = process.env) {
|
|
|
21150
21368
|
|
|
21151
21369
|
// src/conversations/run-transcript-enrichment.ts
|
|
21152
21370
|
import { readFile as readFile15, stat as stat16 } from "fs/promises";
|
|
21153
|
-
import
|
|
21371
|
+
import path24 from "path";
|
|
21154
21372
|
var MESSAGE_COLUMNS2 = [
|
|
21155
21373
|
"id",
|
|
21156
21374
|
"session_id",
|
|
@@ -21226,8 +21444,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
21226
21444
|
}
|
|
21227
21445
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
21228
21446
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
21229
|
-
const dbPath =
|
|
21230
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
21447
|
+
const dbPath = path24.join(profileDir, "state.db");
|
|
21448
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path24.join(profileDir, "sessions"));
|
|
21231
21449
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
21232
21450
|
readStateDbMessages2(dbPath, sessionId),
|
|
21233
21451
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -21290,7 +21508,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
21290
21508
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
21291
21509
|
return [];
|
|
21292
21510
|
}
|
|
21293
|
-
const transcriptPath =
|
|
21511
|
+
const transcriptPath = path24.join(sessionsDir, `${sessionId}.jsonl`);
|
|
21294
21512
|
const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
|
|
21295
21513
|
if (isNodeError16(error, "ENOENT")) {
|
|
21296
21514
|
return "";
|
|
@@ -22283,6 +22501,7 @@ var ConversationRunLifecycle = class {
|
|
|
22283
22501
|
profileName: run.profile,
|
|
22284
22502
|
signal: controller.signal,
|
|
22285
22503
|
logger: this.deps.logger,
|
|
22504
|
+
language: run.language === "en" ? "en" : "zh-CN",
|
|
22286
22505
|
paths: this.deps.paths
|
|
22287
22506
|
});
|
|
22288
22507
|
await this.updateRun(conversationId, runId, {
|
|
@@ -25754,7 +25973,7 @@ var ConversationService = class {
|
|
|
25754
25973
|
}
|
|
25755
25974
|
}
|
|
25756
25975
|
hermesArchiveStateSyncMarkerPath() {
|
|
25757
|
-
return
|
|
25976
|
+
return path25.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
|
|
25758
25977
|
}
|
|
25759
25978
|
prepareClearAllConversationPlan(targetStatus) {
|
|
25760
25979
|
return this.maintenance.prepareClearAllConversationPlan(targetStatus);
|
|
@@ -27640,11 +27859,11 @@ function isSseRequestContext(ctx) {
|
|
|
27640
27859
|
}
|
|
27641
27860
|
return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
|
|
27642
27861
|
}
|
|
27643
|
-
function isSseRequestPath(
|
|
27644
|
-
if (!
|
|
27862
|
+
function isSseRequestPath(path35) {
|
|
27863
|
+
if (!path35) {
|
|
27645
27864
|
return false;
|
|
27646
27865
|
}
|
|
27647
|
-
return
|
|
27866
|
+
return path35 === "/api/v1/conversations/events" || path35 === "/api/v1/profile-creation/events" || path35 === "/api/v1/hermes/update/events" || path35 === "/api/v1/link/update/events" || /^\/api\/v1\/conversations\/[^/]+\/events$/u.test(path35) || /^\/api\/v1\/runs\/[^/]+\/events$/u.test(path35);
|
|
27648
27867
|
}
|
|
27649
27868
|
function isExpectedClientDisconnectError2(error, options = {}) {
|
|
27650
27869
|
if (!(error instanceof Error)) {
|
|
@@ -28476,7 +28695,7 @@ function errorMessage3(error) {
|
|
|
28476
28695
|
|
|
28477
28696
|
// src/hermes/profile-identity.ts
|
|
28478
28697
|
import { readFile as readFile16, stat as stat17 } from "fs/promises";
|
|
28479
|
-
import
|
|
28698
|
+
import path26 from "path";
|
|
28480
28699
|
var MAX_SOUL_MD_LENGTH = 2e4;
|
|
28481
28700
|
async function readHermesProfileIdentity(profileName, paths) {
|
|
28482
28701
|
await assertProfileExists3(profileName, paths);
|
|
@@ -28533,7 +28752,7 @@ async function assertProfileExists3(profileName, paths) {
|
|
|
28533
28752
|
}
|
|
28534
28753
|
}
|
|
28535
28754
|
function resolveSoulPath(profileName) {
|
|
28536
|
-
return
|
|
28755
|
+
return path26.join(resolveHermesProfileDir(profileName), "SOUL.md");
|
|
28537
28756
|
}
|
|
28538
28757
|
function isNodeError18(error, code) {
|
|
28539
28758
|
return error instanceof Error && "code" in error && error.code === code;
|
|
@@ -28549,13 +28768,13 @@ import {
|
|
|
28549
28768
|
rm as rm7,
|
|
28550
28769
|
stat as stat19
|
|
28551
28770
|
} from "fs/promises";
|
|
28552
|
-
import
|
|
28771
|
+
import path28 from "path";
|
|
28553
28772
|
import YAML5 from "yaml";
|
|
28554
28773
|
|
|
28555
28774
|
// src/hermes/link-skill.ts
|
|
28556
28775
|
import { readFile as readFile17, stat as stat18 } from "fs/promises";
|
|
28557
|
-
import
|
|
28558
|
-
import
|
|
28776
|
+
import os6 from "os";
|
|
28777
|
+
import path27 from "path";
|
|
28559
28778
|
import YAML4 from "yaml";
|
|
28560
28779
|
var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
|
|
28561
28780
|
var HERMES_LINK_SKILL_DIR = "hermes-link";
|
|
@@ -28640,7 +28859,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
|
|
|
28640
28859
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
28641
28860
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
28642
28861
|
const externalDir = resolveHermesLinkSkillExternalDir(paths);
|
|
28643
|
-
const skillPath =
|
|
28862
|
+
const skillPath = path27.join(
|
|
28644
28863
|
externalDir,
|
|
28645
28864
|
HERMES_LINK_SKILL_DIR,
|
|
28646
28865
|
HERMES_LINK_SKILL_FILE
|
|
@@ -28722,7 +28941,7 @@ function withDefaultProfilePlaceholder2(profiles) {
|
|
|
28722
28941
|
];
|
|
28723
28942
|
}
|
|
28724
28943
|
function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
|
|
28725
|
-
return
|
|
28944
|
+
return path27.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
|
|
28726
28945
|
}
|
|
28727
28946
|
async function writeHermesLinkSkill(skillPath) {
|
|
28728
28947
|
const existing = await readFile17(skillPath, "utf8").catch((error) => {
|
|
@@ -28817,11 +29036,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
|
|
|
28817
29036
|
const seen = new Set(
|
|
28818
29037
|
entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
|
|
28819
29038
|
);
|
|
28820
|
-
const normalizedExternalDir =
|
|
29039
|
+
const normalizedExternalDir = path27.resolve(externalDir);
|
|
28821
29040
|
return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
|
|
28822
29041
|
}
|
|
28823
29042
|
function externalDirsInclude(current, externalDir, hermesHome) {
|
|
28824
|
-
const normalizedExternalDir =
|
|
29043
|
+
const normalizedExternalDir = path27.resolve(externalDir);
|
|
28825
29044
|
return readExternalDirEntries(current).some(
|
|
28826
29045
|
(entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
|
|
28827
29046
|
);
|
|
@@ -28832,14 +29051,14 @@ function readExternalDirEntries(value) {
|
|
|
28832
29051
|
}
|
|
28833
29052
|
function resolveExternalDirEntry(entry, hermesHome) {
|
|
28834
29053
|
const expanded = expandHome(expandEnvVars(entry));
|
|
28835
|
-
return
|
|
29054
|
+
return path27.resolve(path27.isAbsolute(expanded) ? expanded : path27.join(hermesHome, expanded));
|
|
28836
29055
|
}
|
|
28837
29056
|
function expandHome(value) {
|
|
28838
29057
|
if (value === "~") {
|
|
28839
|
-
return
|
|
29058
|
+
return os6.homedir();
|
|
28840
29059
|
}
|
|
28841
|
-
if (value.startsWith(`~${
|
|
28842
|
-
return
|
|
29060
|
+
if (value.startsWith(`~${path27.sep}`) || value.startsWith("~/")) {
|
|
29061
|
+
return path27.join(os6.homedir(), value.slice(2));
|
|
28843
29062
|
}
|
|
28844
29063
|
return value;
|
|
28845
29064
|
}
|
|
@@ -29391,7 +29610,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
29391
29610
|
return keys;
|
|
29392
29611
|
}
|
|
29393
29612
|
async function writeEnvValues(profileName, values) {
|
|
29394
|
-
const envPath =
|
|
29613
|
+
const envPath = path28.join(resolveHermesProfileDir(profileName), ".env");
|
|
29395
29614
|
const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
|
|
29396
29615
|
if (isNodeError20(error, "ENOENT")) {
|
|
29397
29616
|
return "";
|
|
@@ -29428,8 +29647,8 @@ async function writeEnvValues(profileName, values) {
|
|
|
29428
29647
|
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
29429
29648
|
}
|
|
29430
29649
|
async function copySkills(sourceProfile, targetProfile) {
|
|
29431
|
-
const sourceSkills =
|
|
29432
|
-
const targetSkills =
|
|
29650
|
+
const sourceSkills = path28.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
29651
|
+
const targetSkills = path28.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
29433
29652
|
if (!await pathExists2(sourceSkills)) {
|
|
29434
29653
|
return;
|
|
29435
29654
|
}
|
|
@@ -29524,10 +29743,10 @@ async function readProfileCreationLogLines(paths) {
|
|
|
29524
29743
|
);
|
|
29525
29744
|
}
|
|
29526
29745
|
function profileCreationStatePath(paths) {
|
|
29527
|
-
return
|
|
29746
|
+
return path28.join(paths.runDir, "profile-create-state.json");
|
|
29528
29747
|
}
|
|
29529
29748
|
function profileCreationLogPath(paths) {
|
|
29530
|
-
return
|
|
29749
|
+
return path28.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
29531
29750
|
}
|
|
29532
29751
|
async function clearProfileCreationLogFiles(paths) {
|
|
29533
29752
|
const primary = profileCreationLogPath(paths);
|
|
@@ -29816,7 +30035,7 @@ import {
|
|
|
29816
30035
|
readFile as readFile19,
|
|
29817
30036
|
stat as stat20
|
|
29818
30037
|
} from "fs/promises";
|
|
29819
|
-
import
|
|
30038
|
+
import path29 from "path";
|
|
29820
30039
|
import YAML6 from "yaml";
|
|
29821
30040
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
29822
30041
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -30147,7 +30366,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
30147
30366
|
});
|
|
30148
30367
|
await patchJsonProviderConfig(
|
|
30149
30368
|
profileName,
|
|
30150
|
-
|
|
30369
|
+
path29.join("hindsight", "config.json"),
|
|
30151
30370
|
{
|
|
30152
30371
|
mode: patch.mode,
|
|
30153
30372
|
api_url: patch.apiUrl,
|
|
@@ -30350,7 +30569,7 @@ async function patchHermesMemoryLimits(profileName, patch) {
|
|
|
30350
30569
|
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
30351
30570
|
}
|
|
30352
30571
|
function resolveMemoryDir(profileName) {
|
|
30353
|
-
return
|
|
30572
|
+
return path29.join(resolveHermesProfileDir(profileName), "memories");
|
|
30354
30573
|
}
|
|
30355
30574
|
async function readMemoryStore(profileName, target, limits) {
|
|
30356
30575
|
const filePath = memoryFilePath(profileName, target);
|
|
@@ -30411,7 +30630,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
30411
30630
|
);
|
|
30412
30631
|
}
|
|
30413
30632
|
function memoryFilePath(profileName, target) {
|
|
30414
|
-
return
|
|
30633
|
+
return path29.join(
|
|
30415
30634
|
resolveMemoryDir(profileName),
|
|
30416
30635
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
30417
30636
|
);
|
|
@@ -30471,7 +30690,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
30471
30690
|
configurable: true,
|
|
30472
30691
|
configured: true,
|
|
30473
30692
|
configurationIssue: null,
|
|
30474
|
-
providerConfigPath:
|
|
30693
|
+
providerConfigPath: path29.join(
|
|
30475
30694
|
resolveHermesProfileDir(profileName),
|
|
30476
30695
|
"<provider>.json"
|
|
30477
30696
|
),
|
|
@@ -30865,7 +31084,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
30865
31084
|
stringSetting(
|
|
30866
31085
|
"dbPath",
|
|
30867
31086
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
30868
|
-
config.db_path ??
|
|
31087
|
+
config.db_path ?? path29.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
30869
31088
|
),
|
|
30870
31089
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
30871
31090
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -30901,7 +31120,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
30901
31120
|
stringSetting(
|
|
30902
31121
|
"workingDirectory",
|
|
30903
31122
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
30904
|
-
|
|
31123
|
+
path29.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
30905
31124
|
false
|
|
30906
31125
|
)
|
|
30907
31126
|
];
|
|
@@ -30910,16 +31129,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
30910
31129
|
}
|
|
30911
31130
|
function memoryProviderConfigPath(profileName, provider) {
|
|
30912
31131
|
if (provider === "honcho") {
|
|
30913
|
-
return
|
|
31132
|
+
return path29.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
30914
31133
|
}
|
|
30915
31134
|
if (provider === "mem0") {
|
|
30916
|
-
return
|
|
31135
|
+
return path29.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
30917
31136
|
}
|
|
30918
31137
|
if (provider === "supermemory") {
|
|
30919
|
-
return
|
|
31138
|
+
return path29.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
30920
31139
|
}
|
|
30921
31140
|
if (provider === "hindsight") {
|
|
30922
|
-
return
|
|
31141
|
+
return path29.join(
|
|
30923
31142
|
resolveHermesProfileDir(profileName),
|
|
30924
31143
|
"hindsight",
|
|
30925
31144
|
"config.json"
|
|
@@ -30928,13 +31147,13 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
30928
31147
|
return null;
|
|
30929
31148
|
}
|
|
30930
31149
|
function customProviderConfigPath(profileName, provider) {
|
|
30931
|
-
return
|
|
31150
|
+
return path29.join(
|
|
30932
31151
|
resolveHermesProfileDir(profileName),
|
|
30933
31152
|
`${normalizeCustomProviderId(provider)}.json`
|
|
30934
31153
|
);
|
|
30935
31154
|
}
|
|
30936
31155
|
function customProviderRegistryPath(profileName) {
|
|
30937
|
-
return
|
|
31156
|
+
return path29.join(
|
|
30938
31157
|
resolveHermesProfileDir(profileName),
|
|
30939
31158
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
30940
31159
|
);
|
|
@@ -30986,7 +31205,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
30986
31205
|
);
|
|
30987
31206
|
}
|
|
30988
31207
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
30989
|
-
const pluginsDir =
|
|
31208
|
+
const pluginsDir = path29.join(resolveHermesProfileDir(profileName), "plugins");
|
|
30990
31209
|
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
30991
31210
|
(error) => {
|
|
30992
31211
|
if (isNodeError21(error, "ENOENT")) {
|
|
@@ -31006,7 +31225,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
31006
31225
|
} catch {
|
|
31007
31226
|
continue;
|
|
31008
31227
|
}
|
|
31009
|
-
const providerDir =
|
|
31228
|
+
const providerDir = path29.join(pluginsDir, entry.name);
|
|
31010
31229
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
31011
31230
|
continue;
|
|
31012
31231
|
}
|
|
@@ -31020,7 +31239,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
31020
31239
|
return descriptors;
|
|
31021
31240
|
}
|
|
31022
31241
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
31023
|
-
const providerDir =
|
|
31242
|
+
const providerDir = path29.join(
|
|
31024
31243
|
resolveHermesProfileDir(profileName),
|
|
31025
31244
|
"plugins",
|
|
31026
31245
|
normalizeCustomProviderId(provider)
|
|
@@ -31028,7 +31247,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
31028
31247
|
return isMemoryProviderPluginDir(providerDir);
|
|
31029
31248
|
}
|
|
31030
31249
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
31031
|
-
const source = await readFile19(
|
|
31250
|
+
const source = await readFile19(path29.join(providerDir, "__init__.py"), "utf8").catch(
|
|
31032
31251
|
(error) => {
|
|
31033
31252
|
if (isNodeError21(error, "ENOENT")) {
|
|
31034
31253
|
return "";
|
|
@@ -31040,7 +31259,7 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
31040
31259
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
31041
31260
|
}
|
|
31042
31261
|
async function readPluginMetadata(providerDir) {
|
|
31043
|
-
const raw = await readFile19(
|
|
31262
|
+
const raw = await readFile19(path29.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
31044
31263
|
(error) => {
|
|
31045
31264
|
if (isNodeError21(error, "ENOENT")) {
|
|
31046
31265
|
return "";
|
|
@@ -31052,10 +31271,10 @@ async function readPluginMetadata(providerDir) {
|
|
|
31052
31271
|
}
|
|
31053
31272
|
async function resolveByteRoverCli() {
|
|
31054
31273
|
const candidates = [
|
|
31055
|
-
...(process.env.PATH ?? "").split(
|
|
31056
|
-
|
|
31274
|
+
...(process.env.PATH ?? "").split(path29.delimiter).filter(Boolean).map((dir) => path29.join(dir, "brv")),
|
|
31275
|
+
path29.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
31057
31276
|
"/usr/local/bin/brv",
|
|
31058
|
-
|
|
31277
|
+
path29.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
31059
31278
|
].filter(Boolean);
|
|
31060
31279
|
for (const candidate of candidates) {
|
|
31061
31280
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -31116,7 +31335,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
31116
31335
|
if (entries.length === 0) {
|
|
31117
31336
|
return;
|
|
31118
31337
|
}
|
|
31119
|
-
const envPath =
|
|
31338
|
+
const envPath = path29.join(resolveHermesProfileDir(profileName), ".env");
|
|
31120
31339
|
const existingRaw = await readFile19(envPath, "utf8").catch((error) => {
|
|
31121
31340
|
if (isNodeError21(error, "ENOENT")) {
|
|
31122
31341
|
return "";
|
|
@@ -31287,7 +31506,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
31287
31506
|
return provider;
|
|
31288
31507
|
}
|
|
31289
31508
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
31290
|
-
const configPath =
|
|
31509
|
+
const configPath = path29.join(
|
|
31291
31510
|
resolveHermesProfileDir(profileName),
|
|
31292
31511
|
relativePath
|
|
31293
31512
|
);
|
|
@@ -31316,7 +31535,7 @@ async function readJsonObject(filePath) {
|
|
|
31316
31535
|
} catch {
|
|
31317
31536
|
throw new HermesMemoryError(
|
|
31318
31537
|
"memory_provider_config_invalid",
|
|
31319
|
-
`${
|
|
31538
|
+
`${path29.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
31320
31539
|
);
|
|
31321
31540
|
}
|
|
31322
31541
|
}
|
|
@@ -31940,7 +32159,7 @@ function toMemoryHttpError(error) {
|
|
|
31940
32159
|
|
|
31941
32160
|
// src/hermes/skills.ts
|
|
31942
32161
|
import { readFile as readFile20, readdir as readdir11 } from "fs/promises";
|
|
31943
|
-
import
|
|
32162
|
+
import path30 from "path";
|
|
31944
32163
|
import YAML7 from "yaml";
|
|
31945
32164
|
var HermesSkillNotFoundError = class extends Error {
|
|
31946
32165
|
constructor(skillName) {
|
|
@@ -31954,7 +32173,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
31954
32173
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
31955
32174
|
const profile = await readExistingProfile(profileName, paths);
|
|
31956
32175
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
31957
|
-
const skillsRoot =
|
|
32176
|
+
const skillsRoot = path30.join(profileDir, "skills");
|
|
31958
32177
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
31959
32178
|
findSkillFiles(skillsRoot),
|
|
31960
32179
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -32045,7 +32264,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
32045
32264
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
32046
32265
|
continue;
|
|
32047
32266
|
}
|
|
32048
|
-
const entryPath =
|
|
32267
|
+
const entryPath = path30.join(directory, entry.name);
|
|
32049
32268
|
if (entry.isDirectory()) {
|
|
32050
32269
|
await collectSkillFiles(entryPath, results);
|
|
32051
32270
|
continue;
|
|
@@ -32067,10 +32286,10 @@ async function readSkillMetadata(input) {
|
|
|
32067
32286
|
if (raw === null) {
|
|
32068
32287
|
return null;
|
|
32069
32288
|
}
|
|
32070
|
-
const skillDir =
|
|
32289
|
+
const skillDir = path30.dirname(input.skillFile);
|
|
32071
32290
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
32072
32291
|
const name = normalizeSkillName(
|
|
32073
|
-
readString21(frontmatter.name) ??
|
|
32292
|
+
readString21(frontmatter.name) ?? path30.basename(skillDir)
|
|
32074
32293
|
);
|
|
32075
32294
|
if (!name) {
|
|
32076
32295
|
return null;
|
|
@@ -32089,7 +32308,7 @@ async function readSkillMetadata(input) {
|
|
|
32089
32308
|
enabled: !input.disabled.has(name),
|
|
32090
32309
|
source: provenance.source,
|
|
32091
32310
|
trust: provenance.trust,
|
|
32092
|
-
relativePath:
|
|
32311
|
+
relativePath: path30.relative(input.skillsRoot, skillDir)
|
|
32093
32312
|
};
|
|
32094
32313
|
}
|
|
32095
32314
|
function parseSkillDocument(raw) {
|
|
@@ -32110,8 +32329,8 @@ function parseSkillDocument(raw) {
|
|
|
32110
32329
|
}
|
|
32111
32330
|
}
|
|
32112
32331
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
32113
|
-
const relative =
|
|
32114
|
-
const parts = relative.split(
|
|
32332
|
+
const relative = path30.relative(skillsRoot, skillFile);
|
|
32333
|
+
const parts = relative.split(path30.sep).filter(Boolean);
|
|
32115
32334
|
return parts.length >= 3 ? parts[0] : null;
|
|
32116
32335
|
}
|
|
32117
32336
|
function firstBodyDescription(body) {
|
|
@@ -32158,7 +32377,7 @@ async function readSkillProvenance(root) {
|
|
|
32158
32377
|
return provenance;
|
|
32159
32378
|
}
|
|
32160
32379
|
async function readBundledSkillNames(root) {
|
|
32161
|
-
const raw = await readFile20(
|
|
32380
|
+
const raw = await readFile20(path30.join(root, ".bundled_manifest"), "utf8").catch(
|
|
32162
32381
|
(error) => {
|
|
32163
32382
|
if (isNodeError22(error, "ENOENT")) {
|
|
32164
32383
|
return "";
|
|
@@ -32181,7 +32400,7 @@ async function readBundledSkillNames(root) {
|
|
|
32181
32400
|
return names;
|
|
32182
32401
|
}
|
|
32183
32402
|
async function readHubInstalledSkills(root) {
|
|
32184
|
-
const raw = await readFile20(
|
|
32403
|
+
const raw = await readFile20(path30.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
32185
32404
|
(error) => {
|
|
32186
32405
|
if (isNodeError22(error, "ENOENT")) {
|
|
32187
32406
|
return "";
|
|
@@ -32658,6 +32877,7 @@ function registerRunRoutes(router, options) {
|
|
|
32658
32877
|
const { paths, logger, conversations } = options;
|
|
32659
32878
|
router.post("/api/v1/runs", async (ctx) => {
|
|
32660
32879
|
await authenticateRequest(ctx, paths);
|
|
32880
|
+
const language = readPreferredLanguage(ctx);
|
|
32661
32881
|
const body = await readJsonBody(ctx.req);
|
|
32662
32882
|
const input = readString19(body, "input");
|
|
32663
32883
|
if (!input) {
|
|
@@ -32674,14 +32894,16 @@ function registerRunRoutes(router, options) {
|
|
|
32674
32894
|
session_id: readString19(body, "session_id") ?? readString19(body, "sessionId") ?? void 0,
|
|
32675
32895
|
session_key: readString19(body, "session_key") ?? readString19(body, "sessionKey") ?? void 0
|
|
32676
32896
|
},
|
|
32677
|
-
{ logger, profileName: readOptionalProfileName(body) }
|
|
32897
|
+
{ logger, profileName: readOptionalProfileName(body), language }
|
|
32678
32898
|
);
|
|
32679
32899
|
});
|
|
32680
32900
|
router.get("/api/v1/runs/:runId/events", async (ctx) => {
|
|
32681
32901
|
await authenticateRequest(ctx, paths);
|
|
32902
|
+
const language = readPreferredLanguage(ctx);
|
|
32682
32903
|
const response = await streamHermesRunEvents(ctx.params.runId, {
|
|
32683
32904
|
logger,
|
|
32684
|
-
profileName: readQueryString(ctx.query.profile)
|
|
32905
|
+
profileName: readQueryString(ctx.query.profile),
|
|
32906
|
+
language
|
|
32685
32907
|
});
|
|
32686
32908
|
ctx.status = response.status;
|
|
32687
32909
|
for (const [key, value] of response.headers.entries()) {
|
|
@@ -32703,6 +32925,7 @@ function registerRunRoutes(router, options) {
|
|
|
32703
32925
|
});
|
|
32704
32926
|
router.post("/api/v1/runs/:runId/cancel", async (ctx) => {
|
|
32705
32927
|
await authenticateRequest(ctx, paths);
|
|
32928
|
+
const language = readPreferredLanguage(ctx);
|
|
32706
32929
|
try {
|
|
32707
32930
|
ctx.body = {
|
|
32708
32931
|
ok: true,
|
|
@@ -32714,7 +32937,8 @@ function registerRunRoutes(router, options) {
|
|
|
32714
32937
|
}
|
|
32715
32938
|
await cancelHermesRun(ctx.params.runId, {
|
|
32716
32939
|
logger,
|
|
32717
|
-
profileName: readQueryString(ctx.query.profile)
|
|
32940
|
+
profileName: readQueryString(ctx.query.profile),
|
|
32941
|
+
language
|
|
32718
32942
|
});
|
|
32719
32943
|
ctx.body = { ok: true };
|
|
32720
32944
|
}
|
|
@@ -32844,7 +33068,7 @@ function readModelList(payload) {
|
|
|
32844
33068
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
32845
33069
|
import { spawn as spawn4 } from "child_process";
|
|
32846
33070
|
import { mkdir as mkdir13, readFile as readFile21, rm as rm8 } from "fs/promises";
|
|
32847
|
-
import
|
|
33071
|
+
import path31 from "path";
|
|
32848
33072
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
32849
33073
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
32850
33074
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -33122,13 +33346,13 @@ async function readUpdateLogLines(paths) {
|
|
|
33122
33346
|
);
|
|
33123
33347
|
}
|
|
33124
33348
|
function releaseCachePath(paths) {
|
|
33125
|
-
return
|
|
33349
|
+
return path31.join(paths.indexesDir, "hermes-release-check.json");
|
|
33126
33350
|
}
|
|
33127
33351
|
function updateStatePath(paths) {
|
|
33128
|
-
return
|
|
33352
|
+
return path31.join(paths.runDir, "hermes-update-state.json");
|
|
33129
33353
|
}
|
|
33130
33354
|
function updateLogPath(paths) {
|
|
33131
|
-
return
|
|
33355
|
+
return path31.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
33132
33356
|
}
|
|
33133
33357
|
async function clearUpdateLogFiles(paths) {
|
|
33134
33358
|
const primary = updateLogPath(paths);
|
|
@@ -33229,12 +33453,12 @@ function readString22(payload, key) {
|
|
|
33229
33453
|
import { spawn as spawn6 } from "child_process";
|
|
33230
33454
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
33231
33455
|
import { mkdir as mkdir16, readFile as readFile23, rm as rm11 } from "fs/promises";
|
|
33232
|
-
import
|
|
33456
|
+
import path33 from "path";
|
|
33233
33457
|
|
|
33234
33458
|
// src/daemon/process.ts
|
|
33235
33459
|
import { spawn as spawn5 } from "child_process";
|
|
33236
33460
|
import { mkdir as mkdir15, readFile as readFile22, rm as rm10, writeFile as writeFile4 } from "fs/promises";
|
|
33237
|
-
import
|
|
33461
|
+
import path32 from "path";
|
|
33238
33462
|
|
|
33239
33463
|
// src/daemon/service.ts
|
|
33240
33464
|
import { createServer } from "http";
|
|
@@ -33910,8 +34134,8 @@ function normalizeMessage(value) {
|
|
|
33910
34134
|
|
|
33911
34135
|
// src/runtime/system-info.ts
|
|
33912
34136
|
import { execFileSync } from "child_process";
|
|
33913
|
-
import { readFileSync } from "fs";
|
|
33914
|
-
import
|
|
34137
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
34138
|
+
import os7 from "os";
|
|
33915
34139
|
function readLinkSystemInfo() {
|
|
33916
34140
|
const platform = process.platform;
|
|
33917
34141
|
const hostname = readHostname(platform);
|
|
@@ -33950,7 +34174,7 @@ function readHostname(platform) {
|
|
|
33950
34174
|
return computerName;
|
|
33951
34175
|
}
|
|
33952
34176
|
}
|
|
33953
|
-
return normalizeText(
|
|
34177
|
+
return normalizeText(os7.hostname());
|
|
33954
34178
|
}
|
|
33955
34179
|
function readOsLabel(platform) {
|
|
33956
34180
|
if (platform === "darwin") {
|
|
@@ -33958,17 +34182,17 @@ function readOsLabel(platform) {
|
|
|
33958
34182
|
return version ? `macOS ${version}` : "macOS";
|
|
33959
34183
|
}
|
|
33960
34184
|
if (platform === "linux") {
|
|
33961
|
-
return readLinuxOsRelease() ?? `Linux ${
|
|
34185
|
+
return readLinuxOsRelease() ?? `Linux ${os7.release()}`;
|
|
33962
34186
|
}
|
|
33963
34187
|
if (platform === "win32") {
|
|
33964
|
-
return `Windows ${
|
|
34188
|
+
return `Windows ${os7.release()}`;
|
|
33965
34189
|
}
|
|
33966
|
-
return `${
|
|
34190
|
+
return `${os7.type()} ${os7.release()}`.trim();
|
|
33967
34191
|
}
|
|
33968
34192
|
function readLinuxOsRelease() {
|
|
33969
34193
|
for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
|
|
33970
34194
|
try {
|
|
33971
|
-
return parseLinuxOsRelease(
|
|
34195
|
+
return parseLinuxOsRelease(readFileSync2(file, "utf8"));
|
|
33972
34196
|
} catch {
|
|
33973
34197
|
}
|
|
33974
34198
|
}
|
|
@@ -34010,11 +34234,11 @@ function truncateText(value, maxLength) {
|
|
|
34010
34234
|
}
|
|
34011
34235
|
|
|
34012
34236
|
// src/topology/network.ts
|
|
34013
|
-
import
|
|
34237
|
+
import os9 from "os";
|
|
34014
34238
|
|
|
34015
34239
|
// src/topology/environment.ts
|
|
34016
|
-
import { existsSync, readFileSync as
|
|
34017
|
-
import
|
|
34240
|
+
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
34241
|
+
import os8 from "os";
|
|
34018
34242
|
function detectRuntimeEnvironment(env = process.env) {
|
|
34019
34243
|
if (isWsl(env)) {
|
|
34020
34244
|
return {
|
|
@@ -34043,7 +34267,7 @@ function isWsl(env) {
|
|
|
34043
34267
|
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
|
|
34044
34268
|
return true;
|
|
34045
34269
|
}
|
|
34046
|
-
const release =
|
|
34270
|
+
const release = os8.release().toLowerCase();
|
|
34047
34271
|
return release.includes("microsoft") || release.includes("wsl");
|
|
34048
34272
|
}
|
|
34049
34273
|
function isContainer(env) {
|
|
@@ -34054,7 +34278,7 @@ function isContainer(env) {
|
|
|
34054
34278
|
return true;
|
|
34055
34279
|
}
|
|
34056
34280
|
try {
|
|
34057
|
-
const cgroup =
|
|
34281
|
+
const cgroup = readFileSync3("/proc/1/cgroup", "utf8").toLowerCase();
|
|
34058
34282
|
return /docker|containerd|kubepods|libpod|podman/u.test(cgroup);
|
|
34059
34283
|
} catch {
|
|
34060
34284
|
return false;
|
|
@@ -34088,7 +34312,7 @@ async function discoverRouteCandidates(options) {
|
|
|
34088
34312
|
};
|
|
34089
34313
|
}
|
|
34090
34314
|
function discoverLanIps() {
|
|
34091
|
-
return discoverLanIpsFromInterfaces(
|
|
34315
|
+
return discoverLanIpsFromInterfaces(os9.networkInterfaces());
|
|
34092
34316
|
}
|
|
34093
34317
|
function discoverLanIpsFromInterfaces(interfaces) {
|
|
34094
34318
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -35181,7 +35405,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
35181
35405
|
await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
|
|
35182
35406
|
const log = createRotatingTextLogWriter({
|
|
35183
35407
|
paths,
|
|
35184
|
-
fileName:
|
|
35408
|
+
fileName: path32.basename(daemonLogFile(paths))
|
|
35185
35409
|
});
|
|
35186
35410
|
const scriptPath = currentCliScriptPath();
|
|
35187
35411
|
const write = (chunk) => {
|
|
@@ -35479,7 +35703,7 @@ function terminateChild(child, previousForceKillTimer) {
|
|
|
35479
35703
|
}
|
|
35480
35704
|
}
|
|
35481
35705
|
function supervisorStopIntentPath(paths) {
|
|
35482
|
-
return
|
|
35706
|
+
return path32.join(paths.runDir, "supervisor-stop-intent.json");
|
|
35483
35707
|
}
|
|
35484
35708
|
async function writeSupervisorStopIntent(paths, pid) {
|
|
35485
35709
|
await mkdir15(paths.runDir, { recursive: true, mode: 448 });
|
|
@@ -36042,7 +36266,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
|
|
|
36042
36266
|
};
|
|
36043
36267
|
}
|
|
36044
36268
|
function buildUnixInstallCommand(installerUrl) {
|
|
36045
|
-
const nodeBinDir =
|
|
36269
|
+
const nodeBinDir = path33.dirname(process.execPath);
|
|
36046
36270
|
const fetchScript = [
|
|
36047
36271
|
quoteShellToken(process.execPath),
|
|
36048
36272
|
"--input-type=module",
|
|
@@ -36312,10 +36536,10 @@ async function readUpdateLogLines2(paths) {
|
|
|
36312
36536
|
);
|
|
36313
36537
|
}
|
|
36314
36538
|
function updateStatePath2(paths) {
|
|
36315
|
-
return
|
|
36539
|
+
return path33.join(paths.runDir, "link-update-state.json");
|
|
36316
36540
|
}
|
|
36317
36541
|
function updateLogPath2(paths) {
|
|
36318
|
-
return
|
|
36542
|
+
return path33.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
36319
36543
|
}
|
|
36320
36544
|
async function clearUpdateLogFiles2(paths) {
|
|
36321
36545
|
const primary = updateLogPath2(paths);
|
|
@@ -36399,7 +36623,7 @@ function readString23(payload, key) {
|
|
|
36399
36623
|
}
|
|
36400
36624
|
|
|
36401
36625
|
// src/pairing/pairing.ts
|
|
36402
|
-
import
|
|
36626
|
+
import path34 from "path";
|
|
36403
36627
|
import { rm as rm12 } from "fs/promises";
|
|
36404
36628
|
|
|
36405
36629
|
// src/relay/bootstrap.ts
|
|
@@ -36739,10 +36963,10 @@ async function loadRequiredIdentity2(paths) {
|
|
|
36739
36963
|
}
|
|
36740
36964
|
return identity;
|
|
36741
36965
|
}
|
|
36742
|
-
async function postServerJson(serverBaseUrl,
|
|
36966
|
+
async function postServerJson(serverBaseUrl, path35, body, options) {
|
|
36743
36967
|
let response;
|
|
36744
36968
|
try {
|
|
36745
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
36969
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
|
|
36746
36970
|
method: "POST",
|
|
36747
36971
|
headers: {
|
|
36748
36972
|
accept: "application/json",
|
|
@@ -36790,10 +37014,10 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
36790
37014
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
36791
37015
|
};
|
|
36792
37016
|
}
|
|
36793
|
-
async function patchServerJson(serverBaseUrl,
|
|
37017
|
+
async function patchServerJson(serverBaseUrl, path35, token, body, options) {
|
|
36794
37018
|
let response;
|
|
36795
37019
|
try {
|
|
36796
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
37020
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
|
|
36797
37021
|
method: "PATCH",
|
|
36798
37022
|
headers: {
|
|
36799
37023
|
accept: "application/json",
|
|
@@ -36841,10 +37065,10 @@ function createPairingNetworkError(input) {
|
|
|
36841
37065
|
);
|
|
36842
37066
|
}
|
|
36843
37067
|
function pairingClaimPath(sessionId, paths) {
|
|
36844
|
-
return
|
|
37068
|
+
return path34.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
36845
37069
|
}
|
|
36846
37070
|
function pairingSessionPath(sessionId, paths) {
|
|
36847
|
-
return
|
|
37071
|
+
return path34.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
36848
37072
|
}
|
|
36849
37073
|
function qrPreferredUrls(routes) {
|
|
36850
37074
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -38171,6 +38395,7 @@ export {
|
|
|
38171
38395
|
getGatewayLogFiles,
|
|
38172
38396
|
readRecentGatewayLogEntries,
|
|
38173
38397
|
flushLogFiles,
|
|
38398
|
+
resolveHermesBin,
|
|
38174
38399
|
HermesApiServerUnavailableError,
|
|
38175
38400
|
ensureHermesApiServerAvailable,
|
|
38176
38401
|
readHermesVersion,
|