@hermespilot/link 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-TZVQZFWU.js → chunk-RJB5VUT4.js} +390 -266
- package/dist/cli/index.js +1 -1
- 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.8";
|
|
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
|
}
|
|
@@ -4599,8 +4745,8 @@ async function assertProfileExists(profileName) {
|
|
|
4599
4745
|
if (profileName === "default") {
|
|
4600
4746
|
return;
|
|
4601
4747
|
}
|
|
4602
|
-
const exists = await
|
|
4603
|
-
if (
|
|
4748
|
+
const exists = await stat4(resolveHermesProfileDir(profileName)).then((value) => value.isDirectory()).catch((error) => {
|
|
4749
|
+
if (isNodeError5(error, "ENOENT")) {
|
|
4604
4750
|
return false;
|
|
4605
4751
|
}
|
|
4606
4752
|
throw error;
|
|
@@ -4800,7 +4946,7 @@ function isHealthyPlatformState(value) {
|
|
|
4800
4946
|
function toRecord2(value) {
|
|
4801
4947
|
return typeof value === "object" && value !== null ? value : {};
|
|
4802
4948
|
}
|
|
4803
|
-
function
|
|
4949
|
+
function isNodeError5(error, code) {
|
|
4804
4950
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
4805
4951
|
}
|
|
4806
4952
|
function readString4(payload, key) {
|
|
@@ -5168,7 +5314,7 @@ function firstRecord(...values) {
|
|
|
5168
5314
|
|
|
5169
5315
|
// src/conversations/blob-store.ts
|
|
5170
5316
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5171
|
-
import { mkdir as
|
|
5317
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir4, rm as rm3, stat as stat5, writeFile } from "fs/promises";
|
|
5172
5318
|
import path9 from "path";
|
|
5173
5319
|
|
|
5174
5320
|
// src/conversations/media.ts
|
|
@@ -5623,8 +5769,8 @@ async function writeConversationBlob(paths, conversationId, input, options) {
|
|
|
5623
5769
|
}
|
|
5624
5770
|
const id = `blob_${randomUUID3().replaceAll("-", "")}`;
|
|
5625
5771
|
const filePath = blobPath(paths, id);
|
|
5626
|
-
await
|
|
5627
|
-
await
|
|
5772
|
+
await mkdir5(path9.dirname(filePath), { recursive: true, mode: 448 });
|
|
5773
|
+
await writeFile(filePath, input.bytes, { mode: 384 });
|
|
5628
5774
|
const blob = {
|
|
5629
5775
|
id,
|
|
5630
5776
|
size: input.bytes.byteLength,
|
|
@@ -5651,7 +5797,7 @@ async function readConversationBlob(paths, conversationId, blobId) {
|
|
|
5651
5797
|
const filePath = blobPath(paths, blobId);
|
|
5652
5798
|
const manifest = await readConversationBlobManifest(paths, conversationId, blobId);
|
|
5653
5799
|
const bytes = await readFile6(filePath).catch((error) => {
|
|
5654
|
-
if (
|
|
5800
|
+
if (isNodeError6(error, "ENOENT")) {
|
|
5655
5801
|
throw new LinkHttpError(404, "blob_not_found", "Blob was not found");
|
|
5656
5802
|
}
|
|
5657
5803
|
throw error;
|
|
@@ -5693,7 +5839,7 @@ async function deleteConversationBlobIfUnreferenced(paths, conversationId, blobI
|
|
|
5693
5839
|
async function materializeConversationBlob(paths, conversationId, blobId, manifest) {
|
|
5694
5840
|
const existingPath = manifest.materialized_path;
|
|
5695
5841
|
if (existingPath) {
|
|
5696
|
-
const exists = await
|
|
5842
|
+
const exists = await stat5(existingPath).then((value) => value.isFile()).catch(() => false);
|
|
5697
5843
|
if (exists) {
|
|
5698
5844
|
return existingPath;
|
|
5699
5845
|
}
|
|
@@ -5703,8 +5849,8 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
|
|
|
5703
5849
|
targetDir,
|
|
5704
5850
|
materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
|
|
5705
5851
|
);
|
|
5706
|
-
await
|
|
5707
|
-
await
|
|
5852
|
+
await mkdir5(targetDir, { recursive: true, mode: 448 });
|
|
5853
|
+
await writeFile(targetPath, await readFile6(blobPath(paths, blobId)), {
|
|
5708
5854
|
mode: 384
|
|
5709
5855
|
});
|
|
5710
5856
|
await writeJsonFile(`${blobPath(paths, blobId)}.json`, {
|
|
@@ -5733,11 +5879,11 @@ async function pruneConversationBlobReference(paths, conversationId, blobId) {
|
|
|
5733
5879
|
}
|
|
5734
5880
|
async function listConversationBlobIds(paths, conversationId) {
|
|
5735
5881
|
assertValidConversationId(conversationId);
|
|
5736
|
-
await
|
|
5737
|
-
const entries = await
|
|
5882
|
+
await mkdir5(paths.blobsDir, { recursive: true, mode: 448 });
|
|
5883
|
+
const entries = await readdir4(paths.blobsDir, {
|
|
5738
5884
|
withFileTypes: true
|
|
5739
5885
|
}).catch((error) => {
|
|
5740
|
-
if (
|
|
5886
|
+
if (isNodeError6(error, "ENOENT")) {
|
|
5741
5887
|
return [];
|
|
5742
5888
|
}
|
|
5743
5889
|
throw error;
|
|
@@ -5771,12 +5917,12 @@ function conversationAttachmentsDir(paths, conversationId) {
|
|
|
5771
5917
|
assertValidConversationId(conversationId);
|
|
5772
5918
|
return path9.join(paths.conversationsDir, conversationId, "attachments");
|
|
5773
5919
|
}
|
|
5774
|
-
function
|
|
5920
|
+
function isNodeError6(error, code) {
|
|
5775
5921
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5776
5922
|
}
|
|
5777
5923
|
|
|
5778
5924
|
// src/conversations/profile-runtime.ts
|
|
5779
|
-
import { stat as
|
|
5925
|
+
import { stat as stat6 } from "fs/promises";
|
|
5780
5926
|
var PROFILE_NAME_PATTERN2 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
5781
5927
|
function normalizeProfileName2(profileName) {
|
|
5782
5928
|
const value = profileName?.trim() || "default";
|
|
@@ -5917,8 +6063,8 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
|
|
|
5917
6063
|
async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
5918
6064
|
const profilePath = resolveHermesProfileDir(profileName);
|
|
5919
6065
|
if (profileName !== "default") {
|
|
5920
|
-
const exists = await
|
|
5921
|
-
if (
|
|
6066
|
+
const exists = await stat6(profilePath).then((value) => value.isDirectory()).catch((error) => {
|
|
6067
|
+
if (isNodeError7(error, "ENOENT")) {
|
|
5922
6068
|
return false;
|
|
5923
6069
|
}
|
|
5924
6070
|
throw error;
|
|
@@ -5944,7 +6090,7 @@ function displayNameForProfile(profile) {
|
|
|
5944
6090
|
const custom = profile.displayName?.trim();
|
|
5945
6091
|
return custom || fallbackProfileDisplayName(profile.profileName);
|
|
5946
6092
|
}
|
|
5947
|
-
function
|
|
6093
|
+
function isNodeError7(error, code) {
|
|
5948
6094
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5949
6095
|
}
|
|
5950
6096
|
|
|
@@ -6489,11 +6635,11 @@ function formatContextUsageLines(runtime) {
|
|
|
6489
6635
|
}
|
|
6490
6636
|
|
|
6491
6637
|
// src/conversations/delivery-staging.ts
|
|
6492
|
-
import { mkdir as
|
|
6638
|
+
import { mkdir as mkdir6, rm as rm4 } from "fs/promises";
|
|
6493
6639
|
import path10 from "path";
|
|
6494
6640
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
6495
6641
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
6496
|
-
await
|
|
6642
|
+
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
6497
6643
|
return directory;
|
|
6498
6644
|
}
|
|
6499
6645
|
async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
@@ -6852,7 +6998,7 @@ function isUsableLanIpv4(value) {
|
|
|
6852
6998
|
}
|
|
6853
6999
|
|
|
6854
7000
|
// src/hermes/session-title.ts
|
|
6855
|
-
import { stat as
|
|
7001
|
+
import { stat as stat7 } from "fs/promises";
|
|
6856
7002
|
import path11 from "path";
|
|
6857
7003
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
6858
7004
|
const trimmedSessionId = sessionId.trim();
|
|
@@ -6864,8 +7010,8 @@ async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
|
6864
7010
|
resolveHermesProfileDir(resolvedProfileName),
|
|
6865
7011
|
"state.db"
|
|
6866
7012
|
);
|
|
6867
|
-
const exists = await
|
|
6868
|
-
if (
|
|
7013
|
+
const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
7014
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
6869
7015
|
return false;
|
|
6870
7016
|
}
|
|
6871
7017
|
throw error;
|
|
@@ -6885,8 +7031,8 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
6885
7031
|
resolveHermesProfileDir(resolvedProfileName),
|
|
6886
7032
|
"state.db"
|
|
6887
7033
|
);
|
|
6888
|
-
const exists = await
|
|
6889
|
-
if (
|
|
7034
|
+
const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
7035
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
6890
7036
|
return false;
|
|
6891
7037
|
}
|
|
6892
7038
|
throw error;
|
|
@@ -6949,7 +7095,7 @@ function readCompressionTipFromStateDb(dbPath, sessionId) {
|
|
|
6949
7095
|
db?.close();
|
|
6950
7096
|
}
|
|
6951
7097
|
}
|
|
6952
|
-
function
|
|
7098
|
+
function isNodeError8(error, code) {
|
|
6953
7099
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
6954
7100
|
}
|
|
6955
7101
|
function isValidProfileName(value) {
|
|
@@ -8648,11 +8794,11 @@ function hydrateAgentEventBlocks(blocks, agentEvents) {
|
|
|
8648
8794
|
// src/conversations/conversation-store.ts
|
|
8649
8795
|
import {
|
|
8650
8796
|
appendFile as appendFile2,
|
|
8651
|
-
mkdir as
|
|
8652
|
-
readdir as
|
|
8797
|
+
mkdir as mkdir7,
|
|
8798
|
+
readdir as readdir5,
|
|
8653
8799
|
readFile as readFile7,
|
|
8654
8800
|
rm as rm5,
|
|
8655
|
-
writeFile as
|
|
8801
|
+
writeFile as writeFile2
|
|
8656
8802
|
} from "fs/promises";
|
|
8657
8803
|
import path12 from "path";
|
|
8658
8804
|
var ConversationStore = class {
|
|
@@ -8661,14 +8807,14 @@ var ConversationStore = class {
|
|
|
8661
8807
|
}
|
|
8662
8808
|
paths;
|
|
8663
8809
|
async ensureConversationsDir() {
|
|
8664
|
-
await
|
|
8810
|
+
await mkdir7(this.paths.conversationsDir, { recursive: true, mode: 448 });
|
|
8665
8811
|
}
|
|
8666
8812
|
async listConversationIds() {
|
|
8667
8813
|
await this.ensureConversationsDir();
|
|
8668
|
-
const entries = await
|
|
8814
|
+
const entries = await readdir5(this.paths.conversationsDir, {
|
|
8669
8815
|
withFileTypes: true
|
|
8670
8816
|
}).catch((error) => {
|
|
8671
|
-
if (
|
|
8817
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8672
8818
|
return [];
|
|
8673
8819
|
}
|
|
8674
8820
|
throw error;
|
|
@@ -8676,7 +8822,7 @@ var ConversationStore = class {
|
|
|
8676
8822
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
8677
8823
|
}
|
|
8678
8824
|
async createConversation(manifest, snapshot = createEmptySnapshot2()) {
|
|
8679
|
-
await
|
|
8825
|
+
await mkdir7(this.conversationDir(manifest.id), {
|
|
8680
8826
|
recursive: true,
|
|
8681
8827
|
mode: 448
|
|
8682
8828
|
});
|
|
@@ -8716,7 +8862,7 @@ var ConversationStore = class {
|
|
|
8716
8862
|
conversation_id: conversationId,
|
|
8717
8863
|
created_at: now
|
|
8718
8864
|
};
|
|
8719
|
-
await
|
|
8865
|
+
await mkdir7(this.conversationDir(conversationId), {
|
|
8720
8866
|
recursive: true,
|
|
8721
8867
|
mode: 448
|
|
8722
8868
|
});
|
|
@@ -8737,7 +8883,7 @@ var ConversationStore = class {
|
|
|
8737
8883
|
await this.readManifest(conversationId);
|
|
8738
8884
|
const raw = await readFile7(this.eventsPath(conversationId), "utf8").catch(
|
|
8739
8885
|
(error) => {
|
|
8740
|
-
if (
|
|
8886
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8741
8887
|
return "";
|
|
8742
8888
|
}
|
|
8743
8889
|
throw error;
|
|
@@ -8747,7 +8893,7 @@ var ConversationStore = class {
|
|
|
8747
8893
|
}
|
|
8748
8894
|
overwriteEvents(conversationId, events) {
|
|
8749
8895
|
const content = events.map((event) => JSON.stringify(event)).join("\n");
|
|
8750
|
-
return
|
|
8896
|
+
return writeFile2(
|
|
8751
8897
|
this.eventsPath(conversationId),
|
|
8752
8898
|
content ? `${content}
|
|
8753
8899
|
` : "",
|
|
@@ -8792,18 +8938,18 @@ var ConversationStore = class {
|
|
|
8792
8938
|
function createEmptySnapshot2() {
|
|
8793
8939
|
return { schema_version: 1, messages: [], runs: [] };
|
|
8794
8940
|
}
|
|
8795
|
-
function
|
|
8941
|
+
function isNodeError9(error, code) {
|
|
8796
8942
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8797
8943
|
}
|
|
8798
8944
|
|
|
8799
8945
|
// src/conversations/hermes-session-sync.ts
|
|
8800
8946
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8801
|
-
import { readdir as
|
|
8947
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "fs/promises";
|
|
8802
8948
|
import os4 from "os";
|
|
8803
8949
|
import path14 from "path";
|
|
8804
8950
|
|
|
8805
8951
|
// src/conversations/delivery-import.ts
|
|
8806
|
-
import { lstat, readFile as readFile8, readdir as
|
|
8952
|
+
import { lstat as lstat2, readFile as readFile8, readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
8807
8953
|
import path13 from "path";
|
|
8808
8954
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
8809
8955
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
@@ -8899,8 +9045,8 @@ function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
|
8899
9045
|
};
|
|
8900
9046
|
}
|
|
8901
9047
|
async function collectStagedDeliveryReferences(stagingDir) {
|
|
8902
|
-
const directoryStat = await
|
|
8903
|
-
if (
|
|
9048
|
+
const directoryStat = await lstat2(stagingDir).catch((error) => {
|
|
9049
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
8904
9050
|
throw new LinkHttpError(
|
|
8905
9051
|
404,
|
|
8906
9052
|
"delivery_staging_not_found",
|
|
@@ -8916,7 +9062,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
8916
9062
|
"delivery staging path is not a directory"
|
|
8917
9063
|
);
|
|
8918
9064
|
}
|
|
8919
|
-
const entries = await
|
|
9065
|
+
const entries = await readdir6(stagingDir, { withFileTypes: true });
|
|
8920
9066
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
8921
9067
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
8922
9068
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
@@ -9080,8 +9226,8 @@ function emptyImportResult(input) {
|
|
|
9080
9226
|
}
|
|
9081
9227
|
async function writeBlobFromFile(deps, conversationId, source) {
|
|
9082
9228
|
const sourcePath = resolveMediaSourcePath(source.path);
|
|
9083
|
-
const fileStat = await
|
|
9084
|
-
if (
|
|
9229
|
+
const fileStat = await stat8(sourcePath).catch((error) => {
|
|
9230
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9085
9231
|
throw new LinkHttpError(
|
|
9086
9232
|
404,
|
|
9087
9233
|
"media_source_not_found",
|
|
@@ -9115,7 +9261,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
9115
9261
|
key: sourceKey,
|
|
9116
9262
|
filename: sanitizeFilename(reference.path, "attachment"),
|
|
9117
9263
|
reason: error instanceof Error ? error.message : String(error),
|
|
9118
|
-
...
|
|
9264
|
+
...isNodeError10(error) && error.code ? { code: error.code } : {}
|
|
9119
9265
|
};
|
|
9120
9266
|
}
|
|
9121
9267
|
function isSupportedDeliveryFilename(filename) {
|
|
@@ -9128,7 +9274,7 @@ function readString8(payload, key) {
|
|
|
9128
9274
|
function toRecord7(value) {
|
|
9129
9275
|
return typeof value === "object" && value !== null ? value : {};
|
|
9130
9276
|
}
|
|
9131
|
-
function
|
|
9277
|
+
function isNodeError10(error, code) {
|
|
9132
9278
|
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
9133
9279
|
}
|
|
9134
9280
|
|
|
@@ -9895,9 +10041,9 @@ function rememberKnownHermesConversation(map, sessionId, conversationId) {
|
|
|
9895
10041
|
async function discoverHermesProfileNames() {
|
|
9896
10042
|
const names = /* @__PURE__ */ new Set([DEFAULT_PROFILE_NAME]);
|
|
9897
10043
|
const profilesDir = path14.join(os4.homedir(), ".hermes", "profiles");
|
|
9898
|
-
const entries = await
|
|
10044
|
+
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
9899
10045
|
(error) => {
|
|
9900
|
-
if (
|
|
10046
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
9901
10047
|
return [];
|
|
9902
10048
|
}
|
|
9903
10049
|
throw error;
|
|
@@ -10098,7 +10244,7 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
10098
10244
|
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path14.join(profileDir, "sessions"));
|
|
10099
10245
|
const transcriptPath = path14.join(sessionsDir, `${sessionId}.jsonl`);
|
|
10100
10246
|
const raw = await readFile9(transcriptPath, "utf8").catch((error) => {
|
|
10101
|
-
if (
|
|
10247
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10102
10248
|
return "";
|
|
10103
10249
|
}
|
|
10104
10250
|
throw error;
|
|
@@ -10373,8 +10519,8 @@ function quoteIdentifier(value) {
|
|
|
10373
10519
|
return `"${value.replaceAll('"', '""')}"`;
|
|
10374
10520
|
}
|
|
10375
10521
|
async function isFile(filePath) {
|
|
10376
|
-
return
|
|
10377
|
-
if (
|
|
10522
|
+
return stat9(filePath).then((value) => value.isFile()).catch((error) => {
|
|
10523
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10378
10524
|
return false;
|
|
10379
10525
|
}
|
|
10380
10526
|
throw error;
|
|
@@ -10407,12 +10553,12 @@ function readBoolean(value) {
|
|
|
10407
10553
|
}
|
|
10408
10554
|
return false;
|
|
10409
10555
|
}
|
|
10410
|
-
function
|
|
10556
|
+
function isNodeError11(error, code) {
|
|
10411
10557
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
10412
10558
|
}
|
|
10413
10559
|
|
|
10414
10560
|
// src/conversations/run-lifecycle.ts
|
|
10415
|
-
import { readdir as
|
|
10561
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
10416
10562
|
|
|
10417
10563
|
// src/hermes/api-server.ts
|
|
10418
10564
|
async function listHermesModels(options = {}) {
|
|
@@ -10806,7 +10952,7 @@ function readString10(payload, key) {
|
|
|
10806
10952
|
}
|
|
10807
10953
|
|
|
10808
10954
|
// src/conversations/history-builder.ts
|
|
10809
|
-
import { readFile as readFile10, stat as
|
|
10955
|
+
import { readFile as readFile10, stat as stat10 } from "fs/promises";
|
|
10810
10956
|
import path15 from "path";
|
|
10811
10957
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
10812
10958
|
var HERMES_HISTORY_COLUMNS = [
|
|
@@ -10914,8 +11060,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
10914
11060
|
};
|
|
10915
11061
|
}
|
|
10916
11062
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
10917
|
-
const exists = await
|
|
10918
|
-
if (
|
|
11063
|
+
const exists = await stat10(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
11064
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
10919
11065
|
return false;
|
|
10920
11066
|
}
|
|
10921
11067
|
throw error;
|
|
@@ -10934,7 +11080,7 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
10934
11080
|
}
|
|
10935
11081
|
const transcriptPath = path15.join(sessionsDir, `${sessionId}.jsonl`);
|
|
10936
11082
|
const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
|
|
10937
|
-
if (
|
|
11083
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
10938
11084
|
return "";
|
|
10939
11085
|
}
|
|
10940
11086
|
throw error;
|
|
@@ -11156,7 +11302,7 @@ function readTableColumns2(db, table) {
|
|
|
11156
11302
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
11157
11303
|
);
|
|
11158
11304
|
}
|
|
11159
|
-
function
|
|
11305
|
+
function isNodeError12(error, code) {
|
|
11160
11306
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
11161
11307
|
}
|
|
11162
11308
|
function isValidProfileName2(value) {
|
|
@@ -11174,7 +11320,7 @@ function normalizeProfileForCompare(value) {
|
|
|
11174
11320
|
|
|
11175
11321
|
// src/hermes/stt.ts
|
|
11176
11322
|
import { execFile as execFile3 } from "child_process";
|
|
11177
|
-
import { access as access2, readFile as readFile11, stat as
|
|
11323
|
+
import { access as access2, readFile as readFile11, stat as stat11 } from "fs/promises";
|
|
11178
11324
|
import path16 from "path";
|
|
11179
11325
|
import { promisify as promisify3 } from "util";
|
|
11180
11326
|
var execFileAsync3 = promisify3(execFile3);
|
|
@@ -11334,7 +11480,7 @@ async function resolveExecutablePath(command) {
|
|
|
11334
11480
|
}
|
|
11335
11481
|
async function isExecutableFile(filePath) {
|
|
11336
11482
|
try {
|
|
11337
|
-
const info = await
|
|
11483
|
+
const info = await stat11(filePath);
|
|
11338
11484
|
if (!info.isFile()) {
|
|
11339
11485
|
return false;
|
|
11340
11486
|
}
|
|
@@ -11375,7 +11521,7 @@ async function findDevHermesAgentSource() {
|
|
|
11375
11521
|
return null;
|
|
11376
11522
|
}
|
|
11377
11523
|
async function isDirectory(candidate) {
|
|
11378
|
-
return
|
|
11524
|
+
return stat11(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
11379
11525
|
}
|
|
11380
11526
|
function compactProcessOutput(value) {
|
|
11381
11527
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -12619,8 +12765,8 @@ function formatFilenameList(filenames) {
|
|
|
12619
12765
|
return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
|
|
12620
12766
|
}
|
|
12621
12767
|
async function readdirWithDirs(directory) {
|
|
12622
|
-
return
|
|
12623
|
-
if (
|
|
12768
|
+
return readdir8(directory, { withFileTypes: true }).catch((error) => {
|
|
12769
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
12624
12770
|
return [];
|
|
12625
12771
|
}
|
|
12626
12772
|
throw error;
|
|
@@ -12850,7 +12996,7 @@ function readResponseId(payload) {
|
|
|
12850
12996
|
const response = toRecord11(payload.response);
|
|
12851
12997
|
return readString13(payload, "response_id") ?? readString13(response, "id");
|
|
12852
12998
|
}
|
|
12853
|
-
function
|
|
12999
|
+
function isNodeError13(error, code) {
|
|
12854
13000
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
12855
13001
|
return false;
|
|
12856
13002
|
}
|
|
@@ -13686,7 +13832,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
13686
13832
|
|
|
13687
13833
|
// src/identity/identity.ts
|
|
13688
13834
|
import { generateKeyPairSync, randomUUID as randomUUID8, sign } from "crypto";
|
|
13689
|
-
import { mkdir as
|
|
13835
|
+
import { mkdir as mkdir8, chmod as chmod2 } from "fs/promises";
|
|
13690
13836
|
import { z } from "zod";
|
|
13691
13837
|
var linkIdentitySchema = z.object({
|
|
13692
13838
|
install_id: z.string().min(1),
|
|
@@ -13708,8 +13854,8 @@ async function ensureIdentity(paths = resolveRuntimePaths()) {
|
|
|
13708
13854
|
if (existing) {
|
|
13709
13855
|
return existing;
|
|
13710
13856
|
}
|
|
13711
|
-
await
|
|
13712
|
-
await
|
|
13857
|
+
await mkdir8(paths.homeDir, { recursive: true, mode: 448 });
|
|
13858
|
+
await chmod2(paths.homeDir, 448).catch(() => void 0);
|
|
13713
13859
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
13714
13860
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13715
13861
|
const identity = {
|
|
@@ -14903,7 +15049,7 @@ function createHttpErrorMiddleware(logger) {
|
|
|
14903
15049
|
}
|
|
14904
15050
|
|
|
14905
15051
|
// src/hermes/profiles.ts
|
|
14906
|
-
import {
|
|
15052
|
+
import { readdir as readdir9, readFile as readFile12, rename as rename3, rm as rm6, stat as stat12 } from "fs/promises";
|
|
14907
15053
|
import os5 from "os";
|
|
14908
15054
|
import path17 from "path";
|
|
14909
15055
|
import YAML2 from "yaml";
|
|
@@ -14913,9 +15059,9 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
14913
15059
|
const profiles = /* @__PURE__ */ new Map();
|
|
14914
15060
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
14915
15061
|
const profilesDir = path17.join(os5.homedir(), ".hermes", "profiles");
|
|
14916
|
-
const entries = await
|
|
15062
|
+
const entries = await readdir9(profilesDir, { withFileTypes: true }).catch(
|
|
14917
15063
|
(error) => {
|
|
14918
|
-
if (
|
|
15064
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14919
15065
|
return [];
|
|
14920
15066
|
}
|
|
14921
15067
|
throw error;
|
|
@@ -14939,8 +15085,8 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
14939
15085
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
14940
15086
|
assertProfileName(name);
|
|
14941
15087
|
const profile = await profileInfo(name, paths);
|
|
14942
|
-
const exists = await
|
|
14943
|
-
if (
|
|
15088
|
+
const exists = await stat12(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
15089
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14944
15090
|
return false;
|
|
14945
15091
|
}
|
|
14946
15092
|
throw error;
|
|
@@ -14957,7 +15103,7 @@ async function renameHermesProfile(oldName, newName, paths = resolveRuntimePaths
|
|
|
14957
15103
|
assertMutableProfile(newName);
|
|
14958
15104
|
const oldProfile = await profileInfo(oldName, paths);
|
|
14959
15105
|
const newProfilePath = resolveHermesProfileDir(newName);
|
|
14960
|
-
await
|
|
15106
|
+
await rename3(oldProfile.path, newProfilePath);
|
|
14961
15107
|
const identity = await renameProfileIdentity(paths, {
|
|
14962
15108
|
oldProfileName: oldName,
|
|
14963
15109
|
newProfileName: newName,
|
|
@@ -14980,8 +15126,8 @@ async function updateHermesProfileMetadata(name, metadata, paths = resolveRuntim
|
|
|
14980
15126
|
async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
|
|
14981
15127
|
assertMutableProfile(name);
|
|
14982
15128
|
const profile = await profileInfo(name, paths);
|
|
14983
|
-
const exists = await
|
|
14984
|
-
if (
|
|
15129
|
+
const exists = await stat12(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
15130
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14985
15131
|
return false;
|
|
14986
15132
|
}
|
|
14987
15133
|
throw error;
|
|
@@ -15049,13 +15195,13 @@ function assertProfileName(name) {
|
|
|
15049
15195
|
throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
|
|
15050
15196
|
}
|
|
15051
15197
|
}
|
|
15052
|
-
function
|
|
15198
|
+
function isNodeError14(error, code) {
|
|
15053
15199
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
15054
15200
|
}
|
|
15055
15201
|
async function countSkills(root) {
|
|
15056
|
-
const entries = await
|
|
15202
|
+
const entries = await readdir9(root, { withFileTypes: true }).catch(
|
|
15057
15203
|
(error) => {
|
|
15058
|
-
if (
|
|
15204
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15059
15205
|
return [];
|
|
15060
15206
|
}
|
|
15061
15207
|
throw error;
|
|
@@ -15082,7 +15228,7 @@ async function countConfiguredTools(profileName) {
|
|
|
15082
15228
|
resolveHermesConfigPath(profileName),
|
|
15083
15229
|
"utf8"
|
|
15084
15230
|
).catch((error) => {
|
|
15085
|
-
if (
|
|
15231
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15086
15232
|
return "";
|
|
15087
15233
|
}
|
|
15088
15234
|
throw error;
|
|
@@ -15759,14 +15905,11 @@ function errorMessage(error) {
|
|
|
15759
15905
|
import { spawn as spawn2 } from "child_process";
|
|
15760
15906
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
15761
15907
|
import {
|
|
15762
|
-
copyFile as copyFile2,
|
|
15763
15908
|
cp,
|
|
15764
|
-
mkdir as
|
|
15909
|
+
mkdir as mkdir9,
|
|
15765
15910
|
readFile as readFile13,
|
|
15766
15911
|
rm as rm7,
|
|
15767
|
-
stat as
|
|
15768
|
-
writeFile as writeFile4,
|
|
15769
|
-
rename as rename5
|
|
15912
|
+
stat as stat13
|
|
15770
15913
|
} from "fs/promises";
|
|
15771
15914
|
import path18 from "path";
|
|
15772
15915
|
import YAML3 from "yaml";
|
|
@@ -15816,7 +15959,7 @@ async function startHermesProfileCreation(input, options) {
|
|
|
15816
15959
|
signal: null,
|
|
15817
15960
|
error: null
|
|
15818
15961
|
};
|
|
15819
|
-
await
|
|
15962
|
+
await mkdir9(options.paths.runDir, { recursive: true, mode: 448 });
|
|
15820
15963
|
await writeProfileCreationState(options.paths, started);
|
|
15821
15964
|
await writer.write(`
|
|
15822
15965
|
=== profile creation started ${startedAt} ===
|
|
@@ -16093,7 +16236,7 @@ function randomSuffix(attempt) {
|
|
|
16093
16236
|
async function applyProfileCreationPostSteps(input) {
|
|
16094
16237
|
const profilePath = resolveHermesProfileDir(input.profileName);
|
|
16095
16238
|
if (!await pathExists(profilePath)) {
|
|
16096
|
-
await
|
|
16239
|
+
await ensureDirectoryWithInheritedMetadata(profilePath, 448);
|
|
16097
16240
|
}
|
|
16098
16241
|
if (input.sourceProfile && input.copyScopes.length > 0) {
|
|
16099
16242
|
await copyProfileScopes({
|
|
@@ -16221,7 +16364,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
16221
16364
|
async function writeEnvValues(profileName, values) {
|
|
16222
16365
|
const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
|
|
16223
16366
|
const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
|
|
16224
|
-
if (
|
|
16367
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16225
16368
|
return "";
|
|
16226
16369
|
}
|
|
16227
16370
|
throw error;
|
|
@@ -16246,13 +16389,14 @@ async function writeEnvValues(profileName, values) {
|
|
|
16246
16389
|
nextLines.push(`${key}=${formatEnvValue2(value)}`);
|
|
16247
16390
|
}
|
|
16248
16391
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
16249
|
-
await mkdir11(path18.dirname(envPath), { recursive: true, mode: 448 });
|
|
16250
16392
|
if (existingRaw) {
|
|
16251
|
-
await
|
|
16393
|
+
await atomicWriteFilePreservingMetadata(
|
|
16394
|
+
`${envPath}.bak.${Date.now()}`,
|
|
16395
|
+
existingRaw,
|
|
16396
|
+
{ metadataSourcePath: envPath }
|
|
16397
|
+
);
|
|
16252
16398
|
}
|
|
16253
|
-
|
|
16254
|
-
await writeFile4(tempPath, nextRaw, { mode: 384 });
|
|
16255
|
-
await rename5(tempPath, envPath);
|
|
16399
|
+
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
16256
16400
|
}
|
|
16257
16401
|
async function copySkills(sourceProfile, targetProfile) {
|
|
16258
16402
|
const sourceSkills = path18.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
@@ -16266,6 +16410,10 @@ async function copySkills(sourceProfile, targetProfile) {
|
|
|
16266
16410
|
force: true,
|
|
16267
16411
|
errorOnExist: false
|
|
16268
16412
|
});
|
|
16413
|
+
await inheritOwnerRecursively(
|
|
16414
|
+
targetSkills,
|
|
16415
|
+
resolveHermesProfileDir(targetProfile)
|
|
16416
|
+
);
|
|
16269
16417
|
}
|
|
16270
16418
|
function copyProperty(source, target, key) {
|
|
16271
16419
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
@@ -16277,7 +16425,7 @@ function copyProperty(source, target, key) {
|
|
|
16277
16425
|
async function readYamlConfig(configPath) {
|
|
16278
16426
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
16279
16427
|
(error) => {
|
|
16280
|
-
if (
|
|
16428
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16281
16429
|
return null;
|
|
16282
16430
|
}
|
|
16283
16431
|
throw error;
|
|
@@ -16289,14 +16437,15 @@ async function readYamlConfig(configPath) {
|
|
|
16289
16437
|
};
|
|
16290
16438
|
}
|
|
16291
16439
|
async function writeYamlConfig(configPath, input) {
|
|
16292
|
-
await mkdir11(path18.dirname(configPath), { recursive: true, mode: 448 });
|
|
16293
16440
|
if (input.existingRaw) {
|
|
16294
|
-
await
|
|
16441
|
+
await atomicWriteFilePreservingMetadata(
|
|
16442
|
+
`${configPath}.bak.${Date.now()}`,
|
|
16443
|
+
input.existingRaw,
|
|
16444
|
+
{ metadataSourcePath: configPath }
|
|
16445
|
+
);
|
|
16295
16446
|
}
|
|
16296
16447
|
const document = new YAML3.Document(input.config);
|
|
16297
|
-
|
|
16298
|
-
await writeFile4(tempPath, document.toString(), { mode: 384 });
|
|
16299
|
-
await rename5(tempPath, configPath);
|
|
16448
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
16300
16449
|
}
|
|
16301
16450
|
async function failProfileCreation(input) {
|
|
16302
16451
|
if (input.rollbackProfileName) {
|
|
@@ -16362,8 +16511,8 @@ async function clearProfileCreationLogFiles(paths) {
|
|
|
16362
16511
|
]);
|
|
16363
16512
|
}
|
|
16364
16513
|
async function pathExists(targetPath) {
|
|
16365
|
-
return await
|
|
16366
|
-
if (
|
|
16514
|
+
return await stat13(targetPath).then(() => true).catch((error) => {
|
|
16515
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16367
16516
|
return false;
|
|
16368
16517
|
}
|
|
16369
16518
|
throw error;
|
|
@@ -16410,7 +16559,7 @@ function formatEnvValue2(value) {
|
|
|
16410
16559
|
function escapeRegExp2(value) {
|
|
16411
16560
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16412
16561
|
}
|
|
16413
|
-
function
|
|
16562
|
+
function isNodeError15(error, code) {
|
|
16414
16563
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16415
16564
|
}
|
|
16416
16565
|
|
|
@@ -16619,13 +16768,9 @@ function toProfileToolConfigHttpError(error) {
|
|
|
16619
16768
|
// src/hermes/memory.ts
|
|
16620
16769
|
import {
|
|
16621
16770
|
access as access3,
|
|
16622
|
-
|
|
16623
|
-
mkdir as mkdir12,
|
|
16624
|
-
readdir as readdir9,
|
|
16771
|
+
readdir as readdir10,
|
|
16625
16772
|
readFile as readFile14,
|
|
16626
|
-
|
|
16627
|
-
stat as stat13,
|
|
16628
|
-
writeFile as writeFile5
|
|
16773
|
+
stat as stat14
|
|
16629
16774
|
} from "fs/promises";
|
|
16630
16775
|
import path19 from "path";
|
|
16631
16776
|
import YAML4 from "yaml";
|
|
@@ -16938,12 +17083,11 @@ function isSensitiveConfigKey(key) {
|
|
|
16938
17083
|
}
|
|
16939
17084
|
async function writeCustomProviderConfig(profileName, provider, config) {
|
|
16940
17085
|
const configPath = customProviderConfigPath(profileName, provider);
|
|
16941
|
-
await
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
});
|
|
17086
|
+
await atomicWriteFilePreservingMetadata(
|
|
17087
|
+
configPath,
|
|
17088
|
+
`${JSON.stringify(config, null, 2)}
|
|
17089
|
+
`
|
|
17090
|
+
);
|
|
16947
17091
|
}
|
|
16948
17092
|
function normalizeConfigurableProvider(provider, patch) {
|
|
16949
17093
|
if (provider === CUSTOM_PROVIDER_CARD_ID) {
|
|
@@ -16991,7 +17135,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
16991
17135
|
const configPath = resolveHermesConfigPath(profileName);
|
|
16992
17136
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
16993
17137
|
(error) => {
|
|
16994
|
-
if (
|
|
17138
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16995
17139
|
return null;
|
|
16996
17140
|
}
|
|
16997
17141
|
throw error;
|
|
@@ -17003,17 +17147,13 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
17003
17147
|
memory.provider = provider === "built-in" ? "" : provider;
|
|
17004
17148
|
config.memory = memory;
|
|
17005
17149
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17150
|
+
if (backupPath && existingRaw !== null) {
|
|
17151
|
+
await atomicWriteFilePreservingMetadata(backupPath, existingRaw, {
|
|
17152
|
+
metadataSourcePath: configPath
|
|
17153
|
+
});
|
|
17009
17154
|
}
|
|
17010
17155
|
document.contents = document.createNode(config);
|
|
17011
|
-
|
|
17012
|
-
await writeFile5(tempPath, document.toString(), {
|
|
17013
|
-
encoding: "utf8",
|
|
17014
|
-
mode: 384
|
|
17015
|
-
});
|
|
17016
|
-
await rename6(tempPath, configPath);
|
|
17156
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
17017
17157
|
}
|
|
17018
17158
|
function resolveMemoryDir(profileName) {
|
|
17019
17159
|
return path19.join(resolveHermesProfileDir(profileName), "memories");
|
|
@@ -17021,8 +17161,8 @@ function resolveMemoryDir(profileName) {
|
|
|
17021
17161
|
async function readMemoryStore(profileName, target, limits) {
|
|
17022
17162
|
const filePath = memoryFilePath(profileName, target);
|
|
17023
17163
|
const entries = await readMemoryEntries(filePath);
|
|
17024
|
-
const fileStat = await
|
|
17025
|
-
if (
|
|
17164
|
+
const fileStat = await stat14(filePath).catch((error) => {
|
|
17165
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17026
17166
|
return null;
|
|
17027
17167
|
}
|
|
17028
17168
|
throw error;
|
|
@@ -17051,7 +17191,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
17051
17191
|
}
|
|
17052
17192
|
async function readMemoryEntries(filePath) {
|
|
17053
17193
|
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
17054
|
-
if (
|
|
17194
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17055
17195
|
return "";
|
|
17056
17196
|
}
|
|
17057
17197
|
throw error;
|
|
@@ -17071,17 +17211,10 @@ async function mutateMemoryEntries(profileName, target, mutate) {
|
|
|
17071
17211
|
async function writeMemoryEntries(profileName, target, entries) {
|
|
17072
17212
|
assertWithinLimit(target, entries, await readMemoryLimits(profileName));
|
|
17073
17213
|
const filePath = memoryFilePath(profileName, target);
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
dir,
|
|
17078
|
-
`.mem_${process.pid}_${Date.now()}_${target}.tmp`
|
|
17214
|
+
await atomicWriteFilePreservingMetadata(
|
|
17215
|
+
filePath,
|
|
17216
|
+
entries.join(ENTRY_DELIMITER)
|
|
17079
17217
|
);
|
|
17080
|
-
await writeFile5(tempPath, entries.join(ENTRY_DELIMITER), {
|
|
17081
|
-
encoding: "utf8",
|
|
17082
|
-
mode: 384
|
|
17083
|
-
});
|
|
17084
|
-
await rename6(tempPath, filePath);
|
|
17085
17218
|
}
|
|
17086
17219
|
function memoryFilePath(profileName, target) {
|
|
17087
17220
|
return path19.join(
|
|
@@ -17562,7 +17695,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
17562
17695
|
async function readCustomProviderRegistry(profileName) {
|
|
17563
17696
|
const raw = await readFile14(customProviderRegistryPath(profileName), "utf8").catch(
|
|
17564
17697
|
(error) => {
|
|
17565
|
-
if (
|
|
17698
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17566
17699
|
return "";
|
|
17567
17700
|
}
|
|
17568
17701
|
throw error;
|
|
@@ -17599,19 +17732,17 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
17599
17732
|
{ id: providerId, label: providerId, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" }
|
|
17600
17733
|
].sort((left, right) => left.id.localeCompare(right.id));
|
|
17601
17734
|
const registryPath2 = customProviderRegistryPath(profileName);
|
|
17602
|
-
await
|
|
17603
|
-
await writeFile5(
|
|
17735
|
+
await atomicWriteFilePreservingMetadata(
|
|
17604
17736
|
registryPath2,
|
|
17605
17737
|
`${JSON.stringify({ providers }, null, 2)}
|
|
17606
|
-
|
|
17607
|
-
{ encoding: "utf8", mode: 384 }
|
|
17738
|
+
`
|
|
17608
17739
|
);
|
|
17609
17740
|
}
|
|
17610
17741
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
17611
17742
|
const pluginsDir = path19.join(resolveHermesProfileDir(profileName), "plugins");
|
|
17612
|
-
const entries = await
|
|
17743
|
+
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
17613
17744
|
(error) => {
|
|
17614
|
-
if (
|
|
17745
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17615
17746
|
return [];
|
|
17616
17747
|
}
|
|
17617
17748
|
throw error;
|
|
@@ -17652,7 +17783,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
17652
17783
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
17653
17784
|
const source = await readFile14(path19.join(providerDir, "__init__.py"), "utf8").catch(
|
|
17654
17785
|
(error) => {
|
|
17655
|
-
if (
|
|
17786
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17656
17787
|
return "";
|
|
17657
17788
|
}
|
|
17658
17789
|
throw error;
|
|
@@ -17664,7 +17795,7 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
17664
17795
|
async function readPluginMetadata(providerDir) {
|
|
17665
17796
|
const raw = await readFile14(path19.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
17666
17797
|
(error) => {
|
|
17667
|
-
if (
|
|
17798
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17668
17799
|
return "";
|
|
17669
17800
|
}
|
|
17670
17801
|
throw error;
|
|
@@ -17690,7 +17821,7 @@ async function resolveByteRoverCli() {
|
|
|
17690
17821
|
async function readHolographicProviderConfig(profileName) {
|
|
17691
17822
|
const raw = await readFile14(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
17692
17823
|
(error) => {
|
|
17693
|
-
if (
|
|
17824
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17694
17825
|
return "";
|
|
17695
17826
|
}
|
|
17696
17827
|
throw error;
|
|
@@ -17704,7 +17835,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
17704
17835
|
const configPath = resolveHermesConfigPath(profileName);
|
|
17705
17836
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
17706
17837
|
(error) => {
|
|
17707
|
-
if (
|
|
17838
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17708
17839
|
return null;
|
|
17709
17840
|
}
|
|
17710
17841
|
throw error;
|
|
@@ -17721,17 +17852,15 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
17721
17852
|
}
|
|
17722
17853
|
plugins["hermes-memory-store"] = memoryStore;
|
|
17723
17854
|
config.plugins = plugins;
|
|
17724
|
-
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
17725
17855
|
if (existingRaw) {
|
|
17726
|
-
await
|
|
17856
|
+
await atomicWriteFilePreservingMetadata(
|
|
17857
|
+
`${configPath}.bak.${Date.now()}`,
|
|
17858
|
+
existingRaw,
|
|
17859
|
+
{ metadataSourcePath: configPath }
|
|
17860
|
+
);
|
|
17727
17861
|
}
|
|
17728
17862
|
document.contents = document.createNode(config);
|
|
17729
|
-
|
|
17730
|
-
await writeFile5(tempPath, document.toString(), {
|
|
17731
|
-
encoding: "utf8",
|
|
17732
|
-
mode: 384
|
|
17733
|
-
});
|
|
17734
|
-
await rename6(tempPath, configPath);
|
|
17863
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
17735
17864
|
}
|
|
17736
17865
|
async function patchHermesMemoryEnv(profileName, patch) {
|
|
17737
17866
|
const entries = Object.entries(patch).filter(
|
|
@@ -17742,7 +17871,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
17742
17871
|
}
|
|
17743
17872
|
const envPath = path19.join(resolveHermesProfileDir(profileName), ".env");
|
|
17744
17873
|
const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
|
|
17745
|
-
if (
|
|
17874
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17746
17875
|
return "";
|
|
17747
17876
|
}
|
|
17748
17877
|
throw error;
|
|
@@ -17772,13 +17901,14 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
17772
17901
|
}
|
|
17773
17902
|
}
|
|
17774
17903
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
17775
|
-
await mkdir12(path19.dirname(envPath), { recursive: true, mode: 448 });
|
|
17776
17904
|
if (existingRaw) {
|
|
17777
|
-
await
|
|
17905
|
+
await atomicWriteFilePreservingMetadata(
|
|
17906
|
+
`${envPath}.bak.${Date.now()}`,
|
|
17907
|
+
existingRaw,
|
|
17908
|
+
{ metadataSourcePath: envPath }
|
|
17909
|
+
);
|
|
17778
17910
|
}
|
|
17779
|
-
|
|
17780
|
-
await writeFile5(tempPath, nextRaw, { encoding: "utf8", mode: 384 });
|
|
17781
|
-
await rename6(tempPath, envPath);
|
|
17911
|
+
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
17782
17912
|
}
|
|
17783
17913
|
function isMemoryEnvKeyWritable(key) {
|
|
17784
17914
|
return [
|
|
@@ -17799,7 +17929,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
17799
17929
|
resolveHermesConfigPath(profileName),
|
|
17800
17930
|
"utf8"
|
|
17801
17931
|
).catch((error) => {
|
|
17802
|
-
if (
|
|
17932
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17803
17933
|
return "";
|
|
17804
17934
|
}
|
|
17805
17935
|
throw error;
|
|
@@ -17824,16 +17954,15 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
17824
17954
|
next[key] = value;
|
|
17825
17955
|
}
|
|
17826
17956
|
}
|
|
17827
|
-
await
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
});
|
|
17957
|
+
await atomicWriteFilePreservingMetadata(
|
|
17958
|
+
configPath,
|
|
17959
|
+
`${JSON.stringify(next, null, 2)}
|
|
17960
|
+
`
|
|
17961
|
+
);
|
|
17833
17962
|
}
|
|
17834
17963
|
async function readJsonObject(filePath) {
|
|
17835
17964
|
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
17836
|
-
if (
|
|
17965
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17837
17966
|
return "{}";
|
|
17838
17967
|
}
|
|
17839
17968
|
throw error;
|
|
@@ -17887,7 +18016,7 @@ async function readMemoryLimits(profileName) {
|
|
|
17887
18016
|
resolveHermesConfigPath(profileName),
|
|
17888
18017
|
"utf8"
|
|
17889
18018
|
).catch((error) => {
|
|
17890
|
-
if (
|
|
18019
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
17891
18020
|
return "";
|
|
17892
18021
|
}
|
|
17893
18022
|
throw error;
|
|
@@ -17980,7 +18109,7 @@ function formatEnvValue3(value) {
|
|
|
17980
18109
|
function escapeRegExp3(value) {
|
|
17981
18110
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
17982
18111
|
}
|
|
17983
|
-
function
|
|
18112
|
+
function isNodeError16(error, code) {
|
|
17984
18113
|
return error instanceof Error && "code" in error && error.code === code;
|
|
17985
18114
|
}
|
|
17986
18115
|
|
|
@@ -18388,14 +18517,7 @@ function toMemoryHttpError(error) {
|
|
|
18388
18517
|
}
|
|
18389
18518
|
|
|
18390
18519
|
// 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";
|
|
18520
|
+
import { readFile as readFile15, readdir as readdir11 } from "fs/promises";
|
|
18399
18521
|
import path20 from "path";
|
|
18400
18522
|
import YAML5 from "yaml";
|
|
18401
18523
|
var HermesSkillNotFoundError = class extends Error {
|
|
@@ -18487,9 +18609,9 @@ async function findSkillFiles(root) {
|
|
|
18487
18609
|
return results.sort((left, right) => left.localeCompare(right));
|
|
18488
18610
|
}
|
|
18489
18611
|
async function collectSkillFiles(directory, results) {
|
|
18490
|
-
const entries = await
|
|
18612
|
+
const entries = await readdir11(directory, { withFileTypes: true }).catch(
|
|
18491
18613
|
(error) => {
|
|
18492
|
-
if (
|
|
18614
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18493
18615
|
return [];
|
|
18494
18616
|
}
|
|
18495
18617
|
throw error;
|
|
@@ -18514,7 +18636,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
18514
18636
|
async function readSkillMetadata(input) {
|
|
18515
18637
|
const raw = await readFile15(input.skillFile, "utf8").catch(
|
|
18516
18638
|
(error) => {
|
|
18517
|
-
if (
|
|
18639
|
+
if (isNodeError17(error, "ENOENT") || isNodeError17(error, "EACCES")) {
|
|
18518
18640
|
return null;
|
|
18519
18641
|
}
|
|
18520
18642
|
throw error;
|
|
@@ -18591,7 +18713,7 @@ function normalizeDescription(value) {
|
|
|
18591
18713
|
}
|
|
18592
18714
|
async function readDisabledSkillNames(configPath) {
|
|
18593
18715
|
const raw = await readFile15(configPath, "utf8").catch((error) => {
|
|
18594
|
-
if (
|
|
18716
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18595
18717
|
return "";
|
|
18596
18718
|
}
|
|
18597
18719
|
throw error;
|
|
@@ -18616,7 +18738,7 @@ async function readSkillProvenance(root) {
|
|
|
18616
18738
|
async function readBundledSkillNames(root) {
|
|
18617
18739
|
const raw = await readFile15(path20.join(root, ".bundled_manifest"), "utf8").catch(
|
|
18618
18740
|
(error) => {
|
|
18619
|
-
if (
|
|
18741
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18620
18742
|
return "";
|
|
18621
18743
|
}
|
|
18622
18744
|
throw error;
|
|
@@ -18639,7 +18761,7 @@ async function readBundledSkillNames(root) {
|
|
|
18639
18761
|
async function readHubInstalledSkills(root) {
|
|
18640
18762
|
const raw = await readFile15(path20.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
18641
18763
|
(error) => {
|
|
18642
|
-
if (
|
|
18764
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18643
18765
|
return "";
|
|
18644
18766
|
}
|
|
18645
18767
|
throw error;
|
|
@@ -18711,7 +18833,7 @@ function compareCategoryNames(left, right) {
|
|
|
18711
18833
|
async function readHermesConfigDocument2(configPath) {
|
|
18712
18834
|
const existingRaw = await readFile15(configPath, "utf8").catch(
|
|
18713
18835
|
(error) => {
|
|
18714
|
-
if (
|
|
18836
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
18715
18837
|
return null;
|
|
18716
18838
|
}
|
|
18717
18839
|
throw error;
|
|
@@ -18726,14 +18848,16 @@ async function readHermesConfigDocument2(configPath) {
|
|
|
18726
18848
|
}
|
|
18727
18849
|
async function writeHermesConfigDocument2(input) {
|
|
18728
18850
|
const backupPath = input.existingRaw ? `${input.configPath}.bak.${Date.now()}` : null;
|
|
18729
|
-
await mkdir13(path20.dirname(input.configPath), { recursive: true, mode: 448 });
|
|
18730
18851
|
if (backupPath) {
|
|
18731
|
-
await
|
|
18852
|
+
await atomicWriteFilePreservingMetadata(backupPath, input.existingRaw, {
|
|
18853
|
+
metadataSourcePath: input.configPath
|
|
18854
|
+
});
|
|
18732
18855
|
}
|
|
18733
18856
|
input.document.contents = input.document.createNode(input.config);
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18857
|
+
await atomicWriteFilePreservingMetadata(
|
|
18858
|
+
input.configPath,
|
|
18859
|
+
input.document.toString()
|
|
18860
|
+
);
|
|
18737
18861
|
return backupPath;
|
|
18738
18862
|
}
|
|
18739
18863
|
function readStringList3(value) {
|
|
@@ -18756,7 +18880,7 @@ function ensureRecord3(target, key) {
|
|
|
18756
18880
|
target[key] = current;
|
|
18757
18881
|
return current;
|
|
18758
18882
|
}
|
|
18759
|
-
function
|
|
18883
|
+
function isNodeError17(error, code) {
|
|
18760
18884
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
18761
18885
|
}
|
|
18762
18886
|
|
|
@@ -19238,7 +19362,7 @@ function readModelList(payload) {
|
|
|
19238
19362
|
// src/hermes/updates.ts
|
|
19239
19363
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
19240
19364
|
import { spawn as spawn3 } from "child_process";
|
|
19241
|
-
import { mkdir as
|
|
19365
|
+
import { mkdir as mkdir10, readFile as readFile16, rm as rm8 } from "fs/promises";
|
|
19242
19366
|
import path21 from "path";
|
|
19243
19367
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
19244
19368
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -19299,7 +19423,7 @@ async function startHermesUpdate(options) {
|
|
|
19299
19423
|
signal: null,
|
|
19300
19424
|
error: null
|
|
19301
19425
|
};
|
|
19302
|
-
await
|
|
19426
|
+
await mkdir10(options.paths.runDir, { recursive: true, mode: 448 });
|
|
19303
19427
|
await writer.write(`
|
|
19304
19428
|
=== hermes update started ${startedAt} ===
|
|
19305
19429
|
`);
|
|
@@ -19603,17 +19727,17 @@ function readString17(payload, key) {
|
|
|
19603
19727
|
// src/link/updates.ts
|
|
19604
19728
|
import { spawn as spawn5 } from "child_process";
|
|
19605
19729
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
19606
|
-
import { mkdir as
|
|
19730
|
+
import { mkdir as mkdir13, readFile as readFile18, rm as rm11 } from "fs/promises";
|
|
19607
19731
|
import path23 from "path";
|
|
19608
19732
|
|
|
19609
19733
|
// src/daemon/process.ts
|
|
19610
19734
|
import { spawn as spawn4 } from "child_process";
|
|
19611
|
-
import { mkdir as
|
|
19735
|
+
import { mkdir as mkdir12, readFile as readFile17, rm as rm10 } from "fs/promises";
|
|
19612
19736
|
import path22 from "path";
|
|
19613
19737
|
|
|
19614
19738
|
// src/daemon/service.ts
|
|
19615
19739
|
import { createServer } from "http";
|
|
19616
|
-
import { mkdir as
|
|
19740
|
+
import { mkdir as mkdir11, rm as rm9, writeFile as writeFile3 } from "fs/promises";
|
|
19617
19741
|
|
|
19618
19742
|
// src/relay/control-client.ts
|
|
19619
19743
|
import WebSocket from "ws";
|
|
@@ -20606,8 +20730,8 @@ function pidFilePath(paths = resolveRuntimePaths()) {
|
|
|
20606
20730
|
return `${paths.runDir}/hermeslink.pid`;
|
|
20607
20731
|
}
|
|
20608
20732
|
async function writePidFile(paths) {
|
|
20609
|
-
await
|
|
20610
|
-
await
|
|
20733
|
+
await mkdir11(paths.runDir, { recursive: true, mode: 448 });
|
|
20734
|
+
await writeFile3(pidFilePath(paths), `${process.pid}
|
|
20611
20735
|
`, { mode: 384 });
|
|
20612
20736
|
}
|
|
20613
20737
|
async function closeServer(server) {
|
|
@@ -20681,8 +20805,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
20681
20805
|
return status;
|
|
20682
20806
|
}
|
|
20683
20807
|
}
|
|
20684
|
-
await
|
|
20685
|
-
await
|
|
20808
|
+
await mkdir12(paths.logsDir, { recursive: true, mode: 448 });
|
|
20809
|
+
await mkdir12(paths.runDir, { recursive: true, mode: 448 });
|
|
20686
20810
|
const scriptPath = currentCliScriptPath();
|
|
20687
20811
|
const child = spawn4(process.execPath, [scriptPath, "daemon-supervisor"], {
|
|
20688
20812
|
detached: true,
|
|
@@ -20700,7 +20824,7 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
20700
20824
|
return await getDaemonStatus(paths);
|
|
20701
20825
|
}
|
|
20702
20826
|
async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
20703
|
-
await
|
|
20827
|
+
await mkdir12(paths.logsDir, { recursive: true, mode: 448 });
|
|
20704
20828
|
const log = createRotatingTextLogWriter({
|
|
20705
20829
|
paths,
|
|
20706
20830
|
fileName: path22.basename(daemonLogFile(paths))
|
|
@@ -20944,7 +21068,7 @@ async function startLinkUpdate(options) {
|
|
|
20944
21068
|
error: null,
|
|
20945
21069
|
manual_command: manualCommand
|
|
20946
21070
|
};
|
|
20947
|
-
await
|
|
21071
|
+
await mkdir13(options.paths.runDir, { recursive: true, mode: 448 });
|
|
20948
21072
|
await writer.write(
|
|
20949
21073
|
`
|
|
20950
21074
|
=== link update started ${startedAt} target=${targetVersion} ===
|