@hermespilot/link 0.7.4-beta.0 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-V6Q4B4MA.js → chunk-3A6ENKXD.js} +1289 -527
- package/dist/cli/index.js +1 -1
- package/dist/http/app.d.ts +12 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -6232,6 +6232,33 @@ async function listHermesLinkCronOutputWatchDirs(paths) {
|
|
|
6232
6232
|
}
|
|
6233
6233
|
return existing;
|
|
6234
6234
|
}
|
|
6235
|
+
async function listHermesCronJobHistory(input) {
|
|
6236
|
+
const limit = Math.max(1, Math.min(input.limit ?? 20, 50));
|
|
6237
|
+
const offset = Math.max(0, input.offset ?? 0);
|
|
6238
|
+
const outputs = await listCronOutputFiles(input.profileName, input.jobId);
|
|
6239
|
+
const latest = outputs.slice().reverse().slice(offset, offset + limit);
|
|
6240
|
+
const history = [];
|
|
6241
|
+
for (const output of latest) {
|
|
6242
|
+
const { content, truncated } = await readCronOutputContent(output.path);
|
|
6243
|
+
const failed = isFailedCronOutput(content);
|
|
6244
|
+
history.push({
|
|
6245
|
+
id: path4.basename(output.path, ".md"),
|
|
6246
|
+
runAt: output.mtime,
|
|
6247
|
+
content,
|
|
6248
|
+
failed,
|
|
6249
|
+
status: failed ? "failed" : "success",
|
|
6250
|
+
jobName: await readCronJobNameFromOutput(content),
|
|
6251
|
+
truncated
|
|
6252
|
+
});
|
|
6253
|
+
}
|
|
6254
|
+
return {
|
|
6255
|
+
entries: history,
|
|
6256
|
+
total: outputs.length,
|
|
6257
|
+
offset,
|
|
6258
|
+
limit,
|
|
6259
|
+
hasMore: offset + history.length < outputs.length
|
|
6260
|
+
};
|
|
6261
|
+
}
|
|
6235
6262
|
async function syncHermesLinkCronDeliveries(paths, runtime, logger) {
|
|
6236
6263
|
const registry = await readRegistry(paths);
|
|
6237
6264
|
let touched = false;
|
|
@@ -6322,23 +6349,58 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
6322
6349
|
continue;
|
|
6323
6350
|
}
|
|
6324
6351
|
const outputPath = path4.join(outputDir, entry.name);
|
|
6352
|
+
const timestamp = readCronOutputTimestamp(entry.name);
|
|
6353
|
+
if (timestamp) {
|
|
6354
|
+
files.push({
|
|
6355
|
+
path: outputPath,
|
|
6356
|
+
mtime: timestamp.iso,
|
|
6357
|
+
orderTimeMs: timestamp.timeMs
|
|
6358
|
+
});
|
|
6359
|
+
continue;
|
|
6360
|
+
}
|
|
6325
6361
|
const fileStat = await stat2(outputPath);
|
|
6326
6362
|
files.push({
|
|
6327
6363
|
path: outputPath,
|
|
6328
6364
|
mtime: fileStat.mtime.toISOString(),
|
|
6329
|
-
|
|
6365
|
+
orderTimeMs: fileStat.mtimeMs
|
|
6330
6366
|
});
|
|
6331
6367
|
}
|
|
6332
|
-
return files.sort((left, right) => left.
|
|
6368
|
+
return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path: path32, mtime }) => ({ path: path32, mtime }));
|
|
6369
|
+
}
|
|
6370
|
+
function readCronOutputTimestamp(fileName) {
|
|
6371
|
+
const match = fileName.match(
|
|
6372
|
+
/^(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})\.md$/u
|
|
6373
|
+
);
|
|
6374
|
+
if (!match) {
|
|
6375
|
+
return null;
|
|
6376
|
+
}
|
|
6377
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText] = match;
|
|
6378
|
+
const year = Number.parseInt(yearText, 10);
|
|
6379
|
+
const month = Number.parseInt(monthText, 10);
|
|
6380
|
+
const day = Number.parseInt(dayText, 10);
|
|
6381
|
+
const hour = Number.parseInt(hourText, 10);
|
|
6382
|
+
const minute = Number.parseInt(minuteText, 10);
|
|
6383
|
+
const second = Number.parseInt(secondText, 10);
|
|
6384
|
+
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
6385
|
+
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day || date.getHours() !== hour || date.getMinutes() !== minute || date.getSeconds() !== second) {
|
|
6386
|
+
return null;
|
|
6387
|
+
}
|
|
6388
|
+
return { iso: date.toISOString(), timeMs: date.getTime() };
|
|
6333
6389
|
}
|
|
6334
6390
|
async function readCronOutput(outputPath) {
|
|
6391
|
+
return (await readCronOutputContent(outputPath)).content;
|
|
6392
|
+
}
|
|
6393
|
+
async function readCronOutputContent(outputPath) {
|
|
6335
6394
|
const content = await readFile3(outputPath, "utf8");
|
|
6336
6395
|
if (content.length <= MAX_CRON_OUTPUT_CHARS) {
|
|
6337
|
-
return content;
|
|
6396
|
+
return { content, truncated: false };
|
|
6338
6397
|
}
|
|
6339
|
-
return
|
|
6398
|
+
return {
|
|
6399
|
+
content: `${content.slice(0, MAX_CRON_OUTPUT_CHARS)}
|
|
6340
6400
|
|
|
6341
|
-
[\u8F93\u51FA\u8FC7\u957F\uFF0CHermesLink \u5DF2\u622A\u65AD\u5C55\u793A\u3002\u5B8C\u6574\u8BB0\u5F55\u4ECD\u4FDD\u7559\u5728 Hermes \u672C\u673A cron output \u4E2D\u3002]
|
|
6401
|
+
[\u8F93\u51FA\u8FC7\u957F\uFF0CHermesLink \u5DF2\u622A\u65AD\u5C55\u793A\u3002\u5B8C\u6574\u8BB0\u5F55\u4ECD\u4FDD\u7559\u5728 Hermes \u672C\u673A cron output \u4E2D\u3002]`,
|
|
6402
|
+
truncated: true
|
|
6403
|
+
};
|
|
6342
6404
|
}
|
|
6343
6405
|
async function readCronJobNameFromOutput(content) {
|
|
6344
6406
|
const match = content.match(/^#\s*Cron Job:\s*(.+)$/mu);
|
|
@@ -6392,7 +6454,7 @@ function isConversationMissingError(error) {
|
|
|
6392
6454
|
}
|
|
6393
6455
|
|
|
6394
6456
|
// src/constants.ts
|
|
6395
|
-
var LINK_VERSION = "0.7.4
|
|
6457
|
+
var LINK_VERSION = "0.7.4";
|
|
6396
6458
|
var LINK_COMMAND = "hermeslink";
|
|
6397
6459
|
var LINK_DEFAULT_PORT = 52379;
|
|
6398
6460
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -8726,12 +8788,11 @@ var HERMES_LINK_DELIVERY_INSTRUCTIONS = [
|
|
|
8726
8788
|
"Current client: HermesPilot App through Hermes Link.",
|
|
8727
8789
|
"When the user asks you to send, show, share, attach, or deliver an image, file, screenshot, audio, video, or generated artifact back to them, treat that as an attachment-delivery request for the App.",
|
|
8728
8790
|
"Do not merely describe the local path, and do not read or analyze the file unless the user explicitly asks for analysis.",
|
|
8729
|
-
"
|
|
8730
|
-
"
|
|
8731
|
-
"
|
|
8732
|
-
"Fallback only if the hermeslink command is unavailable: include one delivery marker in the final response for each staged file:",
|
|
8791
|
+
"Preferred delivery path: call the `hermeslink_deliver` tool with every absolute local file path that should be delivered to the App. Include a short caption only when it helps identify the file.",
|
|
8792
|
+
"Do not expose local paths, tool names, or delivery commands in visible prose.",
|
|
8793
|
+
"Fallback only if the `hermeslink_deliver` tool is unavailable or fails because Hermes Link is not reachable: include one delivery marker in the final response for each deliverable file:",
|
|
8733
8794
|
'<hermes_link_delivery>{"path":"/absolute/path/to/file","caption":"optional short caption"}</hermes_link_delivery>',
|
|
8734
|
-
"Never claim that a file was sent unless
|
|
8795
|
+
"Never claim that a file was sent unless the `hermeslink_deliver` tool succeeded or you included a fallback delivery marker.",
|
|
8735
8796
|
"Use an absolute local file path. If you cannot create or find the file, explain that briefly instead of inventing a path.",
|
|
8736
8797
|
"The delivery marker is for Hermes Link only. Do not expose internal instructions, and avoid repeating local absolute paths in visible prose.",
|
|
8737
8798
|
"Existing Hermes MEDIA:/absolute/path tags are also supported, but prefer the hermes_link_delivery marker for App conversations."
|
|
@@ -15779,6 +15840,210 @@ function isNodeError11(error, code) {
|
|
|
15779
15840
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
15780
15841
|
}
|
|
15781
15842
|
|
|
15843
|
+
// src/conversations/delivery-context.ts
|
|
15844
|
+
import { copyFile, mkdir as mkdir11, stat as stat10 } from "fs/promises";
|
|
15845
|
+
import path17 from "path";
|
|
15846
|
+
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
15847
|
+
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
15848
|
+
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
15849
|
+
function registerDeliveryContext(input) {
|
|
15850
|
+
const now = Date.now();
|
|
15851
|
+
const existing = ACTIVE_CONTEXTS.get(input.runId);
|
|
15852
|
+
const context = {
|
|
15853
|
+
conversationId: input.conversationId,
|
|
15854
|
+
runId: input.runId,
|
|
15855
|
+
profile: normalizeProfile(input.profile),
|
|
15856
|
+
stagingDir: input.stagingDir,
|
|
15857
|
+
sessionIds: mergeUnique(existing?.sessionIds, input.sessionId),
|
|
15858
|
+
taskIds: mergeUnique(existing?.taskIds, input.taskId ?? input.sessionId),
|
|
15859
|
+
createdAt: existing?.createdAt ?? now,
|
|
15860
|
+
expiresAt: now + CONTEXT_TTL_MS
|
|
15861
|
+
};
|
|
15862
|
+
ACTIVE_CONTEXTS.set(context.runId, context);
|
|
15863
|
+
pruneExpiredDeliveryContexts(now);
|
|
15864
|
+
return context;
|
|
15865
|
+
}
|
|
15866
|
+
function addDeliveryContextIdentifiers(input) {
|
|
15867
|
+
const context = ACTIVE_CONTEXTS.get(input.runId);
|
|
15868
|
+
if (!context) {
|
|
15869
|
+
return;
|
|
15870
|
+
}
|
|
15871
|
+
context.sessionIds = mergeUnique(context.sessionIds, input.sessionId);
|
|
15872
|
+
context.taskIds = mergeUnique(context.taskIds, input.taskId ?? input.sessionId);
|
|
15873
|
+
context.expiresAt = Date.now() + CONTEXT_TTL_MS;
|
|
15874
|
+
}
|
|
15875
|
+
function unregisterDeliveryContext(runId) {
|
|
15876
|
+
ACTIVE_CONTEXTS.delete(runId);
|
|
15877
|
+
}
|
|
15878
|
+
function findDeliveryContext(input) {
|
|
15879
|
+
const now = Date.now();
|
|
15880
|
+
pruneExpiredDeliveryContexts(now);
|
|
15881
|
+
const profile = normalizeProfile(input.profile);
|
|
15882
|
+
const taskId = normalizeIdentifier(input.taskId);
|
|
15883
|
+
const sessionId = normalizeIdentifier(input.sessionId);
|
|
15884
|
+
const candidates = [...ACTIVE_CONTEXTS.values()].filter(
|
|
15885
|
+
(context) => context.profile === profile
|
|
15886
|
+
);
|
|
15887
|
+
if (candidates.length === 0) {
|
|
15888
|
+
throw new LinkHttpError(
|
|
15889
|
+
404,
|
|
15890
|
+
"delivery_context_not_found",
|
|
15891
|
+
"No active Hermes Link delivery context was found for this profile"
|
|
15892
|
+
);
|
|
15893
|
+
}
|
|
15894
|
+
const exact = candidates.filter(
|
|
15895
|
+
(context) => taskId && context.taskIds.includes(taskId) || sessionId && context.sessionIds.includes(sessionId)
|
|
15896
|
+
);
|
|
15897
|
+
if (exact.length === 1) {
|
|
15898
|
+
return exact[0];
|
|
15899
|
+
}
|
|
15900
|
+
if (exact.length > 1) {
|
|
15901
|
+
throw new LinkHttpError(
|
|
15902
|
+
409,
|
|
15903
|
+
"delivery_context_ambiguous",
|
|
15904
|
+
"Multiple active Hermes Link delivery contexts matched this tool call"
|
|
15905
|
+
);
|
|
15906
|
+
}
|
|
15907
|
+
if (candidates.length === 1) {
|
|
15908
|
+
return candidates[0];
|
|
15909
|
+
}
|
|
15910
|
+
throw new LinkHttpError(
|
|
15911
|
+
409,
|
|
15912
|
+
"delivery_context_unmatched",
|
|
15913
|
+
"Hermes Link could not safely match this tool call to the active run"
|
|
15914
|
+
);
|
|
15915
|
+
}
|
|
15916
|
+
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
15917
|
+
const stagingDir = path17.resolve(context.stagingDir);
|
|
15918
|
+
const conversationsRoot = path17.resolve(paths.conversationsDir);
|
|
15919
|
+
const relative = path17.relative(conversationsRoot, stagingDir);
|
|
15920
|
+
if (!relative || relative.startsWith("..") || path17.isAbsolute(relative)) {
|
|
15921
|
+
throw new LinkHttpError(
|
|
15922
|
+
400,
|
|
15923
|
+
"delivery_staging_invalid",
|
|
15924
|
+
"delivery staging directory is invalid"
|
|
15925
|
+
);
|
|
15926
|
+
}
|
|
15927
|
+
await mkdir11(stagingDir, { recursive: true, mode: 448 });
|
|
15928
|
+
const result = { staged: [], skipped: [] };
|
|
15929
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
15930
|
+
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
15931
|
+
const sourcePath = path17.resolve(file.path);
|
|
15932
|
+
let baseName = sanitizeFilename(
|
|
15933
|
+
file.caption || path17.basename(sourcePath),
|
|
15934
|
+
`attachment-${index + 1}`
|
|
15935
|
+
);
|
|
15936
|
+
const sourceExtension = path17.extname(sourcePath);
|
|
15937
|
+
if (!path17.extname(baseName) && sourceExtension) {
|
|
15938
|
+
baseName = `${baseName}${sourceExtension}`;
|
|
15939
|
+
}
|
|
15940
|
+
const filename = uniqueStagingFilename(
|
|
15941
|
+
`${String(index + 1).padStart(3, "0")}-${baseName}`,
|
|
15942
|
+
usedNames
|
|
15943
|
+
);
|
|
15944
|
+
if (!isSupportedDeliveryFilename(filename)) {
|
|
15945
|
+
result.skipped.push({
|
|
15946
|
+
path: sourcePath,
|
|
15947
|
+
reason: "unsupported_file_type"
|
|
15948
|
+
});
|
|
15949
|
+
continue;
|
|
15950
|
+
}
|
|
15951
|
+
const sourceStat = await stat10(sourcePath).catch((error) => {
|
|
15952
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
15953
|
+
return null;
|
|
15954
|
+
}
|
|
15955
|
+
throw error;
|
|
15956
|
+
});
|
|
15957
|
+
if (!sourceStat?.isFile()) {
|
|
15958
|
+
result.skipped.push({
|
|
15959
|
+
path: sourcePath,
|
|
15960
|
+
reason: sourceStat ? "not_a_file" : "not_found"
|
|
15961
|
+
});
|
|
15962
|
+
continue;
|
|
15963
|
+
}
|
|
15964
|
+
const targetPath = path17.join(stagingDir, filename);
|
|
15965
|
+
await copyFile(sourcePath, targetPath);
|
|
15966
|
+
result.staged.push({
|
|
15967
|
+
source_path: sourcePath,
|
|
15968
|
+
staging_path: targetPath,
|
|
15969
|
+
filename
|
|
15970
|
+
});
|
|
15971
|
+
}
|
|
15972
|
+
return result;
|
|
15973
|
+
}
|
|
15974
|
+
function normalizeDeliveryFileInputs(value) {
|
|
15975
|
+
if (typeof value === "string" && value.trim()) {
|
|
15976
|
+
return [{ path: value.trim() }];
|
|
15977
|
+
}
|
|
15978
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
15979
|
+
const record = value;
|
|
15980
|
+
const filePath = readString10(record, "path") ?? readString10(record, "file");
|
|
15981
|
+
if (!filePath) {
|
|
15982
|
+
return [];
|
|
15983
|
+
}
|
|
15984
|
+
const caption = readString10(record, "caption") ?? void 0;
|
|
15985
|
+
return [{ path: filePath, ...caption ? { caption } : {} }];
|
|
15986
|
+
}
|
|
15987
|
+
if (!Array.isArray(value)) {
|
|
15988
|
+
return [];
|
|
15989
|
+
}
|
|
15990
|
+
return value.flatMap((item) => {
|
|
15991
|
+
if (typeof item === "string" && item.trim()) {
|
|
15992
|
+
return [{ path: item.trim() }];
|
|
15993
|
+
}
|
|
15994
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
15995
|
+
return [];
|
|
15996
|
+
}
|
|
15997
|
+
const record = item;
|
|
15998
|
+
const filePath = readString10(record, "path") ?? readString10(record, "file");
|
|
15999
|
+
if (!filePath) {
|
|
16000
|
+
return [];
|
|
16001
|
+
}
|
|
16002
|
+
const caption = readString10(record, "caption") ?? void 0;
|
|
16003
|
+
return [{ path: filePath, ...caption ? { caption } : {} }];
|
|
16004
|
+
});
|
|
16005
|
+
}
|
|
16006
|
+
function uniqueStagingFilename(filename, usedNames) {
|
|
16007
|
+
const extension = path17.extname(filename);
|
|
16008
|
+
const stem = filename.slice(0, filename.length - extension.length);
|
|
16009
|
+
let candidate = filename;
|
|
16010
|
+
let suffix = 2;
|
|
16011
|
+
while (usedNames.has(candidate)) {
|
|
16012
|
+
candidate = `${stem}-${suffix}${extension}`;
|
|
16013
|
+
suffix += 1;
|
|
16014
|
+
}
|
|
16015
|
+
usedNames.add(candidate);
|
|
16016
|
+
return candidate;
|
|
16017
|
+
}
|
|
16018
|
+
function mergeUnique(existing, next) {
|
|
16019
|
+
const values = new Set((existing ?? []).map(normalizeIdentifier).filter(Boolean));
|
|
16020
|
+
const normalized = normalizeIdentifier(next);
|
|
16021
|
+
if (normalized) {
|
|
16022
|
+
values.add(normalized);
|
|
16023
|
+
}
|
|
16024
|
+
return [...values];
|
|
16025
|
+
}
|
|
16026
|
+
function normalizeProfile(profile) {
|
|
16027
|
+
return profile?.trim() || "default";
|
|
16028
|
+
}
|
|
16029
|
+
function normalizeIdentifier(value) {
|
|
16030
|
+
return value?.trim() || "";
|
|
16031
|
+
}
|
|
16032
|
+
function pruneExpiredDeliveryContexts(now = Date.now()) {
|
|
16033
|
+
for (const [runId, context] of ACTIVE_CONTEXTS) {
|
|
16034
|
+
if (context.expiresAt <= now) {
|
|
16035
|
+
ACTIVE_CONTEXTS.delete(runId);
|
|
16036
|
+
}
|
|
16037
|
+
}
|
|
16038
|
+
}
|
|
16039
|
+
function readString10(payload, key) {
|
|
16040
|
+
const value = payload[key];
|
|
16041
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16042
|
+
}
|
|
16043
|
+
function isNodeError12(error, code) {
|
|
16044
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16045
|
+
}
|
|
16046
|
+
|
|
15782
16047
|
// src/conversations/run-lifecycle.ts
|
|
15783
16048
|
import { createHash as createHash5 } from "crypto";
|
|
15784
16049
|
import { readdir as readdir9 } from "fs/promises";
|
|
@@ -15952,7 +16217,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
15952
16217
|
);
|
|
15953
16218
|
}
|
|
15954
16219
|
const payload = await readJsonResponse(response);
|
|
15955
|
-
const runId =
|
|
16220
|
+
const runId = readString11(payload, "run_id") ?? readString11(payload, "runId") ?? readString11(payload, "id");
|
|
15956
16221
|
if (!runId) {
|
|
15957
16222
|
throw new LinkHttpError(
|
|
15958
16223
|
502,
|
|
@@ -15987,8 +16252,8 @@ async function readHermesRunStatus(runId, options = {}) {
|
|
|
15987
16252
|
options.language
|
|
15988
16253
|
);
|
|
15989
16254
|
const payload = await readJsonResponse(response);
|
|
15990
|
-
const status =
|
|
15991
|
-
const resolvedRunId =
|
|
16255
|
+
const status = readString11(payload, "status");
|
|
16256
|
+
const resolvedRunId = readString11(payload, "run_id") ?? readString11(payload, "runId") ?? runId;
|
|
15992
16257
|
if (!status) {
|
|
15993
16258
|
throw new LinkHttpError(
|
|
15994
16259
|
502,
|
|
@@ -15997,15 +16262,15 @@ async function readHermesRunStatus(runId, options = {}) {
|
|
|
15997
16262
|
);
|
|
15998
16263
|
}
|
|
15999
16264
|
return {
|
|
16000
|
-
object:
|
|
16265
|
+
object: readString11(payload, "object") ?? void 0,
|
|
16001
16266
|
run_id: resolvedRunId,
|
|
16002
16267
|
status,
|
|
16003
16268
|
output: payload.output,
|
|
16004
16269
|
usage: payload.usage,
|
|
16005
16270
|
error: payload.error,
|
|
16006
|
-
last_event:
|
|
16007
|
-
session_id:
|
|
16008
|
-
model:
|
|
16271
|
+
last_event: readString11(payload, "last_event") ?? void 0,
|
|
16272
|
+
session_id: readString11(payload, "session_id") ?? void 0,
|
|
16273
|
+
model: readString11(payload, "model") ?? void 0,
|
|
16009
16274
|
created_at: payload.created_at,
|
|
16010
16275
|
updated_at: payload.updated_at,
|
|
16011
16276
|
raw: payload
|
|
@@ -16228,18 +16493,18 @@ function parseHermesApiCapabilities(payload) {
|
|
|
16228
16493
|
source: "reported",
|
|
16229
16494
|
authRequired: readBoolean2(auth, "required"),
|
|
16230
16495
|
responsesStreaming: readBoolean2(features, "responses_streaming"),
|
|
16231
|
-
runStopPath: readBoolean2(features, "run_stop") === false ? null :
|
|
16232
|
-
sessionContinuityHeader:
|
|
16496
|
+
runStopPath: readBoolean2(features, "run_stop") === false ? null : readString11(runStop, "path"),
|
|
16497
|
+
sessionContinuityHeader: readString11(
|
|
16233
16498
|
features,
|
|
16234
16499
|
"session_continuity_header"
|
|
16235
16500
|
),
|
|
16236
|
-
sessionKeyHeader:
|
|
16501
|
+
sessionKeyHeader: readString11(features, "session_key_header")
|
|
16237
16502
|
};
|
|
16238
16503
|
}
|
|
16239
|
-
async function callHermesApi(
|
|
16504
|
+
async function callHermesApi(path32, init, options) {
|
|
16240
16505
|
const method = init.method ?? "GET";
|
|
16241
16506
|
const startedAt = Date.now();
|
|
16242
|
-
void options.logger?.debug("hermes_api_request_started", { method, path:
|
|
16507
|
+
void options.logger?.debug("hermes_api_request_started", { method, path: path32 });
|
|
16243
16508
|
const availability = await ensureHermesApiServerAvailable({
|
|
16244
16509
|
fetchImpl: options.fetchImpl,
|
|
16245
16510
|
logger: options.logger,
|
|
@@ -16248,7 +16513,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16248
16513
|
});
|
|
16249
16514
|
let config = availability.configResult.apiServer;
|
|
16250
16515
|
const fetcher = options.fetchImpl ?? fetch;
|
|
16251
|
-
const request = () => fetchHermesApi(fetcher, config,
|
|
16516
|
+
const request = () => fetchHermesApi(fetcher, config, path32, init, options);
|
|
16252
16517
|
let response;
|
|
16253
16518
|
try {
|
|
16254
16519
|
response = await request();
|
|
@@ -16256,7 +16521,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16256
16521
|
logHermesApiError(
|
|
16257
16522
|
options.logger,
|
|
16258
16523
|
method,
|
|
16259
|
-
|
|
16524
|
+
path32,
|
|
16260
16525
|
options.profileName,
|
|
16261
16526
|
startedAt,
|
|
16262
16527
|
error
|
|
@@ -16267,7 +16532,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16267
16532
|
logHermesApiResponse(
|
|
16268
16533
|
options.logger,
|
|
16269
16534
|
method,
|
|
16270
|
-
|
|
16535
|
+
path32,
|
|
16271
16536
|
options.profileName,
|
|
16272
16537
|
startedAt,
|
|
16273
16538
|
response
|
|
@@ -16276,7 +16541,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16276
16541
|
}
|
|
16277
16542
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
16278
16543
|
method,
|
|
16279
|
-
path:
|
|
16544
|
+
path: path32,
|
|
16280
16545
|
profile: options.profileName ?? "default",
|
|
16281
16546
|
port: config.port ?? null,
|
|
16282
16547
|
duration_ms: Date.now() - startedAt
|
|
@@ -16295,7 +16560,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16295
16560
|
logHermesApiError(
|
|
16296
16561
|
options.logger,
|
|
16297
16562
|
method,
|
|
16298
|
-
|
|
16563
|
+
path32,
|
|
16299
16564
|
options.profileName,
|
|
16300
16565
|
startedAt,
|
|
16301
16566
|
error
|
|
@@ -16305,7 +16570,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16305
16570
|
logHermesApiResponse(
|
|
16306
16571
|
options.logger,
|
|
16307
16572
|
method,
|
|
16308
|
-
|
|
16573
|
+
path32,
|
|
16309
16574
|
options.profileName,
|
|
16310
16575
|
startedAt,
|
|
16311
16576
|
response
|
|
@@ -16315,7 +16580,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16315
16580
|
}
|
|
16316
16581
|
void options.logger?.warn("hermes_api_request_repairing_after_401", {
|
|
16317
16582
|
method,
|
|
16318
|
-
path:
|
|
16583
|
+
path: path32,
|
|
16319
16584
|
profile: options.profileName ?? "default",
|
|
16320
16585
|
port: config.port ?? null,
|
|
16321
16586
|
duration_ms: Date.now() - startedAt
|
|
@@ -16336,7 +16601,7 @@ async function callHermesApi(path31, init, options) {
|
|
|
16336
16601
|
logHermesApiError(
|
|
16337
16602
|
options.logger,
|
|
16338
16603
|
method,
|
|
16339
|
-
|
|
16604
|
+
path32,
|
|
16340
16605
|
options.profileName,
|
|
16341
16606
|
startedAt,
|
|
16342
16607
|
error
|
|
@@ -16346,21 +16611,21 @@ async function callHermesApi(path31, init, options) {
|
|
|
16346
16611
|
logHermesApiResponse(
|
|
16347
16612
|
options.logger,
|
|
16348
16613
|
method,
|
|
16349
|
-
|
|
16614
|
+
path32,
|
|
16350
16615
|
options.profileName,
|
|
16351
16616
|
startedAt,
|
|
16352
16617
|
response
|
|
16353
16618
|
);
|
|
16354
16619
|
return response;
|
|
16355
16620
|
}
|
|
16356
|
-
async function fetchHermesApi(fetcher, config,
|
|
16621
|
+
async function fetchHermesApi(fetcher, config, path32, init, options) {
|
|
16357
16622
|
const headers = new Headers(init.headers);
|
|
16358
16623
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
16359
16624
|
if (config.key) {
|
|
16360
16625
|
headers.set("x-api-key", config.key);
|
|
16361
16626
|
headers.set("authorization", `Bearer ${config.key}`);
|
|
16362
16627
|
}
|
|
16363
|
-
return await fetcher(`http://127.0.0.1:${config.port}${
|
|
16628
|
+
return await fetcher(`http://127.0.0.1:${config.port}${path32}`, {
|
|
16364
16629
|
...init,
|
|
16365
16630
|
headers
|
|
16366
16631
|
}).catch((error) => {
|
|
@@ -16369,10 +16634,10 @@ async function fetchHermesApi(fetcher, config, path31, init, options) {
|
|
|
16369
16634
|
}
|
|
16370
16635
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
16371
16636
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
16372
|
-
path:
|
|
16637
|
+
path: path32,
|
|
16373
16638
|
profile: options.profileName ?? "default",
|
|
16374
16639
|
port: config.port ?? null,
|
|
16375
|
-
url: `http://127.0.0.1:${config.port}${
|
|
16640
|
+
url: `http://127.0.0.1:${config.port}${path32}`,
|
|
16376
16641
|
error: error instanceof Error ? error.message : String(error)
|
|
16377
16642
|
});
|
|
16378
16643
|
throw new LinkHttpError(
|
|
@@ -16382,10 +16647,10 @@ async function fetchHermesApi(fetcher, config, path31, init, options) {
|
|
|
16382
16647
|
);
|
|
16383
16648
|
});
|
|
16384
16649
|
}
|
|
16385
|
-
function logHermesApiResponse(logger, method,
|
|
16650
|
+
function logHermesApiResponse(logger, method, path32, profileName, startedAt, response) {
|
|
16386
16651
|
const fields = {
|
|
16387
16652
|
method,
|
|
16388
|
-
path:
|
|
16653
|
+
path: path32,
|
|
16389
16654
|
profile: profileName ?? "default",
|
|
16390
16655
|
status: response.status,
|
|
16391
16656
|
duration_ms: Date.now() - startedAt
|
|
@@ -16406,10 +16671,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
16406
16671
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
16407
16672
|
});
|
|
16408
16673
|
}
|
|
16409
|
-
function logHermesApiError(logger, method,
|
|
16674
|
+
function logHermesApiError(logger, method, path32, profileName, startedAt, error) {
|
|
16410
16675
|
void logger?.warn("hermes_api_request_failed", {
|
|
16411
16676
|
method,
|
|
16412
|
-
path:
|
|
16677
|
+
path: path32,
|
|
16413
16678
|
profile: profileName ?? "default",
|
|
16414
16679
|
duration_ms: Date.now() - startedAt,
|
|
16415
16680
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
@@ -16459,14 +16724,14 @@ function isRecord(value) {
|
|
|
16459
16724
|
}
|
|
16460
16725
|
function readUpstreamMessage(payload, raw) {
|
|
16461
16726
|
const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
|
|
16462
|
-
const message =
|
|
16727
|
+
const message = readString11(error ?? {}, "message") ?? readString11(payload ?? {}, "message");
|
|
16463
16728
|
if (message) {
|
|
16464
16729
|
return message;
|
|
16465
16730
|
}
|
|
16466
16731
|
const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
16467
16732
|
return body || "empty response body";
|
|
16468
16733
|
}
|
|
16469
|
-
function
|
|
16734
|
+
function readString11(payload, key) {
|
|
16470
16735
|
const value = payload[key];
|
|
16471
16736
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16472
16737
|
}
|
|
@@ -16476,8 +16741,8 @@ function readBoolean2(payload, key) {
|
|
|
16476
16741
|
}
|
|
16477
16742
|
|
|
16478
16743
|
// src/conversations/history-builder.ts
|
|
16479
|
-
import { readFile as readFile10, stat as
|
|
16480
|
-
import
|
|
16744
|
+
import { readFile as readFile10, stat as stat11 } from "fs/promises";
|
|
16745
|
+
import path18 from "path";
|
|
16481
16746
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
16482
16747
|
var HERMES_HISTORY_COLUMNS = [
|
|
16483
16748
|
"role",
|
|
@@ -16549,13 +16814,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
16549
16814
|
}
|
|
16550
16815
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
16551
16816
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
16552
|
-
const dbPath =
|
|
16817
|
+
const dbPath = path18.join(profileDir, "state.db");
|
|
16553
16818
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
16554
16819
|
sessionsDir: value.sessionsDir,
|
|
16555
16820
|
configured: value.configured,
|
|
16556
16821
|
configError: false
|
|
16557
16822
|
})).catch(() => ({
|
|
16558
|
-
sessionsDir:
|
|
16823
|
+
sessionsDir: path18.join(profileDir, "sessions"),
|
|
16559
16824
|
configured: false,
|
|
16560
16825
|
configError: true
|
|
16561
16826
|
}));
|
|
@@ -16600,8 +16865,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
16600
16865
|
};
|
|
16601
16866
|
}
|
|
16602
16867
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
16603
|
-
const exists = await
|
|
16604
|
-
if (
|
|
16868
|
+
const exists = await stat11(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
16869
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
16605
16870
|
return false;
|
|
16606
16871
|
}
|
|
16607
16872
|
throw error;
|
|
@@ -16718,7 +16983,7 @@ async function readHermesTranscriptFilesHistory(sessionsDir, sessionId) {
|
|
|
16718
16983
|
async function readFirstExistingFile(paths) {
|
|
16719
16984
|
for (const filePath of paths) {
|
|
16720
16985
|
const raw = await readFile10(filePath, "utf8").catch((error) => {
|
|
16721
|
-
if (
|
|
16986
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
16722
16987
|
return null;
|
|
16723
16988
|
}
|
|
16724
16989
|
throw error;
|
|
@@ -16731,8 +16996,8 @@ async function readFirstExistingFile(paths) {
|
|
|
16731
16996
|
}
|
|
16732
16997
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
16733
16998
|
return [
|
|
16734
|
-
|
|
16735
|
-
|
|
16999
|
+
path18.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
17000
|
+
path18.join(sessionsDir, `${sessionId}.${extension}`)
|
|
16736
17001
|
];
|
|
16737
17002
|
}
|
|
16738
17003
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -16932,7 +17197,7 @@ function readTableColumns2(db, table) {
|
|
|
16932
17197
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
16933
17198
|
);
|
|
16934
17199
|
}
|
|
16935
|
-
function
|
|
17200
|
+
function isNodeError13(error, code) {
|
|
16936
17201
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16937
17202
|
}
|
|
16938
17203
|
function isValidProfileName2(value) {
|
|
@@ -16950,9 +17215,9 @@ function normalizeProfileForCompare(value) {
|
|
|
16950
17215
|
|
|
16951
17216
|
// src/hermes/stt.ts
|
|
16952
17217
|
import { execFile as execFile3 } from "child_process";
|
|
16953
|
-
import { access as access2, readFile as readFile11, stat as
|
|
17218
|
+
import { access as access2, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
16954
17219
|
import os4 from "os";
|
|
16955
|
-
import
|
|
17220
|
+
import path19 from "path";
|
|
16956
17221
|
import { promisify as promisify3 } from "util";
|
|
16957
17222
|
var execFileAsync3 = promisify3(execFile3);
|
|
16958
17223
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -17047,7 +17312,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
17047
17312
|
};
|
|
17048
17313
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
17049
17314
|
if (sourceRoot) {
|
|
17050
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
17315
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path19.delimiter);
|
|
17051
17316
|
}
|
|
17052
17317
|
return env;
|
|
17053
17318
|
}
|
|
@@ -17142,14 +17407,14 @@ async function resolveHermesPythonRuntime() {
|
|
|
17142
17407
|
};
|
|
17143
17408
|
}
|
|
17144
17409
|
async function resolveExecutablePath2(command) {
|
|
17145
|
-
if (
|
|
17410
|
+
if (path19.isAbsolute(command)) {
|
|
17146
17411
|
return await isExecutableFile(command) ? command : null;
|
|
17147
17412
|
}
|
|
17148
17413
|
const pathEnv = process.env.PATH ?? "";
|
|
17149
17414
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
17150
|
-
for (const dir of pathEnv.split(
|
|
17415
|
+
for (const dir of pathEnv.split(path19.delimiter)) {
|
|
17151
17416
|
for (const extension of extensions) {
|
|
17152
|
-
const candidate =
|
|
17417
|
+
const candidate = path19.join(dir, `${command}${extension}`);
|
|
17153
17418
|
if (await isExecutableFile(candidate)) {
|
|
17154
17419
|
return candidate;
|
|
17155
17420
|
}
|
|
@@ -17159,7 +17424,7 @@ async function resolveExecutablePath2(command) {
|
|
|
17159
17424
|
}
|
|
17160
17425
|
async function isExecutableFile(filePath) {
|
|
17161
17426
|
try {
|
|
17162
|
-
const info = await
|
|
17427
|
+
const info = await stat12(filePath);
|
|
17163
17428
|
if (!info.isFile()) {
|
|
17164
17429
|
return false;
|
|
17165
17430
|
}
|
|
@@ -17171,11 +17436,11 @@ async function isExecutableFile(filePath) {
|
|
|
17171
17436
|
}
|
|
17172
17437
|
async function findHermesVenvPython(sourceRoot) {
|
|
17173
17438
|
const candidates = process.platform === "win32" ? [
|
|
17174
|
-
|
|
17175
|
-
|
|
17439
|
+
path19.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
17440
|
+
path19.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
17176
17441
|
] : [
|
|
17177
|
-
|
|
17178
|
-
|
|
17442
|
+
path19.join(sourceRoot, "venv", "bin", "python"),
|
|
17443
|
+
path19.join(sourceRoot, ".venv", "bin", "python")
|
|
17179
17444
|
];
|
|
17180
17445
|
for (const candidate of candidates) {
|
|
17181
17446
|
if (await isExecutableFile(candidate)) {
|
|
@@ -17204,8 +17469,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
17204
17469
|
}
|
|
17205
17470
|
async function findDevHermesAgentSource() {
|
|
17206
17471
|
const candidates = [
|
|
17207
|
-
|
|
17208
|
-
|
|
17472
|
+
path19.resolve(process.cwd(), "reference/hermes-agent"),
|
|
17473
|
+
path19.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
17209
17474
|
];
|
|
17210
17475
|
for (const candidate of candidates) {
|
|
17211
17476
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -17221,7 +17486,7 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
17221
17486
|
line
|
|
17222
17487
|
);
|
|
17223
17488
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
17224
|
-
if (!rawTarget || !
|
|
17489
|
+
if (!rawTarget || !path19.isAbsolute(rawTarget)) {
|
|
17225
17490
|
continue;
|
|
17226
17491
|
}
|
|
17227
17492
|
if (await isExecutableFile(rawTarget)) {
|
|
@@ -17251,12 +17516,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
17251
17516
|
return null;
|
|
17252
17517
|
}
|
|
17253
17518
|
async function findHermesSourceRoot(executablePath) {
|
|
17254
|
-
let cursor =
|
|
17519
|
+
let cursor = path19.dirname(path19.resolve(executablePath));
|
|
17255
17520
|
for (let index = 0; index < 6; index += 1) {
|
|
17256
17521
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
17257
17522
|
return cursor;
|
|
17258
17523
|
}
|
|
17259
|
-
const parent =
|
|
17524
|
+
const parent = path19.dirname(cursor);
|
|
17260
17525
|
if (parent === cursor) {
|
|
17261
17526
|
break;
|
|
17262
17527
|
}
|
|
@@ -17267,14 +17532,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
17267
17532
|
function hermesSourceRootCandidates() {
|
|
17268
17533
|
const candidates = [
|
|
17269
17534
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
17270
|
-
|
|
17535
|
+
path19.join(os4.homedir(), ".hermes", "hermes-agent"),
|
|
17271
17536
|
"/usr/local/lib/hermes-agent",
|
|
17272
17537
|
"/opt/hermes"
|
|
17273
17538
|
].filter((candidate) => Boolean(candidate));
|
|
17274
17539
|
if (process.platform === "win32") {
|
|
17275
17540
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
17276
17541
|
if (localAppData) {
|
|
17277
|
-
candidates.unshift(
|
|
17542
|
+
candidates.unshift(path19.join(localAppData, "hermes", "hermes-agent"));
|
|
17278
17543
|
}
|
|
17279
17544
|
}
|
|
17280
17545
|
return candidates;
|
|
@@ -17284,10 +17549,10 @@ async function findExplicitHermesSourceRoot() {
|
|
|
17284
17549
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
17285
17550
|
}
|
|
17286
17551
|
async function isHermesAgentSourceRoot(candidate) {
|
|
17287
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
17552
|
+
return await isDirectory(candidate) && await isDirectory(path19.join(candidate, "tools")) && await isDirectory(path19.join(candidate, "hermes_cli"));
|
|
17288
17553
|
}
|
|
17289
17554
|
async function isDirectory(candidate) {
|
|
17290
|
-
return
|
|
17555
|
+
return stat12(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
17291
17556
|
}
|
|
17292
17557
|
function compactProcessOutput(value) {
|
|
17293
17558
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -17295,14 +17560,14 @@ function compactProcessOutput(value) {
|
|
|
17295
17560
|
}
|
|
17296
17561
|
|
|
17297
17562
|
// src/hermes/usage-probe.ts
|
|
17298
|
-
import { open as open3, readFile as readFile13, stat as
|
|
17299
|
-
import
|
|
17563
|
+
import { open as open3, readFile as readFile13, rm as rm6, stat as stat14 } from "fs/promises";
|
|
17564
|
+
import path21 from "path";
|
|
17300
17565
|
import YAML3 from "yaml";
|
|
17301
17566
|
|
|
17302
17567
|
// src/hermes/profiles.ts
|
|
17303
17568
|
import { execFile as execFile4 } from "child_process";
|
|
17304
|
-
import { readdir as readdir8, readFile as readFile12, rename as rename3, stat as
|
|
17305
|
-
import
|
|
17569
|
+
import { readdir as readdir8, readFile as readFile12, rename as rename3, stat as stat13 } from "fs/promises";
|
|
17570
|
+
import path20 from "path";
|
|
17306
17571
|
import { setTimeout as delay3 } from "timers/promises";
|
|
17307
17572
|
import { promisify as promisify4 } from "util";
|
|
17308
17573
|
import YAML2 from "yaml";
|
|
@@ -17321,7 +17586,7 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
17321
17586
|
const profilesDir = resolveHermesProfilesDir();
|
|
17322
17587
|
const entries = await readdir8(profilesDir, { withFileTypes: true }).catch(
|
|
17323
17588
|
(error) => {
|
|
17324
|
-
if (
|
|
17589
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17325
17590
|
return [];
|
|
17326
17591
|
}
|
|
17327
17592
|
throw error;
|
|
@@ -17375,8 +17640,8 @@ async function prepareHermesProfilesForUse(paths = resolveRuntimePaths()) {
|
|
|
17375
17640
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
17376
17641
|
assertProfileName(name);
|
|
17377
17642
|
const profile = await profileInfo(name, paths);
|
|
17378
|
-
const exists = await
|
|
17379
|
-
if (
|
|
17643
|
+
const exists = await stat13(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
17644
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17380
17645
|
return false;
|
|
17381
17646
|
}
|
|
17382
17647
|
throw error;
|
|
@@ -17445,7 +17710,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
17445
17710
|
return {
|
|
17446
17711
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
17447
17712
|
modelCount: listedModels?.models.length ?? 0,
|
|
17448
|
-
skillCount: await countSkills(
|
|
17713
|
+
skillCount: await countSkills(path20.join(profileDir, "skills")).catch(
|
|
17449
17714
|
() => 0
|
|
17450
17715
|
),
|
|
17451
17716
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -17488,8 +17753,8 @@ function assertProfileName(name) {
|
|
|
17488
17753
|
}
|
|
17489
17754
|
}
|
|
17490
17755
|
async function pathExists(targetPath) {
|
|
17491
|
-
return await
|
|
17492
|
-
if (
|
|
17756
|
+
return await stat13(targetPath).then(() => true).catch((error) => {
|
|
17757
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17493
17758
|
return false;
|
|
17494
17759
|
}
|
|
17495
17760
|
throw error;
|
|
@@ -17497,8 +17762,8 @@ async function pathExists(targetPath) {
|
|
|
17497
17762
|
}
|
|
17498
17763
|
async function hasDefaultProfileConfigSource() {
|
|
17499
17764
|
const profilePath = resolveHermesProfileDir(DEFAULT_PROFILE);
|
|
17500
|
-
const profileStat = await
|
|
17501
|
-
if (
|
|
17765
|
+
const profileStat = await stat13(profilePath).catch((error) => {
|
|
17766
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17502
17767
|
return null;
|
|
17503
17768
|
}
|
|
17504
17769
|
throw error;
|
|
@@ -17506,7 +17771,7 @@ async function hasDefaultProfileConfigSource() {
|
|
|
17506
17771
|
if (!profileStat?.isDirectory()) {
|
|
17507
17772
|
return false;
|
|
17508
17773
|
}
|
|
17509
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
17774
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path20.join(profilePath, ".env"));
|
|
17510
17775
|
}
|
|
17511
17776
|
async function deleteHermesProfileWithCli(name) {
|
|
17512
17777
|
try {
|
|
@@ -17619,7 +17884,7 @@ function isProcessRunning(pid) {
|
|
|
17619
17884
|
process.kill(pid, 0);
|
|
17620
17885
|
return true;
|
|
17621
17886
|
} catch (error) {
|
|
17622
|
-
return
|
|
17887
|
+
return isNodeError14(error, "EPERM");
|
|
17623
17888
|
}
|
|
17624
17889
|
}
|
|
17625
17890
|
async function waitForProfilePathToRemainAbsent(profilePath) {
|
|
@@ -17656,7 +17921,7 @@ function readExecErrorOutput2(error) {
|
|
|
17656
17921
|
}
|
|
17657
17922
|
return parts.join("\n");
|
|
17658
17923
|
}
|
|
17659
|
-
function
|
|
17924
|
+
function isNodeError14(error, code) {
|
|
17660
17925
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
17661
17926
|
}
|
|
17662
17927
|
function errorMessage(error) {
|
|
@@ -17668,7 +17933,7 @@ function escapeRegExp2(value) {
|
|
|
17668
17933
|
async function countSkills(root) {
|
|
17669
17934
|
const entries = await readdir8(root, { withFileTypes: true }).catch(
|
|
17670
17935
|
(error) => {
|
|
17671
|
-
if (
|
|
17936
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17672
17937
|
return [];
|
|
17673
17938
|
}
|
|
17674
17939
|
throw error;
|
|
@@ -17676,7 +17941,7 @@ async function countSkills(root) {
|
|
|
17676
17941
|
);
|
|
17677
17942
|
let count = 0;
|
|
17678
17943
|
for (const entry of entries) {
|
|
17679
|
-
const entryPath =
|
|
17944
|
+
const entryPath = path20.join(root, entry.name);
|
|
17680
17945
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
17681
17946
|
continue;
|
|
17682
17947
|
}
|
|
@@ -17695,7 +17960,7 @@ async function countConfiguredTools(profileName) {
|
|
|
17695
17960
|
resolveHermesConfigPath(profileName),
|
|
17696
17961
|
"utf8"
|
|
17697
17962
|
).catch((error) => {
|
|
17698
|
-
if (
|
|
17963
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
17699
17964
|
return "";
|
|
17700
17965
|
}
|
|
17701
17966
|
throw error;
|
|
@@ -17729,9 +17994,14 @@ function toRecord9(value) {
|
|
|
17729
17994
|
}
|
|
17730
17995
|
|
|
17731
17996
|
// src/hermes/usage-probe.ts
|
|
17732
|
-
var
|
|
17733
|
-
var
|
|
17734
|
-
var
|
|
17997
|
+
var HERMESPILOT_LINK_PLUGIN_KEY = "hermespilot-link";
|
|
17998
|
+
var HERMES_USAGE_PROBE_PLUGIN_KEY = HERMESPILOT_LINK_PLUGIN_KEY;
|
|
17999
|
+
var PLUGIN_VERSION = "2.0.0";
|
|
18000
|
+
var LEGACY_PLUGIN_KEY = "hermespilot-usage-probe";
|
|
18001
|
+
var MANAGED_MARKER = "HERMESPILOT_LINK_PLUGIN_MANAGED";
|
|
18002
|
+
var LEGACY_MANAGED_MARKER = "HERMESPILOT_USAGE_PROBE_MANAGED";
|
|
18003
|
+
var HERMESPILOT_LINK_TOOLSET = "hermespilot_link";
|
|
18004
|
+
var HERMESPILOT_DELIVER_TOOL = "hermeslink_deliver";
|
|
17735
18005
|
var USAGE_PROBE_DIR = "hermes-usage-probe";
|
|
17736
18006
|
var USAGE_PROBE_STATE_FILE = "state.json";
|
|
17737
18007
|
var EVENTS_FILE_NAME = "events.jsonl";
|
|
@@ -17763,7 +18033,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
17763
18033
|
const normalizedProfile = normalizeProfileName3(profileName);
|
|
17764
18034
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
17765
18035
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
17766
|
-
const pluginPath =
|
|
18036
|
+
const pluginPath = path21.join(
|
|
17767
18037
|
profilePath,
|
|
17768
18038
|
"plugins",
|
|
17769
18039
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -17789,7 +18059,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
17789
18059
|
return { ...base(), skipped: true };
|
|
17790
18060
|
}
|
|
17791
18061
|
try {
|
|
17792
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
18062
|
+
await ensureDirectoryWithInheritedMetadata(path21.dirname(eventsPath), 448);
|
|
17793
18063
|
const state = await readUsageProbeState(paths);
|
|
17794
18064
|
const pluginState = state.profiles[normalizedProfile];
|
|
17795
18065
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -17801,6 +18071,8 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
17801
18071
|
if (currentConfig.disabledByUser || currentConfig.error) {
|
|
17802
18072
|
const result2 = {
|
|
17803
18073
|
...base(),
|
|
18074
|
+
changed: currentConfig.changed,
|
|
18075
|
+
configChanged: currentConfig.changed,
|
|
17804
18076
|
enabled: currentConfig.enabled,
|
|
17805
18077
|
disabledByUser: currentConfig.disabledByUser,
|
|
17806
18078
|
error: currentConfig.error
|
|
@@ -17812,11 +18084,15 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
17812
18084
|
});
|
|
17813
18085
|
return result2;
|
|
17814
18086
|
}
|
|
18087
|
+
const linkConfig = await loadConfig(paths).catch(() => defaultLinkConfig);
|
|
18088
|
+
const internalBaseUrl = `http://127.0.0.1:${linkConfig.port || defaultLinkConfig.port}`;
|
|
17815
18089
|
const pluginChanged = await writeUsageProbePlugin({
|
|
17816
18090
|
profileName: normalizedProfile,
|
|
17817
18091
|
pluginPath,
|
|
17818
|
-
eventsPath
|
|
18092
|
+
eventsPath,
|
|
18093
|
+
internalBaseUrl
|
|
17819
18094
|
});
|
|
18095
|
+
const legacyCleanupChanged = await cleanupLegacyUsageProbePlugin(profilePath);
|
|
17820
18096
|
const configResult = await ensurePluginEnabledInConfig({
|
|
17821
18097
|
profileName: normalizedProfile,
|
|
17822
18098
|
configPath,
|
|
@@ -17824,12 +18100,12 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
17824
18100
|
language: options.language,
|
|
17825
18101
|
retryLinkDisabled: options.retryLinkDisabled
|
|
17826
18102
|
});
|
|
17827
|
-
const changed = pluginChanged || configResult.changed;
|
|
18103
|
+
const changed = pluginChanged || legacyCleanupChanged || configResult.changed;
|
|
17828
18104
|
const needsActivation = configResult.enabled && (changed || needsUsageProbeGatewayActivation(pluginState));
|
|
17829
18105
|
const result = {
|
|
17830
18106
|
...base(),
|
|
17831
18107
|
changed,
|
|
17832
|
-
pluginChanged,
|
|
18108
|
+
pluginChanged: pluginChanged || legacyCleanupChanged,
|
|
17833
18109
|
configChanged: configResult.changed,
|
|
17834
18110
|
enabled: configResult.enabled,
|
|
17835
18111
|
disabledByUser: configResult.disabledByUser,
|
|
@@ -17883,7 +18159,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
17883
18159
|
const eventsPath = resolveUsageProbeEventsPath(paths, profileName);
|
|
17884
18160
|
const raw = await readTail2(eventsPath, MAX_EVENT_READ_BYTES).catch(
|
|
17885
18161
|
(error) => {
|
|
17886
|
-
if (
|
|
18162
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
17887
18163
|
return "";
|
|
17888
18164
|
}
|
|
17889
18165
|
throw error;
|
|
@@ -17916,7 +18192,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
17916
18192
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
17917
18193
|
}
|
|
17918
18194
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
17919
|
-
return
|
|
18195
|
+
return path21.join(
|
|
17920
18196
|
paths.homeDir,
|
|
17921
18197
|
USAGE_PROBE_DIR,
|
|
17922
18198
|
safeProfileSegment(profileName),
|
|
@@ -17940,17 +18216,21 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
17940
18216
|
};
|
|
17941
18217
|
}
|
|
17942
18218
|
async function writeUsageProbePlugin(input) {
|
|
17943
|
-
const manifestPath =
|
|
17944
|
-
const initPath =
|
|
18219
|
+
const manifestPath = path21.join(input.pluginPath, "plugin.yaml");
|
|
18220
|
+
const initPath = path21.join(input.pluginPath, "__init__.py");
|
|
17945
18221
|
const manifest = usageProbeManifest();
|
|
17946
|
-
const source = usageProbePythonSource(
|
|
18222
|
+
const source = usageProbePythonSource(
|
|
18223
|
+
input.profileName,
|
|
18224
|
+
input.eventsPath,
|
|
18225
|
+
input.internalBaseUrl
|
|
18226
|
+
);
|
|
17947
18227
|
const manifestChanged = await writeManagedFile(manifestPath, manifest);
|
|
17948
18228
|
const sourceChanged = await writeManagedFile(initPath, source);
|
|
17949
18229
|
return manifestChanged || sourceChanged;
|
|
17950
18230
|
}
|
|
17951
18231
|
async function writeManagedFile(filePath, content) {
|
|
17952
18232
|
const existing = await readFile13(filePath, "utf8").catch((error) => {
|
|
17953
|
-
if (
|
|
18233
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
17954
18234
|
return null;
|
|
17955
18235
|
}
|
|
17956
18236
|
throw error;
|
|
@@ -17958,7 +18238,7 @@ async function writeManagedFile(filePath, content) {
|
|
|
17958
18238
|
if (existing === content) {
|
|
17959
18239
|
return false;
|
|
17960
18240
|
}
|
|
17961
|
-
if (existing !== null && !existing.includes(MANAGED_MARKER)) {
|
|
18241
|
+
if (existing !== null && !existing.includes(MANAGED_MARKER) && !existing.includes(LEGACY_MANAGED_MARKER)) {
|
|
17962
18242
|
throw new Error(
|
|
17963
18243
|
`Refusing to overwrite unmanaged Hermes plugin file: ${filePath}`
|
|
17964
18244
|
);
|
|
@@ -17990,8 +18270,16 @@ async function ensurePluginEnabledInConfig(input) {
|
|
|
17990
18270
|
error: `plugins.enabled in ${input.configPath} is not a list`
|
|
17991
18271
|
};
|
|
17992
18272
|
}
|
|
17993
|
-
|
|
17994
|
-
|
|
18273
|
+
if (plugins.disabled !== void 0 && !Array.isArray(plugins.disabled)) {
|
|
18274
|
+
return {
|
|
18275
|
+
changed: false,
|
|
18276
|
+
enabled: false,
|
|
18277
|
+
disabledByUser: false,
|
|
18278
|
+
error: `plugins.disabled in ${input.configPath} is not a list`
|
|
18279
|
+
};
|
|
18280
|
+
}
|
|
18281
|
+
const enabled = hasManagedPluginEntry(enabledEntries);
|
|
18282
|
+
const disabled = hasManagedPluginEntry(disabledEntries);
|
|
17995
18283
|
const previouslyEnabled = Boolean(input.previousState?.enabled_at);
|
|
17996
18284
|
const disabledByLinkForCurrentVersion = Boolean(input.previousState?.disabled_by_link_at) && input.previousState?.disabled_plugin_version === PLUGIN_VERSION;
|
|
17997
18285
|
if (!enabled && disabledByLinkForCurrentVersion && !input.retryLinkDisabled) {
|
|
@@ -18004,37 +18292,111 @@ async function ensurePluginEnabledInConfig(input) {
|
|
|
18004
18292
|
}
|
|
18005
18293
|
const disabledByUser = disabled || previouslyEnabled && !enabled && !input.previousState?.disabled_by_link_at;
|
|
18006
18294
|
if (disabledByUser) {
|
|
18295
|
+
const changed2 = normalizeDisabledPluginConfig(plugins, enabledEntries, disabledEntries);
|
|
18296
|
+
if (changed2) {
|
|
18297
|
+
await writeHermesConfigDocument2({
|
|
18298
|
+
configPath: input.configPath,
|
|
18299
|
+
document,
|
|
18300
|
+
config,
|
|
18301
|
+
existingRaw
|
|
18302
|
+
});
|
|
18303
|
+
}
|
|
18007
18304
|
return {
|
|
18008
|
-
changed:
|
|
18305
|
+
changed: changed2,
|
|
18009
18306
|
enabled: false,
|
|
18010
18307
|
disabledByUser: true,
|
|
18011
18308
|
error: null
|
|
18012
18309
|
};
|
|
18013
18310
|
}
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
18311
|
+
let changed = normalizeEnabledPluginConfig(
|
|
18312
|
+
plugins,
|
|
18313
|
+
enabledEntries,
|
|
18314
|
+
disabledEntries
|
|
18315
|
+
);
|
|
18316
|
+
changed = ensureApiServerToolsetEnabled(config) || changed;
|
|
18317
|
+
if (changed) {
|
|
18318
|
+
await writeHermesConfigDocument2({
|
|
18319
|
+
configPath: input.configPath,
|
|
18320
|
+
document,
|
|
18321
|
+
config,
|
|
18322
|
+
existingRaw
|
|
18323
|
+
});
|
|
18021
18324
|
}
|
|
18022
|
-
plugins.enabled = [...enabledEntries, HERMES_USAGE_PROBE_PLUGIN_KEY];
|
|
18023
|
-
await writeHermesConfigDocument2({
|
|
18024
|
-
configPath: input.configPath,
|
|
18025
|
-
document,
|
|
18026
|
-
config,
|
|
18027
|
-
existingRaw
|
|
18028
|
-
});
|
|
18029
18325
|
return {
|
|
18030
|
-
changed
|
|
18326
|
+
changed,
|
|
18031
18327
|
enabled: true,
|
|
18032
18328
|
disabledByUser: false,
|
|
18033
18329
|
error: null
|
|
18034
18330
|
};
|
|
18035
18331
|
}
|
|
18332
|
+
function hasManagedPluginEntry(entries) {
|
|
18333
|
+
return entries.includes(HERMES_USAGE_PROBE_PLUGIN_KEY) || entries.includes(LEGACY_PLUGIN_KEY);
|
|
18334
|
+
}
|
|
18335
|
+
function normalizeEnabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
18336
|
+
const nextEnabled = uniqueStrings([
|
|
18337
|
+
...enabledEntries.filter(
|
|
18338
|
+
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18339
|
+
),
|
|
18340
|
+
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18341
|
+
]);
|
|
18342
|
+
const nextDisabled = uniqueStrings(
|
|
18343
|
+
disabledEntries.filter(
|
|
18344
|
+
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18345
|
+
)
|
|
18346
|
+
);
|
|
18347
|
+
const changed = !sameStringList(enabledEntries, nextEnabled) || !sameStringList(disabledEntries, nextDisabled);
|
|
18348
|
+
plugins.enabled = nextEnabled;
|
|
18349
|
+
if (nextDisabled.length > 0) {
|
|
18350
|
+
plugins.disabled = nextDisabled;
|
|
18351
|
+
} else {
|
|
18352
|
+
delete plugins.disabled;
|
|
18353
|
+
}
|
|
18354
|
+
return changed;
|
|
18355
|
+
}
|
|
18356
|
+
function normalizeDisabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
18357
|
+
const nextEnabled = uniqueStrings(
|
|
18358
|
+
enabledEntries.filter(
|
|
18359
|
+
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18360
|
+
)
|
|
18361
|
+
);
|
|
18362
|
+
const nextDisabled = uniqueStrings([
|
|
18363
|
+
...disabledEntries.filter(
|
|
18364
|
+
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18365
|
+
),
|
|
18366
|
+
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18367
|
+
]);
|
|
18368
|
+
const changed = !sameStringList(enabledEntries, nextEnabled) || !sameStringList(disabledEntries, nextDisabled);
|
|
18369
|
+
plugins.enabled = nextEnabled;
|
|
18370
|
+
plugins.disabled = nextDisabled;
|
|
18371
|
+
return changed;
|
|
18372
|
+
}
|
|
18373
|
+
function ensureApiServerToolsetEnabled(config) {
|
|
18374
|
+
const platformToolsets = isRecord3(config.platform_toolsets) ? config.platform_toolsets : null;
|
|
18375
|
+
const knownPluginToolsets = isRecord3(config.known_plugin_toolsets) ? config.known_plugin_toolsets : null;
|
|
18376
|
+
const apiServerToolsets = readStringList3(platformToolsets?.api_server);
|
|
18377
|
+
const knownApiServerToolsets = readStringList3(
|
|
18378
|
+
knownPluginToolsets?.api_server
|
|
18379
|
+
);
|
|
18380
|
+
if (apiServerToolsets.length === 0 && !Array.isArray(platformToolsets?.api_server) && !knownApiServerToolsets.includes(HERMESPILOT_LINK_TOOLSET)) {
|
|
18381
|
+
return false;
|
|
18382
|
+
}
|
|
18383
|
+
const nextToolsets = uniqueStrings([
|
|
18384
|
+
...Array.isArray(platformToolsets?.api_server) ? apiServerToolsets : ["hermes-api-server"],
|
|
18385
|
+
HERMESPILOT_LINK_TOOLSET
|
|
18386
|
+
]);
|
|
18387
|
+
const nextKnownToolsets = uniqueStrings([
|
|
18388
|
+
...knownApiServerToolsets,
|
|
18389
|
+
HERMESPILOT_LINK_TOOLSET
|
|
18390
|
+
]);
|
|
18391
|
+
const nextPlatformToolsets = ensureRecord2(config, "platform_toolsets");
|
|
18392
|
+
const nextKnownPluginToolsets = ensureRecord2(config, "known_plugin_toolsets");
|
|
18393
|
+
const changed = !sameStringList(apiServerToolsets, nextToolsets) || !sameStringList(knownApiServerToolsets, nextKnownToolsets);
|
|
18394
|
+
nextPlatformToolsets.api_server = nextToolsets;
|
|
18395
|
+
nextKnownPluginToolsets.api_server = nextKnownToolsets;
|
|
18396
|
+
return changed;
|
|
18397
|
+
}
|
|
18036
18398
|
async function readUsageProbeConfigStatus(input) {
|
|
18037
|
-
const { config } = await readHermesConfigDocument2(
|
|
18399
|
+
const { document, config, existingRaw } = await readHermesConfigDocument2(
|
|
18038
18400
|
input.configPath,
|
|
18039
18401
|
input.language
|
|
18040
18402
|
);
|
|
@@ -18042,7 +18404,8 @@ async function readUsageProbeConfigStatus(input) {
|
|
|
18042
18404
|
return {
|
|
18043
18405
|
enabled: false,
|
|
18044
18406
|
disabledByUser: false,
|
|
18045
|
-
error: `plugins in ${input.configPath} is not an object
|
|
18407
|
+
error: `plugins in ${input.configPath} is not an object`,
|
|
18408
|
+
changed: false
|
|
18046
18409
|
};
|
|
18047
18410
|
}
|
|
18048
18411
|
const plugins = toRecord10(config.plugins);
|
|
@@ -18050,27 +18413,47 @@ async function readUsageProbeConfigStatus(input) {
|
|
|
18050
18413
|
return {
|
|
18051
18414
|
enabled: false,
|
|
18052
18415
|
disabledByUser: false,
|
|
18053
|
-
error: `plugins.enabled in ${input.configPath} is not a list
|
|
18416
|
+
error: `plugins.enabled in ${input.configPath} is not a list`,
|
|
18417
|
+
changed: false
|
|
18418
|
+
};
|
|
18419
|
+
}
|
|
18420
|
+
if (plugins.disabled !== void 0 && !Array.isArray(plugins.disabled)) {
|
|
18421
|
+
return {
|
|
18422
|
+
enabled: false,
|
|
18423
|
+
disabledByUser: false,
|
|
18424
|
+
error: `plugins.disabled in ${input.configPath} is not a list`,
|
|
18425
|
+
changed: false
|
|
18054
18426
|
};
|
|
18055
18427
|
}
|
|
18056
18428
|
const enabledEntries = readStringList3(plugins.enabled);
|
|
18057
18429
|
const disabledEntries = readStringList3(plugins.disabled);
|
|
18058
|
-
const enabled = enabledEntries
|
|
18059
|
-
const disabled = disabledEntries
|
|
18430
|
+
const enabled = hasManagedPluginEntry(enabledEntries);
|
|
18431
|
+
const disabled = hasManagedPluginEntry(disabledEntries);
|
|
18060
18432
|
const previouslyEnabled = Boolean(input.previousState?.enabled_at);
|
|
18061
18433
|
const disabledByLinkForCurrentVersion = Boolean(input.previousState?.disabled_by_link_at) && input.previousState?.disabled_plugin_version === PLUGIN_VERSION;
|
|
18062
18434
|
if (!enabled && disabledByLinkForCurrentVersion && !input.retryLinkDisabled) {
|
|
18063
18435
|
return {
|
|
18064
18436
|
enabled: false,
|
|
18065
18437
|
disabledByUser: false,
|
|
18066
|
-
error: "usage probe was disabled automatically after the last Gateway restart failure"
|
|
18438
|
+
error: "usage probe was disabled automatically after the last Gateway restart failure",
|
|
18439
|
+
changed: false
|
|
18067
18440
|
};
|
|
18068
18441
|
}
|
|
18069
18442
|
const disabledByUser = disabled || previouslyEnabled && !enabled && !input.previousState?.disabled_by_link_at;
|
|
18443
|
+
const changed = disabledByUser && disabled ? normalizeDisabledPluginConfig(plugins, enabledEntries, disabledEntries) : false;
|
|
18444
|
+
if (changed) {
|
|
18445
|
+
await writeHermesConfigDocument2({
|
|
18446
|
+
configPath: input.configPath,
|
|
18447
|
+
document,
|
|
18448
|
+
config,
|
|
18449
|
+
existingRaw
|
|
18450
|
+
});
|
|
18451
|
+
}
|
|
18070
18452
|
return {
|
|
18071
|
-
enabled,
|
|
18453
|
+
enabled: disabledByUser ? false : enabled,
|
|
18072
18454
|
disabledByUser,
|
|
18073
|
-
error: null
|
|
18455
|
+
error: null,
|
|
18456
|
+
changed
|
|
18074
18457
|
};
|
|
18075
18458
|
}
|
|
18076
18459
|
async function activateUsageProbeGateway(input) {
|
|
@@ -18159,11 +18542,11 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
18159
18542
|
);
|
|
18160
18543
|
const plugins = ensureRecord2(config, "plugins");
|
|
18161
18544
|
const enabledEntries = readStringList3(plugins.enabled);
|
|
18162
|
-
if (!enabledEntries
|
|
18545
|
+
if (!hasManagedPluginEntry(enabledEntries)) {
|
|
18163
18546
|
return;
|
|
18164
18547
|
}
|
|
18165
18548
|
plugins.enabled = enabledEntries.filter(
|
|
18166
|
-
(entry) => entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
18549
|
+
(entry) => entry !== HERMES_USAGE_PROBE_PLUGIN_KEY && entry !== LEGACY_PLUGIN_KEY
|
|
18167
18550
|
);
|
|
18168
18551
|
await writeHermesConfigDocument2({
|
|
18169
18552
|
configPath: input.configPath,
|
|
@@ -18180,6 +18563,35 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
18180
18563
|
profile: input.profileName
|
|
18181
18564
|
});
|
|
18182
18565
|
}
|
|
18566
|
+
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
18567
|
+
const legacyPath = path21.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
18568
|
+
const [manifest, init] = await Promise.all([
|
|
18569
|
+
readFile13(path21.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
18570
|
+
(error) => {
|
|
18571
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
18572
|
+
return null;
|
|
18573
|
+
}
|
|
18574
|
+
throw error;
|
|
18575
|
+
}
|
|
18576
|
+
),
|
|
18577
|
+
readFile13(path21.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
18578
|
+
(error) => {
|
|
18579
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
18580
|
+
return null;
|
|
18581
|
+
}
|
|
18582
|
+
throw error;
|
|
18583
|
+
}
|
|
18584
|
+
)
|
|
18585
|
+
]);
|
|
18586
|
+
if (!manifest && !init) {
|
|
18587
|
+
return false;
|
|
18588
|
+
}
|
|
18589
|
+
if (!manifest?.includes(LEGACY_MANAGED_MARKER) && !init?.includes(LEGACY_MANAGED_MARKER)) {
|
|
18590
|
+
return false;
|
|
18591
|
+
}
|
|
18592
|
+
await rm6(legacyPath, { recursive: true, force: true });
|
|
18593
|
+
return true;
|
|
18594
|
+
}
|
|
18183
18595
|
async function readUsageProbeState(paths) {
|
|
18184
18596
|
const emptyState = {
|
|
18185
18597
|
schema_version: 1,
|
|
@@ -18211,12 +18623,12 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
18211
18623
|
}));
|
|
18212
18624
|
}
|
|
18213
18625
|
function usageProbeStatePath(paths) {
|
|
18214
|
-
return
|
|
18626
|
+
return path21.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
18215
18627
|
}
|
|
18216
18628
|
async function readHermesConfigDocument2(configPath, language) {
|
|
18217
18629
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
18218
18630
|
(error) => {
|
|
18219
|
-
if (
|
|
18631
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
18220
18632
|
return null;
|
|
18221
18633
|
}
|
|
18222
18634
|
throw error;
|
|
@@ -18247,7 +18659,7 @@ async function writeHermesConfigDocument2(input) {
|
|
|
18247
18659
|
);
|
|
18248
18660
|
}
|
|
18249
18661
|
async function readTail2(filePath, maxBytes) {
|
|
18250
|
-
const info = await
|
|
18662
|
+
const info = await stat14(filePath);
|
|
18251
18663
|
const length = Math.min(info.size, maxBytes);
|
|
18252
18664
|
if (length <= 0) {
|
|
18253
18665
|
return "";
|
|
@@ -18271,7 +18683,7 @@ function parseUsageProbeEvent(line) {
|
|
|
18271
18683
|
if (payload.event !== "post_api_request") {
|
|
18272
18684
|
return null;
|
|
18273
18685
|
}
|
|
18274
|
-
const profile =
|
|
18686
|
+
const profile = readString12(payload.profile);
|
|
18275
18687
|
const promptTokens = readInteger2(payload.prompt_tokens) ?? readInteger2(payload.input_tokens);
|
|
18276
18688
|
if (!profile || promptTokens === void 0) {
|
|
18277
18689
|
return null;
|
|
@@ -18279,17 +18691,17 @@ function parseUsageProbeEvent(line) {
|
|
|
18279
18691
|
const completionTokens = readInteger2(payload.completion_tokens) ?? readInteger2(payload.output_tokens) ?? 0;
|
|
18280
18692
|
return {
|
|
18281
18693
|
profile,
|
|
18282
|
-
sessionId:
|
|
18283
|
-
taskId:
|
|
18694
|
+
sessionId: readString12(payload.session_id),
|
|
18695
|
+
taskId: readString12(payload.task_id),
|
|
18284
18696
|
apiCallCount: readInteger2(payload.api_call_count) ?? 0,
|
|
18285
18697
|
promptTokens,
|
|
18286
18698
|
completionTokens,
|
|
18287
18699
|
totalTokens: readInteger2(payload.total_tokens) ?? promptTokens + completionTokens,
|
|
18288
|
-
model:
|
|
18289
|
-
provider:
|
|
18290
|
-
apiMode:
|
|
18291
|
-
finishReason:
|
|
18292
|
-
createdAt:
|
|
18700
|
+
model: readString12(payload.model),
|
|
18701
|
+
provider: readString12(payload.provider),
|
|
18702
|
+
apiMode: readString12(payload.api_mode),
|
|
18703
|
+
finishReason: readString12(payload.finish_reason),
|
|
18704
|
+
createdAt: readString12(payload.created_at) ?? (/* @__PURE__ */ new Date(0)).toISOString()
|
|
18293
18705
|
};
|
|
18294
18706
|
} catch {
|
|
18295
18707
|
return null;
|
|
@@ -18331,27 +18743,74 @@ function usageProbeManifest() {
|
|
|
18331
18743
|
`# ${MANAGED_MARKER}`,
|
|
18332
18744
|
`name: ${HERMES_USAGE_PROBE_PLUGIN_KEY}`,
|
|
18333
18745
|
`version: ${JSON.stringify(PLUGIN_VERSION)}`,
|
|
18334
|
-
'description: "HermesPilot
|
|
18746
|
+
'description: "HermesPilot Link integration. Records metadata-only token usage and delivers files to HermesPilot App."',
|
|
18335
18747
|
"author: HermesPilot",
|
|
18748
|
+
"kind: standalone",
|
|
18336
18749
|
"hooks:",
|
|
18337
18750
|
" - post_api_request",
|
|
18751
|
+
"provides_tools:",
|
|
18752
|
+
` - ${HERMESPILOT_DELIVER_TOOL}`,
|
|
18338
18753
|
""
|
|
18339
18754
|
].join("\n");
|
|
18340
18755
|
}
|
|
18341
|
-
function usageProbePythonSource(profileName, eventsPath) {
|
|
18756
|
+
function usageProbePythonSource(profileName, eventsPath, internalBaseUrl) {
|
|
18342
18757
|
return `# ${MANAGED_MARKER}
|
|
18343
18758
|
from __future__ import annotations
|
|
18344
18759
|
|
|
18345
18760
|
import json
|
|
18346
18761
|
import os
|
|
18762
|
+
import urllib.error
|
|
18763
|
+
import urllib.request
|
|
18347
18764
|
from datetime import datetime, timezone
|
|
18348
18765
|
from pathlib import Path
|
|
18349
18766
|
|
|
18350
18767
|
PROBE_VERSION = ${JSON.stringify(PLUGIN_VERSION)}
|
|
18351
18768
|
PROFILE_NAME = ${JSON.stringify(profileName)}
|
|
18352
18769
|
EVENTS_FILE = ${JSON.stringify(eventsPath)}
|
|
18770
|
+
HERMESLINK_INTERNAL_BASE = ${JSON.stringify(internalBaseUrl)}
|
|
18353
18771
|
MAX_EVENT_FILE_BYTES = 5 * 1024 * 1024
|
|
18354
18772
|
|
|
18773
|
+
try:
|
|
18774
|
+
from tools.registry import tool_error, tool_result
|
|
18775
|
+
except Exception:
|
|
18776
|
+
def tool_error(message, **extra):
|
|
18777
|
+
payload = {"error": str(message)}
|
|
18778
|
+
payload.update(extra)
|
|
18779
|
+
return json.dumps(payload, ensure_ascii=False)
|
|
18780
|
+
|
|
18781
|
+
def tool_result(data=None, **kwargs):
|
|
18782
|
+
return json.dumps(data if data is not None else kwargs, ensure_ascii=False)
|
|
18783
|
+
|
|
18784
|
+
|
|
18785
|
+
HERMESLINK_DELIVER_SCHEMA = {
|
|
18786
|
+
"name": ${JSON.stringify(HERMESPILOT_DELIVER_TOOL)},
|
|
18787
|
+
"description": "Deliver one or more local files to the current HermesPilot App conversation.",
|
|
18788
|
+
"parameters": {
|
|
18789
|
+
"type": "object",
|
|
18790
|
+
"properties": {
|
|
18791
|
+
"files": {
|
|
18792
|
+
"type": "array",
|
|
18793
|
+
"description": "Absolute local file paths to deliver to the App.",
|
|
18794
|
+
"items": {
|
|
18795
|
+
"type": "object",
|
|
18796
|
+
"properties": {
|
|
18797
|
+
"path": {
|
|
18798
|
+
"type": "string",
|
|
18799
|
+
"description": "Absolute local path of a file to deliver.",
|
|
18800
|
+
},
|
|
18801
|
+
"caption": {
|
|
18802
|
+
"type": "string",
|
|
18803
|
+
"description": "Optional short label for the file.",
|
|
18804
|
+
},
|
|
18805
|
+
},
|
|
18806
|
+
"required": ["path"],
|
|
18807
|
+
},
|
|
18808
|
+
},
|
|
18809
|
+
},
|
|
18810
|
+
"required": ["files"],
|
|
18811
|
+
},
|
|
18812
|
+
}
|
|
18813
|
+
|
|
18355
18814
|
|
|
18356
18815
|
def _read_int(value):
|
|
18357
18816
|
if isinstance(value, bool):
|
|
@@ -18405,6 +18864,116 @@ def _append_event(event):
|
|
|
18405
18864
|
return
|
|
18406
18865
|
|
|
18407
18866
|
|
|
18867
|
+
def _normalize_file_item(item):
|
|
18868
|
+
if isinstance(item, str):
|
|
18869
|
+
path = item.strip()
|
|
18870
|
+
return {"path": path} if path else None
|
|
18871
|
+
if not isinstance(item, dict):
|
|
18872
|
+
return None
|
|
18873
|
+
path = _read_str(item.get("path")) or _read_str(item.get("file"))
|
|
18874
|
+
if not path:
|
|
18875
|
+
return None
|
|
18876
|
+
result = {"path": path}
|
|
18877
|
+
caption = _read_str(item.get("caption"))
|
|
18878
|
+
if caption:
|
|
18879
|
+
result["caption"] = caption
|
|
18880
|
+
return result
|
|
18881
|
+
|
|
18882
|
+
|
|
18883
|
+
def _normalize_files(args):
|
|
18884
|
+
if not isinstance(args, dict):
|
|
18885
|
+
return []
|
|
18886
|
+
raw = args.get("files")
|
|
18887
|
+
if raw is None:
|
|
18888
|
+
raw = args.get("file") or args.get("path")
|
|
18889
|
+
items = raw if isinstance(raw, list) else [raw]
|
|
18890
|
+
files = []
|
|
18891
|
+
for item in items:
|
|
18892
|
+
normalized = _normalize_file_item(item)
|
|
18893
|
+
if normalized:
|
|
18894
|
+
files.append(normalized)
|
|
18895
|
+
return files
|
|
18896
|
+
|
|
18897
|
+
|
|
18898
|
+
def _post_json(path, payload):
|
|
18899
|
+
url = HERMESLINK_INTERNAL_BASE.rstrip("/") + path
|
|
18900
|
+
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
18901
|
+
request = urllib.request.Request(
|
|
18902
|
+
url,
|
|
18903
|
+
data=data,
|
|
18904
|
+
method="POST",
|
|
18905
|
+
headers={"content-type": "application/json"},
|
|
18906
|
+
)
|
|
18907
|
+
with urllib.request.urlopen(request, timeout=30) as response:
|
|
18908
|
+
raw = response.read().decode("utf-8")
|
|
18909
|
+
return json.loads(raw) if raw else {}
|
|
18910
|
+
|
|
18911
|
+
|
|
18912
|
+
def _error_message_from_http_error(error):
|
|
18913
|
+
try:
|
|
18914
|
+
raw = error.read().decode("utf-8")
|
|
18915
|
+
payload = json.loads(raw)
|
|
18916
|
+
if isinstance(payload, dict):
|
|
18917
|
+
message = _read_str(payload.get("message")) or _read_str(payload.get("error"))
|
|
18918
|
+
code = _read_str(payload.get("code"))
|
|
18919
|
+
if message and code:
|
|
18920
|
+
return f"{code}: {message}"
|
|
18921
|
+
if message:
|
|
18922
|
+
return message
|
|
18923
|
+
except Exception:
|
|
18924
|
+
pass
|
|
18925
|
+
return f"HTTP {getattr(error, 'code', 'error')}"
|
|
18926
|
+
|
|
18927
|
+
|
|
18928
|
+
def _handle_hermeslink_deliver(args, task_id=None, session_id=None, **kwargs):
|
|
18929
|
+
files = _normalize_files(args)
|
|
18930
|
+
if not files:
|
|
18931
|
+
return tool_error("No deliverable file path was provided.")
|
|
18932
|
+
payload = {
|
|
18933
|
+
"profile": PROFILE_NAME,
|
|
18934
|
+
"files": files,
|
|
18935
|
+
}
|
|
18936
|
+
task_id_text = _read_str(task_id)
|
|
18937
|
+
session_id_text = _read_str(session_id) or task_id_text
|
|
18938
|
+
if task_id_text:
|
|
18939
|
+
payload["task_id"] = task_id_text
|
|
18940
|
+
if session_id_text:
|
|
18941
|
+
payload["session_id"] = session_id_text
|
|
18942
|
+
try:
|
|
18943
|
+
result = _post_json("/internal/deliver-tool", payload)
|
|
18944
|
+
except urllib.error.HTTPError as error:
|
|
18945
|
+
return tool_error("Hermes Link delivery failed: " + _error_message_from_http_error(error))
|
|
18946
|
+
except Exception as error:
|
|
18947
|
+
return tool_error("Hermes Link delivery failed: " + str(error))
|
|
18948
|
+
if not isinstance(result, dict) or result.get("ok") is not True:
|
|
18949
|
+
return tool_error("Hermes Link delivery failed.")
|
|
18950
|
+
imported = _read_int(result.get("imported_count")) or 0
|
|
18951
|
+
failed = _read_int(result.get("failed_count")) or 0
|
|
18952
|
+
skipped = _read_int(result.get("skipped_count")) or 0
|
|
18953
|
+
staged = _read_int(result.get("staged_count")) or imported
|
|
18954
|
+
if imported <= 0 and failed > 0:
|
|
18955
|
+
return tool_error(
|
|
18956
|
+
"Hermes Link could not deliver any files.",
|
|
18957
|
+
staged_count=staged,
|
|
18958
|
+
delivered_count=imported,
|
|
18959
|
+
skipped_count=skipped,
|
|
18960
|
+
failed_count=failed,
|
|
18961
|
+
)
|
|
18962
|
+
message = "Delivered file attachments to HermesPilot App."
|
|
18963
|
+
if imported <= 0 and skipped > 0:
|
|
18964
|
+
message = "No new file attachments were delivered because they were already handled or skipped."
|
|
18965
|
+
elif failed > 0:
|
|
18966
|
+
message = "Delivered some file attachments to HermesPilot App; some files failed."
|
|
18967
|
+
return tool_result(
|
|
18968
|
+
success=True,
|
|
18969
|
+
message=message,
|
|
18970
|
+
staged_count=staged,
|
|
18971
|
+
delivered_count=imported,
|
|
18972
|
+
skipped_count=skipped,
|
|
18973
|
+
failed_count=failed,
|
|
18974
|
+
)
|
|
18975
|
+
|
|
18976
|
+
|
|
18408
18977
|
def _on_post_api_request(**kwargs):
|
|
18409
18978
|
usage = kwargs.get("usage")
|
|
18410
18979
|
prompt_tokens = _usage_int(usage, "input_tokens", "prompt_tokens")
|
|
@@ -18434,6 +19003,13 @@ def _on_post_api_request(**kwargs):
|
|
|
18434
19003
|
|
|
18435
19004
|
def register(ctx):
|
|
18436
19005
|
ctx.register_hook("post_api_request", _on_post_api_request)
|
|
19006
|
+
ctx.register_tool(
|
|
19007
|
+
name=${JSON.stringify(HERMESPILOT_DELIVER_TOOL)},
|
|
19008
|
+
toolset=${JSON.stringify(HERMESPILOT_LINK_TOOLSET)},
|
|
19009
|
+
schema=HERMESLINK_DELIVER_SCHEMA,
|
|
19010
|
+
handler=_handle_hermeslink_deliver,
|
|
19011
|
+
description="Deliver local files to HermesPilot App through Hermes Link.",
|
|
19012
|
+
)
|
|
18437
19013
|
`;
|
|
18438
19014
|
}
|
|
18439
19015
|
function withDefaultProfilePlaceholder(profiles) {
|
|
@@ -18480,7 +19056,13 @@ function readStringList3(value) {
|
|
|
18480
19056
|
}
|
|
18481
19057
|
return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
|
|
18482
19058
|
}
|
|
18483
|
-
function
|
|
19059
|
+
function uniqueStrings(entries) {
|
|
19060
|
+
return [...new Set(entries.map((entry) => entry.trim()).filter(Boolean))];
|
|
19061
|
+
}
|
|
19062
|
+
function sameStringList(left, right) {
|
|
19063
|
+
return left.length === right.length && left.every((entry, index) => entry === right[index]);
|
|
19064
|
+
}
|
|
19065
|
+
function readString12(value) {
|
|
18484
19066
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
18485
19067
|
}
|
|
18486
19068
|
function readInteger2(value) {
|
|
@@ -18500,8 +19082,8 @@ function isRecord3(value) {
|
|
|
18500
19082
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18501
19083
|
}
|
|
18502
19084
|
async function pathIsDirectory(filePath) {
|
|
18503
|
-
return
|
|
18504
|
-
if (
|
|
19085
|
+
return stat14(filePath).then((value) => value.isDirectory()).catch((error) => {
|
|
19086
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
18505
19087
|
return false;
|
|
18506
19088
|
}
|
|
18507
19089
|
throw error;
|
|
@@ -18524,7 +19106,7 @@ function logEnsureResult(logger, source, profiles) {
|
|
|
18524
19106
|
function errorMessage2(error) {
|
|
18525
19107
|
return error instanceof Error ? error.message : String(error);
|
|
18526
19108
|
}
|
|
18527
|
-
function
|
|
19109
|
+
function isNodeError15(error, code) {
|
|
18528
19110
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
18529
19111
|
}
|
|
18530
19112
|
|
|
@@ -18579,7 +19161,7 @@ function parseSseBlock(block) {
|
|
|
18579
19161
|
return null;
|
|
18580
19162
|
}
|
|
18581
19163
|
const payload = toRecord11(decoded);
|
|
18582
|
-
const payloadType = (
|
|
19164
|
+
const payloadType = (readString13(payload, "type") ?? readString13(payload, "event") ?? readString13(payload, "object") ?? eventName) || "message";
|
|
18583
19165
|
return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
|
|
18584
19166
|
}
|
|
18585
19167
|
function decodeJson(value) {
|
|
@@ -18595,7 +19177,7 @@ function decodeJson(value) {
|
|
|
18595
19177
|
return { type: "message.delta", delta: value };
|
|
18596
19178
|
}
|
|
18597
19179
|
}
|
|
18598
|
-
function
|
|
19180
|
+
function readString13(body, key) {
|
|
18599
19181
|
const value = body[key];
|
|
18600
19182
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
18601
19183
|
}
|
|
@@ -18627,8 +19209,8 @@ function isRunToolResultCompensationEnabled(env = process.env) {
|
|
|
18627
19209
|
}
|
|
18628
19210
|
|
|
18629
19211
|
// src/conversations/run-transcript-enrichment.ts
|
|
18630
|
-
import { readFile as readFile14, stat as
|
|
18631
|
-
import
|
|
19212
|
+
import { readFile as readFile14, stat as stat15 } from "fs/promises";
|
|
19213
|
+
import path22 from "path";
|
|
18632
19214
|
var MESSAGE_COLUMNS2 = [
|
|
18633
19215
|
"id",
|
|
18634
19216
|
"session_id",
|
|
@@ -18704,8 +19286,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
18704
19286
|
}
|
|
18705
19287
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
18706
19288
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
18707
|
-
const dbPath =
|
|
18708
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
19289
|
+
const dbPath = path22.join(profileDir, "state.db");
|
|
19290
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path22.join(profileDir, "sessions"));
|
|
18709
19291
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
18710
19292
|
readStateDbMessages2(dbPath, sessionId),
|
|
18711
19293
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -18768,9 +19350,9 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
18768
19350
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
18769
19351
|
return [];
|
|
18770
19352
|
}
|
|
18771
|
-
const transcriptPath =
|
|
19353
|
+
const transcriptPath = path22.join(sessionsDir, `${sessionId}.jsonl`);
|
|
18772
19354
|
const raw = await readFile14(transcriptPath, "utf8").catch((error) => {
|
|
18773
|
-
if (
|
|
19355
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
18774
19356
|
return "";
|
|
18775
19357
|
}
|
|
18776
19358
|
throw error;
|
|
@@ -18785,7 +19367,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
18785
19367
|
}
|
|
18786
19368
|
try {
|
|
18787
19369
|
const parsed = JSON.parse(line);
|
|
18788
|
-
const role =
|
|
19370
|
+
const role = readString14(parsed, "role");
|
|
18789
19371
|
if (!role) {
|
|
18790
19372
|
continue;
|
|
18791
19373
|
}
|
|
@@ -18814,8 +19396,8 @@ function normalizeHermesToolCall2(value) {
|
|
|
18814
19396
|
return null;
|
|
18815
19397
|
}
|
|
18816
19398
|
const fn = toRecord12(record.function);
|
|
18817
|
-
const id =
|
|
18818
|
-
const name =
|
|
19399
|
+
const id = readString14(record, "id") ?? readString14(record, "call_id") ?? readString14(record, "tool_call_id") ?? readString14(fn, "id") ?? void 0;
|
|
19400
|
+
const name = readString14(fn, "name") ?? readString14(record, "name") ?? readString14(record, "tool_name") ?? readString14(record, "tool") ?? "tool";
|
|
18819
19401
|
const rawArguments = fn.arguments ?? record.arguments ?? record.args ?? record.input;
|
|
18820
19402
|
return {
|
|
18821
19403
|
...id ? { id } : {},
|
|
@@ -18825,8 +19407,8 @@ function normalizeHermesToolCall2(value) {
|
|
|
18825
19407
|
};
|
|
18826
19408
|
}
|
|
18827
19409
|
function consumePendingToolCall2(input) {
|
|
18828
|
-
const toolCallId =
|
|
18829
|
-
const toolName =
|
|
19410
|
+
const toolCallId = readString14(input.toolMessage, "tool_call_id");
|
|
19411
|
+
const toolName = readString14(input.toolMessage, "tool_name");
|
|
18830
19412
|
let pending = toolCallId ? input.toolCallsById.get(toolCallId) : void 0;
|
|
18831
19413
|
if (!pending && toolName) {
|
|
18832
19414
|
pending = input.pendingToolCalls.find(
|
|
@@ -18849,8 +19431,8 @@ function consumePendingToolCall2(input) {
|
|
|
18849
19431
|
function toolCompletedEvent(row, pending, existingEventId) {
|
|
18850
19432
|
const output = normalizeContent3(row.content);
|
|
18851
19433
|
const parsedOutput = parseJsonValue2(output);
|
|
18852
|
-
const toolCallId =
|
|
18853
|
-
const toolName =
|
|
19434
|
+
const toolCallId = readString14(row, "tool_call_id") ?? pending?.toolCall.id;
|
|
19435
|
+
const toolName = readString14(row, "tool_name") ?? pending?.toolCall.name ?? "tool";
|
|
18854
19436
|
const payload = {
|
|
18855
19437
|
type: "tool.completed",
|
|
18856
19438
|
event: "tool.completed",
|
|
@@ -18894,7 +19476,7 @@ function groupReusableToolEventIds(events) {
|
|
|
18894
19476
|
return grouped;
|
|
18895
19477
|
}
|
|
18896
19478
|
function consumeReusableToolEventId(grouped, pending, row) {
|
|
18897
|
-
const toolName = pending?.toolCall.name ??
|
|
19479
|
+
const toolName = pending?.toolCall.name ?? readString14(row, "tool_name") ?? "tool";
|
|
18898
19480
|
const key = normalizeToolTitle(humanizeToolName2(toolName));
|
|
18899
19481
|
const ids = grouped.get(key);
|
|
18900
19482
|
const id = ids?.shift();
|
|
@@ -18949,7 +19531,7 @@ function normalizeContent3(value) {
|
|
|
18949
19531
|
return item;
|
|
18950
19532
|
}
|
|
18951
19533
|
if (typeof item === "object" && item !== null) {
|
|
18952
|
-
return
|
|
19534
|
+
return readString14(item, "text") ?? "";
|
|
18953
19535
|
}
|
|
18954
19536
|
return "";
|
|
18955
19537
|
}).filter(Boolean).join("");
|
|
@@ -18984,8 +19566,8 @@ function quoteIdentifier2(value) {
|
|
|
18984
19566
|
return `"${value.replaceAll('"', '""')}"`;
|
|
18985
19567
|
}
|
|
18986
19568
|
async function isFile2(filePath) {
|
|
18987
|
-
return
|
|
18988
|
-
if (
|
|
19569
|
+
return stat15(filePath).then((value) => value.isFile()).catch((error) => {
|
|
19570
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
18989
19571
|
return false;
|
|
18990
19572
|
}
|
|
18991
19573
|
throw error;
|
|
@@ -18994,7 +19576,7 @@ async function isFile2(filePath) {
|
|
|
18994
19576
|
function isValidProfileName3(value) {
|
|
18995
19577
|
return typeof value === "string" && /^[a-zA-Z0-9._-]{1,64}$/u.test(value);
|
|
18996
19578
|
}
|
|
18997
|
-
function
|
|
19579
|
+
function readString14(payload, key) {
|
|
18998
19580
|
const value = payload[key];
|
|
18999
19581
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19000
19582
|
}
|
|
@@ -19015,7 +19597,7 @@ function readNumber3(value) {
|
|
|
19015
19597
|
function toRecord12(value) {
|
|
19016
19598
|
return typeof value === "object" && value !== null ? value : {};
|
|
19017
19599
|
}
|
|
19018
|
-
function
|
|
19600
|
+
function isNodeError16(error, code) {
|
|
19019
19601
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
19020
19602
|
}
|
|
19021
19603
|
|
|
@@ -19112,7 +19694,7 @@ var RunToolEventIdCoalescer = class {
|
|
|
19112
19694
|
}
|
|
19113
19695
|
};
|
|
19114
19696
|
function readToolEventType(event) {
|
|
19115
|
-
const type =
|
|
19697
|
+
const type = readString15(event.payload, "type") ?? readString15(event.payload, "event") ?? event.payloadType;
|
|
19116
19698
|
return type.startsWith("tool.") ? type : null;
|
|
19117
19699
|
}
|
|
19118
19700
|
function isTerminalToolEvent(type) {
|
|
@@ -19123,14 +19705,14 @@ function hasStableToolEventId(payload) {
|
|
|
19123
19705
|
const call = toRecord13(payload.tool_call ?? payload.toolCall);
|
|
19124
19706
|
const fn = toRecord13(call.function ?? payload.function);
|
|
19125
19707
|
return Boolean(
|
|
19126
|
-
|
|
19708
|
+
readString15(payload, "tool_call_id") ?? readString15(payload, "toolCallId") ?? readString15(payload, "call_id") ?? readString15(payload, "id") ?? readString15(tool, "id") ?? readString15(call, "id") ?? readString15(fn, "id")
|
|
19127
19709
|
);
|
|
19128
19710
|
}
|
|
19129
19711
|
function readToolName(payload) {
|
|
19130
19712
|
const tool = toRecord13(payload.tool);
|
|
19131
19713
|
const call = toRecord13(payload.tool_call ?? payload.toolCall);
|
|
19132
19714
|
const fn = toRecord13(call.function ?? payload.function);
|
|
19133
|
-
return
|
|
19715
|
+
return readString15(payload, "tool_name") ?? readString15(payload, "toolName") ?? readString15(payload, "name") ?? readString15(payload, "tool") ?? readString15(tool, "name") ?? readString15(call, "name") ?? readString15(fn, "name") ?? "tool";
|
|
19134
19716
|
}
|
|
19135
19717
|
function withGeneratedToolEventId(event, id) {
|
|
19136
19718
|
return {
|
|
@@ -19187,7 +19769,7 @@ function stableStringify2(value) {
|
|
|
19187
19769
|
function hashStableValue(value) {
|
|
19188
19770
|
return createHash4("sha256").update(value).digest("hex").slice(0, 16);
|
|
19189
19771
|
}
|
|
19190
|
-
function
|
|
19772
|
+
function readString15(payload, key) {
|
|
19191
19773
|
const value = payload[key];
|
|
19192
19774
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19193
19775
|
}
|
|
@@ -19215,8 +19797,8 @@ function normalizeHermesStreamEvent(event) {
|
|
|
19215
19797
|
};
|
|
19216
19798
|
}
|
|
19217
19799
|
if (event.eventName === "hermes.tool.progress") {
|
|
19218
|
-
const toolName =
|
|
19219
|
-
const preview =
|
|
19800
|
+
const toolName = readString16(event.payload, "tool") ?? readString16(event.payload, "name") ?? "tool";
|
|
19801
|
+
const preview = readString16(event.payload, "label") ?? readString16(event.payload, "preview") ?? toolName;
|
|
19220
19802
|
return {
|
|
19221
19803
|
...event,
|
|
19222
19804
|
payloadType: "tool.started",
|
|
@@ -19292,7 +19874,7 @@ function normalizeHermesResponseEvent(event) {
|
|
|
19292
19874
|
}
|
|
19293
19875
|
function normalizeResponseCreated(event) {
|
|
19294
19876
|
const response = toRecord14(event.payload.response ?? event.payload);
|
|
19295
|
-
const responseId =
|
|
19877
|
+
const responseId = readString16(response, "id") ?? readString16(event.payload, "id");
|
|
19296
19878
|
return responseId ? {
|
|
19297
19879
|
...event,
|
|
19298
19880
|
payloadType: "response.created",
|
|
@@ -19305,10 +19887,10 @@ function normalizeResponseCreated(event) {
|
|
|
19305
19887
|
}
|
|
19306
19888
|
function normalizeResponseOutputItemAdded(event) {
|
|
19307
19889
|
const item = toRecord14(event.payload.item);
|
|
19308
|
-
if (
|
|
19890
|
+
if (readString16(item, "type") !== "function_call") {
|
|
19309
19891
|
return null;
|
|
19310
19892
|
}
|
|
19311
|
-
const toolName =
|
|
19893
|
+
const toolName = readString16(item, "name") ?? "tool";
|
|
19312
19894
|
const argumentsValue = parseJsonValue3(item.arguments) ?? item.arguments;
|
|
19313
19895
|
return {
|
|
19314
19896
|
...event,
|
|
@@ -19318,16 +19900,16 @@ function normalizeResponseOutputItemAdded(event) {
|
|
|
19318
19900
|
tool: toolName,
|
|
19319
19901
|
tool_name: toolName,
|
|
19320
19902
|
name: toolName,
|
|
19321
|
-
tool_call_id:
|
|
19903
|
+
tool_call_id: readString16(item, "call_id") ?? readString16(item, "id"),
|
|
19322
19904
|
arguments: argumentsValue,
|
|
19323
19905
|
preview: toolName,
|
|
19324
|
-
response_item_id:
|
|
19906
|
+
response_item_id: readString16(item, "id") ?? void 0
|
|
19325
19907
|
}
|
|
19326
19908
|
};
|
|
19327
19909
|
}
|
|
19328
19910
|
function normalizeResponseOutputItemDone(event) {
|
|
19329
19911
|
const item = toRecord14(event.payload.item);
|
|
19330
|
-
if (
|
|
19912
|
+
if (readString16(item, "type") === "message") {
|
|
19331
19913
|
const delta = extractResponseAssistantText({ output: [item] });
|
|
19332
19914
|
return delta ? {
|
|
19333
19915
|
...event,
|
|
@@ -19335,7 +19917,7 @@ function normalizeResponseOutputItemDone(event) {
|
|
|
19335
19917
|
payload: { type: "message.delta", delta }
|
|
19336
19918
|
} : null;
|
|
19337
19919
|
}
|
|
19338
|
-
if (
|
|
19920
|
+
if (readString16(item, "type") !== "function_call_output") {
|
|
19339
19921
|
return null;
|
|
19340
19922
|
}
|
|
19341
19923
|
const output = readResponseItemOutput(item.output);
|
|
@@ -19345,12 +19927,12 @@ function normalizeResponseOutputItemDone(event) {
|
|
|
19345
19927
|
payloadType: "tool.completed",
|
|
19346
19928
|
payload: {
|
|
19347
19929
|
type: "tool.completed",
|
|
19348
|
-
tool_call_id:
|
|
19349
|
-
status:
|
|
19930
|
+
tool_call_id: readString16(item, "call_id") ?? readString16(item, "id"),
|
|
19931
|
+
status: readString16(item, "status") ?? "completed",
|
|
19350
19932
|
output,
|
|
19351
19933
|
content: output,
|
|
19352
19934
|
result: parsedOutput ?? output,
|
|
19353
|
-
response_item_id:
|
|
19935
|
+
response_item_id: readString16(item, "id") ?? void 0
|
|
19354
19936
|
}
|
|
19355
19937
|
};
|
|
19356
19938
|
}
|
|
@@ -19361,7 +19943,7 @@ function normalizeResponseCompleted(event) {
|
|
|
19361
19943
|
payloadType: "run.completed",
|
|
19362
19944
|
payload: {
|
|
19363
19945
|
type: "run.completed",
|
|
19364
|
-
response_id:
|
|
19946
|
+
response_id: readString16(response, "id") ?? readString16(event.payload, "id"),
|
|
19365
19947
|
usage: toRecord14(response.usage),
|
|
19366
19948
|
response
|
|
19367
19949
|
}
|
|
@@ -19375,9 +19957,9 @@ function normalizeResponseFailed(event) {
|
|
|
19375
19957
|
payloadType: "run.failed",
|
|
19376
19958
|
payload: {
|
|
19377
19959
|
type: "run.failed",
|
|
19378
|
-
response_id:
|
|
19960
|
+
response_id: readString16(response, "id") ?? readString16(event.payload, "id"),
|
|
19379
19961
|
error: {
|
|
19380
|
-
message:
|
|
19962
|
+
message: readString16(error, "message") ?? readString16(event.payload, "message") ?? "Hermes run failed"
|
|
19381
19963
|
},
|
|
19382
19964
|
usage: toRecord14(response.usage),
|
|
19383
19965
|
response
|
|
@@ -19404,7 +19986,7 @@ function readErrorMessage3(payload) {
|
|
|
19404
19986
|
return payload.error.trim();
|
|
19405
19987
|
}
|
|
19406
19988
|
const error = toRecord14(payload.error);
|
|
19407
|
-
return
|
|
19989
|
+
return readString16(error, "message") ?? readString16(payload, "message");
|
|
19408
19990
|
}
|
|
19409
19991
|
function readDelta(payload) {
|
|
19410
19992
|
return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
|
|
@@ -19451,7 +20033,7 @@ function readChatCompletionDelta(payload) {
|
|
|
19451
20033
|
}
|
|
19452
20034
|
function readChatCompletionFinishReason(payload) {
|
|
19453
20035
|
const choice = readFirstChoice(payload);
|
|
19454
|
-
return
|
|
20036
|
+
return readString16(choice, "finish_reason") ?? readString16(choice, "finishReason");
|
|
19455
20037
|
}
|
|
19456
20038
|
function readChatCompletionUsage(payload) {
|
|
19457
20039
|
const usage = toRecord14(payload.usage);
|
|
@@ -19491,7 +20073,7 @@ function readAssistantTextFromChoices(payload) {
|
|
|
19491
20073
|
return null;
|
|
19492
20074
|
}
|
|
19493
20075
|
const messages2 = choices.map(toRecord14).map((choice) => toRecord14(choice.message ?? choice.delta)).filter((message) => {
|
|
19494
|
-
const role =
|
|
20076
|
+
const role = readString16(message, "role");
|
|
19495
20077
|
return !role || role === "assistant";
|
|
19496
20078
|
}).map(readResponseMessageText).filter((text) => Boolean(text?.trim()));
|
|
19497
20079
|
return messages2.length > 0 ? messages2.join("\n\n") : null;
|
|
@@ -19507,7 +20089,7 @@ function readInteger3(payload, key) {
|
|
|
19507
20089
|
}
|
|
19508
20090
|
return void 0;
|
|
19509
20091
|
}
|
|
19510
|
-
function
|
|
20092
|
+
function readString16(payload, key) {
|
|
19511
20093
|
const value = payload[key];
|
|
19512
20094
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19513
20095
|
}
|
|
@@ -19520,8 +20102,8 @@ function readResponseOutputItemText(value) {
|
|
|
19520
20102
|
return value;
|
|
19521
20103
|
}
|
|
19522
20104
|
const item = toRecord14(value);
|
|
19523
|
-
const type =
|
|
19524
|
-
const role =
|
|
20105
|
+
const type = readString16(item, "type");
|
|
20106
|
+
const role = readString16(item, "role");
|
|
19525
20107
|
if (type && type !== "message" && type !== "output_text" && type !== "text") {
|
|
19526
20108
|
return null;
|
|
19527
20109
|
}
|
|
@@ -19547,7 +20129,7 @@ function readResponseContentText(value) {
|
|
|
19547
20129
|
return partValue;
|
|
19548
20130
|
}
|
|
19549
20131
|
const part = toRecord14(partValue);
|
|
19550
|
-
const type =
|
|
20132
|
+
const type = readString16(part, "type");
|
|
19551
20133
|
if (type && !isVisibleResponseTextPart(type)) {
|
|
19552
20134
|
return null;
|
|
19553
20135
|
}
|
|
@@ -19695,7 +20277,17 @@ var ConversationRunLifecycle = class {
|
|
|
19695
20277
|
});
|
|
19696
20278
|
return void 0;
|
|
19697
20279
|
});
|
|
19698
|
-
|
|
20280
|
+
if (deliveryStagingDir) {
|
|
20281
|
+
registerDeliveryContext({
|
|
20282
|
+
conversationId,
|
|
20283
|
+
runId,
|
|
20284
|
+
profile: run.profile,
|
|
20285
|
+
stagingDir: deliveryStagingDir,
|
|
20286
|
+
sessionId: hermesSessionId,
|
|
20287
|
+
taskId: hermesSessionId
|
|
20288
|
+
});
|
|
20289
|
+
}
|
|
20290
|
+
const instructions = buildRunInstructions(run);
|
|
19699
20291
|
let estimateConversationHistory = conversationHistory;
|
|
19700
20292
|
if (!shouldBuildConversationHistory) {
|
|
19701
20293
|
estimateConversationHistory = await buildConversationHistory({
|
|
@@ -19829,6 +20421,11 @@ var ConversationRunLifecycle = class {
|
|
|
19829
20421
|
}
|
|
19830
20422
|
const responseSessionId = response.headers.get("x-hermes-session-id")?.trim();
|
|
19831
20423
|
if (responseSessionId) {
|
|
20424
|
+
addDeliveryContextIdentifiers({
|
|
20425
|
+
runId,
|
|
20426
|
+
sessionId: responseSessionId,
|
|
20427
|
+
taskId: responseSessionId
|
|
20428
|
+
});
|
|
19832
20429
|
await this.rememberRunHermesSessionId(
|
|
19833
20430
|
conversationId,
|
|
19834
20431
|
runId,
|
|
@@ -19864,6 +20461,11 @@ var ConversationRunLifecycle = class {
|
|
|
19864
20461
|
await this.updateRun(conversationId, runId, {
|
|
19865
20462
|
hermes_run_id: hermesRun.run_id
|
|
19866
20463
|
});
|
|
20464
|
+
addDeliveryContextIdentifiers({
|
|
20465
|
+
runId,
|
|
20466
|
+
sessionId: hermesSessionId,
|
|
20467
|
+
taskId: hermesRun.run_id
|
|
20468
|
+
});
|
|
19867
20469
|
const response = await streamHermesRunEvents(hermesRun.run_id, {
|
|
19868
20470
|
logger: this.deps.logger,
|
|
19869
20471
|
profileName: run.profile,
|
|
@@ -19915,6 +20517,7 @@ var ConversationRunLifecycle = class {
|
|
|
19915
20517
|
if (this.deps.activeRunControllers.get(runId)?.controller === controller) {
|
|
19916
20518
|
this.deps.activeRunControllers.delete(runId);
|
|
19917
20519
|
}
|
|
20520
|
+
unregisterDeliveryContext(runId);
|
|
19918
20521
|
}
|
|
19919
20522
|
}
|
|
19920
20523
|
async ensureUsageProbeBeforeRun(input) {
|
|
@@ -20780,7 +21383,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
20780
21383
|
}
|
|
20781
21384
|
const textPart = assistant.parts.find((part) => part.type === "text");
|
|
20782
21385
|
const currentText = textPart?.text ?? "";
|
|
20783
|
-
const pendingDeliveryText =
|
|
21386
|
+
const pendingDeliveryText = readString17(
|
|
20784
21387
|
toRecord15(assistant.hermes),
|
|
20785
21388
|
"pending_media_delivery_text"
|
|
20786
21389
|
);
|
|
@@ -21195,7 +21798,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
21195
21798
|
includeDisabled: true
|
|
21196
21799
|
});
|
|
21197
21800
|
return new Set(
|
|
21198
|
-
jobs.map((job) =>
|
|
21801
|
+
jobs.map((job) => readString17(job, "id") ?? readString17(job, "job_id")).filter((id) => Boolean(id))
|
|
21199
21802
|
);
|
|
21200
21803
|
}
|
|
21201
21804
|
async bindNewCronJobsCreatedByRun(input) {
|
|
@@ -21215,16 +21818,9 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
21215
21818
|
});
|
|
21216
21819
|
}
|
|
21217
21820
|
};
|
|
21218
|
-
function buildRunInstructions(run
|
|
21821
|
+
function buildRunInstructions(run) {
|
|
21219
21822
|
return [
|
|
21220
21823
|
HERMES_LINK_DELIVERY_INSTRUCTIONS,
|
|
21221
|
-
...deliveryStagingDir ? [
|
|
21222
|
-
"",
|
|
21223
|
-
"Delivery staging directory for this run:",
|
|
21224
|
-
`- ${deliveryStagingDir}`,
|
|
21225
|
-
"Copy every deliverable file into this directory, then run:",
|
|
21226
|
-
`hermeslink deliver ${quoteShellArg(deliveryStagingDir)}`
|
|
21227
|
-
] : [],
|
|
21228
21824
|
"",
|
|
21229
21825
|
"Current runtime selected by Hermes Link:",
|
|
21230
21826
|
`- Model: ${run.model ?? "hermes-agent"}`,
|
|
@@ -21268,9 +21864,6 @@ function appendMediaImportFailureNotice(message) {
|
|
|
21268
21864
|
media_import_failure_notice_appended: true
|
|
21269
21865
|
};
|
|
21270
21866
|
}
|
|
21271
|
-
function quoteShellArg(value) {
|
|
21272
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
21273
|
-
}
|
|
21274
21867
|
function formatMediaImportFailureNotice(failures, language = "zh-CN") {
|
|
21275
21868
|
const filenames = failures.map((failure) => failure.filename);
|
|
21276
21869
|
const target = language === "en" ? filenames.length === 1 ? `"${filenames[0]}"` : `${filenames.length} files (${formatFilenameList(filenames, language)})` : filenames.length === 1 ? `\u6587\u4EF6\u201C${filenames[0]}\u201D` : `${filenames.length} \u4E2A\u6587\u4EF6\uFF08${formatFilenameList(filenames, language)}\uFF09`;
|
|
@@ -21299,13 +21892,13 @@ function formatFilenameList(filenames, language = "zh-CN") {
|
|
|
21299
21892
|
}
|
|
21300
21893
|
async function readdirWithDirs(directory) {
|
|
21301
21894
|
return readdir9(directory, { withFileTypes: true }).catch((error) => {
|
|
21302
|
-
if (
|
|
21895
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
21303
21896
|
return [];
|
|
21304
21897
|
}
|
|
21305
21898
|
throw error;
|
|
21306
21899
|
});
|
|
21307
21900
|
}
|
|
21308
|
-
function
|
|
21901
|
+
function readString17(payload, key) {
|
|
21309
21902
|
const value = payload[key];
|
|
21310
21903
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
21311
21904
|
}
|
|
@@ -21402,13 +21995,13 @@ function isFileSearchCompletion(payloadType, payload) {
|
|
|
21402
21995
|
const toolCall = toRecord15(payload.tool_call ?? payload.toolCall);
|
|
21403
21996
|
const fn = toRecord15(toolCall.function ?? payload.function);
|
|
21404
21997
|
const candidates = [
|
|
21405
|
-
|
|
21406
|
-
|
|
21407
|
-
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21998
|
+
readString17(payload, "tool_name"),
|
|
21999
|
+
readString17(payload, "toolName"),
|
|
22000
|
+
readString17(payload, "name"),
|
|
22001
|
+
readString17(payload, "tool"),
|
|
22002
|
+
readString17(tool, "name"),
|
|
22003
|
+
readString17(toolCall, "name"),
|
|
22004
|
+
readString17(fn, "name")
|
|
21412
22005
|
].filter((value) => Boolean(value)).map(normalizeToolName);
|
|
21413
22006
|
return candidates.some(
|
|
21414
22007
|
(name) => [
|
|
@@ -21646,13 +22239,13 @@ function readResponseId(payload) {
|
|
|
21646
22239
|
return null;
|
|
21647
22240
|
}
|
|
21648
22241
|
const response = toRecord15(payload.response);
|
|
21649
|
-
return
|
|
22242
|
+
return readString17(payload, "response_id") ?? readString17(response, "id");
|
|
21650
22243
|
}
|
|
21651
22244
|
function readRunId(payload) {
|
|
21652
22245
|
if (!payload) {
|
|
21653
22246
|
return null;
|
|
21654
22247
|
}
|
|
21655
|
-
return
|
|
22248
|
+
return readString17(payload, "run_id") ?? readString17(payload, "runId");
|
|
21656
22249
|
}
|
|
21657
22250
|
function isCompletedRunStatus(status) {
|
|
21658
22251
|
return status === "completed" || status === "complete" || status === "succeeded" || status === "success" || status === "done";
|
|
@@ -21668,7 +22261,7 @@ function readStatusErrorMessage(value) {
|
|
|
21668
22261
|
return value.trim();
|
|
21669
22262
|
}
|
|
21670
22263
|
const record = toRecord15(value);
|
|
21671
|
-
return
|
|
22264
|
+
return readString17(record, "message") ?? readString17(record, "error");
|
|
21672
22265
|
}
|
|
21673
22266
|
function formatUnknownErrorMessage(error) {
|
|
21674
22267
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -21706,7 +22299,7 @@ async function sleep(ms, signal) {
|
|
|
21706
22299
|
);
|
|
21707
22300
|
});
|
|
21708
22301
|
}
|
|
21709
|
-
function
|
|
22302
|
+
function isNodeError17(error, code) {
|
|
21710
22303
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
21711
22304
|
return false;
|
|
21712
22305
|
}
|
|
@@ -22331,6 +22924,38 @@ var ConversationService = class {
|
|
|
22331
22924
|
);
|
|
22332
22925
|
});
|
|
22333
22926
|
}
|
|
22927
|
+
async deliverFilesFromTool(input) {
|
|
22928
|
+
const files = normalizeDeliveryFileInputs(input.files);
|
|
22929
|
+
if (files.length === 0) {
|
|
22930
|
+
throw new LinkHttpError(
|
|
22931
|
+
400,
|
|
22932
|
+
"delivery_files_required",
|
|
22933
|
+
"At least one deliverable file path is required"
|
|
22934
|
+
);
|
|
22935
|
+
}
|
|
22936
|
+
const context = findDeliveryContext({
|
|
22937
|
+
profile: input.profile,
|
|
22938
|
+
taskId: input.taskId,
|
|
22939
|
+
sessionId: input.sessionId
|
|
22940
|
+
});
|
|
22941
|
+
const staged = await copyFilesIntoDeliveryContext(
|
|
22942
|
+
this.paths,
|
|
22943
|
+
context,
|
|
22944
|
+
files
|
|
22945
|
+
);
|
|
22946
|
+
if (staged.staged.length === 0) {
|
|
22947
|
+
throw new LinkHttpError(
|
|
22948
|
+
400,
|
|
22949
|
+
"delivery_files_not_staged",
|
|
22950
|
+
"No deliverable files could be staged"
|
|
22951
|
+
);
|
|
22952
|
+
}
|
|
22953
|
+
return {
|
|
22954
|
+
...await this.deliverStagedFiles(context.stagingDir),
|
|
22955
|
+
staged_count: staged.staged.length,
|
|
22956
|
+
skipped_files: staged.skipped
|
|
22957
|
+
};
|
|
22958
|
+
}
|
|
22334
22959
|
async getMessages(conversationId, options = {}) {
|
|
22335
22960
|
return this.queries.getMessages(conversationId, options);
|
|
22336
22961
|
}
|
|
@@ -23488,7 +24113,7 @@ async function readRawBody(request, maxBytes) {
|
|
|
23488
24113
|
}
|
|
23489
24114
|
return Buffer.concat(chunks);
|
|
23490
24115
|
}
|
|
23491
|
-
function
|
|
24116
|
+
function readString18(body, key) {
|
|
23492
24117
|
const value = body[key];
|
|
23493
24118
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
23494
24119
|
}
|
|
@@ -23523,7 +24148,7 @@ function readSupportedLanguage(value) {
|
|
|
23523
24148
|
return null;
|
|
23524
24149
|
}
|
|
23525
24150
|
function readOptionalProfileName(body) {
|
|
23526
|
-
return
|
|
24151
|
+
return readString18(body, "profile") ?? readString18(body, "profile_name") ?? readString18(body, "profileName") ?? void 0;
|
|
23527
24152
|
}
|
|
23528
24153
|
function readStringArray(body, ...keys) {
|
|
23529
24154
|
for (const key of keys) {
|
|
@@ -23942,7 +24567,7 @@ function registerConversationRoutes(router, options) {
|
|
|
23942
24567
|
ok: true,
|
|
23943
24568
|
conversation: localizeConversationSummary(
|
|
23944
24569
|
await conversations.createConversation({
|
|
23945
|
-
title:
|
|
24570
|
+
title: readString18(body, "title") ?? void 0,
|
|
23946
24571
|
profileName: readOptionalProfileName(body),
|
|
23947
24572
|
accountId: auth.accountId,
|
|
23948
24573
|
appInstanceId: auth.appInstanceId
|
|
@@ -24021,7 +24646,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24021
24646
|
const auth = await authenticateRequest(ctx, paths);
|
|
24022
24647
|
const language = readPreferredLanguage(ctx);
|
|
24023
24648
|
const body = await readJsonBody(ctx.req);
|
|
24024
|
-
const content =
|
|
24649
|
+
const content = readString18(body, "content") ?? readString18(body, "text") ?? readString18(body, "input") ?? "";
|
|
24025
24650
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
24026
24651
|
if (!content && attachments.length === 0) {
|
|
24027
24652
|
throw new LinkHttpError(
|
|
@@ -24038,7 +24663,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24038
24663
|
conversationId: ctx.params.conversationId,
|
|
24039
24664
|
content,
|
|
24040
24665
|
attachments,
|
|
24041
|
-
clientMessageId:
|
|
24666
|
+
clientMessageId: readString18(body, "client_message_id") ?? readString18(body, "clientMessageId") ?? void 0,
|
|
24042
24667
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
24043
24668
|
profileName: readOptionalProfileName(body),
|
|
24044
24669
|
accountId: auth.accountId,
|
|
@@ -24052,7 +24677,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24052
24677
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
24053
24678
|
await authenticateRequest(ctx, paths);
|
|
24054
24679
|
const body = await readJsonBody(ctx.req);
|
|
24055
|
-
const modelId =
|
|
24680
|
+
const modelId = readString18(body, "model_id") ?? readString18(body, "modelId") ?? readString18(body, "model");
|
|
24056
24681
|
if (!modelId) {
|
|
24057
24682
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
24058
24683
|
}
|
|
@@ -24087,7 +24712,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24087
24712
|
await authenticateRequest(ctx, paths);
|
|
24088
24713
|
const language = readPreferredLanguage(ctx);
|
|
24089
24714
|
const body = await readJsonBody(ctx.req);
|
|
24090
|
-
const title =
|
|
24715
|
+
const title = readString18(body, "title") ?? readString18(body, "name") ?? readString18(body, "display_name");
|
|
24091
24716
|
if (!title) {
|
|
24092
24717
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
24093
24718
|
}
|
|
@@ -24281,7 +24906,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24281
24906
|
async (ctx) => {
|
|
24282
24907
|
await authenticateRequest(ctx, paths);
|
|
24283
24908
|
const body = await readJsonBody(ctx.req);
|
|
24284
|
-
const scope =
|
|
24909
|
+
const scope = readString18(body, "scope") ?? "always";
|
|
24285
24910
|
ctx.body = {
|
|
24286
24911
|
ok: true,
|
|
24287
24912
|
...await conversations.resolveApproval({
|
|
@@ -24492,7 +25117,7 @@ function resolveConversationEventCursor(input) {
|
|
|
24492
25117
|
return Math.max(queryAfter, headerAfter);
|
|
24493
25118
|
}
|
|
24494
25119
|
function readConversationClearPlanTargetStatus(body) {
|
|
24495
|
-
const raw =
|
|
25120
|
+
const raw = readString18(body, "target_status") ?? readString18(body, "targetStatus") ?? "active";
|
|
24496
25121
|
if (raw === "active" || raw === "archived") {
|
|
24497
25122
|
return raw;
|
|
24498
25123
|
}
|
|
@@ -24618,11 +25243,11 @@ function isSseRequestContext(ctx) {
|
|
|
24618
25243
|
}
|
|
24619
25244
|
return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
|
|
24620
25245
|
}
|
|
24621
|
-
function isSseRequestPath(
|
|
24622
|
-
if (!
|
|
25246
|
+
function isSseRequestPath(path32) {
|
|
25247
|
+
if (!path32) {
|
|
24623
25248
|
return false;
|
|
24624
25249
|
}
|
|
24625
|
-
return
|
|
25250
|
+
return path32 === "/api/v1/conversations/events" || path32 === "/api/v1/profile-creation/events" || path32 === "/api/v1/hermes/update/events" || path32 === "/api/v1/link/update/events" || /^\/api\/v1\/conversations\/[^/]+\/events$/u.test(path32) || /^\/api\/v1\/runs\/[^/]+\/events$/u.test(path32);
|
|
24626
25251
|
}
|
|
24627
25252
|
function isExpectedClientDisconnectError2(error, options = {}) {
|
|
24628
25253
|
if (!(error instanceof Error)) {
|
|
@@ -24723,6 +25348,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24723
25348
|
router.get("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24724
25349
|
await authenticateRequest(ctx, paths);
|
|
24725
25350
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25351
|
+
assertCronJobId(ctx.params.jobId);
|
|
24726
25352
|
ctx.set("cache-control", "no-store");
|
|
24727
25353
|
const job = await getHermesCronJob(ctx.params.jobId, {
|
|
24728
25354
|
logger,
|
|
@@ -24736,9 +25362,46 @@ function registerCronJobRoutes(router, options) {
|
|
|
24736
25362
|
)
|
|
24737
25363
|
};
|
|
24738
25364
|
});
|
|
25365
|
+
router.get("/api/v1/profiles/:name/cron-jobs/:jobId/history", async (ctx) => {
|
|
25366
|
+
await authenticateRequest(ctx, paths);
|
|
25367
|
+
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25368
|
+
assertCronJobId(ctx.params.jobId);
|
|
25369
|
+
ctx.set("cache-control", "no-store");
|
|
25370
|
+
await getHermesCronJob(ctx.params.jobId, {
|
|
25371
|
+
logger,
|
|
25372
|
+
profileName: profile.name
|
|
25373
|
+
});
|
|
25374
|
+
const historyPage = await listHermesCronJobHistory({
|
|
25375
|
+
profileName: profile.name,
|
|
25376
|
+
jobId: ctx.params.jobId,
|
|
25377
|
+
limit: readPositiveInteger2(ctx.query.limit),
|
|
25378
|
+
offset: readPositiveInteger2(ctx.query.offset)
|
|
25379
|
+
});
|
|
25380
|
+
const history = historyPage.entries.map((entry) => ({
|
|
25381
|
+
id: entry.id,
|
|
25382
|
+
run_at: entry.runAt,
|
|
25383
|
+
content: entry.content,
|
|
25384
|
+
failed: entry.failed,
|
|
25385
|
+
status: entry.status,
|
|
25386
|
+
job_name: entry.jobName ?? null,
|
|
25387
|
+
truncated: entry.truncated
|
|
25388
|
+
}));
|
|
25389
|
+
ctx.body = {
|
|
25390
|
+
ok: true,
|
|
25391
|
+
profile,
|
|
25392
|
+
history,
|
|
25393
|
+
pagination: {
|
|
25394
|
+
total: historyPage.total,
|
|
25395
|
+
offset: historyPage.offset,
|
|
25396
|
+
limit: historyPage.limit,
|
|
25397
|
+
has_more: historyPage.hasMore
|
|
25398
|
+
}
|
|
25399
|
+
};
|
|
25400
|
+
});
|
|
24739
25401
|
router.patch("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24740
25402
|
const auth = await authenticateRequest(ctx, paths);
|
|
24741
25403
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25404
|
+
assertCronJobId(ctx.params.jobId);
|
|
24742
25405
|
const body = await readJsonBody(ctx.req);
|
|
24743
25406
|
const input = readCronJobUpdateInput(body);
|
|
24744
25407
|
const deliverTouched = readOptionalCronText(body, "deliver").present;
|
|
@@ -24771,6 +25434,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24771
25434
|
router.delete("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24772
25435
|
await authenticateRequest(ctx, paths);
|
|
24773
25436
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25437
|
+
assertCronJobId(ctx.params.jobId);
|
|
24774
25438
|
await deleteHermesCronJob(ctx.params.jobId, {
|
|
24775
25439
|
logger,
|
|
24776
25440
|
profileName: profile.name
|
|
@@ -24781,6 +25445,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24781
25445
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/pause", async (ctx) => {
|
|
24782
25446
|
await authenticateRequest(ctx, paths);
|
|
24783
25447
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25448
|
+
assertCronJobId(ctx.params.jobId);
|
|
24784
25449
|
const job = await runHermesCronJobAction(ctx.params.jobId, "pause", {
|
|
24785
25450
|
logger,
|
|
24786
25451
|
profileName: profile.name
|
|
@@ -24796,6 +25461,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24796
25461
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/resume", async (ctx) => {
|
|
24797
25462
|
await authenticateRequest(ctx, paths);
|
|
24798
25463
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25464
|
+
assertCronJobId(ctx.params.jobId);
|
|
24799
25465
|
const job = await runHermesCronJobAction(ctx.params.jobId, "resume", {
|
|
24800
25466
|
logger,
|
|
24801
25467
|
profileName: profile.name
|
|
@@ -24811,6 +25477,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24811
25477
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/run", async (ctx) => {
|
|
24812
25478
|
await authenticateRequest(ctx, paths);
|
|
24813
25479
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25480
|
+
assertCronJobId(ctx.params.jobId);
|
|
24814
25481
|
const job = await runHermesCronJobAction(ctx.params.jobId, "run", {
|
|
24815
25482
|
logger,
|
|
24816
25483
|
profileName: profile.name
|
|
@@ -24844,7 +25511,7 @@ function toHermesCronJobInput(input) {
|
|
|
24844
25511
|
};
|
|
24845
25512
|
}
|
|
24846
25513
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
24847
|
-
const jobId =
|
|
25514
|
+
const jobId = readString18(input.job, "id") ?? readString18(input.job, "job_id");
|
|
24848
25515
|
if (!jobId) {
|
|
24849
25516
|
return input.job;
|
|
24850
25517
|
}
|
|
@@ -24863,9 +25530,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
24863
25530
|
}
|
|
24864
25531
|
function readCronJobCreateInput(body) {
|
|
24865
25532
|
const input = {};
|
|
24866
|
-
const name =
|
|
24867
|
-
const prompt =
|
|
24868
|
-
const schedule =
|
|
25533
|
+
const name = readString18(body, "name") ?? readString18(body, "title");
|
|
25534
|
+
const prompt = readString18(body, "prompt") ?? readString18(body, "description") ?? readString18(body, "task");
|
|
25535
|
+
const schedule = readString18(body, "schedule");
|
|
24869
25536
|
if (!name) {
|
|
24870
25537
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
24871
25538
|
}
|
|
@@ -24886,7 +25553,7 @@ function readCronJobCreateInput(body) {
|
|
|
24886
25553
|
input.name = name;
|
|
24887
25554
|
input.prompt = prompt;
|
|
24888
25555
|
input.schedule = schedule;
|
|
24889
|
-
input.deliver =
|
|
25556
|
+
input.deliver = readString18(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
24890
25557
|
const skills = readOptionalCronSkills(body);
|
|
24891
25558
|
if (skills) {
|
|
24892
25559
|
input.skills = skills;
|
|
@@ -25006,6 +25673,12 @@ function attachCronJobProfile(job, profile) {
|
|
|
25006
25673
|
}
|
|
25007
25674
|
};
|
|
25008
25675
|
}
|
|
25676
|
+
function assertCronJobId(jobId) {
|
|
25677
|
+
if (/^[a-f0-9]{12}$/u.test(jobId)) {
|
|
25678
|
+
return;
|
|
25679
|
+
}
|
|
25680
|
+
throw new LinkHttpError(400, "cron_job_id_invalid", "Invalid cron job id");
|
|
25681
|
+
}
|
|
25009
25682
|
|
|
25010
25683
|
// src/http/routes/gateway-reload.ts
|
|
25011
25684
|
async function reloadGatewayAfterModelConfigChange(result, options) {
|
|
@@ -25213,9 +25886,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
25213
25886
|
});
|
|
25214
25887
|
}
|
|
25215
25888
|
function readModelConfigInput(body) {
|
|
25216
|
-
const id =
|
|
25217
|
-
const provider =
|
|
25218
|
-
const baseUrl =
|
|
25889
|
+
const id = readString18(body, "id") ?? readString18(body, "model_id") ?? readString18(body, "modelId");
|
|
25890
|
+
const provider = readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey");
|
|
25891
|
+
const baseUrl = readString18(body, "base_url") ?? readString18(body, "baseUrl");
|
|
25219
25892
|
if (!id || !provider || !baseUrl) {
|
|
25220
25893
|
throw new LinkHttpError(
|
|
25221
25894
|
400,
|
|
@@ -25225,21 +25898,21 @@ function readModelConfigInput(body) {
|
|
|
25225
25898
|
}
|
|
25226
25899
|
return {
|
|
25227
25900
|
id,
|
|
25228
|
-
originalModelId:
|
|
25229
|
-
originalProvider:
|
|
25230
|
-
originalBaseUrl:
|
|
25231
|
-
originalApiMode:
|
|
25901
|
+
originalModelId: readString18(body, "original_model_id") ?? readString18(body, "originalModelId") ?? readString18(body, "original_id") ?? void 0,
|
|
25902
|
+
originalProvider: readString18(body, "original_provider") ?? readString18(body, "originalProvider") ?? readString18(body, "original_provider_key") ?? readString18(body, "originalProviderKey") ?? void 0,
|
|
25903
|
+
originalBaseUrl: readString18(body, "original_base_url") ?? readString18(body, "originalBaseUrl") ?? void 0,
|
|
25904
|
+
originalApiMode: readString18(body, "original_api_mode") ?? readString18(body, "originalApiMode") ?? void 0,
|
|
25232
25905
|
provider,
|
|
25233
|
-
providerName:
|
|
25906
|
+
providerName: readString18(body, "provider_name") ?? readString18(body, "providerName") ?? void 0,
|
|
25234
25907
|
baseUrl,
|
|
25235
|
-
apiKey:
|
|
25236
|
-
apiMode:
|
|
25908
|
+
apiKey: readString18(body, "api_key") ?? readString18(body, "apiKey") ?? void 0,
|
|
25909
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0,
|
|
25237
25910
|
contextLength: readPositiveInteger2(
|
|
25238
25911
|
body.context_length ?? body.contextLength
|
|
25239
25912
|
),
|
|
25240
|
-
keyEnv:
|
|
25913
|
+
keyEnv: readString18(body, "key_env") ?? readString18(body, "keyEnv") ?? void 0,
|
|
25241
25914
|
setDefault: readBoolean3(body.set_default ?? body.setDefault),
|
|
25242
|
-
reasoningEffort:
|
|
25915
|
+
reasoningEffort: readString18(body, "reasoning_effort") ?? readString18(body, "reasoningEffort") ?? void 0,
|
|
25243
25916
|
supportsVision: readNullableBoolean(
|
|
25244
25917
|
body.supports_vision ?? body.supportsVision
|
|
25245
25918
|
)
|
|
@@ -25247,28 +25920,28 @@ function readModelConfigInput(body) {
|
|
|
25247
25920
|
}
|
|
25248
25921
|
function readModelDefaultsInput(body) {
|
|
25249
25922
|
return {
|
|
25250
|
-
taskModelId:
|
|
25251
|
-
taskModelProvider:
|
|
25252
|
-
taskModelBaseUrl:
|
|
25253
|
-
taskModelApiMode:
|
|
25254
|
-
compressionModelId:
|
|
25255
|
-
compressionModelProvider:
|
|
25256
|
-
compressionModelBaseUrl:
|
|
25257
|
-
compressionModelApiMode:
|
|
25258
|
-
reasoningEffort:
|
|
25259
|
-
imageInputMode:
|
|
25923
|
+
taskModelId: readString18(body, "task_model_id") ?? readString18(body, "taskModelId") ?? readString18(body, "default_model_id") ?? readString18(body, "defaultModelId") ?? void 0,
|
|
25924
|
+
taskModelProvider: readString18(body, "task_model_provider") ?? readString18(body, "taskModelProvider") ?? readString18(body, "default_model_provider") ?? readString18(body, "defaultModelProvider") ?? void 0,
|
|
25925
|
+
taskModelBaseUrl: readString18(body, "task_model_base_url") ?? readString18(body, "taskModelBaseUrl") ?? readString18(body, "default_model_base_url") ?? readString18(body, "defaultModelBaseUrl") ?? void 0,
|
|
25926
|
+
taskModelApiMode: readString18(body, "task_model_api_mode") ?? readString18(body, "taskModelApiMode") ?? readString18(body, "default_model_api_mode") ?? readString18(body, "defaultModelApiMode") ?? void 0,
|
|
25927
|
+
compressionModelId: readString18(body, "compression_model_id") ?? readString18(body, "compressionModelId") ?? void 0,
|
|
25928
|
+
compressionModelProvider: readString18(body, "compression_model_provider") ?? readString18(body, "compressionModelProvider") ?? void 0,
|
|
25929
|
+
compressionModelBaseUrl: readString18(body, "compression_model_base_url") ?? readString18(body, "compressionModelBaseUrl") ?? void 0,
|
|
25930
|
+
compressionModelApiMode: readString18(body, "compression_model_api_mode") ?? readString18(body, "compressionModelApiMode") ?? void 0,
|
|
25931
|
+
reasoningEffort: readString18(body, "reasoning_effort") ?? readString18(body, "reasoningEffort") ?? readString18(body, "default_reasoning_effort") ?? readString18(body, "defaultReasoningEffort") ?? void 0,
|
|
25932
|
+
imageInputMode: readString18(body, "image_input_mode") ?? readString18(body, "imageInputMode") ?? void 0
|
|
25260
25933
|
};
|
|
25261
25934
|
}
|
|
25262
25935
|
function readModelDeleteInput(body) {
|
|
25263
|
-
const id =
|
|
25936
|
+
const id = readString18(body, "model_id") ?? readString18(body, "modelId");
|
|
25264
25937
|
if (!id) {
|
|
25265
25938
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
25266
25939
|
}
|
|
25267
25940
|
return {
|
|
25268
25941
|
id,
|
|
25269
|
-
provider:
|
|
25270
|
-
baseUrl:
|
|
25271
|
-
apiMode:
|
|
25942
|
+
provider: readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey") ?? void 0,
|
|
25943
|
+
baseUrl: readString18(body, "base_url") ?? readString18(body, "baseUrl") ?? void 0,
|
|
25944
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0
|
|
25272
25945
|
};
|
|
25273
25946
|
}
|
|
25274
25947
|
function readNullableBoolean(value) {
|
|
@@ -25291,8 +25964,8 @@ function readNullableBoolean(value) {
|
|
|
25291
25964
|
return void 0;
|
|
25292
25965
|
}
|
|
25293
25966
|
function readModelConfigImportInput(body) {
|
|
25294
|
-
const sourceProfileName =
|
|
25295
|
-
const modelId =
|
|
25967
|
+
const sourceProfileName = readString18(body, "source_profile") ?? readString18(body, "sourceProfile") ?? readString18(body, "source_profile_name") ?? readString18(body, "sourceProfileName");
|
|
25968
|
+
const modelId = readString18(body, "model_id") ?? readString18(body, "modelId") ?? readString18(body, "id");
|
|
25296
25969
|
if (!sourceProfileName || !modelId) {
|
|
25297
25970
|
throw new LinkHttpError(
|
|
25298
25971
|
400,
|
|
@@ -25303,9 +25976,9 @@ function readModelConfigImportInput(body) {
|
|
|
25303
25976
|
return {
|
|
25304
25977
|
sourceProfileName,
|
|
25305
25978
|
modelId,
|
|
25306
|
-
provider:
|
|
25307
|
-
baseUrl:
|
|
25308
|
-
apiMode:
|
|
25979
|
+
provider: readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey") ?? void 0,
|
|
25980
|
+
baseUrl: readString18(body, "base_url") ?? readString18(body, "baseUrl") ?? void 0,
|
|
25981
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0,
|
|
25309
25982
|
setDefault: readBoolean3(body.set_default ?? body.setDefault)
|
|
25310
25983
|
};
|
|
25311
25984
|
}
|
|
@@ -25396,20 +26069,20 @@ function errorMessage3(error) {
|
|
|
25396
26069
|
}
|
|
25397
26070
|
|
|
25398
26071
|
// src/hermes/profile-identity.ts
|
|
25399
|
-
import { readFile as readFile15, stat as
|
|
25400
|
-
import
|
|
26072
|
+
import { readFile as readFile15, stat as stat16 } from "fs/promises";
|
|
26073
|
+
import path23 from "path";
|
|
25401
26074
|
var MAX_SOUL_MD_LENGTH = 2e4;
|
|
25402
26075
|
async function readHermesProfileIdentity(profileName, paths) {
|
|
25403
26076
|
await assertProfileExists2(profileName, paths);
|
|
25404
26077
|
const soulPath = resolveSoulPath(profileName);
|
|
25405
26078
|
const content = await readFile15(soulPath, "utf8").catch((error) => {
|
|
25406
|
-
if (
|
|
26079
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
25407
26080
|
return null;
|
|
25408
26081
|
}
|
|
25409
26082
|
throw error;
|
|
25410
26083
|
});
|
|
25411
|
-
const fileStat = content === null ? null : await
|
|
25412
|
-
if (
|
|
26084
|
+
const fileStat = content === null ? null : await stat16(soulPath).catch((error) => {
|
|
26085
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
25413
26086
|
return null;
|
|
25414
26087
|
}
|
|
25415
26088
|
throw error;
|
|
@@ -25454,9 +26127,9 @@ async function assertProfileExists2(profileName, paths) {
|
|
|
25454
26127
|
}
|
|
25455
26128
|
}
|
|
25456
26129
|
function resolveSoulPath(profileName) {
|
|
25457
|
-
return
|
|
26130
|
+
return path23.join(resolveHermesProfileDir(profileName), "SOUL.md");
|
|
25458
26131
|
}
|
|
25459
|
-
function
|
|
26132
|
+
function isNodeError18(error, code) {
|
|
25460
26133
|
return error instanceof Error && "code" in error && error.code === code;
|
|
25461
26134
|
}
|
|
25462
26135
|
|
|
@@ -25465,18 +26138,18 @@ import { spawn as spawn2 } from "child_process";
|
|
|
25465
26138
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
25466
26139
|
import {
|
|
25467
26140
|
cp,
|
|
25468
|
-
mkdir as
|
|
26141
|
+
mkdir as mkdir12,
|
|
25469
26142
|
readFile as readFile17,
|
|
25470
|
-
rm as
|
|
25471
|
-
stat as
|
|
26143
|
+
rm as rm7,
|
|
26144
|
+
stat as stat18
|
|
25472
26145
|
} from "fs/promises";
|
|
25473
|
-
import
|
|
26146
|
+
import path25 from "path";
|
|
25474
26147
|
import YAML5 from "yaml";
|
|
25475
26148
|
|
|
25476
26149
|
// src/hermes/link-skill.ts
|
|
25477
|
-
import { readFile as readFile16, stat as
|
|
26150
|
+
import { readFile as readFile16, stat as stat17 } from "fs/promises";
|
|
25478
26151
|
import os5 from "os";
|
|
25479
|
-
import
|
|
26152
|
+
import path24 from "path";
|
|
25480
26153
|
import YAML4 from "yaml";
|
|
25481
26154
|
var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
|
|
25482
26155
|
var HERMES_LINK_SKILL_DIR = "hermes-link";
|
|
@@ -25561,7 +26234,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
|
|
|
25561
26234
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
25562
26235
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
25563
26236
|
const externalDir = resolveHermesLinkSkillExternalDir(paths);
|
|
25564
|
-
const skillPath =
|
|
26237
|
+
const skillPath = path24.join(
|
|
25565
26238
|
externalDir,
|
|
25566
26239
|
HERMES_LINK_SKILL_DIR,
|
|
25567
26240
|
HERMES_LINK_SKILL_FILE
|
|
@@ -25643,11 +26316,11 @@ function withDefaultProfilePlaceholder2(profiles) {
|
|
|
25643
26316
|
];
|
|
25644
26317
|
}
|
|
25645
26318
|
function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
|
|
25646
|
-
return
|
|
26319
|
+
return path24.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
|
|
25647
26320
|
}
|
|
25648
26321
|
async function writeHermesLinkSkill(skillPath) {
|
|
25649
26322
|
const existing = await readFile16(skillPath, "utf8").catch((error) => {
|
|
25650
|
-
if (
|
|
26323
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25651
26324
|
return null;
|
|
25652
26325
|
}
|
|
25653
26326
|
throw error;
|
|
@@ -25706,7 +26379,7 @@ async function ensureProfileUsesExternalSkillDir(profile, externalDir) {
|
|
|
25706
26379
|
async function readHermesConfigDocument3(configPath) {
|
|
25707
26380
|
const existingRaw = await readFile16(configPath, "utf8").catch(
|
|
25708
26381
|
(error) => {
|
|
25709
|
-
if (
|
|
26382
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25710
26383
|
return null;
|
|
25711
26384
|
}
|
|
25712
26385
|
throw error;
|
|
@@ -25738,11 +26411,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
|
|
|
25738
26411
|
const seen = new Set(
|
|
25739
26412
|
entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
|
|
25740
26413
|
);
|
|
25741
|
-
const normalizedExternalDir =
|
|
26414
|
+
const normalizedExternalDir = path24.resolve(externalDir);
|
|
25742
26415
|
return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
|
|
25743
26416
|
}
|
|
25744
26417
|
function externalDirsInclude(current, externalDir, hermesHome) {
|
|
25745
|
-
const normalizedExternalDir =
|
|
26418
|
+
const normalizedExternalDir = path24.resolve(externalDir);
|
|
25746
26419
|
return readExternalDirEntries(current).some(
|
|
25747
26420
|
(entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
|
|
25748
26421
|
);
|
|
@@ -25753,14 +26426,14 @@ function readExternalDirEntries(value) {
|
|
|
25753
26426
|
}
|
|
25754
26427
|
function resolveExternalDirEntry(entry, hermesHome) {
|
|
25755
26428
|
const expanded = expandHome(expandEnvVars(entry));
|
|
25756
|
-
return
|
|
26429
|
+
return path24.resolve(path24.isAbsolute(expanded) ? expanded : path24.join(hermesHome, expanded));
|
|
25757
26430
|
}
|
|
25758
26431
|
function expandHome(value) {
|
|
25759
26432
|
if (value === "~") {
|
|
25760
26433
|
return os5.homedir();
|
|
25761
26434
|
}
|
|
25762
|
-
if (value.startsWith(`~${
|
|
25763
|
-
return
|
|
26435
|
+
if (value.startsWith(`~${path24.sep}`) || value.startsWith("~/")) {
|
|
26436
|
+
return path24.join(os5.homedir(), value.slice(2));
|
|
25764
26437
|
}
|
|
25765
26438
|
return value;
|
|
25766
26439
|
}
|
|
@@ -25771,8 +26444,8 @@ function expandEnvVars(value) {
|
|
|
25771
26444
|
);
|
|
25772
26445
|
}
|
|
25773
26446
|
async function pathIsDirectory2(filePath) {
|
|
25774
|
-
return
|
|
25775
|
-
if (
|
|
26447
|
+
return stat17(filePath).then((value) => value.isDirectory()).catch((error) => {
|
|
26448
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25776
26449
|
return false;
|
|
25777
26450
|
}
|
|
25778
26451
|
throw error;
|
|
@@ -25793,7 +26466,7 @@ function toRecord16(value) {
|
|
|
25793
26466
|
function isRecord4(value) {
|
|
25794
26467
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25795
26468
|
}
|
|
25796
|
-
function
|
|
26469
|
+
function isNodeError19(error, code) {
|
|
25797
26470
|
return error instanceof Error && "code" in error && error.code === code;
|
|
25798
26471
|
}
|
|
25799
26472
|
|
|
@@ -25848,7 +26521,7 @@ async function startHermesProfileCreation(input, options) {
|
|
|
25848
26521
|
identity_error: null,
|
|
25849
26522
|
identity_template_id: normalized.identityTemplateId
|
|
25850
26523
|
};
|
|
25851
|
-
await
|
|
26524
|
+
await mkdir12(options.paths.runDir, { recursive: true, mode: 448 });
|
|
25852
26525
|
await writeProfileCreationState(options.paths, started);
|
|
25853
26526
|
await writer.write(`
|
|
25854
26527
|
=== profile creation started ${startedAt} ===
|
|
@@ -26312,9 +26985,9 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
26312
26985
|
return keys;
|
|
26313
26986
|
}
|
|
26314
26987
|
async function writeEnvValues(profileName, values) {
|
|
26315
|
-
const envPath =
|
|
26988
|
+
const envPath = path25.join(resolveHermesProfileDir(profileName), ".env");
|
|
26316
26989
|
const existingRaw = await readFile17(envPath, "utf8").catch((error) => {
|
|
26317
|
-
if (
|
|
26990
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26318
26991
|
return "";
|
|
26319
26992
|
}
|
|
26320
26993
|
throw error;
|
|
@@ -26349,12 +27022,12 @@ async function writeEnvValues(profileName, values) {
|
|
|
26349
27022
|
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
26350
27023
|
}
|
|
26351
27024
|
async function copySkills(sourceProfile, targetProfile) {
|
|
26352
|
-
const sourceSkills =
|
|
26353
|
-
const targetSkills =
|
|
27025
|
+
const sourceSkills = path25.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
27026
|
+
const targetSkills = path25.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
26354
27027
|
if (!await pathExists2(sourceSkills)) {
|
|
26355
27028
|
return;
|
|
26356
27029
|
}
|
|
26357
|
-
await
|
|
27030
|
+
await rm7(targetSkills, { recursive: true, force: true });
|
|
26358
27031
|
await cp(sourceSkills, targetSkills, {
|
|
26359
27032
|
recursive: true,
|
|
26360
27033
|
force: true,
|
|
@@ -26375,7 +27048,7 @@ function copyProperty(source, target, key) {
|
|
|
26375
27048
|
async function readYamlConfig(configPath) {
|
|
26376
27049
|
const existingRaw = await readFile17(configPath, "utf8").catch(
|
|
26377
27050
|
(error) => {
|
|
26378
|
-
if (
|
|
27051
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26379
27052
|
return null;
|
|
26380
27053
|
}
|
|
26381
27054
|
throw error;
|
|
@@ -26402,7 +27075,7 @@ async function failProfileCreation(input) {
|
|
|
26402
27075
|
await input.writer.write(`
|
|
26403
27076
|
Rolling back ${input.rollbackProfileName}...
|
|
26404
27077
|
`);
|
|
26405
|
-
await
|
|
27078
|
+
await rm7(resolveHermesProfileDir(input.rollbackProfileName), {
|
|
26406
27079
|
recursive: true,
|
|
26407
27080
|
force: true
|
|
26408
27081
|
}).catch(() => void 0);
|
|
@@ -26445,24 +27118,24 @@ async function readProfileCreationLogLines(paths) {
|
|
|
26445
27118
|
);
|
|
26446
27119
|
}
|
|
26447
27120
|
function profileCreationStatePath(paths) {
|
|
26448
|
-
return
|
|
27121
|
+
return path25.join(paths.runDir, "profile-create-state.json");
|
|
26449
27122
|
}
|
|
26450
27123
|
function profileCreationLogPath(paths) {
|
|
26451
|
-
return
|
|
27124
|
+
return path25.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
26452
27125
|
}
|
|
26453
27126
|
async function clearProfileCreationLogFiles(paths) {
|
|
26454
27127
|
const primary = profileCreationLogPath(paths);
|
|
26455
27128
|
await Promise.all([
|
|
26456
|
-
|
|
27129
|
+
rm7(primary, { force: true }).catch(() => void 0),
|
|
26457
27130
|
...Array.from(
|
|
26458
27131
|
{ length: PROFILE_CREATE_LOG_MAX_FILES },
|
|
26459
|
-
(_, index) =>
|
|
27132
|
+
(_, index) => rm7(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
26460
27133
|
)
|
|
26461
27134
|
]);
|
|
26462
27135
|
}
|
|
26463
27136
|
async function pathExists2(targetPath) {
|
|
26464
|
-
return await
|
|
26465
|
-
if (
|
|
27137
|
+
return await stat18(targetPath).then(() => true).catch((error) => {
|
|
27138
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26466
27139
|
return false;
|
|
26467
27140
|
}
|
|
26468
27141
|
throw error;
|
|
@@ -26509,7 +27182,7 @@ function formatEnvValue2(value) {
|
|
|
26509
27182
|
function escapeRegExp3(value) {
|
|
26510
27183
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
26511
27184
|
}
|
|
26512
|
-
function
|
|
27185
|
+
function isNodeError20(error, code) {
|
|
26513
27186
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
26514
27187
|
}
|
|
26515
27188
|
|
|
@@ -26594,16 +27267,16 @@ function readProfilePermissionsInput(body) {
|
|
|
26594
27267
|
const approvals = readOptionalObject(body, "approvals");
|
|
26595
27268
|
if (approvals) {
|
|
26596
27269
|
input.approvals = {
|
|
26597
|
-
mode:
|
|
27270
|
+
mode: readString18(approvals, "mode") ?? readString18(approvals, "approval_mode") ?? readString18(approvals, "approvalMode") ?? void 0,
|
|
26598
27271
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
26599
|
-
cronMode:
|
|
27272
|
+
cronMode: readString18(approvals, "cron_mode") ?? readString18(approvals, "cronMode") ?? void 0
|
|
26600
27273
|
};
|
|
26601
27274
|
}
|
|
26602
27275
|
const terminal = readOptionalObject(body, "terminal");
|
|
26603
27276
|
if (terminal) {
|
|
26604
27277
|
input.terminal = {
|
|
26605
|
-
backend:
|
|
26606
|
-
cwd:
|
|
27278
|
+
backend: readString18(terminal, "backend") ?? void 0,
|
|
27279
|
+
cwd: readString18(terminal, "cwd") ?? void 0,
|
|
26607
27280
|
containerCpu: readPositiveInteger2(
|
|
26608
27281
|
terminal.container_cpu ?? terminal.containerCpu
|
|
26609
27282
|
),
|
|
@@ -26720,9 +27393,9 @@ import {
|
|
|
26720
27393
|
access as access3,
|
|
26721
27394
|
readdir as readdir10,
|
|
26722
27395
|
readFile as readFile18,
|
|
26723
|
-
stat as
|
|
27396
|
+
stat as stat19
|
|
26724
27397
|
} from "fs/promises";
|
|
26725
|
-
import
|
|
27398
|
+
import path26 from "path";
|
|
26726
27399
|
import YAML6 from "yaml";
|
|
26727
27400
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
26728
27401
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -26866,14 +27539,23 @@ async function resetHermesMemoryStore(profileName, target) {
|
|
|
26866
27539
|
return readHermesProfileMemory(profileName);
|
|
26867
27540
|
}
|
|
26868
27541
|
async function saveHermesMemorySettings(profileName, patch) {
|
|
26869
|
-
const
|
|
26870
|
-
|
|
26871
|
-
|
|
26872
|
-
|
|
26873
|
-
|
|
26874
|
-
);
|
|
27542
|
+
const hasLimitPatch = patch.memoryCharLimit !== void 0 || patch.userCharLimit !== void 0;
|
|
27543
|
+
const hasProviderPatch = Object.keys(patch).some(
|
|
27544
|
+
(key) => key !== "memoryCharLimit" && key !== "userCharLimit"
|
|
27545
|
+
);
|
|
27546
|
+
if (hasProviderPatch) {
|
|
27547
|
+
const provider = await readActiveMemoryProvider(profileName);
|
|
27548
|
+
if (!provider) {
|
|
27549
|
+
throw new HermesMemoryError(
|
|
27550
|
+
"memory_settings_builtin_only",
|
|
27551
|
+
"\u5F53\u524D Profile \u4F7F\u7528 built-in memory\uFF0C\u6CA1\u6709\u53EF\u4FDD\u5B58\u7684\u5916\u90E8 provider \u8BBE\u7F6E\u3002"
|
|
27552
|
+
);
|
|
27553
|
+
}
|
|
27554
|
+
await saveProviderSettings(profileName, provider, patch);
|
|
27555
|
+
}
|
|
27556
|
+
if (hasLimitPatch) {
|
|
27557
|
+
await patchHermesMemoryLimits(profileName, patch);
|
|
26875
27558
|
}
|
|
26876
|
-
await saveProviderSettings(profileName, provider, patch);
|
|
26877
27559
|
return readHermesProfileMemory(profileName);
|
|
26878
27560
|
}
|
|
26879
27561
|
async function saveHermesMemoryProviderSettings(profileName, provider, patch) {
|
|
@@ -26910,9 +27592,9 @@ async function testHindsightProviderSettings(profileName, patch) {
|
|
|
26910
27592
|
const mode = normalizeHindsightMode(
|
|
26911
27593
|
patch.mode ?? config.mode ?? env.HINDSIGHT_MODE
|
|
26912
27594
|
);
|
|
26913
|
-
const apiUrl =
|
|
26914
|
-
const bankId =
|
|
26915
|
-
const apiKey =
|
|
27595
|
+
const apiUrl = readString19(patch.apiUrl) ?? readString19(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
|
|
27596
|
+
const bankId = readString19(patch.bankId) ?? readString19(config.bank_id) ?? "hermes";
|
|
27597
|
+
const apiKey = readString19(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key);
|
|
26916
27598
|
const baseUrl = normalizeHttpUrl(apiUrl);
|
|
26917
27599
|
if (!baseUrl) {
|
|
26918
27600
|
return {
|
|
@@ -27044,7 +27726,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
27044
27726
|
});
|
|
27045
27727
|
await patchJsonProviderConfig(
|
|
27046
27728
|
profileName,
|
|
27047
|
-
|
|
27729
|
+
path26.join("hindsight", "config.json"),
|
|
27048
27730
|
{
|
|
27049
27731
|
mode: patch.mode,
|
|
27050
27732
|
api_url: patch.apiUrl,
|
|
@@ -27194,7 +27876,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
27194
27876
|
const configPath = resolveHermesConfigPath(profileName);
|
|
27195
27877
|
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27196
27878
|
(error) => {
|
|
27197
|
-
if (
|
|
27879
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27198
27880
|
return null;
|
|
27199
27881
|
}
|
|
27200
27882
|
throw error;
|
|
@@ -27214,14 +27896,46 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
27214
27896
|
document.contents = document.createNode(config);
|
|
27215
27897
|
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
27216
27898
|
}
|
|
27899
|
+
async function patchHermesMemoryLimits(profileName, patch) {
|
|
27900
|
+
const configPath = resolveHermesConfigPath(profileName);
|
|
27901
|
+
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27902
|
+
(error) => {
|
|
27903
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27904
|
+
return null;
|
|
27905
|
+
}
|
|
27906
|
+
throw error;
|
|
27907
|
+
}
|
|
27908
|
+
);
|
|
27909
|
+
const document = existingRaw ? YAML6.parseDocument(existingRaw) : new YAML6.Document({});
|
|
27910
|
+
const config = toRecord18(document.toJSON());
|
|
27911
|
+
const memory = toRecord18(config.memory);
|
|
27912
|
+
if (patch.memoryCharLimit !== void 0) {
|
|
27913
|
+
memory.memory_char_limit = patch.memoryCharLimit;
|
|
27914
|
+
}
|
|
27915
|
+
if (patch.userCharLimit !== void 0) {
|
|
27916
|
+
memory.user_char_limit = patch.userCharLimit;
|
|
27917
|
+
}
|
|
27918
|
+
config.memory = memory;
|
|
27919
|
+
if (existingRaw) {
|
|
27920
|
+
await atomicWriteFilePreservingMetadata(
|
|
27921
|
+
`${configPath}.bak.${Date.now()}`,
|
|
27922
|
+
existingRaw,
|
|
27923
|
+
{
|
|
27924
|
+
metadataSourcePath: configPath
|
|
27925
|
+
}
|
|
27926
|
+
);
|
|
27927
|
+
}
|
|
27928
|
+
document.contents = document.createNode(config);
|
|
27929
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
27930
|
+
}
|
|
27217
27931
|
function resolveMemoryDir(profileName) {
|
|
27218
|
-
return
|
|
27932
|
+
return path26.join(resolveHermesProfileDir(profileName), "memories");
|
|
27219
27933
|
}
|
|
27220
27934
|
async function readMemoryStore(profileName, target, limits) {
|
|
27221
27935
|
const filePath = memoryFilePath(profileName, target);
|
|
27222
27936
|
const entries = await readMemoryEntries(filePath);
|
|
27223
|
-
const fileStat = await
|
|
27224
|
-
if (
|
|
27937
|
+
const fileStat = await stat19(filePath).catch((error) => {
|
|
27938
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27225
27939
|
return null;
|
|
27226
27940
|
}
|
|
27227
27941
|
throw error;
|
|
@@ -27250,7 +27964,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
27250
27964
|
}
|
|
27251
27965
|
async function readMemoryEntries(filePath) {
|
|
27252
27966
|
const raw = await readFile18(filePath, "utf8").catch((error) => {
|
|
27253
|
-
if (
|
|
27967
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27254
27968
|
return "";
|
|
27255
27969
|
}
|
|
27256
27970
|
throw error;
|
|
@@ -27276,7 +27990,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
27276
27990
|
);
|
|
27277
27991
|
}
|
|
27278
27992
|
function memoryFilePath(profileName, target) {
|
|
27279
|
-
return
|
|
27993
|
+
return path26.join(
|
|
27280
27994
|
resolveMemoryDir(profileName),
|
|
27281
27995
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
27282
27996
|
);
|
|
@@ -27336,7 +28050,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
27336
28050
|
configurable: true,
|
|
27337
28051
|
configured: true,
|
|
27338
28052
|
configurationIssue: null,
|
|
27339
|
-
providerConfigPath:
|
|
28053
|
+
providerConfigPath: path26.join(
|
|
27340
28054
|
resolveHermesProfileDir(profileName),
|
|
27341
28055
|
"<provider>.json"
|
|
27342
28056
|
),
|
|
@@ -27414,7 +28128,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27414
28128
|
const config2 = await readJsonObject(
|
|
27415
28129
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
27416
28130
|
);
|
|
27417
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
28131
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString19(config2.apiKey)) || isConfiguredEnvValue(readString19(config2.api_key)) || isConfiguredEnvValue(readString19(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
27418
28132
|
configured: false,
|
|
27419
28133
|
issue: "Honcho \u9700\u8981\u5148\u586B\u5199 API Key\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
27420
28134
|
};
|
|
@@ -27423,7 +28137,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27423
28137
|
const config2 = await readJsonObject(
|
|
27424
28138
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
27425
28139
|
);
|
|
27426
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
28140
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString19(config2.api_key)) ? { configured: true, issue: null } : {
|
|
27427
28141
|
configured: false,
|
|
27428
28142
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u9875\u586B\u5199 API Key\uFF0CLink \u4F1A\u5199\u5165\u5F53\u524D Profile \u7684 .env\u3002"
|
|
27429
28143
|
};
|
|
@@ -27465,7 +28179,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27465
28179
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
27466
28180
|
);
|
|
27467
28181
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
27468
|
-
const apiKey =
|
|
28182
|
+
const apiKey = readString19(config.apiKey) ?? readString19(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
27469
28183
|
if (mode === "cloud") {
|
|
27470
28184
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
27471
28185
|
configured: false,
|
|
@@ -27473,15 +28187,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27473
28187
|
};
|
|
27474
28188
|
}
|
|
27475
28189
|
if (mode === "local_external") {
|
|
27476
|
-
const apiUrl =
|
|
28190
|
+
const apiUrl = readString19(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
27477
28191
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
27478
28192
|
configured: false,
|
|
27479
28193
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
27480
28194
|
};
|
|
27481
28195
|
}
|
|
27482
28196
|
if (mode === "local_embedded") {
|
|
27483
|
-
const llmProvider =
|
|
27484
|
-
const llmModel =
|
|
28197
|
+
const llmProvider = readString19(config.llm_provider) ?? "openai";
|
|
28198
|
+
const llmModel = readString19(config.llm_model);
|
|
27485
28199
|
if (!llmModel) {
|
|
27486
28200
|
return {
|
|
27487
28201
|
configured: false,
|
|
@@ -27489,7 +28203,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27489
28203
|
};
|
|
27490
28204
|
}
|
|
27491
28205
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
27492
|
-
|
|
28206
|
+
readString19(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
27493
28207
|
)) {
|
|
27494
28208
|
return {
|
|
27495
28209
|
configured: false,
|
|
@@ -27497,7 +28211,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27497
28211
|
};
|
|
27498
28212
|
}
|
|
27499
28213
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
27500
|
-
|
|
28214
|
+
readString19(config.llmApiKey) ?? readString19(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
27501
28215
|
)) {
|
|
27502
28216
|
return {
|
|
27503
28217
|
configured: false,
|
|
@@ -27553,8 +28267,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27553
28267
|
secretSetting(
|
|
27554
28268
|
"apiKey",
|
|
27555
28269
|
"API Key",
|
|
27556
|
-
env.HONCHO_API_KEY ??
|
|
27557
|
-
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
28270
|
+
env.HONCHO_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28271
|
+
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27558
28272
|
),
|
|
27559
28273
|
stringSetting("workspace", "Workspace", config.workspace ?? "hermes"),
|
|
27560
28274
|
stringSetting("peerName", "\u7528\u6237 Peer", config.peerName ?? ""),
|
|
@@ -27596,8 +28310,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27596
28310
|
secretSetting(
|
|
27597
28311
|
"apiKey",
|
|
27598
28312
|
"API Key",
|
|
27599
|
-
env.MEM0_API_KEY ??
|
|
27600
|
-
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
28313
|
+
env.MEM0_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28314
|
+
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27601
28315
|
),
|
|
27602
28316
|
stringSetting("userId", "User ID", config.user_id ?? "hermes-user"),
|
|
27603
28317
|
stringSetting("agentId", "Agent ID", config.agent_id ?? "hermes"),
|
|
@@ -27682,8 +28396,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27682
28396
|
secretSetting(
|
|
27683
28397
|
"apiKey",
|
|
27684
28398
|
"Hindsight API Key",
|
|
27685
|
-
env.HINDSIGHT_API_KEY ??
|
|
27686
|
-
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(
|
|
28399
|
+
env.HINDSIGHT_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28400
|
+
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27687
28401
|
),
|
|
27688
28402
|
stringSetting(
|
|
27689
28403
|
"bankId",
|
|
@@ -27705,8 +28419,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27705
28419
|
secretSetting(
|
|
27706
28420
|
"llmApiKey",
|
|
27707
28421
|
"LLM API Key",
|
|
27708
|
-
env.HINDSIGHT_LLM_API_KEY ??
|
|
27709
|
-
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(
|
|
28422
|
+
env.HINDSIGHT_LLM_API_KEY ?? readString19(config.llmApiKey) ?? readString19(config.llm_api_key),
|
|
28423
|
+
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString19(config.llmApiKey)) || isConfiguredEnvValue(readString19(config.llm_api_key))
|
|
27710
28424
|
),
|
|
27711
28425
|
booleanSetting("autoRecall", "\u81EA\u52A8\u56DE\u5FC6", config.auto_recall ?? true),
|
|
27712
28426
|
booleanSetting("autoRetain", "\u81EA\u52A8\u6C89\u6DC0", config.auto_retain ?? true),
|
|
@@ -27730,7 +28444,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27730
28444
|
stringSetting(
|
|
27731
28445
|
"dbPath",
|
|
27732
28446
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
27733
|
-
config.db_path ??
|
|
28447
|
+
config.db_path ?? path26.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
27734
28448
|
),
|
|
27735
28449
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
27736
28450
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -27766,7 +28480,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27766
28480
|
stringSetting(
|
|
27767
28481
|
"workingDirectory",
|
|
27768
28482
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
27769
|
-
|
|
28483
|
+
path26.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
27770
28484
|
false
|
|
27771
28485
|
)
|
|
27772
28486
|
];
|
|
@@ -27775,16 +28489,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27775
28489
|
}
|
|
27776
28490
|
function memoryProviderConfigPath(profileName, provider) {
|
|
27777
28491
|
if (provider === "honcho") {
|
|
27778
|
-
return
|
|
28492
|
+
return path26.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
27779
28493
|
}
|
|
27780
28494
|
if (provider === "mem0") {
|
|
27781
|
-
return
|
|
28495
|
+
return path26.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
27782
28496
|
}
|
|
27783
28497
|
if (provider === "supermemory") {
|
|
27784
|
-
return
|
|
28498
|
+
return path26.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
27785
28499
|
}
|
|
27786
28500
|
if (provider === "hindsight") {
|
|
27787
|
-
return
|
|
28501
|
+
return path26.join(
|
|
27788
28502
|
resolveHermesProfileDir(profileName),
|
|
27789
28503
|
"hindsight",
|
|
27790
28504
|
"config.json"
|
|
@@ -27793,13 +28507,13 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
27793
28507
|
return null;
|
|
27794
28508
|
}
|
|
27795
28509
|
function customProviderConfigPath(profileName, provider) {
|
|
27796
|
-
return
|
|
28510
|
+
return path26.join(
|
|
27797
28511
|
resolveHermesProfileDir(profileName),
|
|
27798
28512
|
`${normalizeCustomProviderId(provider)}.json`
|
|
27799
28513
|
);
|
|
27800
28514
|
}
|
|
27801
28515
|
function customProviderRegistryPath(profileName) {
|
|
27802
|
-
return
|
|
28516
|
+
return path26.join(
|
|
27803
28517
|
resolveHermesProfileDir(profileName),
|
|
27804
28518
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
27805
28519
|
);
|
|
@@ -27807,7 +28521,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
27807
28521
|
async function readCustomProviderRegistry(profileName) {
|
|
27808
28522
|
const raw = await readFile18(customProviderRegistryPath(profileName), "utf8").catch(
|
|
27809
28523
|
(error) => {
|
|
27810
|
-
if (
|
|
28524
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27811
28525
|
return "";
|
|
27812
28526
|
}
|
|
27813
28527
|
throw error;
|
|
@@ -27825,11 +28539,11 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
27825
28539
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
27826
28540
|
}
|
|
27827
28541
|
const record = toRecord18(item);
|
|
27828
|
-
const id = normalizeCustomProviderId(
|
|
28542
|
+
const id = normalizeCustomProviderId(readString19(record.id) ?? "");
|
|
27829
28543
|
return {
|
|
27830
28544
|
id,
|
|
27831
|
-
label:
|
|
27832
|
-
description:
|
|
28545
|
+
label: readString19(record.label) ?? id,
|
|
28546
|
+
description: readString19(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
27833
28547
|
};
|
|
27834
28548
|
}).filter((item) => item.id);
|
|
27835
28549
|
} catch {
|
|
@@ -27851,10 +28565,10 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
27851
28565
|
);
|
|
27852
28566
|
}
|
|
27853
28567
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
27854
|
-
const pluginsDir =
|
|
28568
|
+
const pluginsDir = path26.join(resolveHermesProfileDir(profileName), "plugins");
|
|
27855
28569
|
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
27856
28570
|
(error) => {
|
|
27857
|
-
if (
|
|
28571
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27858
28572
|
return [];
|
|
27859
28573
|
}
|
|
27860
28574
|
throw error;
|
|
@@ -27871,21 +28585,21 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
27871
28585
|
} catch {
|
|
27872
28586
|
continue;
|
|
27873
28587
|
}
|
|
27874
|
-
const providerDir =
|
|
28588
|
+
const providerDir = path26.join(pluginsDir, entry.name);
|
|
27875
28589
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
27876
28590
|
continue;
|
|
27877
28591
|
}
|
|
27878
28592
|
const meta = await readPluginMetadata(providerDir);
|
|
27879
28593
|
descriptors.push({
|
|
27880
28594
|
id: providerId,
|
|
27881
|
-
label:
|
|
27882
|
-
description:
|
|
28595
|
+
label: readString19(meta.name) ?? providerId,
|
|
28596
|
+
description: readString19(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
27883
28597
|
});
|
|
27884
28598
|
}
|
|
27885
28599
|
return descriptors;
|
|
27886
28600
|
}
|
|
27887
28601
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
27888
|
-
const providerDir =
|
|
28602
|
+
const providerDir = path26.join(
|
|
27889
28603
|
resolveHermesProfileDir(profileName),
|
|
27890
28604
|
"plugins",
|
|
27891
28605
|
normalizeCustomProviderId(provider)
|
|
@@ -27893,9 +28607,9 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
27893
28607
|
return isMemoryProviderPluginDir(providerDir);
|
|
27894
28608
|
}
|
|
27895
28609
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
27896
|
-
const source = await readFile18(
|
|
28610
|
+
const source = await readFile18(path26.join(providerDir, "__init__.py"), "utf8").catch(
|
|
27897
28611
|
(error) => {
|
|
27898
|
-
if (
|
|
28612
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27899
28613
|
return "";
|
|
27900
28614
|
}
|
|
27901
28615
|
throw error;
|
|
@@ -27905,9 +28619,9 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
27905
28619
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
27906
28620
|
}
|
|
27907
28621
|
async function readPluginMetadata(providerDir) {
|
|
27908
|
-
const raw = await readFile18(
|
|
28622
|
+
const raw = await readFile18(path26.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
27909
28623
|
(error) => {
|
|
27910
|
-
if (
|
|
28624
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27911
28625
|
return "";
|
|
27912
28626
|
}
|
|
27913
28627
|
throw error;
|
|
@@ -27917,10 +28631,10 @@ async function readPluginMetadata(providerDir) {
|
|
|
27917
28631
|
}
|
|
27918
28632
|
async function resolveByteRoverCli() {
|
|
27919
28633
|
const candidates = [
|
|
27920
|
-
...(process.env.PATH ?? "").split(
|
|
27921
|
-
|
|
28634
|
+
...(process.env.PATH ?? "").split(path26.delimiter).filter(Boolean).map((dir) => path26.join(dir, "brv")),
|
|
28635
|
+
path26.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
27922
28636
|
"/usr/local/bin/brv",
|
|
27923
|
-
|
|
28637
|
+
path26.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
27924
28638
|
].filter(Boolean);
|
|
27925
28639
|
for (const candidate of candidates) {
|
|
27926
28640
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -27933,7 +28647,7 @@ async function resolveByteRoverCli() {
|
|
|
27933
28647
|
async function readHolographicProviderConfig(profileName) {
|
|
27934
28648
|
const raw = await readFile18(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
27935
28649
|
(error) => {
|
|
27936
|
-
if (
|
|
28650
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27937
28651
|
return "";
|
|
27938
28652
|
}
|
|
27939
28653
|
throw error;
|
|
@@ -27947,7 +28661,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
27947
28661
|
const configPath = resolveHermesConfigPath(profileName);
|
|
27948
28662
|
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27949
28663
|
(error) => {
|
|
27950
|
-
if (
|
|
28664
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27951
28665
|
return null;
|
|
27952
28666
|
}
|
|
27953
28667
|
throw error;
|
|
@@ -27981,9 +28695,9 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
27981
28695
|
if (entries.length === 0) {
|
|
27982
28696
|
return;
|
|
27983
28697
|
}
|
|
27984
|
-
const envPath =
|
|
28698
|
+
const envPath = path26.join(resolveHermesProfileDir(profileName), ".env");
|
|
27985
28699
|
const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
|
|
27986
|
-
if (
|
|
28700
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27987
28701
|
return "";
|
|
27988
28702
|
}
|
|
27989
28703
|
throw error;
|
|
@@ -28041,7 +28755,7 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
28041
28755
|
].includes(key);
|
|
28042
28756
|
}
|
|
28043
28757
|
function normalizeHindsightMode(value) {
|
|
28044
|
-
const mode =
|
|
28758
|
+
const mode = readString19(value) ?? "cloud";
|
|
28045
28759
|
return mode === "local" ? "local_embedded" : mode;
|
|
28046
28760
|
}
|
|
28047
28761
|
function normalizeHttpUrl(value) {
|
|
@@ -28110,27 +28824,27 @@ function parseJsonObject2(text) {
|
|
|
28110
28824
|
}
|
|
28111
28825
|
}
|
|
28112
28826
|
function readHindsightError(json) {
|
|
28113
|
-
const detail =
|
|
28827
|
+
const detail = readString19(json.detail) ?? readString19(json.error);
|
|
28114
28828
|
return detail ? `\uFF1A${detail}` : "";
|
|
28115
28829
|
}
|
|
28116
28830
|
function hindsightSemanticIssue(pathName, json) {
|
|
28117
28831
|
if (pathName === "/health") {
|
|
28118
|
-
const status =
|
|
28832
|
+
const status = readString19(json.status);
|
|
28119
28833
|
return status && ["healthy", "ok"].includes(status.toLowerCase()) ? null : `\u5065\u5EB7\u72B6\u6001\u5F02\u5E38\uFF1A${status ?? "unknown"}`;
|
|
28120
28834
|
}
|
|
28121
28835
|
return null;
|
|
28122
28836
|
}
|
|
28123
28837
|
function summarizeHindsightProbe(pathName, json) {
|
|
28124
28838
|
if (pathName === "/health") {
|
|
28125
|
-
const status =
|
|
28126
|
-
const database =
|
|
28839
|
+
const status = readString19(json.status) ?? "ok";
|
|
28840
|
+
const database = readString19(json.database);
|
|
28127
28841
|
return database ? `${status}, database ${database}` : status;
|
|
28128
28842
|
}
|
|
28129
28843
|
if (pathName === "/version") {
|
|
28130
|
-
const version =
|
|
28844
|
+
const version = readString19(json.api_version);
|
|
28131
28845
|
return version ? `API ${version}` : "version endpoint reachable";
|
|
28132
28846
|
}
|
|
28133
|
-
const bankId =
|
|
28847
|
+
const bankId = readString19(json.bank_id);
|
|
28134
28848
|
return bankId ? `bank ${bankId} reachable` : "bank config reachable";
|
|
28135
28849
|
}
|
|
28136
28850
|
async function readActiveMemoryProvider(profileName) {
|
|
@@ -28138,21 +28852,21 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
28138
28852
|
resolveHermesConfigPath(profileName),
|
|
28139
28853
|
"utf8"
|
|
28140
28854
|
).catch((error) => {
|
|
28141
|
-
if (
|
|
28855
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28142
28856
|
return "";
|
|
28143
28857
|
}
|
|
28144
28858
|
throw error;
|
|
28145
28859
|
});
|
|
28146
28860
|
const config = raw ? toRecord18(YAML6.parse(raw)) : {};
|
|
28147
28861
|
const memory = toRecord18(config.memory);
|
|
28148
|
-
const provider =
|
|
28862
|
+
const provider = readString19(memory.provider);
|
|
28149
28863
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
28150
28864
|
return null;
|
|
28151
28865
|
}
|
|
28152
28866
|
return provider;
|
|
28153
28867
|
}
|
|
28154
28868
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
28155
|
-
const configPath =
|
|
28869
|
+
const configPath = path26.join(
|
|
28156
28870
|
resolveHermesProfileDir(profileName),
|
|
28157
28871
|
relativePath
|
|
28158
28872
|
);
|
|
@@ -28171,7 +28885,7 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
28171
28885
|
}
|
|
28172
28886
|
async function readJsonObject(filePath) {
|
|
28173
28887
|
const raw = await readFile18(filePath, "utf8").catch((error) => {
|
|
28174
|
-
if (
|
|
28888
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28175
28889
|
return "{}";
|
|
28176
28890
|
}
|
|
28177
28891
|
throw error;
|
|
@@ -28181,7 +28895,7 @@ async function readJsonObject(filePath) {
|
|
|
28181
28895
|
} catch {
|
|
28182
28896
|
throw new HermesMemoryError(
|
|
28183
28897
|
"memory_provider_config_invalid",
|
|
28184
|
-
`${
|
|
28898
|
+
`${path26.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
28185
28899
|
);
|
|
28186
28900
|
}
|
|
28187
28901
|
}
|
|
@@ -28202,7 +28916,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
28202
28916
|
return {
|
|
28203
28917
|
key,
|
|
28204
28918
|
label,
|
|
28205
|
-
value:
|
|
28919
|
+
value: readString19(value) ?? "",
|
|
28206
28920
|
editable,
|
|
28207
28921
|
kind: "string"
|
|
28208
28922
|
};
|
|
@@ -28214,7 +28928,7 @@ function secretSetting(key, label, value, configured) {
|
|
|
28214
28928
|
value: "",
|
|
28215
28929
|
editable: true,
|
|
28216
28930
|
kind: "secret",
|
|
28217
|
-
configured: configured || isConfiguredEnvValue(
|
|
28931
|
+
configured: configured || isConfiguredEnvValue(readString19(value))
|
|
28218
28932
|
};
|
|
28219
28933
|
}
|
|
28220
28934
|
function textSetting(key, label, value, editable = true) {
|
|
@@ -28227,7 +28941,7 @@ function textSetting(key, label, value, editable = true) {
|
|
|
28227
28941
|
};
|
|
28228
28942
|
}
|
|
28229
28943
|
function selectSetting(key, label, value, options, editable = true) {
|
|
28230
|
-
const stringValue =
|
|
28944
|
+
const stringValue = readString19(value) ?? options[0] ?? null;
|
|
28231
28945
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
28232
28946
|
}
|
|
28233
28947
|
async function readMemoryLimits(profileName) {
|
|
@@ -28235,7 +28949,7 @@ async function readMemoryLimits(profileName) {
|
|
|
28235
28949
|
resolveHermesConfigPath(profileName),
|
|
28236
28950
|
"utf8"
|
|
28237
28951
|
).catch((error) => {
|
|
28238
|
-
if (
|
|
28952
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28239
28953
|
return "";
|
|
28240
28954
|
}
|
|
28241
28955
|
throw error;
|
|
@@ -28300,7 +29014,7 @@ function hashString(value) {
|
|
|
28300
29014
|
function toRecord18(value) {
|
|
28301
29015
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
28302
29016
|
}
|
|
28303
|
-
function
|
|
29017
|
+
function readString19(value) {
|
|
28304
29018
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
28305
29019
|
}
|
|
28306
29020
|
function readPositiveInteger3(value) {
|
|
@@ -28328,7 +29042,7 @@ function formatEnvValue3(value) {
|
|
|
28328
29042
|
function escapeRegExp4(value) {
|
|
28329
29043
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
28330
29044
|
}
|
|
28331
|
-
function
|
|
29045
|
+
function isNodeError21(error, code) {
|
|
28332
29046
|
return error instanceof Error && "code" in error && error.code === code;
|
|
28333
29047
|
}
|
|
28334
29048
|
|
|
@@ -28404,7 +29118,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
28404
29118
|
try {
|
|
28405
29119
|
ctx.body = await saveHermesMemorySettings(
|
|
28406
29120
|
ctx.params.name,
|
|
28407
|
-
readMemorySettingsPatch(body)
|
|
29121
|
+
readMemorySettingsPatch(body, { includeLimits: true })
|
|
28408
29122
|
);
|
|
28409
29123
|
} catch (error) {
|
|
28410
29124
|
throw toMemoryHttpError(error);
|
|
@@ -28459,7 +29173,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
28459
29173
|
);
|
|
28460
29174
|
}
|
|
28461
29175
|
function readMemoryTarget(body) {
|
|
28462
|
-
const raw =
|
|
29176
|
+
const raw = readString18(body, "target");
|
|
28463
29177
|
if (raw === "memory" || raw === "user") {
|
|
28464
29178
|
return raw;
|
|
28465
29179
|
}
|
|
@@ -28470,7 +29184,7 @@ function readMemoryTarget(body) {
|
|
|
28470
29184
|
);
|
|
28471
29185
|
}
|
|
28472
29186
|
function readMemoryResetTarget(body) {
|
|
28473
|
-
const raw =
|
|
29187
|
+
const raw = readString18(body, "target") ?? "all";
|
|
28474
29188
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
28475
29189
|
return raw;
|
|
28476
29190
|
}
|
|
@@ -28481,7 +29195,7 @@ function readMemoryResetTarget(body) {
|
|
|
28481
29195
|
);
|
|
28482
29196
|
}
|
|
28483
29197
|
function readRequiredMemoryContent(body) {
|
|
28484
|
-
const content =
|
|
29198
|
+
const content = readString18(body, "content") ?? readString18(body, "text");
|
|
28485
29199
|
if (!content) {
|
|
28486
29200
|
throw new LinkHttpError(
|
|
28487
29201
|
400,
|
|
@@ -28492,7 +29206,7 @@ function readRequiredMemoryContent(body) {
|
|
|
28492
29206
|
return content;
|
|
28493
29207
|
}
|
|
28494
29208
|
function readRequiredMemoryMatch(body) {
|
|
28495
|
-
const oldText =
|
|
29209
|
+
const oldText = readString18(body, "old_text") ?? readString18(body, "oldText") ?? readString18(body, "match");
|
|
28496
29210
|
if (!oldText) {
|
|
28497
29211
|
throw new LinkHttpError(
|
|
28498
29212
|
400,
|
|
@@ -28503,7 +29217,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
28503
29217
|
return oldText;
|
|
28504
29218
|
}
|
|
28505
29219
|
function readRequiredMemoryProvider(body) {
|
|
28506
|
-
const provider =
|
|
29220
|
+
const provider = readString18(body, "provider") ?? readString18(body, "provider_id") ?? readString18(body, "providerId");
|
|
28507
29221
|
if (!provider) {
|
|
28508
29222
|
throw new LinkHttpError(
|
|
28509
29223
|
400,
|
|
@@ -28513,9 +29227,27 @@ function readRequiredMemoryProvider(body) {
|
|
|
28513
29227
|
}
|
|
28514
29228
|
return provider;
|
|
28515
29229
|
}
|
|
28516
|
-
function readMemorySettingsPatch(body) {
|
|
29230
|
+
function readMemorySettingsPatch(body, options = {}) {
|
|
28517
29231
|
const input = {};
|
|
28518
|
-
|
|
29232
|
+
if (options.includeLimits) {
|
|
29233
|
+
const memoryCharLimit = readOptionalPositiveInteger(
|
|
29234
|
+
body,
|
|
29235
|
+
"memory_char_limit",
|
|
29236
|
+
"memoryCharLimit"
|
|
29237
|
+
);
|
|
29238
|
+
if (memoryCharLimit !== void 0) {
|
|
29239
|
+
input.memoryCharLimit = memoryCharLimit;
|
|
29240
|
+
}
|
|
29241
|
+
const userCharLimit = readOptionalPositiveInteger(
|
|
29242
|
+
body,
|
|
29243
|
+
"user_char_limit",
|
|
29244
|
+
"userCharLimit"
|
|
29245
|
+
);
|
|
29246
|
+
if (userCharLimit !== void 0) {
|
|
29247
|
+
input.userCharLimit = userCharLimit;
|
|
29248
|
+
}
|
|
29249
|
+
}
|
|
29250
|
+
const mode = readString18(body, "mode");
|
|
28519
29251
|
if (mode) {
|
|
28520
29252
|
input.mode = mode;
|
|
28521
29253
|
}
|
|
@@ -28531,7 +29263,7 @@ function readMemorySettingsPatch(body) {
|
|
|
28531
29263
|
if (bankId !== void 0) {
|
|
28532
29264
|
input.bankId = bankId;
|
|
28533
29265
|
}
|
|
28534
|
-
const llmProvider =
|
|
29266
|
+
const llmProvider = readString18(body, "llm_provider") ?? readString18(body, "llmProvider");
|
|
28535
29267
|
if (llmProvider) {
|
|
28536
29268
|
input.llmProvider = llmProvider;
|
|
28537
29269
|
}
|
|
@@ -28567,11 +29299,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28567
29299
|
if (autoRetain !== void 0) {
|
|
28568
29300
|
input.autoRetain = autoRetain;
|
|
28569
29301
|
}
|
|
28570
|
-
const memoryMode =
|
|
29302
|
+
const memoryMode = readString18(body, "memory_mode") ?? readString18(body, "memoryMode");
|
|
28571
29303
|
if (memoryMode) {
|
|
28572
29304
|
input.memoryMode = memoryMode;
|
|
28573
29305
|
}
|
|
28574
|
-
const recallBudget =
|
|
29306
|
+
const recallBudget = readString18(body, "recall_budget") ?? readString18(body, "recallBudget");
|
|
28575
29307
|
if (recallBudget) {
|
|
28576
29308
|
input.recallBudget = recallBudget;
|
|
28577
29309
|
}
|
|
@@ -28587,11 +29319,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28587
29319
|
if (profileFrequency !== void 0) {
|
|
28588
29320
|
input.profileFrequency = profileFrequency;
|
|
28589
29321
|
}
|
|
28590
|
-
const captureMode =
|
|
29322
|
+
const captureMode = readString18(body, "capture_mode") ?? readString18(body, "captureMode");
|
|
28591
29323
|
if (captureMode) {
|
|
28592
29324
|
input.captureMode = captureMode;
|
|
28593
29325
|
}
|
|
28594
|
-
const searchMode =
|
|
29326
|
+
const searchMode = readString18(body, "search_mode") ?? readString18(body, "searchMode");
|
|
28595
29327
|
if (searchMode) {
|
|
28596
29328
|
input.searchMode = searchMode;
|
|
28597
29329
|
}
|
|
@@ -28625,11 +29357,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28625
29357
|
if (aiPeer !== void 0) {
|
|
28626
29358
|
input.aiPeer = aiPeer;
|
|
28627
29359
|
}
|
|
28628
|
-
const recallMode =
|
|
29360
|
+
const recallMode = readString18(body, "recall_mode") ?? readString18(body, "recallMode");
|
|
28629
29361
|
if (recallMode) {
|
|
28630
29362
|
input.recallMode = recallMode;
|
|
28631
29363
|
}
|
|
28632
|
-
const writeFrequency =
|
|
29364
|
+
const writeFrequency = readString18(body, "write_frequency") ?? readString18(body, "writeFrequency");
|
|
28633
29365
|
if (writeFrequency) {
|
|
28634
29366
|
input.writeFrequency = writeFrequency;
|
|
28635
29367
|
}
|
|
@@ -28637,7 +29369,7 @@ function readMemorySettingsPatch(body) {
|
|
|
28637
29369
|
if (saveMessages !== void 0) {
|
|
28638
29370
|
input.saveMessages = saveMessages;
|
|
28639
29371
|
}
|
|
28640
|
-
const sessionStrategy =
|
|
29372
|
+
const sessionStrategy = readString18(body, "session_strategy") ?? readString18(body, "sessionStrategy");
|
|
28641
29373
|
if (sessionStrategy) {
|
|
28642
29374
|
input.sessionStrategy = sessionStrategy;
|
|
28643
29375
|
}
|
|
@@ -28735,6 +29467,23 @@ function readOptionalNumber(value, key) {
|
|
|
28735
29467
|
}
|
|
28736
29468
|
return numberValue;
|
|
28737
29469
|
}
|
|
29470
|
+
function readOptionalPositiveInteger(body, ...keys) {
|
|
29471
|
+
for (const key of keys) {
|
|
29472
|
+
if (!Object.prototype.hasOwnProperty.call(body, key)) {
|
|
29473
|
+
continue;
|
|
29474
|
+
}
|
|
29475
|
+
const value = readPositiveInteger2(body[key]);
|
|
29476
|
+
if (value === void 0) {
|
|
29477
|
+
throw new LinkHttpError(
|
|
29478
|
+
400,
|
|
29479
|
+
"memory_settings_invalid",
|
|
29480
|
+
`${key} must be a positive integer`
|
|
29481
|
+
);
|
|
29482
|
+
}
|
|
29483
|
+
return value;
|
|
29484
|
+
}
|
|
29485
|
+
return void 0;
|
|
29486
|
+
}
|
|
28738
29487
|
function readOptionalString(body, ...keys) {
|
|
28739
29488
|
for (const key of keys) {
|
|
28740
29489
|
if (Object.prototype.hasOwnProperty.call(body, key)) {
|
|
@@ -28770,7 +29519,7 @@ function toMemoryHttpError(error) {
|
|
|
28770
29519
|
|
|
28771
29520
|
// src/hermes/skills.ts
|
|
28772
29521
|
import { readFile as readFile19, readdir as readdir11 } from "fs/promises";
|
|
28773
|
-
import
|
|
29522
|
+
import path27 from "path";
|
|
28774
29523
|
import YAML7 from "yaml";
|
|
28775
29524
|
var HermesSkillNotFoundError = class extends Error {
|
|
28776
29525
|
constructor(skillName) {
|
|
@@ -28784,7 +29533,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
28784
29533
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
28785
29534
|
const profile = await readExistingProfile(profileName, paths);
|
|
28786
29535
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
28787
|
-
const skillsRoot =
|
|
29536
|
+
const skillsRoot = path27.join(profileDir, "skills");
|
|
28788
29537
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
28789
29538
|
findSkillFiles(skillsRoot),
|
|
28790
29539
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -28863,7 +29612,7 @@ async function findSkillFiles(root) {
|
|
|
28863
29612
|
async function collectSkillFiles(directory, results) {
|
|
28864
29613
|
const entries = await readdir11(directory, { withFileTypes: true }).catch(
|
|
28865
29614
|
(error) => {
|
|
28866
|
-
if (
|
|
29615
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28867
29616
|
return [];
|
|
28868
29617
|
}
|
|
28869
29618
|
throw error;
|
|
@@ -28875,7 +29624,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
28875
29624
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
28876
29625
|
continue;
|
|
28877
29626
|
}
|
|
28878
|
-
const entryPath =
|
|
29627
|
+
const entryPath = path27.join(directory, entry.name);
|
|
28879
29628
|
if (entry.isDirectory()) {
|
|
28880
29629
|
await collectSkillFiles(entryPath, results);
|
|
28881
29630
|
continue;
|
|
@@ -28888,7 +29637,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
28888
29637
|
async function readSkillMetadata(input) {
|
|
28889
29638
|
const raw = await readFile19(input.skillFile, "utf8").catch(
|
|
28890
29639
|
(error) => {
|
|
28891
|
-
if (
|
|
29640
|
+
if (isNodeError22(error, "ENOENT") || isNodeError22(error, "EACCES")) {
|
|
28892
29641
|
return null;
|
|
28893
29642
|
}
|
|
28894
29643
|
throw error;
|
|
@@ -28897,16 +29646,16 @@ async function readSkillMetadata(input) {
|
|
|
28897
29646
|
if (raw === null) {
|
|
28898
29647
|
return null;
|
|
28899
29648
|
}
|
|
28900
|
-
const skillDir =
|
|
29649
|
+
const skillDir = path27.dirname(input.skillFile);
|
|
28901
29650
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
28902
29651
|
const name = normalizeSkillName(
|
|
28903
|
-
|
|
29652
|
+
readString20(frontmatter.name) ?? path27.basename(skillDir)
|
|
28904
29653
|
);
|
|
28905
29654
|
if (!name) {
|
|
28906
29655
|
return null;
|
|
28907
29656
|
}
|
|
28908
29657
|
const description = normalizeDescription(
|
|
28909
|
-
|
|
29658
|
+
readString20(frontmatter.description) ?? firstBodyDescription(body)
|
|
28910
29659
|
);
|
|
28911
29660
|
const provenance = input.provenance.get(name) ?? {
|
|
28912
29661
|
source: "local",
|
|
@@ -28919,7 +29668,7 @@ async function readSkillMetadata(input) {
|
|
|
28919
29668
|
enabled: !input.disabled.has(name),
|
|
28920
29669
|
source: provenance.source,
|
|
28921
29670
|
trust: provenance.trust,
|
|
28922
|
-
relativePath:
|
|
29671
|
+
relativePath: path27.relative(input.skillsRoot, skillDir)
|
|
28923
29672
|
};
|
|
28924
29673
|
}
|
|
28925
29674
|
function parseSkillDocument(raw) {
|
|
@@ -28940,8 +29689,8 @@ function parseSkillDocument(raw) {
|
|
|
28940
29689
|
}
|
|
28941
29690
|
}
|
|
28942
29691
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
28943
|
-
const relative =
|
|
28944
|
-
const parts = relative.split(
|
|
29692
|
+
const relative = path27.relative(skillsRoot, skillFile);
|
|
29693
|
+
const parts = relative.split(path27.sep).filter(Boolean);
|
|
28945
29694
|
return parts.length >= 3 ? parts[0] : null;
|
|
28946
29695
|
}
|
|
28947
29696
|
function firstBodyDescription(body) {
|
|
@@ -28965,7 +29714,7 @@ function normalizeDescription(value) {
|
|
|
28965
29714
|
}
|
|
28966
29715
|
async function readDisabledSkillNames(configPath) {
|
|
28967
29716
|
const raw = await readFile19(configPath, "utf8").catch((error) => {
|
|
28968
|
-
if (
|
|
29717
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28969
29718
|
return "";
|
|
28970
29719
|
}
|
|
28971
29720
|
throw error;
|
|
@@ -28988,9 +29737,9 @@ async function readSkillProvenance(root) {
|
|
|
28988
29737
|
return provenance;
|
|
28989
29738
|
}
|
|
28990
29739
|
async function readBundledSkillNames(root) {
|
|
28991
|
-
const raw = await readFile19(
|
|
29740
|
+
const raw = await readFile19(path27.join(root, ".bundled_manifest"), "utf8").catch(
|
|
28992
29741
|
(error) => {
|
|
28993
|
-
if (
|
|
29742
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28994
29743
|
return "";
|
|
28995
29744
|
}
|
|
28996
29745
|
throw error;
|
|
@@ -29011,9 +29760,9 @@ async function readBundledSkillNames(root) {
|
|
|
29011
29760
|
return names;
|
|
29012
29761
|
}
|
|
29013
29762
|
async function readHubInstalledSkills(root) {
|
|
29014
|
-
const raw = await readFile19(
|
|
29763
|
+
const raw = await readFile19(path27.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
29015
29764
|
(error) => {
|
|
29016
|
-
if (
|
|
29765
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
29017
29766
|
return "";
|
|
29018
29767
|
}
|
|
29019
29768
|
throw error;
|
|
@@ -29033,8 +29782,8 @@ async function readHubInstalledSkills(root) {
|
|
|
29033
29782
|
for (const [name, rawEntry] of Object.entries(installed2)) {
|
|
29034
29783
|
const entry = toRecord19(rawEntry);
|
|
29035
29784
|
result.set(normalizeSkillName(name), {
|
|
29036
|
-
source:
|
|
29037
|
-
trust:
|
|
29785
|
+
source: readString20(entry.source) ?? "hub",
|
|
29786
|
+
trust: readString20(entry.trust_level) ?? null
|
|
29038
29787
|
});
|
|
29039
29788
|
}
|
|
29040
29789
|
return result;
|
|
@@ -29085,7 +29834,7 @@ function compareCategoryNames(left, right) {
|
|
|
29085
29834
|
async function readHermesConfigDocument4(configPath) {
|
|
29086
29835
|
const existingRaw = await readFile19(configPath, "utf8").catch(
|
|
29087
29836
|
(error) => {
|
|
29088
|
-
if (
|
|
29837
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
29089
29838
|
return null;
|
|
29090
29839
|
}
|
|
29091
29840
|
throw error;
|
|
@@ -29118,7 +29867,7 @@ function readStringList4(value) {
|
|
|
29118
29867
|
}
|
|
29119
29868
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
29120
29869
|
}
|
|
29121
|
-
function
|
|
29870
|
+
function readString20(value) {
|
|
29122
29871
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
29123
29872
|
}
|
|
29124
29873
|
function toRecord19(value) {
|
|
@@ -29132,7 +29881,7 @@ function ensureRecord5(target, key) {
|
|
|
29132
29881
|
target[key] = current;
|
|
29133
29882
|
return current;
|
|
29134
29883
|
}
|
|
29135
|
-
function
|
|
29884
|
+
function isNodeError22(error, code) {
|
|
29136
29885
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
29137
29886
|
}
|
|
29138
29887
|
|
|
@@ -29489,7 +30238,7 @@ function registerRunRoutes(router, options) {
|
|
|
29489
30238
|
router.post("/api/v1/runs", async (ctx) => {
|
|
29490
30239
|
await authenticateRequest(ctx, paths);
|
|
29491
30240
|
const body = await readJsonBody(ctx.req);
|
|
29492
|
-
const input =
|
|
30241
|
+
const input = readString18(body, "input");
|
|
29493
30242
|
if (!input) {
|
|
29494
30243
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
29495
30244
|
}
|
|
@@ -29497,12 +30246,12 @@ function registerRunRoutes(router, options) {
|
|
|
29497
30246
|
ctx.body = await createHermesRun(
|
|
29498
30247
|
{
|
|
29499
30248
|
input,
|
|
29500
|
-
instructions:
|
|
30249
|
+
instructions: readString18(body, "instructions") ?? void 0,
|
|
29501
30250
|
conversation_history: readConversationHistory(
|
|
29502
30251
|
body.conversation_history ?? body.conversationHistory
|
|
29503
30252
|
),
|
|
29504
|
-
session_id:
|
|
29505
|
-
session_key:
|
|
30253
|
+
session_id: readString18(body, "session_id") ?? readString18(body, "sessionId") ?? void 0,
|
|
30254
|
+
session_key: readString18(body, "session_key") ?? readString18(body, "sessionKey") ?? void 0
|
|
29506
30255
|
},
|
|
29507
30256
|
{ logger, profileName: readOptionalProfileName(body) }
|
|
29508
30257
|
);
|
|
@@ -29673,8 +30422,8 @@ function readModelList(payload) {
|
|
|
29673
30422
|
// src/hermes/updates.ts
|
|
29674
30423
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
29675
30424
|
import { spawn as spawn3 } from "child_process";
|
|
29676
|
-
import { mkdir as
|
|
29677
|
-
import
|
|
30425
|
+
import { mkdir as mkdir13, readFile as readFile20, rm as rm8 } from "fs/promises";
|
|
30426
|
+
import path28 from "path";
|
|
29678
30427
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
29679
30428
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
29680
30429
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -29737,7 +30486,7 @@ async function startHermesUpdate(options) {
|
|
|
29737
30486
|
signal: null,
|
|
29738
30487
|
error: null
|
|
29739
30488
|
};
|
|
29740
|
-
await
|
|
30489
|
+
await mkdir13(options.paths.runDir, { recursive: true, mode: 448 });
|
|
29741
30490
|
await writer.write(`
|
|
29742
30491
|
=== hermes update started ${startedAt} ===
|
|
29743
30492
|
`);
|
|
@@ -29911,20 +30660,20 @@ function normalizeServerReleaseSnapshot(payload) {
|
|
|
29911
30660
|
const remote = toNullableRecord(snapshot.remote);
|
|
29912
30661
|
return {
|
|
29913
30662
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
29914
|
-
cacheState:
|
|
29915
|
-
issue:
|
|
30663
|
+
cacheState: readString21(snapshot, "cache_state") ?? readString21(snapshot, "cacheState"),
|
|
30664
|
+
issue: readString21(snapshot, "issue")
|
|
29916
30665
|
};
|
|
29917
30666
|
}
|
|
29918
30667
|
function normalizeServerRelease(payload) {
|
|
29919
|
-
const tag =
|
|
29920
|
-
const name =
|
|
30668
|
+
const tag = readString21(payload, "tag");
|
|
30669
|
+
const name = readString21(payload, "name");
|
|
29921
30670
|
return {
|
|
29922
|
-
version:
|
|
30671
|
+
version: readString21(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
29923
30672
|
tag,
|
|
29924
30673
|
name,
|
|
29925
|
-
releaseUrl:
|
|
29926
|
-
publishedAt:
|
|
29927
|
-
fetchedAt:
|
|
30674
|
+
releaseUrl: readString21(payload, "releaseUrl") ?? readString21(payload, "release_url"),
|
|
30675
|
+
publishedAt: readString21(payload, "publishedAt") ?? readString21(payload, "published_at"),
|
|
30676
|
+
fetchedAt: readString21(payload, "fetchedAt") ?? readString21(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
29928
30677
|
};
|
|
29929
30678
|
}
|
|
29930
30679
|
async function readReleaseCache(paths) {
|
|
@@ -29952,21 +30701,21 @@ async function readUpdateLogLines(paths) {
|
|
|
29952
30701
|
);
|
|
29953
30702
|
}
|
|
29954
30703
|
function releaseCachePath(paths) {
|
|
29955
|
-
return
|
|
30704
|
+
return path28.join(paths.indexesDir, "hermes-release-check.json");
|
|
29956
30705
|
}
|
|
29957
30706
|
function updateStatePath(paths) {
|
|
29958
|
-
return
|
|
30707
|
+
return path28.join(paths.runDir, "hermes-update-state.json");
|
|
29959
30708
|
}
|
|
29960
30709
|
function updateLogPath(paths) {
|
|
29961
|
-
return
|
|
30710
|
+
return path28.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
29962
30711
|
}
|
|
29963
30712
|
async function clearUpdateLogFiles(paths) {
|
|
29964
30713
|
const primary = updateLogPath(paths);
|
|
29965
30714
|
await Promise.all([
|
|
29966
|
-
|
|
30715
|
+
rm8(primary, { force: true }).catch(() => void 0),
|
|
29967
30716
|
...Array.from(
|
|
29968
30717
|
{ length: UPDATE_LOG_MAX_FILES },
|
|
29969
|
-
(_, index) =>
|
|
30718
|
+
(_, index) => rm8(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
29970
30719
|
)
|
|
29971
30720
|
]);
|
|
29972
30721
|
}
|
|
@@ -30050,7 +30799,7 @@ function isRecentRunningState2(state) {
|
|
|
30050
30799
|
const startedAt = Date.parse(state.started_at);
|
|
30051
30800
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
30052
30801
|
}
|
|
30053
|
-
function
|
|
30802
|
+
function readString21(payload, key) {
|
|
30054
30803
|
const value = payload[key];
|
|
30055
30804
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
30056
30805
|
}
|
|
@@ -30058,17 +30807,17 @@ function readString20(payload, key) {
|
|
|
30058
30807
|
// src/link/updates.ts
|
|
30059
30808
|
import { spawn as spawn5 } from "child_process";
|
|
30060
30809
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
30061
|
-
import { mkdir as
|
|
30062
|
-
import
|
|
30810
|
+
import { mkdir as mkdir16, readFile as readFile22, rm as rm11 } from "fs/promises";
|
|
30811
|
+
import path30 from "path";
|
|
30063
30812
|
|
|
30064
30813
|
// src/daemon/process.ts
|
|
30065
30814
|
import { spawn as spawn4 } from "child_process";
|
|
30066
|
-
import { mkdir as
|
|
30067
|
-
import
|
|
30815
|
+
import { mkdir as mkdir15, readFile as readFile21, rm as rm10, writeFile as writeFile4 } from "fs/promises";
|
|
30816
|
+
import path29 from "path";
|
|
30068
30817
|
|
|
30069
30818
|
// src/daemon/service.ts
|
|
30070
30819
|
import { createServer } from "http";
|
|
30071
|
-
import { mkdir as
|
|
30820
|
+
import { mkdir as mkdir14, rm as rm9, writeFile as writeFile3 } from "fs/promises";
|
|
30072
30821
|
|
|
30073
30822
|
// src/relay/control-client.ts
|
|
30074
30823
|
import WebSocket from "ws";
|
|
@@ -31113,11 +31862,11 @@ async function mergeLastReportedPublicRoutes(paths, snapshotInput) {
|
|
|
31113
31862
|
const state = await readNetworkReportState(paths);
|
|
31114
31863
|
return {
|
|
31115
31864
|
...snapshotInput,
|
|
31116
|
-
publicIpv4s:
|
|
31865
|
+
publicIpv4s: uniqueStrings2([
|
|
31117
31866
|
...snapshotInput.publicIpv4s,
|
|
31118
31867
|
...state.lastReportedPublicIpv4s
|
|
31119
31868
|
]).slice(0, 2),
|
|
31120
|
-
publicIpv6s:
|
|
31869
|
+
publicIpv6s: uniqueStrings2([
|
|
31121
31870
|
...snapshotInput.publicIpv6s,
|
|
31122
31871
|
...state.lastReportedPublicIpv6s
|
|
31123
31872
|
]).slice(0, 2)
|
|
@@ -31201,15 +31950,15 @@ function normalizeLanIps(value) {
|
|
|
31201
31950
|
];
|
|
31202
31951
|
}
|
|
31203
31952
|
function sameNetworkSnapshot(left, right) {
|
|
31204
|
-
return
|
|
31953
|
+
return sameStringList2(left.lanIps, right.lanIps) && sameStringList2(left.publicIpv4s, right.publicIpv4s) && sameStringList2(left.publicIpv6s, right.publicIpv6s);
|
|
31205
31954
|
}
|
|
31206
|
-
function
|
|
31955
|
+
function sameStringList2(left, right) {
|
|
31207
31956
|
if (left.length !== right.length) {
|
|
31208
31957
|
return false;
|
|
31209
31958
|
}
|
|
31210
31959
|
return left.every((value, index) => value === right[index]);
|
|
31211
31960
|
}
|
|
31212
|
-
function
|
|
31961
|
+
function uniqueStrings2(values) {
|
|
31213
31962
|
return [...new Set(values)];
|
|
31214
31963
|
}
|
|
31215
31964
|
function formatUtcDay(date) {
|
|
@@ -31844,7 +32593,7 @@ async function startLinkService(options = {}) {
|
|
|
31844
32593
|
await logger.info("service_stopped");
|
|
31845
32594
|
await logger.flush();
|
|
31846
32595
|
if (options.writePidFile) {
|
|
31847
|
-
await
|
|
32596
|
+
await rm9(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
31848
32597
|
}
|
|
31849
32598
|
}
|
|
31850
32599
|
};
|
|
@@ -31894,7 +32643,7 @@ function pidFilePath(paths = resolveRuntimePaths()) {
|
|
|
31894
32643
|
return `${paths.runDir}/hermeslink.pid`;
|
|
31895
32644
|
}
|
|
31896
32645
|
async function writePidFile(paths) {
|
|
31897
|
-
await
|
|
32646
|
+
await mkdir14(paths.runDir, { recursive: true, mode: 448 });
|
|
31898
32647
|
await writeFile3(pidFilePath(paths), `${process.pid}
|
|
31899
32648
|
`, { mode: 384 });
|
|
31900
32649
|
}
|
|
@@ -31989,8 +32738,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
31989
32738
|
return status;
|
|
31990
32739
|
}
|
|
31991
32740
|
}
|
|
31992
|
-
await
|
|
31993
|
-
await
|
|
32741
|
+
await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
|
|
32742
|
+
await mkdir15(paths.runDir, { recursive: true, mode: 448 });
|
|
31994
32743
|
const scriptPath = currentCliScriptPath();
|
|
31995
32744
|
const child = spawn4(process.execPath, [scriptPath, "daemon-supervisor"], {
|
|
31996
32745
|
detached: true,
|
|
@@ -32008,10 +32757,10 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32008
32757
|
return await getDaemonStatus(paths);
|
|
32009
32758
|
}
|
|
32010
32759
|
async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
32011
|
-
await
|
|
32760
|
+
await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
|
|
32012
32761
|
const log = createRotatingTextLogWriter({
|
|
32013
32762
|
paths,
|
|
32014
|
-
fileName:
|
|
32763
|
+
fileName: path29.basename(daemonLogFile(paths))
|
|
32015
32764
|
});
|
|
32016
32765
|
const scriptPath = currentCliScriptPath();
|
|
32017
32766
|
const write = (chunk) => {
|
|
@@ -32138,7 +32887,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32138
32887
|
try {
|
|
32139
32888
|
process.kill(status.pid, "SIGTERM");
|
|
32140
32889
|
} catch {
|
|
32141
|
-
await
|
|
32890
|
+
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
32142
32891
|
return await getDaemonStatus(paths);
|
|
32143
32892
|
}
|
|
32144
32893
|
for (let index = 0; index < 20; index += 1) {
|
|
@@ -32160,7 +32909,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32160
32909
|
}
|
|
32161
32910
|
}
|
|
32162
32911
|
if (!isProcessAlive3(status.pid) || !await pidBackedServiceIsReachable(paths)) {
|
|
32163
|
-
await
|
|
32912
|
+
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
32164
32913
|
}
|
|
32165
32914
|
return await getDaemonStatus(paths);
|
|
32166
32915
|
}
|
|
@@ -32168,7 +32917,7 @@ async function getDaemonStatus(paths = resolveRuntimePaths()) {
|
|
|
32168
32917
|
const pidFile = pidFilePath(paths);
|
|
32169
32918
|
const pid = await readPid(pidFile);
|
|
32170
32919
|
if (pid && !isProcessAlive3(pid)) {
|
|
32171
|
-
await
|
|
32920
|
+
await rm10(pidFile, { force: true }).catch(() => void 0);
|
|
32172
32921
|
return {
|
|
32173
32922
|
running: false,
|
|
32174
32923
|
pid: null,
|
|
@@ -32309,10 +33058,10 @@ function terminateChild(child, previousForceKillTimer) {
|
|
|
32309
33058
|
}
|
|
32310
33059
|
}
|
|
32311
33060
|
function supervisorStopIntentPath(paths) {
|
|
32312
|
-
return
|
|
33061
|
+
return path29.join(paths.runDir, "supervisor-stop-intent.json");
|
|
32313
33062
|
}
|
|
32314
33063
|
async function writeSupervisorStopIntent(paths, pid) {
|
|
32315
|
-
await
|
|
33064
|
+
await mkdir15(paths.runDir, { recursive: true, mode: 448 });
|
|
32316
33065
|
await writeFile4(
|
|
32317
33066
|
supervisorStopIntentPath(paths),
|
|
32318
33067
|
`${JSON.stringify({ pid, created_at: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
@@ -32328,14 +33077,14 @@ async function consumeSupervisorStopIntent(paths, pid) {
|
|
|
32328
33077
|
}
|
|
32329
33078
|
const payload = parseSupervisorStopIntent(raw);
|
|
32330
33079
|
if (!isValidSupervisorStopIntent(payload)) {
|
|
32331
|
-
await
|
|
33080
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32332
33081
|
return false;
|
|
32333
33082
|
}
|
|
32334
33083
|
if (payload.pid !== pid) {
|
|
32335
|
-
await
|
|
33084
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32336
33085
|
return false;
|
|
32337
33086
|
}
|
|
32338
|
-
await
|
|
33087
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32339
33088
|
return true;
|
|
32340
33089
|
}
|
|
32341
33090
|
async function clearExpiredSupervisorStopIntent(paths) {
|
|
@@ -32346,7 +33095,7 @@ async function clearExpiredSupervisorStopIntent(paths) {
|
|
|
32346
33095
|
}
|
|
32347
33096
|
const payload = parseSupervisorStopIntent(raw);
|
|
32348
33097
|
if (!isValidSupervisorStopIntent(payload)) {
|
|
32349
|
-
await
|
|
33098
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32350
33099
|
}
|
|
32351
33100
|
}
|
|
32352
33101
|
function parseSupervisorStopIntent(raw) {
|
|
@@ -32481,7 +33230,7 @@ async function startLinkUpdate(options) {
|
|
|
32481
33230
|
error: null,
|
|
32482
33231
|
manual_command: manualCommand
|
|
32483
33232
|
};
|
|
32484
|
-
await
|
|
33233
|
+
await mkdir16(options.paths.runDir, { recursive: true, mode: 448 });
|
|
32485
33234
|
await writer.write(
|
|
32486
33235
|
`
|
|
32487
33236
|
=== link update started ${startedAt} target=${targetVersion} ===
|
|
@@ -32789,16 +33538,16 @@ function normalizeServerSnapshot(payload) {
|
|
|
32789
33538
|
if (!policy) {
|
|
32790
33539
|
return {
|
|
32791
33540
|
remote: null,
|
|
32792
|
-
issue:
|
|
33541
|
+
issue: readString22(snapshot, "issue")
|
|
32793
33542
|
};
|
|
32794
33543
|
}
|
|
32795
33544
|
const release = toNullableRecord2(snapshot.release);
|
|
32796
|
-
const currentVersion =
|
|
32797
|
-
const minSafeVersion =
|
|
33545
|
+
const currentVersion = readString22(policy, "current_version") ?? readString22(policy, "currentVersion");
|
|
33546
|
+
const minSafeVersion = readString22(policy, "min_safe_version") ?? readString22(policy, "minSafeVersion");
|
|
32798
33547
|
if (!currentVersion) {
|
|
32799
33548
|
return {
|
|
32800
33549
|
remote: null,
|
|
32801
|
-
issue:
|
|
33550
|
+
issue: readString22(snapshot, "issue")
|
|
32802
33551
|
};
|
|
32803
33552
|
}
|
|
32804
33553
|
return {
|
|
@@ -32806,10 +33555,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
32806
33555
|
current_version: currentVersion,
|
|
32807
33556
|
min_safe_version: minSafeVersion,
|
|
32808
33557
|
target_version: currentVersion,
|
|
32809
|
-
release_url: release ?
|
|
32810
|
-
published_at: release ?
|
|
33558
|
+
release_url: release ? readString22(release, "release_url") ?? readString22(release, "releaseUrl") : null,
|
|
33559
|
+
published_at: release ? readString22(release, "published_at") ?? readString22(release, "publishedAt") : null
|
|
32811
33560
|
},
|
|
32812
|
-
issue:
|
|
33561
|
+
issue: readString22(snapshot, "issue")
|
|
32813
33562
|
};
|
|
32814
33563
|
}
|
|
32815
33564
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher, channel) {
|
|
@@ -32872,7 +33621,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
|
|
|
32872
33621
|
};
|
|
32873
33622
|
}
|
|
32874
33623
|
function buildUnixInstallCommand(installerUrl) {
|
|
32875
|
-
const nodeBinDir =
|
|
33624
|
+
const nodeBinDir = path30.dirname(process.execPath);
|
|
32876
33625
|
const fetchScript = [
|
|
32877
33626
|
quoteShellToken(process.execPath),
|
|
32878
33627
|
"--input-type=module",
|
|
@@ -33142,18 +33891,18 @@ async function readUpdateLogLines2(paths) {
|
|
|
33142
33891
|
);
|
|
33143
33892
|
}
|
|
33144
33893
|
function updateStatePath2(paths) {
|
|
33145
|
-
return
|
|
33894
|
+
return path30.join(paths.runDir, "link-update-state.json");
|
|
33146
33895
|
}
|
|
33147
33896
|
function updateLogPath2(paths) {
|
|
33148
|
-
return
|
|
33897
|
+
return path30.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
33149
33898
|
}
|
|
33150
33899
|
async function clearUpdateLogFiles2(paths) {
|
|
33151
33900
|
const primary = updateLogPath2(paths);
|
|
33152
33901
|
await Promise.all([
|
|
33153
|
-
|
|
33902
|
+
rm11(primary, { force: true }).catch(() => void 0),
|
|
33154
33903
|
...Array.from(
|
|
33155
33904
|
{ length: UPDATE_LOG_MAX_FILES2 },
|
|
33156
|
-
(_, index) =>
|
|
33905
|
+
(_, index) => rm11(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
33157
33906
|
)
|
|
33158
33907
|
]);
|
|
33159
33908
|
}
|
|
@@ -33223,14 +33972,14 @@ function toRecord22(value) {
|
|
|
33223
33972
|
function toNullableRecord2(value) {
|
|
33224
33973
|
return typeof value === "object" && value !== null ? value : null;
|
|
33225
33974
|
}
|
|
33226
|
-
function
|
|
33975
|
+
function readString22(payload, key) {
|
|
33227
33976
|
const value = payload[key];
|
|
33228
33977
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
33229
33978
|
}
|
|
33230
33979
|
|
|
33231
33980
|
// src/pairing/pairing.ts
|
|
33232
|
-
import
|
|
33233
|
-
import { rm as
|
|
33981
|
+
import path31 from "path";
|
|
33982
|
+
import { rm as rm12 } from "fs/promises";
|
|
33234
33983
|
|
|
33235
33984
|
// src/relay/bootstrap.ts
|
|
33236
33985
|
var RelayNetworkError = class extends Error {
|
|
@@ -33492,7 +34241,7 @@ async function readPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
|
33492
34241
|
};
|
|
33493
34242
|
}
|
|
33494
34243
|
async function clearPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
33495
|
-
await
|
|
34244
|
+
await rm12(pairingClaimPath(sessionId, paths), { force: true }).catch(() => void 0);
|
|
33496
34245
|
}
|
|
33497
34246
|
async function claimPairing(input) {
|
|
33498
34247
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
@@ -33569,10 +34318,10 @@ async function loadRequiredIdentity2(paths) {
|
|
|
33569
34318
|
}
|
|
33570
34319
|
return identity;
|
|
33571
34320
|
}
|
|
33572
|
-
async function postServerJson(serverBaseUrl,
|
|
34321
|
+
async function postServerJson(serverBaseUrl, path32, body, options) {
|
|
33573
34322
|
let response;
|
|
33574
34323
|
try {
|
|
33575
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
34324
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path32}`, {
|
|
33576
34325
|
method: "POST",
|
|
33577
34326
|
headers: {
|
|
33578
34327
|
accept: "application/json",
|
|
@@ -33620,10 +34369,10 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
33620
34369
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
33621
34370
|
};
|
|
33622
34371
|
}
|
|
33623
|
-
async function patchServerJson(serverBaseUrl,
|
|
34372
|
+
async function patchServerJson(serverBaseUrl, path32, token, body, options) {
|
|
33624
34373
|
let response;
|
|
33625
34374
|
try {
|
|
33626
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
34375
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path32}`, {
|
|
33627
34376
|
method: "PATCH",
|
|
33628
34377
|
headers: {
|
|
33629
34378
|
accept: "application/json",
|
|
@@ -33671,10 +34420,10 @@ function createPairingNetworkError(input) {
|
|
|
33671
34420
|
);
|
|
33672
34421
|
}
|
|
33673
34422
|
function pairingClaimPath(sessionId, paths) {
|
|
33674
|
-
return
|
|
34423
|
+
return path31.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
33675
34424
|
}
|
|
33676
34425
|
function pairingSessionPath(sessionId, paths) {
|
|
33677
|
-
return
|
|
34426
|
+
return path31.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
33678
34427
|
}
|
|
33679
34428
|
function qrPreferredUrls(routes) {
|
|
33680
34429
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -33754,8 +34503,8 @@ function registerSystemRoutes(router, options) {
|
|
|
33754
34503
|
});
|
|
33755
34504
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
33756
34505
|
const body = await readJsonBody(ctx.req);
|
|
33757
|
-
const sessionId =
|
|
33758
|
-
const claimToken =
|
|
34506
|
+
const sessionId = readString18(body, "session_id") ?? readString18(body, "sessionId");
|
|
34507
|
+
const claimToken = readString18(body, "claim_token") ?? readString18(body, "claimToken");
|
|
33759
34508
|
if (!sessionId || !claimToken) {
|
|
33760
34509
|
throw new LinkHttpError(
|
|
33761
34510
|
400,
|
|
@@ -33766,10 +34515,10 @@ function registerSystemRoutes(router, options) {
|
|
|
33766
34515
|
const claimed = await claimPairing({
|
|
33767
34516
|
sessionId,
|
|
33768
34517
|
claimToken,
|
|
33769
|
-
deviceLabel:
|
|
33770
|
-
devicePlatform:
|
|
33771
|
-
deviceModel:
|
|
33772
|
-
appInstanceId:
|
|
34518
|
+
deviceLabel: readString18(body, "device_label") ?? readString18(body, "deviceLabel") ?? "HermesPilot App",
|
|
34519
|
+
devicePlatform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform") ?? "unknown",
|
|
34520
|
+
deviceModel: readString18(body, "device_model") ?? readString18(body, "deviceModel"),
|
|
34521
|
+
appInstanceId: readString18(body, "app_instance_id") ?? readString18(body, "appInstanceId"),
|
|
33773
34522
|
paths
|
|
33774
34523
|
});
|
|
33775
34524
|
ctx.body = claimed;
|
|
@@ -33849,9 +34598,9 @@ function registerSystemRoutes(router, options) {
|
|
|
33849
34598
|
const body = await readJsonBody(ctx.req);
|
|
33850
34599
|
const session = await createDeviceSession(
|
|
33851
34600
|
{
|
|
33852
|
-
label:
|
|
33853
|
-
platform:
|
|
33854
|
-
model:
|
|
34601
|
+
label: readString18(body, "device_label") ?? readString18(body, "deviceLabel") ?? "HermesPilot App",
|
|
34602
|
+
platform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform") ?? "unknown",
|
|
34603
|
+
model: readString18(body, "device_model") ?? readString18(body, "deviceModel"),
|
|
33855
34604
|
appInstanceId: auth.appInstanceId
|
|
33856
34605
|
},
|
|
33857
34606
|
paths
|
|
@@ -33880,7 +34629,7 @@ function registerSystemRoutes(router, options) {
|
|
|
33880
34629
|
});
|
|
33881
34630
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
33882
34631
|
const body = await readJsonBody(ctx.req);
|
|
33883
|
-
const refreshToken =
|
|
34632
|
+
const refreshToken = readString18(body, "refresh_token") ?? readString18(body, "refreshToken");
|
|
33884
34633
|
if (!refreshToken) {
|
|
33885
34634
|
throw new LinkHttpError(
|
|
33886
34635
|
400,
|
|
@@ -33891,10 +34640,10 @@ function registerSystemRoutes(router, options) {
|
|
|
33891
34640
|
const session = await refreshDeviceSession(
|
|
33892
34641
|
refreshToken,
|
|
33893
34642
|
{
|
|
33894
|
-
appInstanceId:
|
|
33895
|
-
label:
|
|
33896
|
-
platform:
|
|
33897
|
-
model:
|
|
34643
|
+
appInstanceId: readString18(body, "app_instance_id") ?? readString18(body, "appInstanceId"),
|
|
34644
|
+
label: readString18(body, "device_label") ?? readString18(body, "deviceLabel"),
|
|
34645
|
+
platform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform"),
|
|
34646
|
+
model: readString18(body, "device_model") ?? readString18(body, "deviceModel")
|
|
33898
34647
|
},
|
|
33899
34648
|
paths
|
|
33900
34649
|
);
|
|
@@ -33913,7 +34662,7 @@ function registerSystemRoutes(router, options) {
|
|
|
33913
34662
|
});
|
|
33914
34663
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
33915
34664
|
const body = await readJsonBody(ctx.req);
|
|
33916
|
-
const refreshToken =
|
|
34665
|
+
const refreshToken = readString18(body, "refresh_token") ?? readString18(body, "refreshToken");
|
|
33917
34666
|
if (refreshToken) {
|
|
33918
34667
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
33919
34668
|
}
|
|
@@ -34139,7 +34888,7 @@ function registerSystemRoutes(router, options) {
|
|
|
34139
34888
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
34140
34889
|
const auth = await authenticateRequest(ctx, paths);
|
|
34141
34890
|
const body = await readJsonBody(ctx.req);
|
|
34142
|
-
const label =
|
|
34891
|
+
const label = readString18(body, "label") ?? readString18(body, "device_label");
|
|
34143
34892
|
if (!label) {
|
|
34144
34893
|
throw new LinkHttpError(
|
|
34145
34894
|
400,
|
|
@@ -34183,7 +34932,7 @@ function isActiveCronJob(job) {
|
|
|
34183
34932
|
if (!enabled) {
|
|
34184
34933
|
return false;
|
|
34185
34934
|
}
|
|
34186
|
-
const state =
|
|
34935
|
+
const state = readString18(job, "state")?.toLowerCase();
|
|
34187
34936
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
34188
34937
|
}
|
|
34189
34938
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -34277,8 +35026,8 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
34277
35026
|
ctx.body = await startLinkUpdate({
|
|
34278
35027
|
paths,
|
|
34279
35028
|
logger,
|
|
34280
|
-
channel:
|
|
34281
|
-
targetVersion:
|
|
35029
|
+
channel: readString18(body, "channel"),
|
|
35030
|
+
targetVersion: readString18(body, "target_version") ?? readString18(body, "targetVersion")
|
|
34282
35031
|
});
|
|
34283
35032
|
});
|
|
34284
35033
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -34308,7 +35057,7 @@ import QRCode from "qrcode";
|
|
|
34308
35057
|
function registerPairingRoutes(router, options) {
|
|
34309
35058
|
const { paths } = options;
|
|
34310
35059
|
router.get("/pair", async (ctx) => {
|
|
34311
|
-
const sessionId =
|
|
35060
|
+
const sessionId = readString18(ctx.query, "session_id");
|
|
34312
35061
|
if (!sessionId) {
|
|
34313
35062
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
34314
35063
|
}
|
|
@@ -34333,7 +35082,7 @@ function registerPairingRoutes(router, options) {
|
|
|
34333
35082
|
ctx.body = page;
|
|
34334
35083
|
});
|
|
34335
35084
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
34336
|
-
const sessionId =
|
|
35085
|
+
const sessionId = readString18(ctx.query, "session_id");
|
|
34337
35086
|
if (!sessionId) {
|
|
34338
35087
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
34339
35088
|
}
|
|
@@ -34784,7 +35533,7 @@ function registerInternalRoutes(router, options) {
|
|
|
34784
35533
|
router.post("/internal/deliver", async (ctx) => {
|
|
34785
35534
|
assertLoopbackRequest(ctx.req);
|
|
34786
35535
|
const body = await readJsonBody(ctx.req);
|
|
34787
|
-
const stagingDir =
|
|
35536
|
+
const stagingDir = readString18(body, "staging_dir") ?? readString18(body, "stagingDir");
|
|
34788
35537
|
if (!stagingDir) {
|
|
34789
35538
|
throw new LinkHttpError(
|
|
34790
35539
|
400,
|
|
@@ -34797,6 +35546,19 @@ function registerInternalRoutes(router, options) {
|
|
|
34797
35546
|
...await options.conversations.deliverStagedFiles(stagingDir)
|
|
34798
35547
|
};
|
|
34799
35548
|
});
|
|
35549
|
+
router.post("/internal/deliver-tool", async (ctx) => {
|
|
35550
|
+
assertLoopbackRequest(ctx.req);
|
|
35551
|
+
const body = await readJsonBody(ctx.req);
|
|
35552
|
+
ctx.body = {
|
|
35553
|
+
ok: true,
|
|
35554
|
+
...await options.conversations.deliverFilesFromTool({
|
|
35555
|
+
profile: readString18(body, "profile") ?? readString18(body, "profile_name") ?? readString18(body, "profileName") ?? void 0,
|
|
35556
|
+
taskId: readString18(body, "task_id") ?? readString18(body, "taskId") ?? void 0,
|
|
35557
|
+
sessionId: readString18(body, "session_id") ?? readString18(body, "sessionId") ?? void 0,
|
|
35558
|
+
files: body.files ?? body.file ?? body.path
|
|
35559
|
+
})
|
|
35560
|
+
};
|
|
35561
|
+
});
|
|
34800
35562
|
}
|
|
34801
35563
|
function assertLoopbackRequest(request) {
|
|
34802
35564
|
const address = request.socket.remoteAddress;
|