@signetai/connector-hermes-agent 0.226.20 → 0.226.24
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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +508 -83
- package/package.json +3 -3
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;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
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 {
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
35430
|
+
const writeDir = resolveContainedWritePath(targetDir, targetRoot);
|
|
35040
35431
|
const sourceDir = getPluginSourceDir();
|
|
35041
|
-
|
|
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 =
|
|
35436
|
+
const dst = resolveContainedWritePath(join6(writeDir, file), targetRoot);
|
|
35046
35437
|
if (existsSync2(src)) {
|
|
35047
|
-
|
|
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 (!
|
|
35445
|
+
function uninstallPlugin(targetDir, targetKind, targetRoot) {
|
|
35446
|
+
if (!pathEntryExists(targetDir))
|
|
35056
35447
|
return [];
|
|
35057
|
-
|
|
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 (
|
|
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
|
-
|
|
35197
|
-
|
|
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 =
|
|
35217
|
-
if (!
|
|
35603
|
+
const backupPath = resolveContainedWritePath(getProviderBackupPath(hermesHome), targetRoot);
|
|
35604
|
+
if (!pathEntryExists(backupPath))
|
|
35218
35605
|
return null;
|
|
35219
|
-
|
|
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
|
-
|
|
35280
|
-
|
|
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
|
-
|
|
35302
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35409
|
-
|
|
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
|
-
|
|
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
|
|
35609
|
-
const profileHome = join6(
|
|
35610
|
-
return resolveContainedWritePath(profileHome, join6(
|
|
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
|
-
|
|
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",
|
|
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 =
|
|
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
|
-
|
|
35726
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
36205
|
+
const userPluginRemoved = uninstallPlugin(userPluginTarget, "user", targetRoot);
|
|
35784
36206
|
filesRemoved.push(...userPluginRemoved);
|
|
35785
|
-
const providerConfig = restoreOrClearProvider(hermesHome,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.226.24",
|
|
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.
|
|
29
|
-
"@signetai/core": "0.226.
|
|
28
|
+
"@signetai/connector-base": "0.226.24",
|
|
29
|
+
"@signetai/core": "0.226.24"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|