@hermespilot/link 0.3.7 → 0.3.9
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-TZVQZFWU.js → chunk-7OVDWXR7.js} +642 -341
- package/dist/cli/index.js +89 -33
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -1099,58 +1099,200 @@ function readProfileAvatarType(row) {
|
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
1101
|
// src/hermes/cron-link-delivery.ts
|
|
1102
|
-
import { mkdir as
|
|
1102
|
+
import { mkdir as mkdir3, readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
1103
1103
|
import path4 from "path";
|
|
1104
1104
|
|
|
1105
1105
|
// src/storage/atomic-json.ts
|
|
1106
|
-
import {
|
|
1106
|
+
import { readFile } from "fs/promises";
|
|
1107
|
+
|
|
1108
|
+
// src/storage/atomic-file.ts
|
|
1107
1109
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1110
|
+
import {
|
|
1111
|
+
chmod,
|
|
1112
|
+
chown,
|
|
1113
|
+
lstat,
|
|
1114
|
+
mkdir as mkdir2,
|
|
1115
|
+
open,
|
|
1116
|
+
readdir,
|
|
1117
|
+
rename,
|
|
1118
|
+
rm,
|
|
1119
|
+
stat
|
|
1120
|
+
} from "fs/promises";
|
|
1108
1121
|
import path2 from "path";
|
|
1109
|
-
async function
|
|
1122
|
+
async function atomicWriteFilePreservingMetadata(filePath, value, options = {}) {
|
|
1123
|
+
const resolvedPath = path2.resolve(filePath);
|
|
1124
|
+
const directory = path2.dirname(resolvedPath);
|
|
1125
|
+
await ensureDirectoryWithInheritedMetadata(
|
|
1126
|
+
directory,
|
|
1127
|
+
options.directoryMode ?? 448
|
|
1128
|
+
);
|
|
1129
|
+
const existingMetadata = await readExistingFileMetadata(resolvedPath) ?? (options.metadataSourcePath ? await readExistingFileMetadata(path2.resolve(options.metadataSourcePath)) : null);
|
|
1130
|
+
const directoryMetadata = await readPathMetadata(directory);
|
|
1131
|
+
const metadata = {
|
|
1132
|
+
uid: existingMetadata?.uid ?? directoryMetadata.uid,
|
|
1133
|
+
gid: existingMetadata?.gid ?? directoryMetadata.gid,
|
|
1134
|
+
mode: existingMetadata?.mode ?? options.mode ?? 384
|
|
1135
|
+
};
|
|
1136
|
+
const tempPath = path2.join(
|
|
1137
|
+
directory,
|
|
1138
|
+
`.${path2.basename(resolvedPath)}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`
|
|
1139
|
+
);
|
|
1110
1140
|
try {
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1141
|
+
const handle = await open(tempPath, "wx", metadata.mode);
|
|
1142
|
+
try {
|
|
1143
|
+
if (typeof value === "string") {
|
|
1144
|
+
await handle.writeFile(value, options.encoding ?? "utf8");
|
|
1145
|
+
} else {
|
|
1146
|
+
await handle.writeFile(value);
|
|
1147
|
+
}
|
|
1148
|
+
await handle.sync();
|
|
1149
|
+
} finally {
|
|
1150
|
+
await handle.close();
|
|
1151
|
+
}
|
|
1152
|
+
await applyMetadata(tempPath, metadata);
|
|
1153
|
+
await rename(tempPath, resolvedPath);
|
|
1113
1154
|
} catch (error) {
|
|
1155
|
+
await rm(tempPath, { force: true });
|
|
1156
|
+
throw error;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
async function ensureDirectoryWithInheritedMetadata(directory, mode) {
|
|
1160
|
+
const { source, missing } = await findExistingAncestor(directory);
|
|
1161
|
+
await mkdir2(directory, { recursive: true, mode });
|
|
1162
|
+
for (const missingDirectory of missing) {
|
|
1163
|
+
await applyMetadata(missingDirectory, {
|
|
1164
|
+
uid: source.uid,
|
|
1165
|
+
gid: source.gid,
|
|
1166
|
+
mode
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
async function inheritOwnerRecursively(targetPath, sourcePath) {
|
|
1171
|
+
if (process.platform === "win32") {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const source = await readPathMetadata(sourcePath);
|
|
1175
|
+
await applyOwnerRecursively(targetPath, source);
|
|
1176
|
+
}
|
|
1177
|
+
async function findExistingAncestor(directory) {
|
|
1178
|
+
const missing = [];
|
|
1179
|
+
let current = path2.resolve(directory);
|
|
1180
|
+
while (true) {
|
|
1181
|
+
const currentStat = await stat(current).catch((error) => {
|
|
1182
|
+
if (isNodeError(error, "ENOENT")) {
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
throw error;
|
|
1186
|
+
});
|
|
1187
|
+
if (currentStat) {
|
|
1188
|
+
if (!currentStat.isDirectory()) {
|
|
1189
|
+
throw new Error(`${current} is not a directory`);
|
|
1190
|
+
}
|
|
1191
|
+
return {
|
|
1192
|
+
source: metadataFromStats(currentStat),
|
|
1193
|
+
missing: missing.reverse()
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
missing.push(current);
|
|
1197
|
+
const parent = path2.dirname(current);
|
|
1198
|
+
if (parent === current) {
|
|
1199
|
+
throw new Error(`No existing parent directory for ${directory}`);
|
|
1200
|
+
}
|
|
1201
|
+
current = parent;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
async function readExistingFileMetadata(filePath) {
|
|
1205
|
+
const fileStat = await stat(filePath).catch((error) => {
|
|
1114
1206
|
if (isNodeError(error, "ENOENT")) {
|
|
1115
1207
|
return null;
|
|
1116
1208
|
}
|
|
1117
1209
|
throw error;
|
|
1210
|
+
});
|
|
1211
|
+
if (!fileStat) {
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
if (!fileStat.isFile()) {
|
|
1215
|
+
throw new Error(`${filePath} is not a file`);
|
|
1118
1216
|
}
|
|
1217
|
+
return metadataFromStats(fileStat);
|
|
1119
1218
|
}
|
|
1120
|
-
async function
|
|
1121
|
-
await
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1219
|
+
async function readPathMetadata(filePath) {
|
|
1220
|
+
return metadataFromStats(await stat(filePath));
|
|
1221
|
+
}
|
|
1222
|
+
async function applyMetadata(filePath, metadata) {
|
|
1223
|
+
await applyOwner(filePath, metadata);
|
|
1224
|
+
await chmod(filePath, metadata.mode);
|
|
1225
|
+
}
|
|
1226
|
+
async function applyOwnerRecursively(filePath, metadata) {
|
|
1227
|
+
const current = await lstat(filePath);
|
|
1228
|
+
if (current.isSymbolicLink()) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
await applyOwner(filePath, metadata);
|
|
1232
|
+
if (!current.isDirectory()) {
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const entries = await readdir(filePath, { withFileTypes: true });
|
|
1236
|
+
await Promise.all(
|
|
1237
|
+
entries.map(
|
|
1238
|
+
(entry) => applyOwnerRecursively(path2.join(filePath, entry.name), metadata)
|
|
1239
|
+
)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
async function applyOwner(filePath, metadata) {
|
|
1243
|
+
if (process.platform === "win32") {
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const currentUid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
1247
|
+
const currentGid = typeof process.getgid === "function" ? process.getgid() : void 0;
|
|
1248
|
+
if (metadata.uid === currentUid && metadata.gid === currentGid) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1126
1251
|
try {
|
|
1127
|
-
await
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1252
|
+
await chown(filePath, metadata.uid, metadata.gid);
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
const current = await stat(filePath);
|
|
1255
|
+
if (current.uid !== metadata.uid || current.gid !== metadata.gid) {
|
|
1256
|
+
throw error;
|
|
1257
|
+
}
|
|
1131
1258
|
}
|
|
1259
|
+
}
|
|
1260
|
+
function metadataFromStats(statsValue) {
|
|
1261
|
+
return {
|
|
1262
|
+
uid: statsValue.uid,
|
|
1263
|
+
gid: statsValue.gid,
|
|
1264
|
+
mode: statsValue.mode & 511
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function isNodeError(error, code) {
|
|
1268
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/storage/atomic-json.ts
|
|
1272
|
+
async function readJsonFile(filePath) {
|
|
1132
1273
|
try {
|
|
1133
|
-
await
|
|
1274
|
+
const raw = await readFile(filePath, "utf8");
|
|
1275
|
+
return JSON.parse(raw);
|
|
1134
1276
|
} catch (error) {
|
|
1135
|
-
|
|
1277
|
+
if (isNodeError2(error, "ENOENT")) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1136
1280
|
throw error;
|
|
1137
1281
|
}
|
|
1138
1282
|
}
|
|
1139
|
-
function
|
|
1283
|
+
async function writeJsonFile(filePath, value, mode = 384) {
|
|
1284
|
+
const payload = `${JSON.stringify(value, null, 2)}
|
|
1285
|
+
`;
|
|
1286
|
+
await atomicWriteFilePreservingMetadata(filePath, payload, { mode });
|
|
1287
|
+
}
|
|
1288
|
+
function isNodeError2(error, code) {
|
|
1140
1289
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1141
1290
|
}
|
|
1142
1291
|
|
|
1143
1292
|
// src/hermes/config.ts
|
|
1144
1293
|
import { randomBytes } from "crypto";
|
|
1145
1294
|
import net from "net";
|
|
1146
|
-
import {
|
|
1147
|
-
copyFile,
|
|
1148
|
-
mkdir as mkdir3,
|
|
1149
|
-
readFile as readFile2,
|
|
1150
|
-
readdir,
|
|
1151
|
-
rename as rename2,
|
|
1152
|
-
writeFile
|
|
1153
|
-
} from "fs/promises";
|
|
1295
|
+
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
1154
1296
|
import os from "os";
|
|
1155
1297
|
import path3 from "path";
|
|
1156
1298
|
import YAML from "yaml";
|
|
@@ -1374,7 +1516,7 @@ async function readHermesSessionsDir(profileName = "default", configPath = resol
|
|
|
1374
1516
|
async function readHermesApiServerConfig(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
|
|
1375
1517
|
const existingRaw = await readFile2(configPath, "utf8").catch(
|
|
1376
1518
|
(error) => {
|
|
1377
|
-
if (
|
|
1519
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
1378
1520
|
return null;
|
|
1379
1521
|
}
|
|
1380
1522
|
throw error;
|
|
@@ -1399,7 +1541,7 @@ async function readHermesApiServerConfig(profileName = "default", configPath = r
|
|
|
1399
1541
|
async function readHermesModelConfig(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
|
|
1400
1542
|
const existingRaw = await readFile2(configPath, "utf8").catch(
|
|
1401
1543
|
(error) => {
|
|
1402
|
-
if (
|
|
1544
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
1403
1545
|
return null;
|
|
1404
1546
|
}
|
|
1405
1547
|
throw error;
|
|
@@ -1920,7 +2062,7 @@ async function ensureHermesApiServerConfig(profileName = "default", configPath =
|
|
|
1920
2062
|
async function ensureHermesApiServerConfigUnlocked(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
|
|
1921
2063
|
const existingRaw = await readFile2(configPath, "utf8").catch(
|
|
1922
2064
|
(error) => {
|
|
1923
|
-
if (
|
|
2065
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
1924
2066
|
return null;
|
|
1925
2067
|
}
|
|
1926
2068
|
throw error;
|
|
@@ -1986,12 +2128,13 @@ async function ensureHermesApiServerConfigUnlocked(profileName = "default", conf
|
|
|
1986
2128
|
};
|
|
1987
2129
|
}
|
|
1988
2130
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2131
|
+
if (backupPath && existingRaw !== null) {
|
|
2132
|
+
await atomicWriteFilePreservingMetadata(backupPath, existingRaw, {
|
|
2133
|
+
metadataSourcePath: configPath
|
|
2134
|
+
});
|
|
1992
2135
|
}
|
|
1993
2136
|
document.contents = document.createNode(config);
|
|
1994
|
-
await
|
|
2137
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
1995
2138
|
return {
|
|
1996
2139
|
configPath,
|
|
1997
2140
|
apiServer: applyEnvOverrides(
|
|
@@ -2017,7 +2160,7 @@ async function ensureHermesApiServerConfigUnlocked(profileName = "default", conf
|
|
|
2017
2160
|
async function readHermesConfigDocument(configPath) {
|
|
2018
2161
|
const existingRaw = await readFile2(configPath, "utf8").catch(
|
|
2019
2162
|
(error) => {
|
|
2020
|
-
if (
|
|
2163
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
2021
2164
|
return null;
|
|
2022
2165
|
}
|
|
2023
2166
|
throw error;
|
|
@@ -2032,14 +2175,16 @@ async function readHermesConfigDocument(configPath) {
|
|
|
2032
2175
|
}
|
|
2033
2176
|
async function writeHermesConfigDocument(input) {
|
|
2034
2177
|
const backupPath = input.existingRaw ? `${input.configPath}.bak.${Date.now()}` : null;
|
|
2035
|
-
await mkdir3(path3.dirname(input.configPath), { recursive: true, mode: 448 });
|
|
2036
2178
|
if (backupPath) {
|
|
2037
|
-
await
|
|
2179
|
+
await atomicWriteFilePreservingMetadata(backupPath, input.existingRaw, {
|
|
2180
|
+
metadataSourcePath: input.configPath
|
|
2181
|
+
});
|
|
2038
2182
|
}
|
|
2039
2183
|
input.document.contents = input.document.createNode(input.config);
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2184
|
+
await atomicWriteFilePreservingMetadata(
|
|
2185
|
+
input.configPath,
|
|
2186
|
+
input.document.toString()
|
|
2187
|
+
);
|
|
2043
2188
|
return backupPath;
|
|
2044
2189
|
}
|
|
2045
2190
|
function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffort) {
|
|
@@ -3361,9 +3506,9 @@ async function readConfiguredApiServerPorts(excludedProfileName) {
|
|
|
3361
3506
|
resolveDefaultHermesRoot(resolveHermesProfileDir("default")),
|
|
3362
3507
|
"profiles"
|
|
3363
3508
|
);
|
|
3364
|
-
const entries = await
|
|
3509
|
+
const entries = await readdir2(profilesRoot, { withFileTypes: true }).catch(
|
|
3365
3510
|
(error) => {
|
|
3366
|
-
if (
|
|
3511
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
3367
3512
|
return [];
|
|
3368
3513
|
}
|
|
3369
3514
|
throw error;
|
|
@@ -3385,7 +3530,7 @@ async function addConfiguredApiServerPort(ports, profileName, excludedProfileNam
|
|
|
3385
3530
|
resolveHermesConfigPath(profileName),
|
|
3386
3531
|
"utf8"
|
|
3387
3532
|
).catch((error) => {
|
|
3388
|
-
if (
|
|
3533
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
3389
3534
|
return "";
|
|
3390
3535
|
}
|
|
3391
3536
|
throw error;
|
|
@@ -3458,7 +3603,7 @@ async function readHermesApiServerEnvOverrides(profileName) {
|
|
|
3458
3603
|
async function readHermesEnvFile(profileName) {
|
|
3459
3604
|
const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
|
|
3460
3605
|
const raw = await readFile2(envPath, "utf8").catch((error) => {
|
|
3461
|
-
if (
|
|
3606
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
3462
3607
|
return "";
|
|
3463
3608
|
}
|
|
3464
3609
|
throw error;
|
|
@@ -3483,7 +3628,7 @@ async function writeHermesEnvValue(profileName, key, value) {
|
|
|
3483
3628
|
const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
|
|
3484
3629
|
const existingRaw = await readFile2(envPath, "utf8").catch(
|
|
3485
3630
|
(error) => {
|
|
3486
|
-
if (
|
|
3631
|
+
if (isNodeError3(error, "ENOENT")) {
|
|
3487
3632
|
return "";
|
|
3488
3633
|
}
|
|
3489
3634
|
throw error;
|
|
@@ -3506,13 +3651,14 @@ async function writeHermesEnvValue(profileName, key, value) {
|
|
|
3506
3651
|
nextLines.push(`${key}=${formatEnvValue(value)}`);
|
|
3507
3652
|
}
|
|
3508
3653
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
3509
|
-
await mkdir3(path3.dirname(envPath), { recursive: true, mode: 448 });
|
|
3510
3654
|
if (existingRaw) {
|
|
3511
|
-
await
|
|
3655
|
+
await atomicWriteFilePreservingMetadata(
|
|
3656
|
+
`${envPath}.bak.${Date.now()}`,
|
|
3657
|
+
existingRaw,
|
|
3658
|
+
{ metadataSourcePath: envPath }
|
|
3659
|
+
);
|
|
3512
3660
|
}
|
|
3513
|
-
|
|
3514
|
-
await writeFile(tempPath, nextRaw, { mode: 384 });
|
|
3515
|
-
await rename2(tempPath, envPath);
|
|
3661
|
+
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
3516
3662
|
}
|
|
3517
3663
|
function applyEnvOverrides(config, env, withDefaults) {
|
|
3518
3664
|
const host = config.host ?? env.host;
|
|
@@ -3591,7 +3737,7 @@ function ensureRecord(parent, key) {
|
|
|
3591
3737
|
function resolveHermesConfiguredPath(value, baseDir) {
|
|
3592
3738
|
return path3.isAbsolute(value) ? path3.normalize(value) : path3.resolve(baseDir, value);
|
|
3593
3739
|
}
|
|
3594
|
-
function
|
|
3740
|
+
function isNodeError3(error, code) {
|
|
3595
3741
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
3596
3742
|
}
|
|
3597
3743
|
function resolveDefaultHermesRoot(hermesHome) {
|
|
@@ -3716,9 +3862,9 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
3716
3862
|
"output",
|
|
3717
3863
|
jobId
|
|
3718
3864
|
);
|
|
3719
|
-
const entries = await
|
|
3865
|
+
const entries = await readdir3(outputDir, { withFileTypes: true }).catch(
|
|
3720
3866
|
(error) => {
|
|
3721
|
-
if (
|
|
3867
|
+
if (isNodeError4(error, "ENOENT")) {
|
|
3722
3868
|
return [];
|
|
3723
3869
|
}
|
|
3724
3870
|
throw error;
|
|
@@ -3730,7 +3876,7 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
3730
3876
|
continue;
|
|
3731
3877
|
}
|
|
3732
3878
|
const outputPath = path4.join(outputDir, entry.name);
|
|
3733
|
-
const fileStat = await
|
|
3879
|
+
const fileStat = await stat2(outputPath);
|
|
3734
3880
|
files.push({
|
|
3735
3881
|
path: outputPath,
|
|
3736
3882
|
mtime: fileStat.mtime.toISOString(),
|
|
@@ -3763,7 +3909,7 @@ async function readRegistry(paths) {
|
|
|
3763
3909
|
return { version: REGISTRY_VERSION, bindings: [] };
|
|
3764
3910
|
}
|
|
3765
3911
|
async function writeRegistry(paths, registry) {
|
|
3766
|
-
await
|
|
3912
|
+
await mkdir3(paths.indexesDir, { recursive: true, mode: 448 });
|
|
3767
3913
|
await writeJsonFile(registryPath(paths), registry);
|
|
3768
3914
|
}
|
|
3769
3915
|
function registryPath(paths) {
|
|
@@ -3789,13 +3935,13 @@ function readString3(record, ...keys) {
|
|
|
3789
3935
|
}
|
|
3790
3936
|
return void 0;
|
|
3791
3937
|
}
|
|
3792
|
-
function
|
|
3938
|
+
function isNodeError4(error, code) {
|
|
3793
3939
|
return error instanceof Error && error.code === code;
|
|
3794
3940
|
}
|
|
3795
3941
|
|
|
3796
3942
|
// src/hermes/gateway.ts
|
|
3797
3943
|
import { execFile as execFile2, spawn } from "child_process";
|
|
3798
|
-
import { access, readFile as readFile5, stat as
|
|
3944
|
+
import { access, readFile as readFile5, stat as stat4 } from "fs/promises";
|
|
3799
3945
|
import path7 from "path";
|
|
3800
3946
|
import { setTimeout as delay } from "timers/promises";
|
|
3801
3947
|
import { promisify as promisify2 } from "util";
|
|
@@ -3815,7 +3961,7 @@ function isLinkHttpError(error) {
|
|
|
3815
3961
|
}
|
|
3816
3962
|
|
|
3817
3963
|
// src/runtime/logger.ts
|
|
3818
|
-
import { appendFile, mkdir as
|
|
3964
|
+
import { appendFile, mkdir as mkdir4, open as open2, readFile as readFile4, rename as rename2, rm as rm2, stat as stat3 } from "fs/promises";
|
|
3819
3965
|
import os3 from "os";
|
|
3820
3966
|
import path6 from "path";
|
|
3821
3967
|
|
|
@@ -3824,7 +3970,7 @@ import os2 from "os";
|
|
|
3824
3970
|
import path5 from "path";
|
|
3825
3971
|
|
|
3826
3972
|
// src/constants.ts
|
|
3827
|
-
var LINK_VERSION = "0.3.
|
|
3973
|
+
var LINK_VERSION = "0.3.9";
|
|
3828
3974
|
var LINK_COMMAND = "hermeslink";
|
|
3829
3975
|
var LINK_DEFAULT_PORT = 52379;
|
|
3830
3976
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -3900,7 +4046,7 @@ var FileLogger = class {
|
|
|
3900
4046
|
return this.queue;
|
|
3901
4047
|
}
|
|
3902
4048
|
async appendEntry(entry) {
|
|
3903
|
-
await
|
|
4049
|
+
await mkdir4(this.paths.logsDir, { recursive: true, mode: 448 });
|
|
3904
4050
|
const line = `${JSON.stringify(entry)}
|
|
3905
4051
|
`;
|
|
3906
4052
|
await this.rotateIfNeeded(Buffer.byteLength(line, "utf8"));
|
|
@@ -3936,7 +4082,7 @@ function createRotatingTextLogWriter(options) {
|
|
|
3936
4082
|
return queue;
|
|
3937
4083
|
}
|
|
3938
4084
|
const next = queue.then(async () => {
|
|
3939
|
-
await
|
|
4085
|
+
await mkdir4(paths.logsDir, { recursive: true, mode: 448 });
|
|
3940
4086
|
await rotateLogFileIfNeeded(filePath, buffer.length, maxFileBytes, maxFiles);
|
|
3941
4087
|
await appendFile(filePath, buffer, { mode: 384 });
|
|
3942
4088
|
}).catch(() => void 0);
|
|
@@ -4037,7 +4183,7 @@ function clampLimit(value) {
|
|
|
4037
4183
|
return Math.min(MAX_READ_LIMIT, Math.max(1, Math.floor(value)));
|
|
4038
4184
|
}
|
|
4039
4185
|
async function rotateLogFileIfNeeded(filePath, nextBytes, maxFileBytes, maxFiles) {
|
|
4040
|
-
const current = await
|
|
4186
|
+
const current = await stat3(filePath).catch(() => null);
|
|
4041
4187
|
if (!current || current.size === 0 || current.size + nextBytes <= maxFileBytes) {
|
|
4042
4188
|
return;
|
|
4043
4189
|
}
|
|
@@ -4152,7 +4298,7 @@ async function readTail(filePath, maxBytes) {
|
|
|
4152
4298
|
return tail?.content ?? null;
|
|
4153
4299
|
}
|
|
4154
4300
|
async function readTailWithMetadata(filePath, maxBytes) {
|
|
4155
|
-
const info = await
|
|
4301
|
+
const info = await stat3(filePath).catch(() => null);
|
|
4156
4302
|
if (!info || info.size <= 0) {
|
|
4157
4303
|
return null;
|
|
4158
4304
|
}
|
|
@@ -4176,7 +4322,7 @@ async function readTailWithMetadata(filePath, maxBytes) {
|
|
|
4176
4322
|
}
|
|
4177
4323
|
async function moveIfExists(from, to) {
|
|
4178
4324
|
await rm2(to, { force: true }).catch(() => void 0);
|
|
4179
|
-
await
|
|
4325
|
+
await rename2(from, to).catch((error) => {
|
|
4180
4326
|
if (error.code !== "ENOENT") {
|
|
4181
4327
|
throw error;
|
|
4182
4328
|
}
|
|
@@ -4309,6 +4455,9 @@ var DEFAULT_START_TIMEOUT_MS = 12e3;
|
|
|
4309
4455
|
var HEALTH_TIMEOUT_MS = 1500;
|
|
4310
4456
|
var MIN_API_SERVER_VERSION = "0.4.0";
|
|
4311
4457
|
var PROFILE_NAME_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
4458
|
+
var DASHBOARD_STATUS_URL = "http://127.0.0.1:9119/api/status";
|
|
4459
|
+
var DASHBOARD_STATUS_TIMEOUT_MS = 1500;
|
|
4460
|
+
var MAX_VERSION_LOG_OUTPUT_LENGTH = 1200;
|
|
4312
4461
|
var gatewayStartInFlightByProfile = /* @__PURE__ */ new Map();
|
|
4313
4462
|
async function ensureHermesApiServerAvailable(options = {}) {
|
|
4314
4463
|
const profileName = normalizeProfileName(options.profileName);
|
|
@@ -4425,28 +4574,56 @@ async function reloadHermesGateway(options = {}) {
|
|
|
4425
4574
|
}
|
|
4426
4575
|
return ensureHermesApiServerAvailable({ ...options, forceRestart: true });
|
|
4427
4576
|
}
|
|
4428
|
-
async function readHermesVersion() {
|
|
4429
|
-
const { stdout } = await execHermesVersion();
|
|
4430
|
-
const raw = stdout.trim();
|
|
4431
|
-
const version = parseHermesVersion(raw);
|
|
4432
|
-
return {
|
|
4433
|
-
raw,
|
|
4434
|
-
version,
|
|
4435
|
-
supportsApiServer: version ? compareSemver(version, MIN_API_SERVER_VERSION) >= 0 : null
|
|
4436
|
-
};
|
|
4437
|
-
}
|
|
4438
|
-
async function execHermesVersion() {
|
|
4577
|
+
async function readHermesVersion(options = {}) {
|
|
4439
4578
|
try {
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
} catch {
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4579
|
+
const { stdout } = await execHermesVersion(options.logger);
|
|
4580
|
+
const raw = stdout.trim();
|
|
4581
|
+
const version = parseHermesVersion(raw);
|
|
4582
|
+
return buildHermesVersionInfo(raw, version);
|
|
4583
|
+
} catch (cliError) {
|
|
4584
|
+
const dashboardStatusUrl = options.dashboardStatusUrl ?? DASHBOARD_STATUS_URL;
|
|
4585
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_requested", {
|
|
4586
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4587
|
+
reason: cliError instanceof Error ? cliError.message : String(cliError)
|
|
4448
4588
|
});
|
|
4589
|
+
try {
|
|
4590
|
+
const fallback = await readHermesDashboardVersion({
|
|
4591
|
+
fetchImpl: options.fetchImpl,
|
|
4592
|
+
statusUrl: dashboardStatusUrl,
|
|
4593
|
+
timeoutMs: options.dashboardTimeoutMs
|
|
4594
|
+
});
|
|
4595
|
+
void options.logger?.info("hermes_version_dashboard_fallback_succeeded", {
|
|
4596
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4597
|
+
hermes_version: fallback.version
|
|
4598
|
+
});
|
|
4599
|
+
return fallback;
|
|
4600
|
+
} catch (dashboardError) {
|
|
4601
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_failed", {
|
|
4602
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4603
|
+
error: dashboardError instanceof Error ? dashboardError.message : String(dashboardError)
|
|
4604
|
+
});
|
|
4605
|
+
throw new Error(
|
|
4606
|
+
`Hermes version detection failed. CLI: ${cliError instanceof Error ? cliError.message : String(cliError)}; dashboard fallback: ${dashboardError instanceof Error ? dashboardError.message : String(dashboardError)}`
|
|
4607
|
+
);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
async function execHermesVersion(logger) {
|
|
4612
|
+
const hermesBin = resolveHermesBin();
|
|
4613
|
+
const failures = [];
|
|
4614
|
+
for (const args of [["version"], ["--version"]]) {
|
|
4615
|
+
try {
|
|
4616
|
+
return await execFileAsync2(hermesBin, args, {
|
|
4617
|
+
timeout: 5e3,
|
|
4618
|
+
windowsHide: true
|
|
4619
|
+
});
|
|
4620
|
+
} catch (error) {
|
|
4621
|
+
const failure = describeVersionCommandFailure(hermesBin, args, error);
|
|
4622
|
+
failures.push(failure.summary);
|
|
4623
|
+
void logger?.warn("hermes_version_cli_command_failed", failure.fields);
|
|
4624
|
+
}
|
|
4449
4625
|
}
|
|
4626
|
+
throw new Error(failures.join("; "));
|
|
4450
4627
|
}
|
|
4451
4628
|
function assertHermesRunsApiSupported(version, status) {
|
|
4452
4629
|
if (status !== 404) {
|
|
@@ -4471,7 +4648,7 @@ async function startHermesGatewayOnce(paths, profileName, logger) {
|
|
|
4471
4648
|
return await gatewayStartInFlightByProfile.get(profileName);
|
|
4472
4649
|
}
|
|
4473
4650
|
async function startHermesGateway(paths, profileName, logger) {
|
|
4474
|
-
const version = await readHermesVersion().catch((error) => {
|
|
4651
|
+
const version = await readHermesVersion({ logger }).catch((error) => {
|
|
4475
4652
|
void logger?.error("gateway_hermes_cli_unavailable", {
|
|
4476
4653
|
error: error instanceof Error ? error.message : String(error)
|
|
4477
4654
|
});
|
|
@@ -4583,7 +4760,7 @@ async function restartHermesGatewayServiceIfAvailable(options) {
|
|
|
4583
4760
|
return {
|
|
4584
4761
|
pid: null,
|
|
4585
4762
|
logPath,
|
|
4586
|
-
version: await readHermesVersion().catch(() => null),
|
|
4763
|
+
version: await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
4587
4764
|
...logHint ? { logHint } : {}
|
|
4588
4765
|
};
|
|
4589
4766
|
}
|
|
@@ -4599,8 +4776,8 @@ async function assertProfileExists(profileName) {
|
|
|
4599
4776
|
if (profileName === "default") {
|
|
4600
4777
|
return;
|
|
4601
4778
|
}
|
|
4602
|
-
const exists = await
|
|
4603
|
-
if (
|
|
4779
|
+
const exists = await stat4(resolveHermesProfileDir(profileName)).then((value) => value.isDirectory()).catch((error) => {
|
|
4780
|
+
if (isNodeError5(error, "ENOENT")) {
|
|
4604
4781
|
return false;
|
|
4605
4782
|
}
|
|
4606
4783
|
throw error;
|
|
@@ -4800,7 +4977,7 @@ function isHealthyPlatformState(value) {
|
|
|
4800
4977
|
function toRecord2(value) {
|
|
4801
4978
|
return typeof value === "object" && value !== null ? value : {};
|
|
4802
4979
|
}
|
|
4803
|
-
function
|
|
4980
|
+
function isNodeError5(error, code) {
|
|
4804
4981
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
4805
4982
|
}
|
|
4806
4983
|
function readString4(payload, key) {
|
|
@@ -4826,6 +5003,72 @@ function parseHermesVersion(value) {
|
|
|
4826
5003
|
const match = /\bv?(\d+\.\d+\.\d+)\b/u.exec(value);
|
|
4827
5004
|
return match?.[1] ?? null;
|
|
4828
5005
|
}
|
|
5006
|
+
function buildHermesVersionInfo(raw, version) {
|
|
5007
|
+
return {
|
|
5008
|
+
raw,
|
|
5009
|
+
version,
|
|
5010
|
+
supportsApiServer: version ? compareSemver(version, MIN_API_SERVER_VERSION) >= 0 : null
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
async function readHermesDashboardVersion(options = {}) {
|
|
5014
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
5015
|
+
const controller = new AbortController();
|
|
5016
|
+
const timer = setTimeout(
|
|
5017
|
+
() => controller.abort(),
|
|
5018
|
+
options.timeoutMs ?? DASHBOARD_STATUS_TIMEOUT_MS
|
|
5019
|
+
);
|
|
5020
|
+
try {
|
|
5021
|
+
const response = await fetcher(options.statusUrl ?? DASHBOARD_STATUS_URL, {
|
|
5022
|
+
method: "GET",
|
|
5023
|
+
headers: { accept: "application/json" },
|
|
5024
|
+
signal: controller.signal
|
|
5025
|
+
});
|
|
5026
|
+
if (!response.ok) {
|
|
5027
|
+
throw new Error(`Hermes dashboard returned HTTP ${response.status}`);
|
|
5028
|
+
}
|
|
5029
|
+
const payload = await response.json().catch(() => null);
|
|
5030
|
+
const record = toRecord2(payload);
|
|
5031
|
+
const versionText = readString4(record, "version");
|
|
5032
|
+
if (!versionText) {
|
|
5033
|
+
throw new Error("Hermes dashboard status did not include a version");
|
|
5034
|
+
}
|
|
5035
|
+
const raw = truncateVersionLogOutput(JSON.stringify(record));
|
|
5036
|
+
const version = parseHermesVersion(versionText) ?? parseHermesVersion(raw);
|
|
5037
|
+
return buildHermesVersionInfo(raw, version);
|
|
5038
|
+
} catch (error) {
|
|
5039
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
5040
|
+
throw new Error("Hermes dashboard version probe timed out");
|
|
5041
|
+
}
|
|
5042
|
+
throw error;
|
|
5043
|
+
} finally {
|
|
5044
|
+
clearTimeout(timer);
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
function describeVersionCommandFailure(hermesBin, args, error) {
|
|
5048
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5049
|
+
const output = truncateVersionLogOutput(readExecErrorOutput2(error));
|
|
5050
|
+
return {
|
|
5051
|
+
summary: `${hermesBin} ${args.join(" ")} failed: ${message}`,
|
|
5052
|
+
fields: {
|
|
5053
|
+
hermes_bin: hermesBin,
|
|
5054
|
+
command: args.join(" "),
|
|
5055
|
+
error: message,
|
|
5056
|
+
...output ? { output } : {}
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
function readExecErrorOutput2(error) {
|
|
5061
|
+
if (typeof error !== "object" || error === null) {
|
|
5062
|
+
return "";
|
|
5063
|
+
}
|
|
5064
|
+
const stdout = "stdout" in error && error.stdout != null ? String(error.stdout) : "";
|
|
5065
|
+
const stderr = "stderr" in error && error.stderr != null ? String(error.stderr) : "";
|
|
5066
|
+
return `${stdout}
|
|
5067
|
+
${stderr}`.trim();
|
|
5068
|
+
}
|
|
5069
|
+
function truncateVersionLogOutput(value) {
|
|
5070
|
+
return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
|
|
5071
|
+
}
|
|
4829
5072
|
function compareSemver(left, right) {
|
|
4830
5073
|
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
|
|
4831
5074
|
const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -5168,7 +5411,7 @@ function firstRecord(...values) {
|
|
|
5168
5411
|
|
|
5169
5412
|
// src/conversations/blob-store.ts
|
|
5170
5413
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5171
|
-
import { mkdir as
|
|
5414
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir4, rm as rm3, stat as stat5, writeFile } from "fs/promises";
|
|
5172
5415
|
import path9 from "path";
|
|
5173
5416
|
|
|
5174
5417
|
// src/conversations/media.ts
|
|
@@ -5623,8 +5866,8 @@ async function writeConversationBlob(paths, conversationId, input, options) {
|
|
|
5623
5866
|
}
|
|
5624
5867
|
const id = `blob_${randomUUID3().replaceAll("-", "")}`;
|
|
5625
5868
|
const filePath = blobPath(paths, id);
|
|
5626
|
-
await
|
|
5627
|
-
await
|
|
5869
|
+
await mkdir5(path9.dirname(filePath), { recursive: true, mode: 448 });
|
|
5870
|
+
await writeFile(filePath, input.bytes, { mode: 384 });
|
|
5628
5871
|
const blob = {
|
|
5629
5872
|
id,
|
|
5630
5873
|
size: input.bytes.byteLength,
|
|
@@ -5651,7 +5894,7 @@ async function readConversationBlob(paths, conversationId, blobId) {
|
|
|
5651
5894
|
const filePath = blobPath(paths, blobId);
|
|
5652
5895
|
const manifest = await readConversationBlobManifest(paths, conversationId, blobId);
|
|
5653
5896
|
const bytes = await readFile6(filePath).catch((error) => {
|
|
5654
|
-
if (
|
|
5897
|
+
if (isNodeError6(error, "ENOENT")) {
|
|
5655
5898
|
throw new LinkHttpError(404, "blob_not_found", "Blob was not found");
|
|
5656
5899
|
}
|
|
5657
5900
|
throw error;
|
|
@@ -5693,7 +5936,7 @@ async function deleteConversationBlobIfUnreferenced(paths, conversationId, blobI
|
|
|
5693
5936
|
async function materializeConversationBlob(paths, conversationId, blobId, manifest) {
|
|
5694
5937
|
const existingPath = manifest.materialized_path;
|
|
5695
5938
|
if (existingPath) {
|
|
5696
|
-
const exists = await
|
|
5939
|
+
const exists = await stat5(existingPath).then((value) => value.isFile()).catch(() => false);
|
|
5697
5940
|
if (exists) {
|
|
5698
5941
|
return existingPath;
|
|
5699
5942
|
}
|
|
@@ -5703,8 +5946,8 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
|
|
|
5703
5946
|
targetDir,
|
|
5704
5947
|
materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
|
|
5705
5948
|
);
|
|
5706
|
-
await
|
|
5707
|
-
await
|
|
5949
|
+
await mkdir5(targetDir, { recursive: true, mode: 448 });
|
|
5950
|
+
await writeFile(targetPath, await readFile6(blobPath(paths, blobId)), {
|
|
5708
5951
|
mode: 384
|
|
5709
5952
|
});
|
|
5710
5953
|
await writeJsonFile(`${blobPath(paths, blobId)}.json`, {
|
|
@@ -5733,11 +5976,11 @@ async function pruneConversationBlobReference(paths, conversationId, blobId) {
|
|
|
5733
5976
|
}
|
|
5734
5977
|
async function listConversationBlobIds(paths, conversationId) {
|
|
5735
5978
|
assertValidConversationId(conversationId);
|
|
5736
|
-
await
|
|
5737
|
-
const entries = await
|
|
5979
|
+
await mkdir5(paths.blobsDir, { recursive: true, mode: 448 });
|
|
5980
|
+
const entries = await readdir4(paths.blobsDir, {
|
|
5738
5981
|
withFileTypes: true
|
|
5739
5982
|
}).catch((error) => {
|
|
5740
|
-
if (
|
|
5983
|
+
if (isNodeError6(error, "ENOENT")) {
|
|
5741
5984
|
return [];
|
|
5742
5985
|
}
|
|
5743
5986
|
throw error;
|
|
@@ -5771,12 +6014,12 @@ function conversationAttachmentsDir(paths, conversationId) {
|
|
|
5771
6014
|
assertValidConversationId(conversationId);
|
|
5772
6015
|
return path9.join(paths.conversationsDir, conversationId, "attachments");
|
|
5773
6016
|
}
|
|
5774
|
-
function
|
|
6017
|
+
function isNodeError6(error, code) {
|
|
5775
6018
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5776
6019
|
}
|
|
5777
6020
|
|
|
5778
6021
|
// src/conversations/profile-runtime.ts
|
|
5779
|
-
import { stat as
|
|
6022
|
+
import { stat as stat6 } from "fs/promises";
|
|
5780
6023
|
var PROFILE_NAME_PATTERN2 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
5781
6024
|
function normalizeProfileName2(profileName) {
|
|
5782
6025
|
const value = profileName?.trim() || "default";
|
|
@@ -5917,8 +6160,8 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
|
|
|
5917
6160
|
async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
5918
6161
|
const profilePath = resolveHermesProfileDir(profileName);
|
|
5919
6162
|
if (profileName !== "default") {
|
|
5920
|
-
const exists = await
|
|
5921
|
-
if (
|
|
6163
|
+
const exists = await stat6(profilePath).then((value) => value.isDirectory()).catch((error) => {
|
|
6164
|
+
if (isNodeError7(error, "ENOENT")) {
|
|
5922
6165
|
return false;
|
|
5923
6166
|
}
|
|
5924
6167
|
throw error;
|
|
@@ -5944,7 +6187,7 @@ function displayNameForProfile(profile) {
|
|
|
5944
6187
|
const custom = profile.displayName?.trim();
|
|
5945
6188
|
return custom || fallbackProfileDisplayName(profile.profileName);
|
|
5946
6189
|
}
|
|
5947
|
-
function
|
|
6190
|
+
function isNodeError7(error, code) {
|
|
5948
6191
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5949
6192
|
}
|
|
5950
6193
|
|
|
@@ -6489,11 +6732,11 @@ function formatContextUsageLines(runtime) {
|
|
|
6489
6732
|
}
|
|
6490
6733
|
|
|
6491
6734
|
// src/conversations/delivery-staging.ts
|
|
6492
|
-
import { mkdir as
|
|
6735
|
+
import { mkdir as mkdir6, rm as rm4 } from "fs/promises";
|
|
6493
6736
|
import path10 from "path";
|
|
6494
6737
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
6495
6738
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
6496
|
-
await
|
|
6739
|
+
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
6497
6740
|
return directory;
|
|
6498
6741
|
}
|
|
6499
6742
|
async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
@@ -6852,7 +7095,7 @@ function isUsableLanIpv4(value) {
|
|
|
6852
7095
|
}
|
|
6853
7096
|
|
|
6854
7097
|
// src/hermes/session-title.ts
|
|
6855
|
-
import { stat as
|
|
7098
|
+
import { stat as stat7 } from "fs/promises";
|
|
6856
7099
|
import path11 from "path";
|
|
6857
7100
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
6858
7101
|
const trimmedSessionId = sessionId.trim();
|
|
@@ -6864,8 +7107,8 @@ async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
|
6864
7107
|
resolveHermesProfileDir(resolvedProfileName),
|
|
6865
7108
|
"state.db"
|
|
6866
7109
|
);
|
|
6867
|
-
const exists = await
|
|
6868
|
-
if (
|
|
7110
|
+
const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
7111
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
6869
7112
|
return false;
|
|
6870
7113
|
}
|
|
6871
7114
|
throw error;
|
|
@@ -6885,8 +7128,8 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
6885
7128
|
resolveHermesProfileDir(resolvedProfileName),
|
|
6886
7129
|
"state.db"
|
|
6887
7130
|
);
|
|
6888
|
-
const exists = await
|
|
6889
|
-
if (
|
|
7131
|
+
const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
7132
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
6890
7133
|
return false;
|
|
6891
7134
|
}
|
|
6892
7135
|
throw error;
|
|
@@ -6949,7 +7192,7 @@ function readCompressionTipFromStateDb(dbPath, sessionId) {
|
|
|
6949
7192
|
db?.close();
|
|
6950
7193
|
}
|
|
6951
7194
|
}
|
|
6952
|
-
function
|
|
7195
|
+
function isNodeError8(error, code) {
|
|
6953
7196
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
6954
7197
|
}
|
|
6955
7198
|
function isValidProfileName(value) {
|
|
@@ -8648,11 +8891,11 @@ function hydrateAgentEventBlocks(blocks, agentEvents) {
|
|
|
8648
8891
|
// src/conversations/conversation-store.ts
|
|
8649
8892
|
import {
|
|
8650
8893
|
appendFile as appendFile2,
|
|
8651
|
-
mkdir as
|
|
8652
|
-
readdir as
|
|
8894
|
+
mkdir as mkdir7,
|
|
8895
|
+
readdir as readdir5,
|
|
8653
8896
|
readFile as readFile7,
|
|
8654
8897
|
rm as rm5,
|
|
8655
|
-
writeFile as
|
|
8898
|
+
writeFile as writeFile2
|
|
8656
8899
|
} from "fs/promises";
|
|
8657
8900
|
import path12 from "path";
|
|
8658
8901
|
var ConversationStore = class {
|
|
@@ -8661,14 +8904,14 @@ var ConversationStore = class {
|
|
|
8661
8904
|
}
|
|
8662
8905
|
paths;
|
|
8663
8906
|
async ensureConversationsDir() {
|
|
8664
|
-
await
|
|
8907
|
+
await mkdir7(this.paths.conversationsDir, { recursive: true, mode: 448 });
|
|
8665
8908
|
}
|
|
8666
8909
|
async listConversationIds() {
|
|
8667
8910
|
await this.ensureConversationsDir();
|
|
8668
|
-
const entries = await
|
|
8911
|
+
const entries = await readdir5(this.paths.conversationsDir, {
|
|
8669
8912
|
withFileTypes: true
|
|
8670
8913
|
}).catch((error) => {
|
|
8671
|
-
if (
|
|
8914
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8672
8915
|
return [];
|
|
8673
8916
|
}
|
|
8674
8917
|
throw error;
|
|
@@ -8676,7 +8919,7 @@ var ConversationStore = class {
|
|
|
8676
8919
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
8677
8920
|
}
|
|
8678
8921
|
async createConversation(manifest, snapshot = createEmptySnapshot2()) {
|
|
8679
|
-
await
|
|
8922
|
+
await mkdir7(this.conversationDir(manifest.id), {
|
|
8680
8923
|
recursive: true,
|
|
8681
8924
|
mode: 448
|
|
8682
8925
|
});
|
|
@@ -8716,7 +8959,7 @@ var ConversationStore = class {
|
|
|
8716
8959
|
conversation_id: conversationId,
|
|
8717
8960
|
created_at: now
|
|
8718
8961
|
};
|
|
8719
|
-
await
|
|
8962
|
+
await mkdir7(this.conversationDir(conversationId), {
|
|
8720
8963
|
recursive: true,
|
|
8721
8964
|
mode: 448
|
|
8722
8965
|
});
|
|
@@ -8737,7 +8980,7 @@ var ConversationStore = class {
|
|
|
8737
8980
|
await this.readManifest(conversationId);
|
|
8738
8981
|
const raw = await readFile7(this.eventsPath(conversationId), "utf8").catch(
|
|
8739
8982
|
(error) => {
|
|
8740
|
-
if (
|
|
8983
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8741
8984
|
return "";
|
|
8742
8985
|
}
|
|
8743
8986
|
throw error;
|
|
@@ -8747,7 +8990,7 @@ var ConversationStore = class {
|
|
|
8747
8990
|
}
|
|
8748
8991
|
overwriteEvents(conversationId, events) {
|
|
8749
8992
|
const content = events.map((event) => JSON.stringify(event)).join("\n");
|
|
8750
|
-
return
|
|
8993
|
+
return writeFile2(
|
|
8751
8994
|
this.eventsPath(conversationId),
|
|
8752
8995
|
content ? `${content}
|
|
8753
8996
|
` : "",
|
|
@@ -8792,18 +9035,18 @@ var ConversationStore = class {
|
|
|
8792
9035
|
function createEmptySnapshot2() {
|
|
8793
9036
|
return { schema_version: 1, messages: [], runs: [] };
|
|
8794
9037
|
}
|
|
8795
|
-
function
|
|
9038
|
+
function isNodeError9(error, code) {
|
|
8796
9039
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8797
9040
|
}
|
|
8798
9041
|
|
|
8799
9042
|
// src/conversations/hermes-session-sync.ts
|
|
8800
9043
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8801
|
-
import { readdir as
|
|
9044
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "fs/promises";
|
|
8802
9045
|
import os4 from "os";
|
|
8803
9046
|
import path14 from "path";
|
|
8804
9047
|
|
|
8805
9048
|
// src/conversations/delivery-import.ts
|
|
8806
|
-
import { lstat, readFile as readFile8, readdir as
|
|
9049
|
+
import { lstat as lstat2, readFile as readFile8, readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
8807
9050
|
import path13 from "path";
|
|
8808
9051
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
8809
9052
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
@@ -8899,8 +9142,8 @@ function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
|
8899
9142
|
};
|
|
8900
9143
|
}
|
|
8901
9144
|
async function collectStagedDeliveryReferences(stagingDir) {
|
|
8902
|
-
const directoryStat = await
|
|
8903
|
-
if (
|
|
9145
|
+
const directoryStat = await lstat2(stagingDir).catch((error) => {
|
|
9146
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
8904
9147
|
throw new LinkHttpError(
|
|
8905
9148
|
404,
|
|
8906
9149
|
"delivery_staging_not_found",
|
|
@@ -8916,7 +9159,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
8916
9159
|
"delivery staging path is not a directory"
|
|
8917
9160
|
);
|
|
8918
9161
|
}
|
|
8919
|
-
const entries = await
|
|
9162
|
+
const entries = await readdir6(stagingDir, { withFileTypes: true });
|
|
8920
9163
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
8921
9164
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
8922
9165
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
@@ -9080,8 +9323,8 @@ function emptyImportResult(input) {
|
|
|
9080
9323
|
}
|
|
9081
9324
|
async function writeBlobFromFile(deps, conversationId, source) {
|
|
9082
9325
|
const sourcePath = resolveMediaSourcePath(source.path);
|
|
9083
|
-
const fileStat = await
|
|
9084
|
-
if (
|
|
9326
|
+
const fileStat = await stat8(sourcePath).catch((error) => {
|
|
9327
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9085
9328
|
throw new LinkHttpError(
|
|
9086
9329
|
404,
|
|
9087
9330
|
"media_source_not_found",
|
|
@@ -9115,7 +9358,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
9115
9358
|
key: sourceKey,
|
|
9116
9359
|
filename: sanitizeFilename(reference.path, "attachment"),
|
|
9117
9360
|
reason: error instanceof Error ? error.message : String(error),
|
|
9118
|
-
...
|
|
9361
|
+
...isNodeError10(error) && error.code ? { code: error.code } : {}
|
|
9119
9362
|
};
|
|
9120
9363
|
}
|
|
9121
9364
|
function isSupportedDeliveryFilename(filename) {
|
|
@@ -9128,7 +9371,7 @@ function readString8(payload, key) {
|
|
|
9128
9371
|
function toRecord7(value) {
|
|
9129
9372
|
return typeof value === "object" && value !== null ? value : {};
|
|
9130
9373
|
}
|
|
9131
|
-
function
|
|
9374
|
+
function isNodeError10(error, code) {
|
|
9132
9375
|
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
9133
9376
|
}
|
|
9134
9377
|
|
|
@@ -9895,9 +10138,9 @@ function rememberKnownHermesConversation(map, sessionId, conversationId) {
|
|
|
9895
10138
|
async function discoverHermesProfileNames() {
|
|
9896
10139
|
const names = /* @__PURE__ */ new Set([DEFAULT_PROFILE_NAME]);
|
|
9897
10140
|
const profilesDir = path14.join(os4.homedir(), ".hermes", "profiles");
|
|
9898
|
-
const entries = await
|
|
10141
|
+
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
9899
10142
|
(error) => {
|
|
9900
|
-
if (
|
|
10143
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
9901
10144
|
return [];
|
|
9902
10145
|
}
|
|
9903
10146
|
throw error;
|
|
@@ -10098,7 +10341,7 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
10098
10341
|
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path14.join(profileDir, "sessions"));
|
|
10099
10342
|
const transcriptPath = path14.join(sessionsDir, `${sessionId}.jsonl`);
|
|
10100
10343
|
const raw = await readFile9(transcriptPath, "utf8").catch((error) => {
|
|
10101
|
-
if (
|
|
10344
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10102
10345
|
return "";
|
|
10103
10346
|
}
|
|
10104
10347
|
throw error;
|
|
@@ -10373,8 +10616,8 @@ function quoteIdentifier(value) {
|
|
|
10373
10616
|
return `"${value.replaceAll('"', '""')}"`;
|
|
10374
10617
|
}
|
|
10375
10618
|
async function isFile(filePath) {
|
|
10376
|
-
return
|
|
10377
|
-
if (
|
|
10619
|
+
return stat9(filePath).then((value) => value.isFile()).catch((error) => {
|
|
10620
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10378
10621
|
return false;
|
|
10379
10622
|
}
|
|
10380
10623
|
throw error;
|
|
@@ -10407,12 +10650,12 @@ function readBoolean(value) {
|
|
|
10407
10650
|
}
|
|
10408
10651
|
return false;
|
|
10409
10652
|
}
|
|
10410
|
-
function
|
|
10653
|
+
function isNodeError11(error, code) {
|
|
10411
10654
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
10412
10655
|
}
|
|
10413
10656
|
|
|
10414
10657
|
// src/conversations/run-lifecycle.ts
|
|
10415
|
-
import { readdir as
|
|
10658
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
10416
10659
|
|
|
10417
10660
|
// src/hermes/api-server.ts
|
|
10418
10661
|
async function listHermesModels(options = {}) {
|
|
@@ -10525,7 +10768,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
10525
10768
|
);
|
|
10526
10769
|
if (response.status === 404 || response.status === 503) {
|
|
10527
10770
|
assertHermesRunsApiSupported(
|
|
10528
|
-
await readHermesVersion().catch(() => null),
|
|
10771
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10529
10772
|
response.status
|
|
10530
10773
|
);
|
|
10531
10774
|
throw new LinkHttpError(
|
|
@@ -10552,7 +10795,7 @@ async function streamHermesRunEvents(runId, options = {}) {
|
|
|
10552
10795
|
options
|
|
10553
10796
|
);
|
|
10554
10797
|
assertHermesRunsApiSupported(
|
|
10555
|
-
await readHermesVersion().catch(() => null),
|
|
10798
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10556
10799
|
response.status
|
|
10557
10800
|
);
|
|
10558
10801
|
if (!response.ok || !response.body) {
|
|
@@ -10595,7 +10838,7 @@ async function streamHermesResponses(input, options = {}) {
|
|
|
10595
10838
|
);
|
|
10596
10839
|
if (response.status === 404 || response.status === 503) {
|
|
10597
10840
|
assertHermesRunsApiSupported(
|
|
10598
|
-
await readHermesVersion().catch(() => null),
|
|
10841
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10599
10842
|
response.status
|
|
10600
10843
|
);
|
|
10601
10844
|
throw new LinkHttpError(
|
|
@@ -10806,7 +11049,7 @@ function readString10(payload, key) {
|
|
|
10806
11049
|
}
|
|
10807
11050
|
|
|
10808
11051
|
// src/conversations/history-builder.ts
|
|
10809
|
-
import { readFile as readFile10, stat as
|
|
11052
|
+
import { readFile as readFile10, stat as stat10 } from "fs/promises";
|
|
10810
11053
|
import path15 from "path";
|
|
10811
11054
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
10812
11055
|
var HERMES_HISTORY_COLUMNS = [
|
|
@@ -10914,8 +11157,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
10914
11157
|
};
|
|
10915
11158
|
}
|
|
10916
11159
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
10917
|
-
const exists = await
|
|
10918
|
-
if (
|
|
11160
|
+
const exists = await stat10(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
11161
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
10919
11162
|
return false;
|
|
10920
11163
|
}
|
|
10921
11164
|
throw error;
|
|
@@ -10934,7 +11177,7 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
10934
11177
|
}
|
|
10935
11178
|
const transcriptPath = path15.join(sessionsDir, `${sessionId}.jsonl`);
|
|
10936
11179
|
const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
|
|
10937
|
-
if (
|
|
11180
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
10938
11181
|
return "";
|
|
10939
11182
|
}
|
|
10940
11183
|
throw error;
|
|
@@ -11156,7 +11399,7 @@ function readTableColumns2(db, table) {
|
|
|
11156
11399
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
11157
11400
|
);
|
|
11158
11401
|
}
|
|
11159
|
-
function
|
|
11402
|
+
function isNodeError12(error, code) {
|
|
11160
11403
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
11161
11404
|
}
|
|
11162
11405
|
function isValidProfileName2(value) {
|
|
@@ -11174,7 +11417,7 @@ function normalizeProfileForCompare(value) {
|
|
|
11174
11417
|
|
|
11175
11418
|
// src/hermes/stt.ts
|
|
11176
11419
|
import { execFile as execFile3 } from "child_process";
|
|
11177
|
-
import { access as access2, readFile as readFile11, stat as
|
|
11420
|
+
import { access as access2, readFile as readFile11, stat as stat11 } from "fs/promises";
|
|
11178
11421
|
import path16 from "path";
|
|
11179
11422
|
import { promisify as promisify3 } from "util";
|
|
11180
11423
|
var execFileAsync3 = promisify3(execFile3);
|
|
@@ -11334,7 +11577,7 @@ async function resolveExecutablePath(command) {
|
|
|
11334
11577
|
}
|
|
11335
11578
|
async function isExecutableFile(filePath) {
|
|
11336
11579
|
try {
|
|
11337
|
-
const info = await
|
|
11580
|
+
const info = await stat11(filePath);
|
|
11338
11581
|
if (!info.isFile()) {
|
|
11339
11582
|
return false;
|
|
11340
11583
|
}
|
|
@@ -11375,7 +11618,7 @@ async function findDevHermesAgentSource() {
|
|
|
11375
11618
|
return null;
|
|
11376
11619
|
}
|
|
11377
11620
|
async function isDirectory(candidate) {
|
|
11378
|
-
return
|
|
11621
|
+
return stat11(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
11379
11622
|
}
|
|
11380
11623
|
function compactProcessOutput(value) {
|
|
11381
11624
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -12619,8 +12862,8 @@ function formatFilenameList(filenames) {
|
|
|
12619
12862
|
return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
|
|
12620
12863
|
}
|
|
12621
12864
|
async function readdirWithDirs(directory) {
|
|
12622
|
-
return
|
|
12623
|
-
if (
|
|
12865
|
+
return readdir8(directory, { withFileTypes: true }).catch((error) => {
|
|
12866
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
12624
12867
|
return [];
|
|
12625
12868
|
}
|
|
12626
12869
|
throw error;
|
|
@@ -12850,7 +13093,7 @@ function readResponseId(payload) {
|
|
|
12850
13093
|
const response = toRecord11(payload.response);
|
|
12851
13094
|
return readString13(payload, "response_id") ?? readString13(response, "id");
|
|
12852
13095
|
}
|
|
12853
|
-
function
|
|
13096
|
+
function isNodeError13(error, code) {
|
|
12854
13097
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
12855
13098
|
return false;
|
|
12856
13099
|
}
|
|
@@ -13686,7 +13929,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
13686
13929
|
|
|
13687
13930
|
// src/identity/identity.ts
|
|
13688
13931
|
import { generateKeyPairSync, randomUUID as randomUUID8, sign } from "crypto";
|
|
13689
|
-
import { mkdir as
|
|
13932
|
+
import { mkdir as mkdir8, chmod as chmod2 } from "fs/promises";
|
|
13690
13933
|
import { z } from "zod";
|
|
13691
13934
|
var linkIdentitySchema = z.object({
|
|
13692
13935
|
install_id: z.string().min(1),
|
|
@@ -13708,8 +13951,8 @@ async function ensureIdentity(paths = resolveRuntimePaths()) {
|
|
|
13708
13951
|
if (existing) {
|
|
13709
13952
|
return existing;
|
|
13710
13953
|
}
|
|
13711
|
-
await
|
|
13712
|
-
await
|
|
13954
|
+
await mkdir8(paths.homeDir, { recursive: true, mode: 448 });
|
|
13955
|
+
await chmod2(paths.homeDir, 448).catch(() => void 0);
|
|
13713
13956
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
13714
13957
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13715
13958
|
const identity = {
|
|
@@ -14903,7 +15146,7 @@ function createHttpErrorMiddleware(logger) {
|
|
|
14903
15146
|
}
|
|
14904
15147
|
|
|
14905
15148
|
// src/hermes/profiles.ts
|
|
14906
|
-
import {
|
|
15149
|
+
import { readdir as readdir9, readFile as readFile12, rename as rename3, rm as rm6, stat as stat12 } from "fs/promises";
|
|
14907
15150
|
import os5 from "os";
|
|
14908
15151
|
import path17 from "path";
|
|
14909
15152
|
import YAML2 from "yaml";
|
|
@@ -14913,9 +15156,9 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
14913
15156
|
const profiles = /* @__PURE__ */ new Map();
|
|
14914
15157
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
14915
15158
|
const profilesDir = path17.join(os5.homedir(), ".hermes", "profiles");
|
|
14916
|
-
const entries = await
|
|
15159
|
+
const entries = await readdir9(profilesDir, { withFileTypes: true }).catch(
|
|
14917
15160
|
(error) => {
|
|
14918
|
-
if (
|
|
15161
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14919
15162
|
return [];
|
|
14920
15163
|
}
|
|
14921
15164
|
throw error;
|
|
@@ -14939,8 +15182,8 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
14939
15182
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
14940
15183
|
assertProfileName(name);
|
|
14941
15184
|
const profile = await profileInfo(name, paths);
|
|
14942
|
-
const exists = await
|
|
14943
|
-
if (
|
|
15185
|
+
const exists = await stat12(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
15186
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14944
15187
|
return false;
|
|
14945
15188
|
}
|
|
14946
15189
|
throw error;
|
|
@@ -14957,7 +15200,7 @@ async function renameHermesProfile(oldName, newName, paths = resolveRuntimePaths
|
|
|
14957
15200
|
assertMutableProfile(newName);
|
|
14958
15201
|
const oldProfile = await profileInfo(oldName, paths);
|
|
14959
15202
|
const newProfilePath = resolveHermesProfileDir(newName);
|
|
14960
|
-
await
|
|
15203
|
+
await rename3(oldProfile.path, newProfilePath);
|
|
14961
15204
|
const identity = await renameProfileIdentity(paths, {
|
|
14962
15205
|
oldProfileName: oldName,
|
|
14963
15206
|
newProfileName: newName,
|
|
@@ -14980,8 +15223,8 @@ async function updateHermesProfileMetadata(name, metadata, paths = resolveRuntim
|
|
|
14980
15223
|
async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
|
|
14981
15224
|
assertMutableProfile(name);
|
|
14982
15225
|
const profile = await profileInfo(name, paths);
|
|
14983
|
-
const exists = await
|
|
14984
|
-
if (
|
|
15226
|
+
const exists = await stat12(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
15227
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14985
15228
|
return false;
|
|
14986
15229
|
}
|
|
14987
15230
|
throw error;
|
|
@@ -15049,13 +15292,13 @@ function assertProfileName(name) {
|
|
|
15049
15292
|
throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
|
|
15050
15293
|
}
|
|
15051
15294
|
}
|
|
15052
|
-
function
|
|
15295
|
+
function isNodeError14(error, code) {
|
|
15053
15296
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
15054
15297
|
}
|
|
15055
15298
|
async function countSkills(root) {
|
|
15056
|
-
const entries = await
|
|
15299
|
+
const entries = await readdir9(root, { withFileTypes: true }).catch(
|
|
15057
15300
|
(error) => {
|
|
15058
|
-
if (
|
|
15301
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15059
15302
|
return [];
|
|
15060
15303
|
}
|
|
15061
15304
|
throw error;
|
|
@@ -15082,7 +15325,7 @@ async function countConfiguredTools(profileName) {
|
|
|
15082
15325
|
resolveHermesConfigPath(profileName),
|
|
15083
15326
|
"utf8"
|
|
15084
15327
|
).catch((error) => {
|
|
15085
|
-
if (
|
|
15328
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15086
15329
|
return "";
|
|
15087
15330
|
}
|
|
15088
15331
|
throw error;
|
|
@@ -15759,14 +16002,11 @@ function errorMessage(error) {
|
|
|
15759
16002
|
import { spawn as spawn2 } from "child_process";
|
|
15760
16003
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
15761
16004
|
import {
|
|
15762
|
-
copyFile as copyFile2,
|
|
15763
16005
|
cp,
|
|
15764
|
-
mkdir as
|
|
16006
|
+
mkdir as mkdir9,
|
|
15765
16007
|
readFile as readFile13,
|
|
15766
16008
|
rm as rm7,
|
|
15767
|
-
stat as
|
|
15768
|
-
writeFile as writeFile4,
|
|
15769
|
-
rename as rename5
|
|
16009
|
+
stat as stat13
|
|
15770
16010
|
} from "fs/promises";
|
|
15771
16011
|
import path18 from "path";
|
|
15772
16012
|
import YAML3 from "yaml";
|
|
@@ -15816,7 +16056,7 @@ async function startHermesProfileCreation(input, options) {
|
|
|
15816
16056
|
signal: null,
|
|
15817
16057
|
error: null
|
|
15818
16058
|
};
|
|
15819
|
-
await
|
|
16059
|
+
await mkdir9(options.paths.runDir, { recursive: true, mode: 448 });
|
|
15820
16060
|
await writeProfileCreationState(options.paths, started);
|
|
15821
16061
|
await writer.write(`
|
|
15822
16062
|
=== profile creation started ${startedAt} ===
|
|
@@ -16093,7 +16333,7 @@ function randomSuffix(attempt) {
|
|
|
16093
16333
|
async function applyProfileCreationPostSteps(input) {
|
|
16094
16334
|
const profilePath = resolveHermesProfileDir(input.profileName);
|
|
16095
16335
|
if (!await pathExists(profilePath)) {
|
|
16096
|
-
await
|
|
16336
|
+
await ensureDirectoryWithInheritedMetadata(profilePath, 448);
|
|
16097
16337
|
}
|
|
16098
16338
|
if (input.sourceProfile && input.copyScopes.length > 0) {
|
|
16099
16339
|
await copyProfileScopes({
|
|
@@ -16221,7 +16461,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
16221
16461
|
async function writeEnvValues(profileName, values) {
|
|
16222
16462
|
const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
|
|
16223
16463
|
const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
|
|
16224
|
-
if (
|
|
16464
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16225
16465
|
return "";
|
|
16226
16466
|
}
|
|
16227
16467
|
throw error;
|
|
@@ -16246,13 +16486,14 @@ async function writeEnvValues(profileName, values) {
|
|
|
16246
16486
|
nextLines.push(`${key}=${formatEnvValue2(value)}`);
|
|
16247
16487
|
}
|
|
16248
16488
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
16249
|
-
await mkdir11(path18.dirname(envPath), { recursive: true, mode: 448 });
|
|
16250
16489
|
if (existingRaw) {
|
|
16251
|
-
await
|
|
16490
|
+
await atomicWriteFilePreservingMetadata(
|
|
16491
|
+
`${envPath}.bak.${Date.now()}`,
|
|
16492
|
+
existingRaw,
|
|
16493
|
+
{ metadataSourcePath: envPath }
|
|
16494
|
+
);
|
|
16252
16495
|
}
|
|
16253
|
-
|
|
16254
|
-
await writeFile4(tempPath, nextRaw, { mode: 384 });
|
|
16255
|
-
await rename5(tempPath, envPath);
|
|
16496
|
+
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
16256
16497
|
}
|
|
16257
16498
|
async function copySkills(sourceProfile, targetProfile) {
|
|
16258
16499
|
const sourceSkills = path18.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
@@ -16266,6 +16507,10 @@ async function copySkills(sourceProfile, targetProfile) {
|
|
|
16266
16507
|
force: true,
|
|
16267
16508
|
errorOnExist: false
|
|
16268
16509
|
});
|
|
16510
|
+
await inheritOwnerRecursively(
|
|
16511
|
+
targetSkills,
|
|
16512
|
+
resolveHermesProfileDir(targetProfile)
|
|
16513
|
+
);
|
|
16269
16514
|
}
|
|
16270
16515
|
function copyProperty(source, target, key) {
|
|
16271
16516
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
@@ -16277,7 +16522,7 @@ function copyProperty(source, target, key) {
|
|
|
16277
16522
|
async function readYamlConfig(configPath) {
|
|
16278
16523
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
16279
16524
|
(error) => {
|
|
16280
|
-
if (
|
|
16525
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16281
16526
|
return null;
|
|
16282
16527
|
}
|
|
16283
16528
|
throw error;
|
|
@@ -16289,14 +16534,15 @@ async function readYamlConfig(configPath) {
|
|
|
16289
16534
|
};
|
|
16290
16535
|
}
|
|
16291
16536
|
async function writeYamlConfig(configPath, input) {
|
|
16292
|
-
await mkdir11(path18.dirname(configPath), { recursive: true, mode: 448 });
|
|
16293
16537
|
if (input.existingRaw) {
|
|
16294
|
-
await
|
|
16538
|
+
await atomicWriteFilePreservingMetadata(
|
|
16539
|
+
`${configPath}.bak.${Date.now()}`,
|
|
16540
|
+
input.existingRaw,
|
|
16541
|
+
{ metadataSourcePath: configPath }
|
|
16542
|
+
);
|
|
16295
16543
|
}
|
|
16296
16544
|
const document = new YAML3.Document(input.config);
|
|
16297
|
-
|
|
16298
|
-
await writeFile4(tempPath, document.toString(), { mode: 384 });
|
|
16299
|
-
await rename5(tempPath, configPath);
|
|
16545
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
16300
16546
|
}
|
|
16301
16547
|
async function failProfileCreation(input) {
|
|
16302
16548
|
if (input.rollbackProfileName) {
|
|
@@ -16362,8 +16608,8 @@ async function clearProfileCreationLogFiles(paths) {
|
|
|
16362
16608
|
]);
|
|
16363
16609
|
}
|
|
16364
16610
|
async function pathExists(targetPath) {
|
|
16365
|
-
return await
|
|
16366
|
-
if (
|
|
16611
|
+
return await stat13(targetPath).then(() => true).catch((error) => {
|
|
16612
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16367
16613
|
return false;
|
|
16368
16614
|
}
|
|
16369
16615
|
throw error;
|
|
@@ -16410,7 +16656,7 @@ function formatEnvValue2(value) {
|
|
|
16410
16656
|
function escapeRegExp2(value) {
|
|
16411
16657
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16412
16658
|
}
|
|
16413
|
-
function
|
|
16659
|
+
function isNodeError15(error, code) {
|
|
16414
16660
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16415
16661
|
}
|
|
16416
16662
|
|
|
@@ -16619,13 +16865,9 @@ function toProfileToolConfigHttpError(error) {
|
|
|
16619
16865
|
// src/hermes/memory.ts
|
|
16620
16866
|
import {
|
|
16621
16867
|
access as access3,
|
|
16622
|
-
|
|
16623
|
-
mkdir as mkdir12,
|
|
16624
|
-
readdir as readdir9,
|
|
16868
|
+
readdir as readdir10,
|
|
16625
16869
|
readFile as readFile14,
|
|
16626
|
-
|
|
16627
|
-
stat as stat13,
|
|
16628
|
-
writeFile as writeFile5
|
|
16870
|
+
stat as stat14
|
|
16629
16871
|
} from "fs/promises";
|
|
16630
16872
|
import path19 from "path";
|
|
16631
16873
|
import YAML4 from "yaml";
|
|
@@ -16938,12 +17180,11 @@ function isSensitiveConfigKey(key) {
|
|
|
16938
17180
|
}
|
|
16939
17181
|
async function writeCustomProviderConfig(profileName, provider, config) {
|
|
16940
17182
|
const configPath = customProviderConfigPath(profileName, provider);
|
|
16941
|
-
await
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
});
|
|
17183
|
+
await atomicWriteFilePreservingMetadata(
|
|
17184
|
+
configPath,
|
|
17185
|
+
`${JSON.stringify(config, null, 2)}
|
|
17186
|
+
`
|
|
17187
|
+
);
|
|
16947
17188
|
}
|
|
16948
17189
|
function normalizeConfigurableProvider(provider, patch) {
|
|
16949
17190
|
if (provider === CUSTOM_PROVIDER_CARD_ID) {
|
|
@@ -16991,7 +17232,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
16991
17232
|
const configPath = resolveHermesConfigPath(profileName);
|
|
16992
17233
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
16993
17234
|
(error) => {
|
|
16994
|
-
if (
|
|
17235
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16995
17236
|
return null;
|
|
16996
17237
|
}
|
|
16997
17238
|
throw error;
|
|
@@ -17003,17 +17244,13 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
17003
17244
|
memory.provider = provider === "built-in" ? "" : provider;
|
|
17004
17245
|
config.memory = memory;
|
|
17005
17246
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17247
|
+
if (backupPath && existingRaw !== null) {
|
|
17248
|
+
await atomicWriteFilePreservingMetadata(backupPath, existingRaw, {
|
|
17249
|
+
metadataSourcePath: configPath
|
|
17250
|
+
});
|
|
17009
17251
|
}
|
|
17010
17252
|
document.contents = document.createNode(config);
|
|
17011
|
-
|
|
17012
|
-
await writeFile5(tempPath, document.toString(), {
|
|
17013
|
-
encoding: "utf8",
|
|
17014
|
-
mode: 384
|
|
17015
|
-
});
|
|
17016
|
-
await rename6(tempPath, configPath);
|
|
17253
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
17017
17254
|
}
|
|
17018
17255
|
function resolveMemoryDir(profileName) {
|
|
17019
17256
|
return path19.join(resolveHermesProfileDir(profileName), "memories");
|
|
@@ -17021,8 +17258,8 @@ function resolveMemoryDir(profileName) {
|
|
|
17021
17258
|
async function readMemoryStore(profileName, target, limits) {
|
|
17022
17259
|
const filePath = memoryFilePath(profileName, target);
|
|
17023
17260
|
const entries = await readMemoryEntries(filePath);
|
|
17024
|
-
const fileStat = await
|
|
17025
|
-
if (
|
|
17261
|
+
const fileStat = await stat14(filePath).catch((error) => {
|
|
17262
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17026
17263
|
return null;
|
|
17027
17264
|
}
|
|
17028
17265
|
throw error;
|
|
@@ -17051,7 +17288,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
17051
17288
|
}
|
|
17052
17289
|
async function readMemoryEntries(filePath) {
|
|
17053
17290
|
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
17054
|
-
if (
|
|
17291
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17055
17292
|
return "";
|
|
17056
17293
|
}
|
|
17057
17294
|
throw error;
|
|
@@ -17071,17 +17308,10 @@ async function mutateMemoryEntries(profileName, target, mutate) {
|
|
|
17071
17308
|
async function writeMemoryEntries(profileName, target, entries) {
|
|
17072
17309
|
assertWithinLimit(target, entries, await readMemoryLimits(profileName));
|
|
17073
17310
|
const filePath = memoryFilePath(profileName, target);
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
dir,
|
|
17078
|
-
`.mem_${process.pid}_${Date.now()}_${target}.tmp`
|
|
17311
|
+
await atomicWriteFilePreservingMetadata(
|
|
17312
|
+
filePath,
|
|
17313
|
+
entries.join(ENTRY_DELIMITER)
|
|
17079
17314
|
);
|
|
17080
|
-
await writeFile5(tempPath, entries.join(ENTRY_DELIMITER), {
|
|
17081
|
-
encoding: "utf8",
|
|
17082
|
-
mode: 384
|
|
17083
|
-
});
|
|
17084
|
-
await rename6(tempPath, filePath);
|
|
17085
17315
|
}
|
|
17086
17316
|
function memoryFilePath(profileName, target) {
|
|
17087
17317
|
return path19.join(
|
|
@@ -17562,7 +17792,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
17562
17792
|
async function readCustomProviderRegistry(profileName) {
|
|
17563
17793
|
const raw = await readFile14(customProviderRegistryPath(profileName), "utf8").catch(
|
|
17564
17794
|
(error) => {
|
|
17565
|
-
if (
|
|
17795
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17566
17796
|
return "";
|
|
17567
17797
|
}
|
|
17568
17798
|
throw error;
|
|
@@ -17599,19 +17829,17 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
17599
17829
|
{ id: providerId, label: providerId, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" }
|
|
17600
17830
|
].sort((left, right) => left.id.localeCompare(right.id));
|
|
17601
17831
|
const registryPath2 = customProviderRegistryPath(profileName);
|
|
17602
|
-
await
|
|
17603
|
-
await writeFile5(
|
|
17832
|
+
await atomicWriteFilePreservingMetadata(
|
|
17604
17833
|
registryPath2,
|
|
17605
17834
|
`${JSON.stringify({ providers }, null, 2)}
|
|
17606
|
-
|
|
17607
|
-
{ encoding: "utf8", mode: 384 }
|
|
17835
|
+
`
|
|
17608
17836
|
);
|
|
17609
17837
|
}
|
|
17610
17838
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
17611
17839
|
const pluginsDir = path19.join(resolveHermesProfileDir(profileName), "plugins");
|
|
17612
|
-
const entries = await
|
|
17840
|
+
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
17613
17841
|
(error) => {
|
|
17614
|
-
if (
|
|
17842
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17615
17843
|
return [];
|
|
17616
17844
|
}
|
|
17617
17845
|
throw error;
|
|
@@ -17652,7 +17880,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
17652
17880
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
17653
17881
|
const source = await readFile14(path19.join(providerDir, "__init__.py"), "utf8").catch(
|
|
17654
17882
|
(error) => {
|
|
17655
|
-
if (
|
|
17883
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17656
17884
|
return "";
|
|
17657
17885
|
}
|
|
17658
17886
|
throw error;
|
|
@@ -17664,7 +17892,7 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
17664
17892
|
async function readPluginMetadata(providerDir) {
|
|
17665
17893
|
const raw = await readFile14(path19.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
17666
17894
|
(error) => {
|
|
17667
|
-
if (
|
|
17895
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17668
17896
|
return "";
|
|
17669
17897
|
}
|
|
17670
17898
|
throw error;
|
|
@@ -17690,7 +17918,7 @@ async function resolveByteRoverCli() {
|
|
|
17690
17918
|
async function readHolographicProviderConfig(profileName) {
|
|
17691
17919
|
const raw = await readFile14(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
17692
17920
|
(error) => {
|
|
17693
|
-
if (
|
|
17921
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17694
17922
|
return "";
|
|
17695
17923
|
}
|
|
17696
17924
|
throw error;
|
|
@@ -17704,7 +17932,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
17704
17932
|
const configPath = resolveHermesConfigPath(profileName);
|
|
17705
17933
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
17706
17934
|
(error) => {
|
|
17707
|
-
if (
|
|
17935
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17708
17936
|
return null;
|
|
17709
17937
|
}
|
|
17710
17938
|
throw error;
|
|
@@ -17721,17 +17949,15 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
17721
17949
|
}
|
|
17722
17950
|
plugins["hermes-memory-store"] = memoryStore;
|
|
17723
17951
|
config.plugins = plugins;
|
|
17724
|
-
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
17725
17952
|
if (existingRaw) {
|
|
17726
|
-
await
|
|
17953
|
+
await atomicWriteFilePreservingMetadata(
|
|
17954
|
+
`${configPath}.bak.${Date.now()}`,
|
|
17955
|
+
existingRaw,
|
|
17956
|
+
{ metadataSourcePath: configPath }
|
|
17957
|
+
);
|
|
17727
17958
|
}
|
|
17728
17959
|
document.contents = document.createNode(config);
|
|
17729
|
-
|
|
17730
|
-
await writeFile5(tempPath, document.toString(), {
|
|
17731
|
-
encoding: "utf8",
|
|
17732
|
-
mode: 384
|
|
17733
|
-
});
|
|
17734
|
-
await rename6(tempPath, configPath);
|
|
17960
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
17735
17961
|
}
|
|
17736
17962
|
async function patchHermesMemoryEnv(profileName, patch) {
|
|
17737
17963
|
const entries = Object.entries(patch).filter(
|
|
@@ -17742,7 +17968,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
17742
17968
|
}
|
|
17743
17969
|
const envPath = path19.join(resolveHermesProfileDir(profileName), ".env");
|
|
17744
17970
|
const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
|
|
17745
|
-
if (
|
|
17971
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17746
17972
|
return "";
|
|
17747
17973
|
}
|
|
17748
17974
|
throw error;
|
|
@@ -17772,13 +17998,14 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
17772
17998
|
}
|
|
17773
17999
|
}
|
|
17774
18000
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
17775
|
-
await mkdir12(path19.dirname(envPath), { recursive: true, mode: 448 });
|
|
17776
18001
|
if (existingRaw) {
|
|
17777
|
-
await
|
|
18002
|
+
await atomicWriteFilePreservingMetadata(
|
|
18003
|
+
`${envPath}.bak.${Date.now()}`,
|
|
18004
|
+
existingRaw,
|
|
18005
|
+
{ metadataSourcePath: envPath }
|
|
18006
|
+
);
|
|
17778
18007
|
}
|
|
17779
|
-
|
|
17780
|
-
await writeFile5(tempPath, nextRaw, { encoding: "utf8", mode: 384 });
|
|
17781
|
-
await rename6(tempPath, envPath);
|
|
18008
|
+
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
17782
18009
|
}
|
|
17783
18010
|
function isMemoryEnvKeyWritable(key) {
|
|
17784
18011
|
return [
|
|
@@ -17799,7 +18026,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
17799
18026
|
resolveHermesConfigPath(profileName),
|
|
17800
18027
|
"utf8"
|
|
17801
18028
|
).catch((error) => {
|
|
17802
|
-
if (
|
|
18029
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17803
18030
|
return "";
|
|
17804
18031
|
}
|
|
17805
18032
|
throw error;
|
|
@@ -17824,16 +18051,15 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
17824
18051
|
next[key] = value;
|
|
17825
18052
|
}
|
|
17826
18053
|
}
|
|
17827
|
-
await
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
});
|
|
18054
|
+
await atomicWriteFilePreservingMetadata(
|
|
18055
|
+
configPath,
|
|
18056
|
+
`${JSON.stringify(next, null, 2)}
|
|
18057
|
+
`
|
|
18058
|
+
);
|
|
17833
18059
|
}
|
|
17834
18060
|
async function readJsonObject(filePath) {
|
|
17835
18061
|
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
17836
|
-
if (
|
|
18062
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17837
18063
|
return "{}";
|
|
17838
18064
|
}
|
|
17839
18065
|
throw error;
|
|
@@ -17887,7 +18113,7 @@ async function readMemoryLimits(profileName) {
|
|
|
17887
18113
|
resolveHermesConfigPath(profileName),
|
|
17888
18114
|
"utf8"
|
|
17889
18115
|
).catch((error) => {
|
|
17890
|
-
if (
|
|
18116
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17891
18117
|
return "";
|
|
17892
18118
|
}
|
|
17893
18119
|
throw error;
|
|
@@ -17980,7 +18206,7 @@ function formatEnvValue3(value) {
|
|
|
17980
18206
|
function escapeRegExp3(value) {
|
|
17981
18207
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
17982
18208
|
}
|
|
17983
|
-
function
|
|
18209
|
+
function isNodeError16(error, code) {
|
|
17984
18210
|
return error instanceof Error && "code" in error && error.code === code;
|
|
17985
18211
|
}
|
|
17986
18212
|
|
|
@@ -18388,14 +18614,7 @@ function toMemoryHttpError(error) {
|
|
|
18388
18614
|
}
|
|
18389
18615
|
|
|
18390
18616
|
// src/hermes/skills.ts
|
|
18391
|
-
import {
|
|
18392
|
-
copyFile as copyFile4,
|
|
18393
|
-
mkdir as mkdir13,
|
|
18394
|
-
readFile as readFile15,
|
|
18395
|
-
readdir as readdir10,
|
|
18396
|
-
rename as rename7,
|
|
18397
|
-
writeFile as writeFile6
|
|
18398
|
-
} from "fs/promises";
|
|
18617
|
+
import { readFile as readFile15, readdir as readdir11 } from "fs/promises";
|
|
18399
18618
|
import path20 from "path";
|
|
18400
18619
|
import YAML5 from "yaml";
|
|
18401
18620
|
var HermesSkillNotFoundError = class extends Error {
|
|
@@ -18487,9 +18706,9 @@ async function findSkillFiles(root) {
|
|
|
18487
18706
|
return results.sort((left, right) => left.localeCompare(right));
|
|
18488
18707
|
}
|
|
18489
18708
|
async function collectSkillFiles(directory, results) {
|
|
18490
|
-
const entries = await
|
|
18709
|
+
const entries = await readdir11(directory, { withFileTypes: true }).catch(
|
|
18491
18710
|
(error) => {
|
|
18492
|
-
if (
|
|
18711
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18493
18712
|
return [];
|
|
18494
18713
|
}
|
|
18495
18714
|
throw error;
|
|
@@ -18514,7 +18733,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
18514
18733
|
async function readSkillMetadata(input) {
|
|
18515
18734
|
const raw = await readFile15(input.skillFile, "utf8").catch(
|
|
18516
18735
|
(error) => {
|
|
18517
|
-
if (
|
|
18736
|
+
if (isNodeError17(error, "ENOENT") || isNodeError17(error, "EACCES")) {
|
|
18518
18737
|
return null;
|
|
18519
18738
|
}
|
|
18520
18739
|
throw error;
|
|
@@ -18591,7 +18810,7 @@ function normalizeDescription(value) {
|
|
|
18591
18810
|
}
|
|
18592
18811
|
async function readDisabledSkillNames(configPath) {
|
|
18593
18812
|
const raw = await readFile15(configPath, "utf8").catch((error) => {
|
|
18594
|
-
if (
|
|
18813
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18595
18814
|
return "";
|
|
18596
18815
|
}
|
|
18597
18816
|
throw error;
|
|
@@ -18616,7 +18835,7 @@ async function readSkillProvenance(root) {
|
|
|
18616
18835
|
async function readBundledSkillNames(root) {
|
|
18617
18836
|
const raw = await readFile15(path20.join(root, ".bundled_manifest"), "utf8").catch(
|
|
18618
18837
|
(error) => {
|
|
18619
|
-
if (
|
|
18838
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18620
18839
|
return "";
|
|
18621
18840
|
}
|
|
18622
18841
|
throw error;
|
|
@@ -18639,7 +18858,7 @@ async function readBundledSkillNames(root) {
|
|
|
18639
18858
|
async function readHubInstalledSkills(root) {
|
|
18640
18859
|
const raw = await readFile15(path20.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
18641
18860
|
(error) => {
|
|
18642
|
-
if (
|
|
18861
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18643
18862
|
return "";
|
|
18644
18863
|
}
|
|
18645
18864
|
throw error;
|
|
@@ -18711,7 +18930,7 @@ function compareCategoryNames(left, right) {
|
|
|
18711
18930
|
async function readHermesConfigDocument2(configPath) {
|
|
18712
18931
|
const existingRaw = await readFile15(configPath, "utf8").catch(
|
|
18713
18932
|
(error) => {
|
|
18714
|
-
if (
|
|
18933
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18715
18934
|
return null;
|
|
18716
18935
|
}
|
|
18717
18936
|
throw error;
|
|
@@ -18726,14 +18945,16 @@ async function readHermesConfigDocument2(configPath) {
|
|
|
18726
18945
|
}
|
|
18727
18946
|
async function writeHermesConfigDocument2(input) {
|
|
18728
18947
|
const backupPath = input.existingRaw ? `${input.configPath}.bak.${Date.now()}` : null;
|
|
18729
|
-
await mkdir13(path20.dirname(input.configPath), { recursive: true, mode: 448 });
|
|
18730
18948
|
if (backupPath) {
|
|
18731
|
-
await
|
|
18949
|
+
await atomicWriteFilePreservingMetadata(backupPath, input.existingRaw, {
|
|
18950
|
+
metadataSourcePath: input.configPath
|
|
18951
|
+
});
|
|
18732
18952
|
}
|
|
18733
18953
|
input.document.contents = input.document.createNode(input.config);
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18954
|
+
await atomicWriteFilePreservingMetadata(
|
|
18955
|
+
input.configPath,
|
|
18956
|
+
input.document.toString()
|
|
18957
|
+
);
|
|
18737
18958
|
return backupPath;
|
|
18738
18959
|
}
|
|
18739
18960
|
function readStringList3(value) {
|
|
@@ -18756,7 +18977,7 @@ function ensureRecord3(target, key) {
|
|
|
18756
18977
|
target[key] = current;
|
|
18757
18978
|
return current;
|
|
18758
18979
|
}
|
|
18759
|
-
function
|
|
18980
|
+
function isNodeError17(error, code) {
|
|
18760
18981
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
18761
18982
|
}
|
|
18762
18983
|
|
|
@@ -19238,7 +19459,7 @@ function readModelList(payload) {
|
|
|
19238
19459
|
// src/hermes/updates.ts
|
|
19239
19460
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
19240
19461
|
import { spawn as spawn3 } from "child_process";
|
|
19241
|
-
import { mkdir as
|
|
19462
|
+
import { mkdir as mkdir10, readFile as readFile16, rm as rm8 } from "fs/promises";
|
|
19242
19463
|
import path21 from "path";
|
|
19243
19464
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
19244
19465
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -19252,7 +19473,10 @@ var runningUpdate = null;
|
|
|
19252
19473
|
async function readHermesUpdateCheck(options) {
|
|
19253
19474
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
19254
19475
|
const [local, remoteResult] = await Promise.all([
|
|
19255
|
-
readHermesVersion(
|
|
19476
|
+
readHermesVersion({
|
|
19477
|
+
fetchImpl: options.fetchImpl,
|
|
19478
|
+
logger: options.logger
|
|
19479
|
+
}).catch((error) => ({
|
|
19256
19480
|
raw: error instanceof Error ? error.message : String(error),
|
|
19257
19481
|
version: null
|
|
19258
19482
|
})),
|
|
@@ -19299,7 +19523,7 @@ async function startHermesUpdate(options) {
|
|
|
19299
19523
|
signal: null,
|
|
19300
19524
|
error: null
|
|
19301
19525
|
};
|
|
19302
|
-
await
|
|
19526
|
+
await mkdir10(options.paths.runDir, { recursive: true, mode: 448 });
|
|
19303
19527
|
await writer.write(`
|
|
19304
19528
|
=== hermes update started ${startedAt} ===
|
|
19305
19529
|
`);
|
|
@@ -19603,17 +19827,17 @@ function readString17(payload, key) {
|
|
|
19603
19827
|
// src/link/updates.ts
|
|
19604
19828
|
import { spawn as spawn5 } from "child_process";
|
|
19605
19829
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
19606
|
-
import { mkdir as
|
|
19830
|
+
import { mkdir as mkdir13, readFile as readFile18, rm as rm11 } from "fs/promises";
|
|
19607
19831
|
import path23 from "path";
|
|
19608
19832
|
|
|
19609
19833
|
// src/daemon/process.ts
|
|
19610
19834
|
import { spawn as spawn4 } from "child_process";
|
|
19611
|
-
import { mkdir as
|
|
19835
|
+
import { mkdir as mkdir12, readFile as readFile17, rm as rm10 } from "fs/promises";
|
|
19612
19836
|
import path22 from "path";
|
|
19613
19837
|
|
|
19614
19838
|
// src/daemon/service.ts
|
|
19615
19839
|
import { createServer } from "http";
|
|
19616
|
-
import { mkdir as
|
|
19840
|
+
import { mkdir as mkdir11, rm as rm9, writeFile as writeFile3 } from "fs/promises";
|
|
19617
19841
|
|
|
19618
19842
|
// src/relay/control-client.ts
|
|
19619
19843
|
import WebSocket from "ws";
|
|
@@ -20606,8 +20830,8 @@ function pidFilePath(paths = resolveRuntimePaths()) {
|
|
|
20606
20830
|
return `${paths.runDir}/hermeslink.pid`;
|
|
20607
20831
|
}
|
|
20608
20832
|
async function writePidFile(paths) {
|
|
20609
|
-
await
|
|
20610
|
-
await
|
|
20833
|
+
await mkdir11(paths.runDir, { recursive: true, mode: 448 });
|
|
20834
|
+
await writeFile3(pidFilePath(paths), `${process.pid}
|
|
20611
20835
|
`, { mode: 384 });
|
|
20612
20836
|
}
|
|
20613
20837
|
async function closeServer(server) {
|
|
@@ -20681,8 +20905,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
20681
20905
|
return status;
|
|
20682
20906
|
}
|
|
20683
20907
|
}
|
|
20684
|
-
await
|
|
20685
|
-
await
|
|
20908
|
+
await mkdir12(paths.logsDir, { recursive: true, mode: 448 });
|
|
20909
|
+
await mkdir12(paths.runDir, { recursive: true, mode: 448 });
|
|
20686
20910
|
const scriptPath = currentCliScriptPath();
|
|
20687
20911
|
const child = spawn4(process.execPath, [scriptPath, "daemon-supervisor"], {
|
|
20688
20912
|
detached: true,
|
|
@@ -20700,7 +20924,7 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
20700
20924
|
return await getDaemonStatus(paths);
|
|
20701
20925
|
}
|
|
20702
20926
|
async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
20703
|
-
await
|
|
20927
|
+
await mkdir12(paths.logsDir, { recursive: true, mode: 448 });
|
|
20704
20928
|
const log = createRotatingTextLogWriter({
|
|
20705
20929
|
paths,
|
|
20706
20930
|
fileName: path22.basename(daemonLogFile(paths))
|
|
@@ -20944,7 +21168,7 @@ async function startLinkUpdate(options) {
|
|
|
20944
21168
|
error: null,
|
|
20945
21169
|
manual_command: manualCommand
|
|
20946
21170
|
};
|
|
20947
|
-
await
|
|
21171
|
+
await mkdir13(options.paths.runDir, { recursive: true, mode: 448 });
|
|
20948
21172
|
await writer.write(
|
|
20949
21173
|
`
|
|
20950
21174
|
=== link update started ${startedAt} target=${targetVersion} ===
|
|
@@ -21313,6 +21537,17 @@ import path24 from "path";
|
|
|
21313
21537
|
import { rm as rm12 } from "fs/promises";
|
|
21314
21538
|
|
|
21315
21539
|
// src/relay/bootstrap.ts
|
|
21540
|
+
var RelayNetworkError = class extends Error {
|
|
21541
|
+
constructor(relayBaseUrl, causeMessage) {
|
|
21542
|
+
super(
|
|
21543
|
+
`Cannot reach Hermes Relay at ${relayBaseUrl}. Please check your network connection, VPN, or proxy settings, then try again.`
|
|
21544
|
+
);
|
|
21545
|
+
this.relayBaseUrl = relayBaseUrl;
|
|
21546
|
+
this.causeMessage = causeMessage;
|
|
21547
|
+
}
|
|
21548
|
+
relayBaseUrl;
|
|
21549
|
+
causeMessage;
|
|
21550
|
+
};
|
|
21316
21551
|
async function bootstrapRelayLink(options) {
|
|
21317
21552
|
const fetcher = options.fetchImpl ?? fetch;
|
|
21318
21553
|
const baseUrl = options.relayBaseUrl.replace(/\/+$/u, "");
|
|
@@ -21353,14 +21588,23 @@ async function bootstrapRelayLink(options) {
|
|
|
21353
21588
|
};
|
|
21354
21589
|
}
|
|
21355
21590
|
async function postJson(fetcher, url, token, body) {
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21591
|
+
let response;
|
|
21592
|
+
try {
|
|
21593
|
+
response = await fetcher(url, {
|
|
21594
|
+
method: "POST",
|
|
21595
|
+
headers: {
|
|
21596
|
+
authorization: `Bearer ${token}`,
|
|
21597
|
+
"content-type": "application/json"
|
|
21598
|
+
},
|
|
21599
|
+
body: JSON.stringify(body)
|
|
21600
|
+
});
|
|
21601
|
+
} catch (error) {
|
|
21602
|
+
const baseUrl = new URL(url).origin;
|
|
21603
|
+
throw new RelayNetworkError(
|
|
21604
|
+
baseUrl,
|
|
21605
|
+
error instanceof Error ? error.message : String(error)
|
|
21606
|
+
);
|
|
21607
|
+
}
|
|
21364
21608
|
const payload = await response.json().catch(() => null);
|
|
21365
21609
|
if (!response.ok) {
|
|
21366
21610
|
const message = readErrorMessage4(payload) ?? `Relay request failed with HTTP ${response.status}`;
|
|
@@ -21388,14 +21632,22 @@ async function preparePairing(paths = resolveRuntimePaths()) {
|
|
|
21388
21632
|
const config = await loadConfig(paths);
|
|
21389
21633
|
const identity = await ensureIdentity(paths);
|
|
21390
21634
|
const systemInfo = readLinkSystemInfo();
|
|
21391
|
-
const created = await postServerJson(
|
|
21392
|
-
|
|
21393
|
-
|
|
21394
|
-
|
|
21395
|
-
|
|
21396
|
-
|
|
21397
|
-
|
|
21398
|
-
|
|
21635
|
+
const created = await postServerJson(
|
|
21636
|
+
config.serverBaseUrl,
|
|
21637
|
+
"/api/v1/link-pairings",
|
|
21638
|
+
{
|
|
21639
|
+
install_id: identity.install_id,
|
|
21640
|
+
link_id: identity.link_id ?? void 0,
|
|
21641
|
+
display_name: systemInfo.defaultDisplayName,
|
|
21642
|
+
platform: systemInfo.platform,
|
|
21643
|
+
hostname: systemInfo.hostname ?? void 0,
|
|
21644
|
+
public_key_pem: identity.public_key_pem
|
|
21645
|
+
},
|
|
21646
|
+
{
|
|
21647
|
+
target: "server",
|
|
21648
|
+
action: "create pairing session"
|
|
21649
|
+
}
|
|
21650
|
+
);
|
|
21399
21651
|
const relayBaseUrl = created.relayBaseUrl || config.relayBaseUrl;
|
|
21400
21652
|
let assigned;
|
|
21401
21653
|
let updatedIdentity;
|
|
@@ -21417,28 +21669,43 @@ async function preparePairing(paths = resolveRuntimePaths()) {
|
|
|
21417
21669
|
installId: updatedIdentity.install_id,
|
|
21418
21670
|
publicKeyPem: updatedIdentity.public_key_pem
|
|
21419
21671
|
});
|
|
21420
|
-
await patchServerJson(
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21672
|
+
await patchServerJson(
|
|
21673
|
+
config.serverBaseUrl,
|
|
21674
|
+
`/api/v1/link-pairings/${created.sessionId}/link`,
|
|
21675
|
+
created.pairingToken,
|
|
21676
|
+
{
|
|
21677
|
+
install_id: updatedIdentity.install_id,
|
|
21678
|
+
link_id: assigned.linkId,
|
|
21679
|
+
link_version: LINK_VERSION,
|
|
21680
|
+
display_name: systemInfo.defaultDisplayName,
|
|
21681
|
+
platform: systemInfo.platform,
|
|
21682
|
+
hostname: systemInfo.hostname ?? void 0,
|
|
21683
|
+
lan_ips: routes.lanIps,
|
|
21684
|
+
public_ipv4s: routes.publicIpv4s,
|
|
21685
|
+
public_ipv6s: routes.publicIpv6s,
|
|
21686
|
+
preferred_urls: routes.preferredUrls,
|
|
21687
|
+
environment: routes.environment
|
|
21688
|
+
},
|
|
21689
|
+
{
|
|
21690
|
+
target: "server",
|
|
21691
|
+
action: "finalize pairing"
|
|
21692
|
+
}
|
|
21693
|
+
);
|
|
21433
21694
|
} catch (error) {
|
|
21695
|
+
const reportedError = error instanceof RelayNetworkError ? createPairingNetworkError({
|
|
21696
|
+
target: "relay",
|
|
21697
|
+
action: "connect to Relay",
|
|
21698
|
+
baseUrl: error.relayBaseUrl,
|
|
21699
|
+
detail: error.causeMessage
|
|
21700
|
+
}) : error;
|
|
21434
21701
|
await reportPairingErrorToServer({
|
|
21435
21702
|
serverBaseUrl: config.serverBaseUrl,
|
|
21436
21703
|
sessionId: created.sessionId,
|
|
21437
21704
|
source: "link",
|
|
21438
21705
|
pairingToken: created.pairingToken,
|
|
21439
|
-
error: pairingErrorSnapshot("prepare_pairing",
|
|
21706
|
+
error: pairingErrorSnapshot("prepare_pairing", reportedError)
|
|
21440
21707
|
});
|
|
21441
|
-
throw
|
|
21708
|
+
throw reportedError;
|
|
21442
21709
|
}
|
|
21443
21710
|
const qrPayload = {
|
|
21444
21711
|
kind: "hermes_link_pairing",
|
|
@@ -21538,6 +21805,10 @@ async function claimPairing(input) {
|
|
|
21538
21805
|
{
|
|
21539
21806
|
claim_token: input.claimToken,
|
|
21540
21807
|
app_instance_id: input.appInstanceId ?? void 0
|
|
21808
|
+
},
|
|
21809
|
+
{
|
|
21810
|
+
target: "server",
|
|
21811
|
+
action: "verify pairing claim"
|
|
21541
21812
|
}
|
|
21542
21813
|
);
|
|
21543
21814
|
} catch (error) {
|
|
@@ -21585,15 +21856,25 @@ async function loadRequiredIdentity2(paths) {
|
|
|
21585
21856
|
}
|
|
21586
21857
|
return identity;
|
|
21587
21858
|
}
|
|
21588
|
-
async function postServerJson(serverBaseUrl, path25, body) {
|
|
21589
|
-
|
|
21590
|
-
|
|
21591
|
-
|
|
21592
|
-
|
|
21593
|
-
|
|
21594
|
-
|
|
21595
|
-
|
|
21596
|
-
|
|
21859
|
+
async function postServerJson(serverBaseUrl, path25, body, options) {
|
|
21860
|
+
let response;
|
|
21861
|
+
try {
|
|
21862
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
21863
|
+
method: "POST",
|
|
21864
|
+
headers: {
|
|
21865
|
+
accept: "application/json",
|
|
21866
|
+
"content-type": "application/json"
|
|
21867
|
+
},
|
|
21868
|
+
body: JSON.stringify(body)
|
|
21869
|
+
});
|
|
21870
|
+
} catch (error) {
|
|
21871
|
+
throw createPairingNetworkError({
|
|
21872
|
+
target: options.target,
|
|
21873
|
+
action: options.action,
|
|
21874
|
+
baseUrl: serverBaseUrl,
|
|
21875
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
21876
|
+
});
|
|
21877
|
+
}
|
|
21597
21878
|
return readJsonResponse2(response);
|
|
21598
21879
|
}
|
|
21599
21880
|
async function reportPairingErrorToServer(input) {
|
|
@@ -21626,16 +21907,26 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
21626
21907
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
21627
21908
|
};
|
|
21628
21909
|
}
|
|
21629
|
-
async function patchServerJson(serverBaseUrl, path25, token, body) {
|
|
21630
|
-
|
|
21631
|
-
|
|
21632
|
-
|
|
21633
|
-
|
|
21634
|
-
|
|
21635
|
-
|
|
21636
|
-
|
|
21637
|
-
|
|
21638
|
-
|
|
21910
|
+
async function patchServerJson(serverBaseUrl, path25, token, body, options) {
|
|
21911
|
+
let response;
|
|
21912
|
+
try {
|
|
21913
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
21914
|
+
method: "PATCH",
|
|
21915
|
+
headers: {
|
|
21916
|
+
accept: "application/json",
|
|
21917
|
+
authorization: `Bearer ${token}`,
|
|
21918
|
+
"content-type": "application/json"
|
|
21919
|
+
},
|
|
21920
|
+
body: JSON.stringify(body)
|
|
21921
|
+
});
|
|
21922
|
+
} catch (error) {
|
|
21923
|
+
throw createPairingNetworkError({
|
|
21924
|
+
target: options.target,
|
|
21925
|
+
action: options.action,
|
|
21926
|
+
baseUrl: serverBaseUrl,
|
|
21927
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
21928
|
+
});
|
|
21929
|
+
}
|
|
21639
21930
|
return readJsonResponse2(response);
|
|
21640
21931
|
}
|
|
21641
21932
|
async function readJsonResponse2(response) {
|
|
@@ -21657,6 +21948,15 @@ function readErrorMessage5(payload) {
|
|
|
21657
21948
|
const message = error.message;
|
|
21658
21949
|
return typeof message === "string" ? message : null;
|
|
21659
21950
|
}
|
|
21951
|
+
function createPairingNetworkError(input) {
|
|
21952
|
+
const baseMessage = input.target === "server" ? `HermesPilot Server is unreachable while trying to ${input.action}.` : `Hermes Relay is unreachable while trying to ${input.action}.`;
|
|
21953
|
+
const hint = "If you are using a VPN, proxy, or corporate network, try turning it off and retrying.";
|
|
21954
|
+
return new LinkHttpError(
|
|
21955
|
+
503,
|
|
21956
|
+
input.target === "server" ? "pairing_server_unreachable" : "pairing_relay_unreachable",
|
|
21957
|
+
`${baseMessage} Please check whether ${input.baseUrl} is reachable. ${hint} Detail: ${input.detail}`
|
|
21958
|
+
);
|
|
21959
|
+
}
|
|
21660
21960
|
function pairingClaimPath(sessionId, paths) {
|
|
21661
21961
|
return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
21662
21962
|
}
|
|
@@ -22792,6 +23092,7 @@ export {
|
|
|
22792
23092
|
createFileLogger,
|
|
22793
23093
|
getLinkLogFile,
|
|
22794
23094
|
ensureHermesApiServerAvailable,
|
|
23095
|
+
readHermesVersion,
|
|
22795
23096
|
loadConfig,
|
|
22796
23097
|
saveConfig,
|
|
22797
23098
|
normalizeLanHost,
|