@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.
@@ -1099,58 +1099,200 @@ function readProfileAvatarType(row) {
1099
1099
  }
1100
1100
 
1101
1101
  // src/hermes/cron-link-delivery.ts
1102
- import { mkdir as mkdir4, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
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 { mkdir as mkdir2, open, readFile, rename, rm } from "fs/promises";
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 readJsonFile(filePath) {
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 raw = await readFile(filePath, "utf8");
1112
- return JSON.parse(raw);
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 writeJsonFile(filePath, value, mode = 384) {
1121
- await mkdir2(path2.dirname(filePath), { recursive: true, mode: 448 });
1122
- const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
1123
- const payload = `${JSON.stringify(value, null, 2)}
1124
- `;
1125
- const handle = await open(tmpPath, "w", mode);
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 handle.writeFile(payload, "utf8");
1128
- await handle.sync();
1129
- } finally {
1130
- await handle.close();
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 rename(tmpPath, filePath);
1274
+ const raw = await readFile(filePath, "utf8");
1275
+ return JSON.parse(raw);
1134
1276
  } catch (error) {
1135
- await rm(tmpPath, { force: true });
1277
+ if (isNodeError2(error, "ENOENT")) {
1278
+ return null;
1279
+ }
1136
1280
  throw error;
1137
1281
  }
1138
1282
  }
1139
- function isNodeError(error, code) {
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 (isNodeError2(error, "ENOENT")) {
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 (isNodeError2(error, "ENOENT")) {
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 (isNodeError2(error, "ENOENT")) {
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
- await mkdir3(path3.dirname(configPath), { recursive: true, mode: 448 });
1990
- if (backupPath) {
1991
- await copyFile(configPath, backupPath);
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 writeFile(configPath, document.toString(), { mode: 384 });
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 (isNodeError2(error, "ENOENT")) {
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 copyFile(input.configPath, backupPath);
2179
+ await atomicWriteFilePreservingMetadata(backupPath, input.existingRaw, {
2180
+ metadataSourcePath: input.configPath
2181
+ });
2038
2182
  }
2039
2183
  input.document.contents = input.document.createNode(input.config);
2040
- const tempPath = `${input.configPath}.tmp.${process.pid}.${Date.now()}`;
2041
- await writeFile(tempPath, input.document.toString(), { mode: 384 });
2042
- await rename2(tempPath, input.configPath);
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 readdir(profilesRoot, { withFileTypes: true }).catch(
3509
+ const entries = await readdir2(profilesRoot, { withFileTypes: true }).catch(
3365
3510
  (error) => {
3366
- if (isNodeError2(error, "ENOENT")) {
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 (isNodeError2(error, "ENOENT")) {
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 (isNodeError2(error, "ENOENT")) {
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 (isNodeError2(error, "ENOENT")) {
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 copyFile(envPath, `${envPath}.bak.${Date.now()}`);
3655
+ await atomicWriteFilePreservingMetadata(
3656
+ `${envPath}.bak.${Date.now()}`,
3657
+ existingRaw,
3658
+ { metadataSourcePath: envPath }
3659
+ );
3512
3660
  }
3513
- const tempPath = `${envPath}.tmp.${process.pid}.${Date.now()}`;
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 isNodeError2(error, code) {
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 readdir2(outputDir, { withFileTypes: true }).catch(
3865
+ const entries = await readdir3(outputDir, { withFileTypes: true }).catch(
3720
3866
  (error) => {
3721
- if (isNodeError3(error, "ENOENT")) {
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 stat(outputPath);
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 mkdir4(paths.indexesDir, { recursive: true, mode: 448 });
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 isNodeError3(error, code) {
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 stat3 } from "fs/promises";
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 mkdir5, open as open2, readFile as readFile4, rename as rename3, rm as rm2, stat as stat2 } from "fs/promises";
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.7";
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 mkdir5(this.paths.logsDir, { recursive: true, mode: 448 });
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 mkdir5(paths.logsDir, { recursive: true, mode: 448 });
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 stat2(filePath).catch(() => null);
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 stat2(filePath).catch(() => null);
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 rename3(from, to).catch((error) => {
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 stat3(resolveHermesProfileDir(profileName)).then((value) => value.isDirectory()).catch((error) => {
4603
- if (isNodeError4(error, "ENOENT")) {
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 isNodeError4(error, code) {
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 mkdir6, readFile as readFile6, readdir as readdir3, rm as rm3, stat as stat4, writeFile as writeFile2 } from "fs/promises";
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 mkdir6(path9.dirname(filePath), { recursive: true, mode: 448 });
5627
- await writeFile2(filePath, input.bytes, { mode: 384 });
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 (isNodeError5(error, "ENOENT")) {
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 stat4(existingPath).then((value) => value.isFile()).catch(() => false);
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 mkdir6(targetDir, { recursive: true, mode: 448 });
5707
- await writeFile2(targetPath, await readFile6(blobPath(paths, blobId)), {
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 mkdir6(paths.blobsDir, { recursive: true, mode: 448 });
5737
- const entries = await readdir3(paths.blobsDir, {
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 (isNodeError5(error, "ENOENT")) {
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 isNodeError5(error, code) {
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 stat5 } from "fs/promises";
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 stat5(profilePath).then((value) => value.isDirectory()).catch((error) => {
5921
- if (isNodeError6(error, "ENOENT")) {
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 isNodeError6(error, code) {
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 mkdir7, rm as rm4 } from "fs/promises";
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 mkdir7(directory, { recursive: true, mode: 448 });
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 stat6 } from "fs/promises";
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 stat6(dbPath).then((value) => value.isFile()).catch((error) => {
6868
- if (isNodeError7(error, "ENOENT")) {
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 stat6(dbPath).then((value) => value.isFile()).catch((error) => {
6889
- if (isNodeError7(error, "ENOENT")) {
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 isNodeError7(error, code) {
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 mkdir8,
8652
- readdir as readdir4,
8797
+ mkdir as mkdir7,
8798
+ readdir as readdir5,
8653
8799
  readFile as readFile7,
8654
8800
  rm as rm5,
8655
- writeFile as writeFile3
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 mkdir8(this.paths.conversationsDir, { recursive: true, mode: 448 });
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 readdir4(this.paths.conversationsDir, {
8814
+ const entries = await readdir5(this.paths.conversationsDir, {
8669
8815
  withFileTypes: true
8670
8816
  }).catch((error) => {
8671
- if (isNodeError8(error, "ENOENT")) {
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 mkdir8(this.conversationDir(manifest.id), {
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 mkdir8(this.conversationDir(conversationId), {
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 (isNodeError8(error, "ENOENT")) {
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 writeFile3(
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 isNodeError8(error, code) {
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 readdir6, readFile as readFile9, stat as stat8 } from "fs/promises";
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 readdir5, stat as stat7 } from "fs/promises";
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 lstat(stagingDir).catch((error) => {
8903
- if (isNodeError9(error, "ENOENT")) {
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 readdir5(stagingDir, { withFileTypes: true });
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 stat7(sourcePath).catch((error) => {
9084
- if (isNodeError9(error, "ENOENT")) {
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
- ...isNodeError9(error) && error.code ? { code: error.code } : {}
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 isNodeError9(error, code) {
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 readdir6(profilesDir, { withFileTypes: true }).catch(
10044
+ const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
9899
10045
  (error) => {
9900
- if (isNodeError10(error, "ENOENT")) {
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 (isNodeError10(error, "ENOENT")) {
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 stat8(filePath).then((value) => value.isFile()).catch((error) => {
10377
- if (isNodeError10(error, "ENOENT")) {
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 isNodeError10(error, code) {
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 readdir7 } from "fs/promises";
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 stat9 } from "fs/promises";
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 stat9(dbPath).then((value) => value.isFile()).catch((error) => {
10918
- if (isNodeError11(error, "ENOENT")) {
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 (isNodeError11(error, "ENOENT")) {
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 isNodeError11(error, code) {
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 stat10 } from "fs/promises";
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 stat10(filePath);
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 stat10(candidate).then((info) => info.isDirectory()).catch(() => false);
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 readdir7(directory, { withFileTypes: true }).catch((error) => {
12623
- if (isNodeError12(error, "ENOENT")) {
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 isNodeError12(error, code) {
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 mkdir9, chmod } from "fs/promises";
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 mkdir9(paths.homeDir, { recursive: true, mode: 448 });
13712
- await chmod(paths.homeDir, 448).catch(() => void 0);
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 { mkdir as mkdir10, readdir as readdir8, readFile as readFile12, rename as rename4, rm as rm6, stat as stat11 } from "fs/promises";
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 readdir8(profilesDir, { withFileTypes: true }).catch(
15062
+ const entries = await readdir9(profilesDir, { withFileTypes: true }).catch(
14917
15063
  (error) => {
14918
- if (isNodeError13(error, "ENOENT")) {
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 stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
14943
- if (isNodeError13(error, "ENOENT")) {
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 rename4(oldProfile.path, newProfilePath);
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 stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
14984
- if (isNodeError13(error, "ENOENT")) {
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 isNodeError13(error, code) {
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 readdir8(root, { withFileTypes: true }).catch(
15202
+ const entries = await readdir9(root, { withFileTypes: true }).catch(
15057
15203
  (error) => {
15058
- if (isNodeError13(error, "ENOENT")) {
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 (isNodeError13(error, "ENOENT")) {
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 mkdir11,
15909
+ mkdir as mkdir9,
15765
15910
  readFile as readFile13,
15766
15911
  rm as rm7,
15767
- stat as stat12,
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 mkdir11(options.paths.runDir, { recursive: true, mode: 448 });
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 mkdir11(profilePath, { recursive: true, mode: 448 });
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 (isNodeError14(error, "ENOENT")) {
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 copyFile2(envPath, `${envPath}.bak.${Date.now()}`);
16393
+ await atomicWriteFilePreservingMetadata(
16394
+ `${envPath}.bak.${Date.now()}`,
16395
+ existingRaw,
16396
+ { metadataSourcePath: envPath }
16397
+ );
16252
16398
  }
16253
- const tempPath = `${envPath}.tmp.${process.pid}.${Date.now()}`;
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 (isNodeError14(error, "ENOENT")) {
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 copyFile2(configPath, `${configPath}.bak.${Date.now()}`);
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
- const tempPath = `${configPath}.tmp.${process.pid}.${Date.now()}`;
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 stat12(targetPath).then(() => true).catch((error) => {
16366
- if (isNodeError14(error, "ENOENT")) {
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 isNodeError14(error, code) {
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
- copyFile as copyFile3,
16623
- mkdir as mkdir12,
16624
- readdir as readdir9,
16771
+ readdir as readdir10,
16625
16772
  readFile as readFile14,
16626
- rename as rename6,
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 mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
16942
- await writeFile5(configPath, `${JSON.stringify(config, null, 2)}
16943
- `, {
16944
- encoding: "utf8",
16945
- mode: 384
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 (isNodeError15(error, "ENOENT")) {
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
- await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
17007
- if (backupPath) {
17008
- await copyFile3(configPath, backupPath);
17150
+ if (backupPath && existingRaw !== null) {
17151
+ await atomicWriteFilePreservingMetadata(backupPath, existingRaw, {
17152
+ metadataSourcePath: configPath
17153
+ });
17009
17154
  }
17010
17155
  document.contents = document.createNode(config);
17011
- const tempPath = `${configPath}.tmp.${process.pid}.${Date.now()}`;
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 stat13(filePath).catch((error) => {
17025
- if (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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
- const dir = path19.dirname(filePath);
17075
- await mkdir12(dir, { recursive: true, mode: 448 });
17076
- const tempPath = path19.join(
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 (isNodeError15(error, "ENOENT")) {
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 mkdir12(path19.dirname(registryPath2), { recursive: true, mode: 448 });
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 readdir9(pluginsDir, { withFileTypes: true }).catch(
17743
+ const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
17613
17744
  (error) => {
17614
- if (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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 copyFile3(configPath, `${configPath}.bak.${Date.now()}`);
17856
+ await atomicWriteFilePreservingMetadata(
17857
+ `${configPath}.bak.${Date.now()}`,
17858
+ existingRaw,
17859
+ { metadataSourcePath: configPath }
17860
+ );
17727
17861
  }
17728
17862
  document.contents = document.createNode(config);
17729
- const tempPath = `${configPath}.tmp.${process.pid}.${Date.now()}`;
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 (isNodeError15(error, "ENOENT")) {
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 copyFile3(envPath, `${envPath}.bak.${Date.now()}`);
17905
+ await atomicWriteFilePreservingMetadata(
17906
+ `${envPath}.bak.${Date.now()}`,
17907
+ existingRaw,
17908
+ { metadataSourcePath: envPath }
17909
+ );
17778
17910
  }
17779
- const tempPath = `${envPath}.tmp.${process.pid}.${Date.now()}`;
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 (isNodeError15(error, "ENOENT")) {
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 mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
17828
- await writeFile5(configPath, `${JSON.stringify(next, null, 2)}
17829
- `, {
17830
- encoding: "utf8",
17831
- mode: 384
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 (isNodeError15(error, "ENOENT")) {
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 (isNodeError15(error, "ENOENT")) {
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 isNodeError15(error, code) {
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 readdir10(directory, { withFileTypes: true }).catch(
18612
+ const entries = await readdir11(directory, { withFileTypes: true }).catch(
18491
18613
  (error) => {
18492
- if (isNodeError16(error, "ENOENT")) {
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 (isNodeError16(error, "ENOENT") || isNodeError16(error, "EACCES")) {
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 (isNodeError16(error, "ENOENT")) {
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 (isNodeError16(error, "ENOENT")) {
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 (isNodeError16(error, "ENOENT")) {
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 (isNodeError16(error, "ENOENT")) {
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 copyFile4(input.configPath, backupPath);
18852
+ await atomicWriteFilePreservingMetadata(backupPath, input.existingRaw, {
18853
+ metadataSourcePath: input.configPath
18854
+ });
18732
18855
  }
18733
18856
  input.document.contents = input.document.createNode(input.config);
18734
- const tempPath = `${input.configPath}.tmp.${process.pid}.${Date.now()}`;
18735
- await writeFile6(tempPath, input.document.toString(), { mode: 384 });
18736
- await rename7(tempPath, input.configPath);
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 isNodeError16(error, code) {
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 mkdir14, readFile as readFile16, rm as rm8 } from "fs/promises";
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 mkdir14(options.paths.runDir, { recursive: true, mode: 448 });
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 mkdir17, readFile as readFile18, rm as rm11 } from "fs/promises";
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 mkdir16, readFile as readFile17, rm as rm10 } from "fs/promises";
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 mkdir15, rm as rm9, writeFile as writeFile7 } from "fs/promises";
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 mkdir15(paths.runDir, { recursive: true, mode: 448 });
20610
- await writeFile7(pidFilePath(paths), `${process.pid}
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 mkdir16(paths.logsDir, { recursive: true, mode: 448 });
20685
- await mkdir16(paths.runDir, { recursive: true, mode: 448 });
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 mkdir16(paths.logsDir, { recursive: true, mode: 448 });
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 mkdir17(options.paths.runDir, { recursive: true, mode: 448 });
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} ===