@signetai/connector-hermes-agent 0.226.23 → 0.226.25

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/index.d.ts CHANGED
@@ -43,6 +43,7 @@ export declare class HermesAgentConnector extends BaseConnector {
43
43
  export declare function diagnoseHermesIntegration(opts?: {
44
44
  readonly hermesHome?: string;
45
45
  readonly hermesRepo?: string | null;
46
+ readonly targetRoot?: string;
46
47
  readonly daemonUrl?: string;
47
48
  }): Promise<HermesDoctorReport>;
48
49
  export declare function createConnector(): HermesAgentConnector;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAuB,MAAM,wBAAwB,CAAC;AAqCtH,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAClD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAwsBD,qBAAa,oBAAqB,SAAQ,aAAa;IACtD,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,SAAS,kBAAkB;IAEpC,YAAY,IAAI,MAAM;IAItB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAwB;gBAEpC,MAAM,CAAC,EAAE,qBAAqB;IAK1C,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAKrB,aAAa,IAAI,MAAM;IAIjB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,aAAa,CAAC;IA6LvF,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IA2D3C,WAAW,IAAI,OAAO;IAQhB,QAAQ,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAS7C;AAED,wBAAsB,yBAAyB,CAAC,IAAI,CAAC,EAAE;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuG9B;AAED,wBAAgB,eAAe,IAAI,oBAAoB,CAEtD;AAED,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAuB,MAAM,wBAAwB,CAAC;AAqCtH,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAClD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAmqCD,qBAAa,oBAAqB,SAAQ,aAAa;IACtD,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,SAAS,kBAAkB;IAEpC,YAAY,IAAI,MAAM;IAItB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAwB;gBAEpC,MAAM,CAAC,EAAE,qBAAqB;IAK1C,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,aAAa;IAKrB,aAAa,IAAI,MAAM;IAKjB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,aAAa,CAAC;IAyLvF,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IA4D3C,WAAW,IAAI,OAAO;IAShB,QAAQ,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAU7C;AAED,wBAAsB,yBAAyB,CAAC,IAAI,CAAC,EAAE;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwG9B;AAED,wBAAgB,eAAe,IAAI,oBAAoB,CAEtD;AAED,eAAe,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -17412,9 +17412,22 @@ var home = homedir8();
17412
17412
 
17413
17413
  // src/index.ts
17414
17414
  import { createHash as createHash3 } from "node:crypto";
17415
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, realpathSync as realpathSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
17415
+ import {
17416
+ closeSync,
17417
+ constants,
17418
+ existsSync as existsSync2,
17419
+ fstatSync,
17420
+ ftruncateSync,
17421
+ lstatSync as lstatSync2,
17422
+ mkdirSync,
17423
+ openSync,
17424
+ readFileSync as readFileSync2,
17425
+ readdirSync,
17426
+ realpathSync as realpathSync3,
17427
+ writeSync
17428
+ } from "node:fs";
17416
17429
  import { homedir } from "node:os";
17417
- import { dirname as dirname8, isAbsolute as isAbsolute2, join as join6, relative as relative2 } from "node:path";
17430
+ import { dirname as dirname8, isAbsolute as isAbsolute2, join as join6, relative as relative2, resolve as resolvePath, sep as sep2 } from "node:path";
17418
17431
  import { fileURLToPath as fileURLToPath3 } from "node:url";
17419
17432
 
17420
17433
  // ../../../libs/connector-base/dist/index.js
@@ -34998,9 +35011,270 @@ var REQUIRED_TOOL_NAMES = [
34998
35011
  "recall",
34999
35012
  "remember"
35000
35013
  ];
35014
+ var DESCRIPTOR_ROOT = process.platform === "linux" ? "/proc/self/fd" : process.platform === "darwin" ? "/dev/fd" : null;
35015
+ var DESCRIPTOR_WRITES_SUPPORTED = DESCRIPTOR_ROOT !== null && typeof constants.O_DIRECTORY === "number" && typeof constants.O_NOFOLLOW === "number";
35016
+ var DESCRIPTOR_WRITE_UNAVAILABLE_ERROR = "Targeted Hermes profile writes require descriptor-backed no-follow filesystem support";
35017
+ function pathEntryExists(path) {
35018
+ try {
35019
+ lstatSync2(path);
35020
+ return true;
35021
+ } catch {
35022
+ return false;
35023
+ }
35024
+ }
35025
+ function descriptorPath(fd) {
35026
+ if (DESCRIPTOR_ROOT === null)
35027
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35028
+ return join6(DESCRIPTOR_ROOT, String(fd));
35029
+ }
35030
+ function isPathWithin(root, candidate) {
35031
+ const rel = relative2(root, candidate);
35032
+ return !rel.startsWith("..") && !isAbsolute2(rel);
35033
+ }
35034
+ function openDirectoryNoFollow(path) {
35035
+ if (!DESCRIPTOR_WRITES_SUPPORTED)
35036
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35037
+ return openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
35038
+ }
35039
+ function closeDirectory(fd) {
35040
+ try {
35041
+ closeSync(fd);
35042
+ } catch {}
35043
+ }
35044
+ var SECURE_REMOVAL_CHANGED_STATUS = 75;
35045
+ var SECURE_REMOVAL_SCRIPT = [
35046
+ "import os, sys",
35047
+ "expected_dev = int(sys.argv[1])",
35048
+ "expected_ino = int(sys.argv[2])",
35049
+ "operation = sys.argv[3]",
35050
+ "name = sys.argv[4]",
35051
+ "expected_parent = sys.argv[5]",
35052
+ "descriptor_root = sys.argv[6]",
35053
+ "if os.path.realpath(os.path.join(descriptor_root, '3')) != expected_parent:",
35054
+ " raise SystemExit(75)",
35055
+ "try:",
35056
+ " current = os.stat(name, dir_fd=3, follow_symlinks=False)",
35057
+ "except FileNotFoundError:",
35058
+ " raise SystemExit(75)",
35059
+ "if current.st_dev != expected_dev or current.st_ino != expected_ino:",
35060
+ " raise SystemExit(75)",
35061
+ "try:",
35062
+ " if operation == 'directory':",
35063
+ " os.rmdir(name, dir_fd=3)",
35064
+ " elif operation == 'file':",
35065
+ " os.unlink(name, dir_fd=3)",
35066
+ " elif operation != 'verify':",
35067
+ " raise SystemExit(76)",
35068
+ "except FileNotFoundError:",
35069
+ " raise SystemExit(75)"
35070
+ ].join(`
35071
+ `);
35072
+ function getPythonCandidates() {
35073
+ const configuredPython = process.env.PYTHON?.trim();
35074
+ if (configuredPython)
35075
+ return [{ command: configuredPython, args: [] }];
35076
+ return process.platform === "win32" ? [
35077
+ { command: "py", args: ["-3"] },
35078
+ { command: "python", args: [] }
35079
+ ] : [
35080
+ { command: "python3", args: [] },
35081
+ { command: "python", args: [] }
35082
+ ];
35083
+ }
35084
+ function runSecureEntryOperation(parentFd, parentPath, name, expectedDev, expectedIno, operation) {
35085
+ if (DESCRIPTOR_ROOT === null)
35086
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35087
+ const errors = [];
35088
+ for (const candidate of getPythonCandidates()) {
35089
+ const result = spawnSyncHidden(candidate.command, [
35090
+ ...candidate.args,
35091
+ "-c",
35092
+ SECURE_REMOVAL_SCRIPT,
35093
+ String(expectedDev),
35094
+ String(expectedIno),
35095
+ operation,
35096
+ name,
35097
+ parentPath,
35098
+ DESCRIPTOR_ROOT
35099
+ ], {
35100
+ encoding: "utf-8",
35101
+ stdio: ["ignore", "pipe", "pipe", parentFd],
35102
+ timeout: 5000
35103
+ });
35104
+ if (result.error) {
35105
+ errors.push(`${candidate.command}: ${result.error.message}`);
35106
+ continue;
35107
+ }
35108
+ if (result.status === 0)
35109
+ return;
35110
+ if (result.status === SECURE_REMOVAL_CHANGED_STATUS) {
35111
+ throw new Error(`Hermes directory entry changed during secure removal: ${name}`);
35112
+ }
35113
+ errors.push(`${candidate.command}: exited ${result.status ?? "without a status"}`);
35114
+ }
35115
+ throw new Error(`${DESCRIPTOR_WRITE_UNAVAILABLE_ERROR}: ${errors.join("; ") || "No Python interpreter found"}`);
35116
+ }
35117
+ function assertEntryIdentityNoFollow(parentFd, parentPath, name, expectedDev, expectedIno) {
35118
+ runSecureEntryOperation(parentFd, parentPath, name, expectedDev, expectedIno, "verify");
35119
+ }
35120
+ function removeEntryNoFollow(parentFd, parentPath, name, expectedDev, expectedIno, directory) {
35121
+ runSecureEntryOperation(parentFd, parentPath, name, expectedDev, expectedIno, directory ? "directory" : "file");
35122
+ }
35123
+ function sameStatIdentity(left, right) {
35124
+ return left.dev === right.dev && left.ino === right.ino;
35125
+ }
35126
+ function ensureContainedDirectory(directory, targetRoot) {
35127
+ const safeDirectory = resolveContainedWritePath(directory, targetRoot);
35128
+ if (!DESCRIPTOR_WRITES_SUPPORTED) {
35129
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35130
+ }
35131
+ const absoluteDirectory = resolvePath(directory);
35132
+ const rootPath = resolvePath(targetRoot);
35133
+ if (!isPathWithin(rootPath, safeDirectory)) {
35134
+ throw new Error(`Hermes target directory escapes validated root: ${directory}`);
35135
+ }
35136
+ let existing = absoluteDirectory;
35137
+ const missing = [];
35138
+ while (!pathEntryExists(existing)) {
35139
+ const parent = dirname8(existing);
35140
+ if (parent === existing)
35141
+ throw new Error(`Hermes target directory has no existing ancestor: ${directory}`);
35142
+ missing.unshift(existing.slice(parent.length + 1));
35143
+ existing = parent;
35144
+ }
35145
+ const existingReal = realpathSync3(existing);
35146
+ if (existingReal !== existing) {
35147
+ throw new Error(`Hermes target directory is symlinked and cannot be used for writes: ${directory}`);
35148
+ }
35149
+ let fd = openDirectoryNoFollow(existing);
35150
+ let expected = existingReal;
35151
+ try {
35152
+ for (const component of missing) {
35153
+ const childPath = join6(descriptorPath(fd), component);
35154
+ try {
35155
+ mkdirSync(childPath);
35156
+ } catch (error) {
35157
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
35158
+ if (code !== "EEXIST")
35159
+ throw error;
35160
+ }
35161
+ const childFd = openDirectoryNoFollow(childPath);
35162
+ try {
35163
+ expected = join6(expected, component);
35164
+ if (realpathSync3(descriptorPath(childFd)) !== expected) {
35165
+ throw new Error(`Hermes target directory changed during secure creation: ${directory}`);
35166
+ }
35167
+ } catch (error) {
35168
+ closeDirectory(childFd);
35169
+ throw error;
35170
+ }
35171
+ closeDirectory(fd);
35172
+ fd = childFd;
35173
+ }
35174
+ } finally {
35175
+ closeDirectory(fd);
35176
+ }
35177
+ }
35178
+ function writeContainedFile(targetPath, content, targetRoot) {
35179
+ const safePath = resolveContainedWritePath(targetPath, targetRoot);
35180
+ if (!DESCRIPTOR_WRITES_SUPPORTED) {
35181
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35182
+ }
35183
+ const rootPath = resolvePath(targetRoot);
35184
+ ensureContainedDirectory(dirname8(safePath), targetRoot);
35185
+ const relativePath = relative2(rootPath, safePath);
35186
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
35187
+ throw new Error(`Hermes target file escapes validated root: ${targetPath}`);
35188
+ }
35189
+ const components = relativePath.split(sep2);
35190
+ const fileName = components.pop();
35191
+ if (!fileName || components.some((component) => component === "" || component === "." || component === "..")) {
35192
+ throw new Error(`Hermes target file has an invalid relative path: ${targetPath}`);
35193
+ }
35194
+ let parentFd = openDirectoryNoFollow(rootPath);
35195
+ try {
35196
+ if (realpathSync3(descriptorPath(parentFd)) !== rootPath) {
35197
+ throw new Error(`Hermes target root changed during secure write: ${targetRoot}`);
35198
+ }
35199
+ let expected = rootPath;
35200
+ for (const component of components) {
35201
+ const childFd = openDirectoryNoFollow(join6(descriptorPath(parentFd), component));
35202
+ try {
35203
+ expected = join6(expected, component);
35204
+ if (realpathSync3(descriptorPath(childFd)) !== expected) {
35205
+ throw new Error(`Hermes target directory changed during secure write: ${targetPath}`);
35206
+ }
35207
+ } catch (error) {
35208
+ closeDirectory(childFd);
35209
+ throw error;
35210
+ }
35211
+ closeDirectory(parentFd);
35212
+ parentFd = childFd;
35213
+ }
35214
+ const filePath = join6(descriptorPath(parentFd), fileName);
35215
+ let existingFileIdentity;
35216
+ try {
35217
+ const existing = lstatSync2(filePath);
35218
+ if (existing.isSymbolicLink()) {
35219
+ throw new Error(`Hermes target file is symlinked and cannot be used for writes: ${targetPath}`);
35220
+ }
35221
+ existingFileIdentity = { dev: existing.dev, ino: existing.ino };
35222
+ } catch (error) {
35223
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
35224
+ if (code !== "ENOENT")
35225
+ throw error;
35226
+ }
35227
+ const fileFd = openSync(filePath, constants.O_WRONLY | constants.O_NOFOLLOW | (existingFileIdentity === undefined ? constants.O_CREAT | constants.O_EXCL : 0), 438);
35228
+ try {
35229
+ const expectedFile = join6(expected, fileName);
35230
+ if (existingFileIdentity !== undefined && !sameStatIdentity(existingFileIdentity, fstatSync(fileFd))) {
35231
+ throw new Error(`Hermes target file changed during secure write: ${targetPath}`);
35232
+ }
35233
+ if (realpathSync3(descriptorPath(fileFd)) !== expectedFile) {
35234
+ throw new Error(`Hermes target file changed during secure write: ${targetPath}`);
35235
+ }
35236
+ ftruncateSync(fileFd, 0);
35237
+ const bytes = typeof content === "string" ? Buffer.from(content) : Buffer.from(content);
35238
+ let offset = 0;
35239
+ while (offset < bytes.length) {
35240
+ const written = writeSync(fileFd, bytes, offset, bytes.length - offset);
35241
+ if (written <= 0)
35242
+ throw new Error(`Hermes target file write made no progress: ${targetPath}`);
35243
+ offset += written;
35244
+ }
35245
+ } finally {
35246
+ closeDirectory(fileFd);
35247
+ }
35248
+ } finally {
35249
+ closeDirectory(parentFd);
35250
+ }
35251
+ }
35252
+ function ensureTargetDirectory(directory, targetRoot) {
35253
+ ensureContainedDirectory(directory, targetRoot);
35254
+ }
35255
+ function writeTargetFile(path, content, targetRoot) {
35256
+ writeContainedFile(path, content, targetRoot);
35257
+ }
35258
+ function rejectSymlinkedPathComponents(path, root, targetPath) {
35259
+ if (!isPathWithin(root, path)) {
35260
+ throw new Error(`Hermes target path escapes validated root: ${targetPath}`);
35261
+ }
35262
+ let current = path;
35263
+ while (true) {
35264
+ if (lstatSync2(current).isSymbolicLink()) {
35265
+ throw new Error(`Hermes target path is symlinked and cannot be used for writes: ${targetPath}`);
35266
+ }
35267
+ if (current === root)
35268
+ return;
35269
+ const parent = dirname8(current);
35270
+ if (parent === current)
35271
+ throw new Error(`Hermes target path escapes validated root: ${targetPath}`);
35272
+ current = parent;
35273
+ }
35274
+ }
35001
35275
  function resolveContainedWritePath(targetPath, targetRoot) {
35002
- let rootPath = targetRoot;
35003
- while (!existsSync2(rootPath)) {
35276
+ let rootPath = resolvePath(targetRoot);
35277
+ while (!pathEntryExists(rootPath)) {
35004
35278
  const parent = dirname8(rootPath);
35005
35279
  if (parent === rootPath)
35006
35280
  throw new Error(`Hermes target root does not exist: ${targetRoot}`);
@@ -35010,22 +35284,139 @@ function resolveContainedWritePath(targetPath, targetRoot) {
35010
35284
  throw new Error(`Hermes target root is symlinked and cannot be used for writes: ${targetRoot}`);
35011
35285
  }
35012
35286
  const root = realpathSync3(rootPath);
35013
- let existing = targetPath;
35287
+ let existing = resolvePath(targetPath);
35014
35288
  const missing = [];
35015
- while (!existsSync2(existing)) {
35289
+ while (!pathEntryExists(existing)) {
35016
35290
  const parent = dirname8(existing);
35017
35291
  if (parent === existing)
35018
35292
  throw new Error(`Hermes target path does not have an existing ancestor: ${targetPath}`);
35019
35293
  missing.unshift(existing.slice(parent.length + 1));
35020
35294
  existing = parent;
35021
35295
  }
35022
- const candidate = join6(realpathSync3(existing), ...missing);
35296
+ rejectSymlinkedPathComponents(existing, rootPath, targetPath);
35297
+ const candidate = join6(existing, ...missing);
35023
35298
  const rel = relative2(root, candidate);
35024
35299
  if (rel.startsWith("..") || isAbsolute2(rel)) {
35025
35300
  throw new Error(`Hermes target path escapes validated root: ${targetPath}`);
35026
35301
  }
35027
35302
  return candidate;
35028
35303
  }
35304
+ function removeDirectoryContentsNoFollow(directoryFd, expectedDirectoryPath) {
35305
+ if (realpathSync3(descriptorPath(directoryFd)) !== expectedDirectoryPath) {
35306
+ throw new Error(`Hermes target directory changed during secure removal: ${expectedDirectoryPath}`);
35307
+ }
35308
+ for (const entry of readdirSync(descriptorPath(directoryFd), { withFileTypes: true })) {
35309
+ const childPath = join6(descriptorPath(directoryFd), entry.name);
35310
+ const expectedChildPath = join6(expectedDirectoryPath, entry.name);
35311
+ if (entry.isDirectory() && !entry.isSymbolicLink()) {
35312
+ const childFd = openDirectoryNoFollow(childPath);
35313
+ try {
35314
+ const childIdentity2 = fstatSync(childFd);
35315
+ if (realpathSync3(descriptorPath(childFd)) !== expectedChildPath) {
35316
+ throw new Error(`Hermes target directory changed during secure removal: ${expectedChildPath}`);
35317
+ }
35318
+ assertEntryIdentityNoFollow(directoryFd, expectedDirectoryPath, entry.name, childIdentity2.dev, childIdentity2.ino);
35319
+ removeDirectoryContentsNoFollow(childFd, expectedChildPath);
35320
+ removeEntryNoFollow(directoryFd, expectedDirectoryPath, entry.name, childIdentity2.dev, childIdentity2.ino, true);
35321
+ } finally {
35322
+ closeDirectory(childFd);
35323
+ }
35324
+ continue;
35325
+ }
35326
+ const childIdentity = lstatSync2(childPath);
35327
+ removeEntryNoFollow(directoryFd, expectedDirectoryPath, entry.name, childIdentity.dev, childIdentity.ino, false);
35328
+ }
35329
+ }
35330
+ function removeContainedDirectory(targetPath, targetRoot, targetKind) {
35331
+ if (!DESCRIPTOR_WRITES_SUPPORTED)
35332
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35333
+ const safeTargetDir = resolveContainedWritePath(targetPath, targetRoot);
35334
+ const rootPath = resolvePath(targetRoot);
35335
+ const relativePath = relative2(rootPath, safeTargetDir);
35336
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
35337
+ throw new Error(`Hermes target directory escapes validated root: ${targetPath}`);
35338
+ }
35339
+ const components = relativePath.split(sep2);
35340
+ const targetName = components.pop();
35341
+ if (!targetName || components.some((component) => component === "" || component === "." || component === "..")) {
35342
+ throw new Error(`Hermes target directory has an invalid relative path: ${targetPath}`);
35343
+ }
35344
+ let parentFd = openDirectoryNoFollow(rootPath);
35345
+ try {
35346
+ let expected = rootPath;
35347
+ for (const component of components) {
35348
+ const childFd = openDirectoryNoFollow(join6(descriptorPath(parentFd), component));
35349
+ try {
35350
+ expected = join6(expected, component);
35351
+ if (realpathSync3(descriptorPath(childFd)) !== expected) {
35352
+ throw new Error(`Hermes target directory changed during secure removal: ${targetPath}`);
35353
+ }
35354
+ } catch (error) {
35355
+ closeDirectory(childFd);
35356
+ throw error;
35357
+ }
35358
+ closeDirectory(parentFd);
35359
+ parentFd = childFd;
35360
+ }
35361
+ const targetEntryPath = join6(descriptorPath(parentFd), targetName);
35362
+ const targetFd = openDirectoryNoFollow(targetEntryPath);
35363
+ try {
35364
+ const targetIdentity = fstatSync(targetFd);
35365
+ const expectedTarget = join6(expected, targetName);
35366
+ if (realpathSync3(descriptorPath(targetFd)) !== expectedTarget) {
35367
+ throw new Error(`Hermes target directory changed during secure removal: ${targetPath}`);
35368
+ }
35369
+ const marker = readInstallMarkerFromDirectory(targetFd);
35370
+ if (marker === null || marker.targetKind !== targetKind) {
35371
+ throw new Error(`Refusing to uninstall unowned Hermes plugin path: ${targetPath} (missing or invalid ${INSTALL_MARKER_FILE})`);
35372
+ }
35373
+ assertEntryIdentityNoFollow(parentFd, expected, targetName, targetIdentity.dev, targetIdentity.ino);
35374
+ removeDirectoryContentsNoFollow(targetFd, expectedTarget);
35375
+ removeEntryNoFollow(parentFd, expected, targetName, targetIdentity.dev, targetIdentity.ino, true);
35376
+ } finally {
35377
+ closeDirectory(targetFd);
35378
+ }
35379
+ } finally {
35380
+ closeDirectory(parentFd);
35381
+ }
35382
+ }
35383
+ function removeContainedFile(targetPath, targetRoot) {
35384
+ if (!DESCRIPTOR_WRITES_SUPPORTED)
35385
+ throw new Error(DESCRIPTOR_WRITE_UNAVAILABLE_ERROR);
35386
+ const safeTargetPath = resolveContainedWritePath(targetPath, targetRoot);
35387
+ const rootPath = resolvePath(targetRoot);
35388
+ const relativePath = relative2(rootPath, safeTargetPath);
35389
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
35390
+ throw new Error(`Hermes target file escapes validated root: ${targetPath}`);
35391
+ }
35392
+ const components = relativePath.split(sep2);
35393
+ const fileName = components.pop();
35394
+ if (!fileName || components.some((component) => component === "" || component === "." || component === "..")) {
35395
+ throw new Error(`Hermes target file has an invalid relative path: ${targetPath}`);
35396
+ }
35397
+ let parentFd = openDirectoryNoFollow(rootPath);
35398
+ try {
35399
+ let expected = rootPath;
35400
+ for (const component of components) {
35401
+ const childFd = openDirectoryNoFollow(join6(descriptorPath(parentFd), component));
35402
+ try {
35403
+ expected = join6(expected, component);
35404
+ if (realpathSync3(descriptorPath(childFd)) !== expected) {
35405
+ throw new Error(`Hermes target file changed during secure removal: ${targetPath}`);
35406
+ }
35407
+ } catch (error) {
35408
+ closeDirectory(childFd);
35409
+ throw error;
35410
+ }
35411
+ closeDirectory(parentFd);
35412
+ parentFd = childFd;
35413
+ }
35414
+ const targetFile = lstatSync2(join6(descriptorPath(parentFd), fileName));
35415
+ removeEntryNoFollow(parentFd, expected, fileName, targetFile.dev, targetFile.ino, false);
35416
+ } finally {
35417
+ closeDirectory(parentFd);
35418
+ }
35419
+ }
35029
35420
  function getRepoPluginTargetDir(hermesRepo) {
35030
35421
  return join6(hermesRepo, "plugins", "memory", "signet");
35031
35422
  }
@@ -35036,29 +35427,25 @@ function getProviderBackupPath(hermesHome) {
35036
35427
  return join6(hermesHome, PROVIDER_BACKUP_FILE);
35037
35428
  }
35038
35429
  function installPlugin(targetDir, targetKind, targetRoot) {
35039
- const writeDir = targetRoot ? resolveContainedWritePath(targetDir, targetRoot) : targetDir;
35430
+ const writeDir = resolveContainedWritePath(targetDir, targetRoot);
35040
35431
  const sourceDir = getPluginSourceDir();
35041
- mkdirSync(writeDir, { recursive: true });
35432
+ ensureTargetDirectory(writeDir, targetRoot);
35042
35433
  const written = [];
35043
35434
  for (const file of PLUGIN_FILES) {
35044
35435
  const src = join6(sourceDir, file);
35045
- const dst = targetRoot ? resolveContainedWritePath(join6(writeDir, file), targetRoot) : join6(writeDir, file);
35436
+ const dst = resolveContainedWritePath(join6(writeDir, file), targetRoot);
35046
35437
  if (existsSync2(src)) {
35047
- writeFileSync2(dst, readFileSync2(src));
35438
+ writeTargetFile(dst, readFileSync2(src), targetRoot);
35048
35439
  written.push(dst);
35049
35440
  }
35050
35441
  }
35051
- written.push(writeInstallMarker(writeDir, targetKind));
35442
+ written.push(writeInstallMarker(writeDir, targetKind, targetRoot));
35052
35443
  return written;
35053
35444
  }
35054
- function uninstallPlugin(targetDir, targetRoot) {
35055
- if (!existsSync2(targetDir))
35445
+ function uninstallPlugin(targetDir, targetKind, targetRoot) {
35446
+ if (!pathEntryExists(targetDir))
35056
35447
  return [];
35057
- const safeTargetDir = targetRoot ? resolveContainedWritePath(targetDir, targetRoot) : targetDir;
35058
- if (!readInstallMarker(safeTargetDir)) {
35059
- throw new Error(`Refusing to uninstall unowned Hermes plugin path: ${targetDir} (missing or invalid ${INSTALL_MARKER_FILE})`);
35060
- }
35061
- rmSync(safeTargetDir, { recursive: true, force: true });
35448
+ removeContainedDirectory(targetDir, targetRoot, targetKind);
35062
35449
  return [targetDir];
35063
35450
  }
35064
35451
  function getConfigCandidates(hermesHome, targetRoot) {
@@ -35180,11 +35567,11 @@ function parseDottedProviderLine(line) {
35180
35567
  function setDottedProviderLine(lines, line, value) {
35181
35568
  lines[line] = `memory.provider: ${value}`;
35182
35569
  }
35183
- function writeProviderBackup(hermesHome, configPath, providerKind, previousProvider) {
35570
+ function writeProviderBackup(hermesHome, configPath, providerKind, previousProvider, targetRoot) {
35184
35571
  if (previousProvider === "signet")
35185
35572
  return null;
35186
- const backupPath = getProviderBackupPath(hermesHome);
35187
- if (existsSync2(backupPath))
35573
+ const backupPath = resolveContainedWritePath(getProviderBackupPath(hermesHome), targetRoot);
35574
+ if (pathEntryExists(backupPath))
35188
35575
  return null;
35189
35576
  const backup = {
35190
35577
  schemaVersion: 1,
@@ -35193,13 +35580,13 @@ function writeProviderBackup(hermesHome, configPath, providerKind, previousProvi
35193
35580
  previousProvider,
35194
35581
  createdAt: new Date().toISOString()
35195
35582
  };
35196
- mkdirSync(dirname8(backupPath), { recursive: true });
35197
- writeFileSync2(backupPath, `${JSON.stringify(backup, null, 2)}
35198
- `);
35583
+ ensureTargetDirectory(dirname8(backupPath), targetRoot);
35584
+ writeTargetFile(backupPath, `${JSON.stringify(backup, null, 2)}
35585
+ `, targetRoot);
35199
35586
  return backupPath;
35200
35587
  }
35201
- function readProviderBackup(hermesHome) {
35202
- const backupPath = getProviderBackupPath(hermesHome);
35588
+ function readProviderBackup(hermesHome, targetRoot) {
35589
+ const backupPath = targetRoot ? resolveContainedWritePath(getProviderBackupPath(hermesHome), targetRoot) : getProviderBackupPath(hermesHome);
35203
35590
  if (!existsSync2(backupPath))
35204
35591
  return null;
35205
35592
  try {
@@ -35213,10 +35600,17 @@ function readProviderBackup(hermesHome) {
35213
35600
  }
35214
35601
  }
35215
35602
  function removeProviderBackup(hermesHome, targetRoot) {
35216
- const backupPath = targetRoot ? resolveContainedWritePath(getProviderBackupPath(hermesHome), targetRoot) : getProviderBackupPath(hermesHome);
35217
- if (!existsSync2(backupPath))
35603
+ const backupPath = resolveContainedWritePath(getProviderBackupPath(hermesHome), targetRoot);
35604
+ if (!pathEntryExists(backupPath))
35218
35605
  return null;
35219
- rmSync(backupPath, { force: true });
35606
+ try {
35607
+ removeContainedFile(backupPath, targetRoot);
35608
+ } catch (error) {
35609
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
35610
+ if (code !== "ENOENT")
35611
+ throw error;
35612
+ return null;
35613
+ }
35220
35614
  return backupPath;
35221
35615
  }
35222
35616
  function trimTrailingSlashes(value) {
@@ -35226,8 +35620,8 @@ function trimTrailingSlashes(value) {
35226
35620
  }
35227
35621
  return value.slice(0, end);
35228
35622
  }
35229
- function isProviderConfigured(hermesHome) {
35230
- const config = readConfigYaml(hermesHome);
35623
+ function isProviderConfigured(hermesHome, targetRoot) {
35624
+ const config = readConfigYaml(hermesHome, targetRoot);
35231
35625
  if (!config)
35232
35626
  return false;
35233
35627
  const lines = config.content.split(/\r?\n/);
@@ -35262,13 +35656,13 @@ function configureProvider(hermesHome, warnings, targetRoot) {
35262
35656
  let changed = false;
35263
35657
  const dottedWasSignet = dottedProviderLineIsSignet(lines[dottedProvider] ?? "");
35264
35658
  if (!dottedWasSignet) {
35265
- backupPath = writeProviderBackup(hermesHome, configPath, "dotted", parseDottedProviderLine(lines[dottedProvider] ?? "") ?? "");
35659
+ backupPath = writeProviderBackup(hermesHome, configPath, "dotted", parseDottedProviderLine(lines[dottedProvider] ?? "") ?? "", targetRoot);
35266
35660
  setDottedProviderLine(lines, dottedProvider, "signet");
35267
35661
  changed = true;
35268
35662
  }
35269
35663
  if (block !== null && typeof block === "object" && block.provider !== null && block.provider !== undefined) {
35270
35664
  if (dottedWasSignet) {
35271
- const nestedBackupPath = writeProviderBackup(hermesHome, configPath, "nested", parseProviderLine(lines[block.provider] ?? "") ?? "");
35665
+ const nestedBackupPath = writeProviderBackup(hermesHome, configPath, "nested", parseProviderLine(lines[block.provider] ?? "") ?? "", targetRoot);
35272
35666
  backupPath = nestedBackupPath ?? backupPath;
35273
35667
  }
35274
35668
  lines.splice(block.provider, 1);
@@ -35276,10 +35670,10 @@ function configureProvider(hermesHome, warnings, targetRoot) {
35276
35670
  }
35277
35671
  if (!changed)
35278
35672
  return { configPath: null, backupPath: null };
35279
- mkdirSync(dirname8(configPath), { recursive: true });
35280
- writeFileSync2(configPath, `${lines.join(`
35673
+ ensureTargetDirectory(dirname8(configPath), targetRoot);
35674
+ writeTargetFile(configPath, `${lines.join(`
35281
35675
  `).replace(/\n+$/g, "")}
35282
- `);
35676
+ `, targetRoot);
35283
35677
  return { configPath, backupPath };
35284
35678
  }
35285
35679
  if (content && block === null) {
@@ -35289,7 +35683,7 @@ function configureProvider(hermesHome, warnings, targetRoot) {
35289
35683
  if (block !== null && typeof block === "object" && block.provider !== null && block.provider !== undefined) {
35290
35684
  if (providerLineIsSignet(lines[block.provider] ?? ""))
35291
35685
  return { configPath: null, backupPath: null };
35292
- backupPath = writeProviderBackup(hermesHome, configPath, "nested", parseProviderLine(lines[block.provider] ?? "") ?? "");
35686
+ backupPath = writeProviderBackup(hermesHome, configPath, "nested", parseProviderLine(lines[block.provider] ?? "") ?? "", targetRoot);
35293
35687
  lines[block.provider] = `${(lines[block.provider] ?? "").match(/^\s*/)?.[0] ?? " "}provider: signet`;
35294
35688
  } else if (block !== null && typeof block === "object") {
35295
35689
  lines.splice(block.start + 1, 0, `${" ".repeat(block.indent)}provider: signet`);
@@ -35298,25 +35692,23 @@ function configureProvider(hermesHome, warnings, targetRoot) {
35298
35692
  lines.push("");
35299
35693
  lines.push("memory:", " provider: signet");
35300
35694
  }
35301
- mkdirSync(dirname8(configPath), { recursive: true });
35302
- writeFileSync2(configPath, `${lines.join(`
35695
+ ensureTargetDirectory(dirname8(configPath), targetRoot);
35696
+ writeTargetFile(configPath, `${lines.join(`
35303
35697
  `).replace(/\n+$/g, "")}
35304
- `);
35698
+ `, targetRoot);
35305
35699
  return { configPath, backupPath };
35306
35700
  }
35307
35701
  function restoreOrClearProvider(hermesHome, targetRoot) {
35308
- const safeConfigPath = targetRoot ? resolveContainedWritePath(resolveConfigPath(hermesHome), targetRoot) : resolveConfigPath(hermesHome);
35309
- const config = readConfigYaml(hermesHome);
35702
+ const safeConfigPath = resolveContainedWritePath(resolveConfigPath(hermesHome), targetRoot);
35703
+ const config = readConfigYaml(hermesHome, targetRoot);
35310
35704
  if (!config)
35311
35705
  return { configPath: null, backupPath: removeProviderBackup(hermesHome, targetRoot) };
35312
- if (targetRoot)
35313
- resolveContainedWritePath(config.path, targetRoot);
35314
35706
  const lines = config.content.replace(/\r\n/g, `
35315
35707
  `).split(`
35316
35708
  `);
35317
35709
  const block = findMemoryBlock(lines);
35318
35710
  const dottedProvider = findDottedProvider(lines);
35319
- const backup = readProviderBackup(hermesHome);
35711
+ const backup = readProviderBackup(hermesHome, targetRoot);
35320
35712
  let configChanged = false;
35321
35713
  if (dottedProvider !== null && dottedProviderLineIsSignet(lines[dottedProvider] ?? "")) {
35322
35714
  setDottedProviderLine(lines, dottedProvider, backup?.providerKind === "dotted" ? backup.previousProvider : "''");
@@ -35335,9 +35727,9 @@ function restoreOrClearProvider(hermesHome, targetRoot) {
35335
35727
  configChanged = true;
35336
35728
  }
35337
35729
  if (configChanged) {
35338
- writeFileSync2(safeConfigPath, `${lines.join(`
35730
+ writeTargetFile(safeConfigPath, `${lines.join(`
35339
35731
  `).replace(/\n+$/g, "")}
35340
- `);
35732
+ `, targetRoot);
35341
35733
  }
35342
35734
  return {
35343
35735
  configPath: configChanged ? safeConfigPath : null,
@@ -35386,8 +35778,8 @@ function computePluginSourceHash() {
35386
35778
  }
35387
35779
  return hash.digest("hex");
35388
35780
  }
35389
- function writeInstallMarker(targetDir, targetKind) {
35390
- const markerPath = join6(targetDir, INSTALL_MARKER_FILE);
35781
+ function writeInstallMarker(targetDir, targetKind, targetRoot) {
35782
+ const markerPath = resolveContainedWritePath(join6(targetDir, INSTALL_MARKER_FILE), targetRoot);
35391
35783
  const marker = {
35392
35784
  connector: "@signetai/connector-hermes-agent",
35393
35785
  schemaVersion: 1,
@@ -35396,30 +35788,55 @@ function writeInstallMarker(targetDir, targetKind) {
35396
35788
  targetKind,
35397
35789
  installedAt: new Date().toISOString()
35398
35790
  };
35399
- writeFileSync2(markerPath, `${JSON.stringify(marker, null, 2)}
35400
- `);
35791
+ writeTargetFile(markerPath, `${JSON.stringify(marker, null, 2)}
35792
+ `, targetRoot);
35401
35793
  return markerPath;
35402
35794
  }
35795
+ function parseInstallMarker(content) {
35796
+ try {
35797
+ const parsed = JSON.parse(content);
35798
+ if (parsed.connector === "@signetai/connector-hermes-agent" && parsed.schemaVersion === 1 && typeof parsed.connectorVersion === "string" && typeof parsed.sourceHash === "string" && (parsed.targetKind === "user" || parsed.targetKind === "repo") && typeof parsed.installedAt === "string") {
35799
+ return parsed;
35800
+ }
35801
+ } catch {}
35802
+ return null;
35803
+ }
35403
35804
  function readInstallMarker(targetDir) {
35404
35805
  const markerPath = join6(targetDir, INSTALL_MARKER_FILE);
35405
35806
  if (!existsSync2(markerPath))
35406
35807
  return null;
35407
35808
  try {
35408
- const parsed = JSON.parse(readFileSync2(markerPath, "utf-8"));
35409
- if (parsed.connector === "@signetai/connector-hermes-agent" && parsed.schemaVersion === 1 && typeof parsed.connectorVersion === "string" && typeof parsed.sourceHash === "string" && (parsed.targetKind === "user" || parsed.targetKind === "repo") && typeof parsed.installedAt === "string") {
35410
- return parsed;
35411
- }
35809
+ return parseInstallMarker(readFileSync2(markerPath, "utf-8"));
35810
+ } catch {
35412
35811
  return null;
35812
+ }
35813
+ }
35814
+ function readInstallMarkerFromDirectory(directoryFd) {
35815
+ let markerFd;
35816
+ try {
35817
+ markerFd = openSync(join6(descriptorPath(directoryFd), INSTALL_MARKER_FILE), constants.O_RDONLY | constants.O_NOFOLLOW);
35818
+ return parseInstallMarker(readFileSync2(markerFd, "utf-8"));
35413
35819
  } catch {
35414
35820
  return null;
35821
+ } finally {
35822
+ if (markerFd !== undefined)
35823
+ closeDirectory(markerFd);
35415
35824
  }
35416
35825
  }
35417
35826
  function pluginMarkerIsFresh(targetDir) {
35418
35827
  const marker = readInstallMarker(targetDir);
35419
35828
  return marker !== null && marker.sourceHash === computePluginSourceHash();
35420
35829
  }
35421
- function pluginLooksCurrent(targetDir) {
35422
- return pluginHasStaticToolSchemas(join6(targetDir, "__init__.py")) && pluginMarkerIsFresh(targetDir);
35830
+ function pluginLooksCurrent(targetDir, targetRoot) {
35831
+ let safeTargetDir = targetDir;
35832
+ if (targetRoot) {
35833
+ try {
35834
+ safeTargetDir = resolveContainedWritePath(targetDir, targetRoot);
35835
+ } catch {
35836
+ return false;
35837
+ }
35838
+ }
35839
+ return pluginHasStaticToolSchemas(join6(safeTargetDir, "__init__.py")) && pluginMarkerIsFresh(safeTargetDir);
35423
35840
  }
35424
35841
  function probeHermesProvider(hermesRepo) {
35425
35842
  if (!existsSync2(hermesRepo)) {
@@ -35605,9 +36022,9 @@ class HermesAgentConnector extends BaseConnector {
35605
36022
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(this.target.profile)) {
35606
36023
  throw new Error("Invalid Hermes profile name; use 1-64 letters, numbers, '.', '_' or '-'.");
35607
36024
  }
35608
- const userHome2 = process.env.HOME?.trim() || homedir();
35609
- const profileHome = join6(userHome2, ".hermes", "profiles", this.target.profile);
35610
- return resolveContainedWritePath(profileHome, join6(userHome2, ".hermes", "profiles"));
36025
+ const hermesHome = resolveHermesHomePath();
36026
+ const profileHome = join6(hermesHome, "profiles", this.target.profile);
36027
+ return resolveContainedWritePath(profileHome, join6(hermesHome, "profiles"));
35611
36028
  }
35612
36029
  return resolveHermesHomePath();
35613
36030
  }
@@ -35617,7 +36034,8 @@ class HermesAgentConnector extends BaseConnector {
35617
36034
  return resolveHermesRepoPath();
35618
36035
  }
35619
36036
  getConfigPath() {
35620
- return resolveConfigPath(this.getHermesHome());
36037
+ const hermesHome = this.getHermesHome();
36038
+ return resolveConfigPath(hermesHome, this.target?.profile ? hermesHome : undefined);
35621
36039
  }
35622
36040
  async install(basePath, options = {}) {
35623
36041
  const filesWritten = [];
@@ -35630,10 +36048,11 @@ class HermesAgentConnector extends BaseConnector {
35630
36048
  }
35631
36049
  const hermesHome = this.getHermesHome();
35632
36050
  const hermesRepo = this.getHermesRepo();
36051
+ const targetRoot = hermesHome;
35633
36052
  let userPluginInstalled = false;
35634
36053
  let repoPluginInstalled = false;
35635
36054
  try {
35636
- const pluginFiles = installPlugin(getUserPluginTargetDir(hermesHome), "user", options.profile ? hermesHome : undefined);
36055
+ const pluginFiles = installPlugin(getUserPluginTargetDir(hermesHome), "user", targetRoot);
35637
36056
  filesWritten.push(...pluginFiles);
35638
36057
  userPluginInstalled = true;
35639
36058
  } catch (e) {
@@ -35642,7 +36061,7 @@ class HermesAgentConnector extends BaseConnector {
35642
36061
  }
35643
36062
  if (hermesRepo) {
35644
36063
  try {
35645
- const pluginFiles = installPlugin(getRepoPluginTargetDir(hermesRepo), "repo");
36064
+ const pluginFiles = installPlugin(getRepoPluginTargetDir(hermesRepo), "repo", hermesRepo);
35646
36065
  filesWritten.push(...pluginFiles);
35647
36066
  repoPluginInstalled = true;
35648
36067
  } catch (e) {
@@ -35660,7 +36079,7 @@ class HermesAgentConnector extends BaseConnector {
35660
36079
  warnings
35661
36080
  };
35662
36081
  }
35663
- const envPath = options.profile ? resolveContainedWritePath(join6(hermesHome, ".env"), hermesHome) : join6(hermesHome, ".env");
36082
+ const envPath = resolveContainedWritePath(join6(hermesHome, ".env"), targetRoot);
35664
36083
  let configuredSignetAgentId = "default";
35665
36084
  const configuredDaemonUrl = (process.env.SIGNET_DAEMON_URL?.trim() || "http://127.0.0.1:3850").replace(/[\r\n]+/g, "");
35666
36085
  try {
@@ -35722,8 +36141,8 @@ ${key}=${value}
35722
36141
  changed = true;
35723
36142
  }
35724
36143
  if (changed) {
35725
- mkdirSync(hermesHome, { recursive: true });
35726
- writeFileSync2(envPath, envContent);
36144
+ ensureTargetDirectory(hermesHome, targetRoot);
36145
+ writeTargetFile(envPath, envContent, targetRoot);
35727
36146
  configsPatched.push(envPath);
35728
36147
  }
35729
36148
  } catch (e) {
@@ -35734,14 +36153,14 @@ ${key}=${value}
35734
36153
  if (registrationError) {
35735
36154
  return { success: false, message: registrationError, filesWritten, configsPatched, warnings };
35736
36155
  }
35737
- const providerConfig = configureProvider(hermesHome, warnings, options.profile ? hermesHome : undefined);
36156
+ const providerConfig = configureProvider(hermesHome, warnings, targetRoot);
35738
36157
  if (providerConfig.configPath) {
35739
36158
  configsPatched.push(providerConfig.configPath);
35740
36159
  }
35741
36160
  if (providerConfig.backupPath) {
35742
36161
  filesWritten.push(providerConfig.backupPath);
35743
36162
  }
35744
- if (!isProviderConfigured(hermesHome)) {
36163
+ if (!isProviderConfigured(hermesHome, targetRoot)) {
35745
36164
  return {
35746
36165
  success: false,
35747
36166
  message: "Hermes Agent integration incomplete — Signet provider was deployed but not activated in Hermes config",
@@ -35772,24 +36191,27 @@ ${key}=${value}
35772
36191
  const configsPatched = [];
35773
36192
  const hermesRepo = this.getHermesRepo();
35774
36193
  if (hermesRepo) {
35775
- const removed = uninstallPlugin(getRepoPluginTargetDir(hermesRepo));
36194
+ const removed = uninstallPlugin(getRepoPluginTargetDir(hermesRepo), "repo", hermesRepo);
35776
36195
  filesRemoved.push(...removed);
35777
36196
  }
35778
36197
  const hermesHome = this.getHermesHome();
35779
36198
  const userPluginTarget = getUserPluginTargetDir(hermesHome);
35780
- if (this.target?.profile && (!existsSync2(userPluginTarget) || readInstallMarker(userPluginTarget) === null)) {
36199
+ const targetRoot = hermesHome;
36200
+ const safeUserPluginTarget = resolveContainedWritePath(userPluginTarget, targetRoot);
36201
+ const marker = readInstallMarker(safeUserPluginTarget);
36202
+ if (!pathEntryExists(safeUserPluginTarget) || marker === null || marker.targetKind !== "user") {
35781
36203
  return { filesRemoved, configsPatched };
35782
36204
  }
35783
- const userPluginRemoved = uninstallPlugin(userPluginTarget, this.target?.profile ? hermesHome : undefined);
36205
+ const userPluginRemoved = uninstallPlugin(userPluginTarget, "user", targetRoot);
35784
36206
  filesRemoved.push(...userPluginRemoved);
35785
- const providerConfig = restoreOrClearProvider(hermesHome, this.target?.profile ? hermesHome : undefined);
36207
+ const providerConfig = restoreOrClearProvider(hermesHome, targetRoot);
35786
36208
  if (providerConfig.configPath) {
35787
36209
  configsPatched.push(providerConfig.configPath);
35788
36210
  }
35789
36211
  if (providerConfig.backupPath) {
35790
36212
  filesRemoved.push(providerConfig.backupPath);
35791
36213
  }
35792
- const envPath = this.target?.profile ? resolveContainedWritePath(join6(hermesHome, ".env"), hermesHome) : join6(hermesHome, ".env");
36214
+ const envPath = resolveContainedWritePath(join6(hermesHome, ".env"), targetRoot);
35793
36215
  if (existsSync2(envPath)) {
35794
36216
  try {
35795
36217
  let envContent = readFileSync2(envPath, "utf-8");
@@ -35810,10 +36232,10 @@ ${key}=${value}
35810
36232
  }
35811
36233
  }
35812
36234
  if (changed) {
35813
- writeFileSync2(envPath, `${envContent.replace(/\n{3,}/g, `
36235
+ writeTargetFile(envPath, `${envContent.replace(/\n{3,}/g, `
35814
36236
 
35815
36237
  `).trimEnd()}
35816
- `);
36238
+ `, targetRoot);
35817
36239
  configsPatched.push(envPath);
35818
36240
  }
35819
36241
  } catch (e) {
@@ -35825,11 +36247,12 @@ ${key}=${value}
35825
36247
  isInstalled() {
35826
36248
  const hermesHome = this.getHermesHome();
35827
36249
  const hermesRepo = this.getHermesRepo();
35828
- if (!isProviderConfigured(hermesHome))
36250
+ const targetRoot = this.target?.profile ? hermesHome : undefined;
36251
+ if (!isProviderConfigured(hermesHome, targetRoot))
35829
36252
  return false;
35830
36253
  if (hermesRepo)
35831
36254
  return pluginLooksCurrent(getRepoPluginTargetDir(hermesRepo));
35832
- return pluginLooksCurrent(getUserPluginTargetDir(hermesHome));
36255
+ return pluginLooksCurrent(getUserPluginTargetDir(hermesHome), targetRoot);
35833
36256
  }
35834
36257
  async diagnose() {
35835
36258
  const hermesHome = this.getHermesHome();
@@ -35837,6 +36260,7 @@ ${key}=${value}
35837
36260
  return diagnoseHermesIntegration({
35838
36261
  hermesHome,
35839
36262
  hermesRepo,
36263
+ targetRoot: this.target?.profile ? hermesHome : undefined,
35840
36264
  daemonUrl: (process.env.SIGNET_DAEMON_URL?.trim() || "http://127.0.0.1:3850").replace(/[\r\n]+/g, "")
35841
36265
  });
35842
36266
  }
@@ -35844,8 +36268,9 @@ ${key}=${value}
35844
36268
  async function diagnoseHermesIntegration(opts) {
35845
36269
  const hermesHome = opts?.hermesHome ?? resolveHermesHomePath();
35846
36270
  const hermesRepo = opts && "hermesRepo" in opts ? opts.hermesRepo ?? null : resolveHermesRepoPath();
36271
+ const targetRoot = opts?.targetRoot;
35847
36272
  const daemonUrl = opts?.daemonUrl ?? (process.env.SIGNET_DAEMON_URL?.trim() || "http://127.0.0.1:3850");
35848
- const configPath = resolveConfigPath(hermesHome);
36273
+ const configPath = resolveConfigPath(hermesHome, targetRoot);
35849
36274
  const userPluginDir = getUserPluginTargetDir(hermesHome);
35850
36275
  const repoPluginDir = hermesRepo ? getRepoPluginTargetDir(hermesRepo) : null;
35851
36276
  const checks = [];
@@ -35857,7 +36282,7 @@ async function diagnoseHermesIntegration(opts) {
35857
36282
  } catch (error) {
35858
36283
  pluginSourceError = error instanceof Error ? error.message : String(error);
35859
36284
  }
35860
- const userPluginCurrent = pluginSourceDir !== null && pluginLooksCurrent(userPluginDir);
36285
+ const userPluginCurrent = pluginSourceDir !== null && pluginLooksCurrent(userPluginDir, targetRoot);
35861
36286
  const repoPluginCurrent = pluginSourceDir !== null && repoPluginDir ? pluginLooksCurrent(repoPluginDir) : false;
35862
36287
  const probe = hermesRepo ? probeHermesProvider(hermesRepo) : userPluginCurrent ? { ok: true, toolNames: REQUIRED_TOOL_NAMES, error: null } : { ok: false, toolNames: [], error: "Hermes repo not found and user plugin is missing or stale" };
35863
36288
  const daemon = await checkDaemon(daemonUrl);
@@ -35878,9 +36303,9 @@ async function diagnoseHermesIntegration(opts) {
35878
36303
  checks.push({
35879
36304
  id: "provider-config",
35880
36305
  label: "Hermes memory provider",
35881
- ok: isProviderConfigured(hermesHome),
35882
- detail: existsSync2(configPath) ? `${configPath} ${isProviderConfigured(hermesHome) ? "sets" : "does not set"} memory.provider=signet` : `${configPath} does not exist`,
35883
- fix: isProviderConfigured(hermesHome) ? undefined : "Run `signet setup --harness hermes-agent`."
36306
+ ok: isProviderConfigured(hermesHome, targetRoot),
36307
+ detail: existsSync2(configPath) ? `${configPath} ${isProviderConfigured(hermesHome, targetRoot) ? "sets" : "does not set"} memory.provider=signet` : `${configPath} does not exist`,
36308
+ fix: isProviderConfigured(hermesHome, targetRoot) ? undefined : "Run `signet setup --harness hermes-agent`."
35884
36309
  });
35885
36310
  checks.push({
35886
36311
  id: "user-plugin",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signetai/connector-hermes-agent",
3
- "version": "0.226.23",
3
+ "version": "0.226.25",
4
4
  "description": "Signet connector for Hermes Agent — installs Signet as a pluggable memory provider",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,8 +25,8 @@
25
25
  "typecheck": "tsc --noEmit"
26
26
  },
27
27
  "dependencies": {
28
- "@signetai/connector-base": "0.226.23",
29
- "@signetai/core": "0.226.23"
28
+ "@signetai/connector-base": "0.226.25",
29
+ "@signetai/core": "0.226.25"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.0.0",