@hermespilot/link 0.7.4-beta.0 → 0.7.5-beta.0
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-E7KAHPLU.js} +1311 -530
- 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.
|
|
6457
|
+
var LINK_VERSION = "0.7.5-beta.0";
|
|
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) {
|
|
@@ -23872,11 +24497,15 @@ function registerConversationRoutes(router, options) {
|
|
|
23872
24497
|
router.get("/api/v1/conversations", async (ctx) => {
|
|
23873
24498
|
const auth = await authenticateRequest(ctx, paths);
|
|
23874
24499
|
const language = readPreferredLanguage(ctx);
|
|
23875
|
-
|
|
24500
|
+
const cursor = readConversationListCursor(ctx.query);
|
|
24501
|
+
const forceSync = readConversationListForce(ctx.query);
|
|
24502
|
+
await prepareConversationListRead(conversations, logger, auth, {
|
|
24503
|
+
syncHermesSessions: forceSync || !cursor
|
|
24504
|
+
});
|
|
23876
24505
|
ctx.set("cache-control", "no-store");
|
|
23877
24506
|
const result = await conversations.listConversationPage({
|
|
23878
24507
|
limit: readLimit(ctx.query.limit),
|
|
23879
|
-
cursor
|
|
24508
|
+
cursor
|
|
23880
24509
|
});
|
|
23881
24510
|
const localized = localizeConversationListPage(result, language);
|
|
23882
24511
|
ctx.body = {
|
|
@@ -23942,7 +24571,7 @@ function registerConversationRoutes(router, options) {
|
|
|
23942
24571
|
ok: true,
|
|
23943
24572
|
conversation: localizeConversationSummary(
|
|
23944
24573
|
await conversations.createConversation({
|
|
23945
|
-
title:
|
|
24574
|
+
title: readString18(body, "title") ?? void 0,
|
|
23946
24575
|
profileName: readOptionalProfileName(body),
|
|
23947
24576
|
accountId: auth.accountId,
|
|
23948
24577
|
appInstanceId: auth.appInstanceId
|
|
@@ -24021,7 +24650,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24021
24650
|
const auth = await authenticateRequest(ctx, paths);
|
|
24022
24651
|
const language = readPreferredLanguage(ctx);
|
|
24023
24652
|
const body = await readJsonBody(ctx.req);
|
|
24024
|
-
const content =
|
|
24653
|
+
const content = readString18(body, "content") ?? readString18(body, "text") ?? readString18(body, "input") ?? "";
|
|
24025
24654
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
24026
24655
|
if (!content && attachments.length === 0) {
|
|
24027
24656
|
throw new LinkHttpError(
|
|
@@ -24038,7 +24667,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24038
24667
|
conversationId: ctx.params.conversationId,
|
|
24039
24668
|
content,
|
|
24040
24669
|
attachments,
|
|
24041
|
-
clientMessageId:
|
|
24670
|
+
clientMessageId: readString18(body, "client_message_id") ?? readString18(body, "clientMessageId") ?? void 0,
|
|
24042
24671
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
24043
24672
|
profileName: readOptionalProfileName(body),
|
|
24044
24673
|
accountId: auth.accountId,
|
|
@@ -24052,7 +24681,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24052
24681
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
24053
24682
|
await authenticateRequest(ctx, paths);
|
|
24054
24683
|
const body = await readJsonBody(ctx.req);
|
|
24055
|
-
const modelId =
|
|
24684
|
+
const modelId = readString18(body, "model_id") ?? readString18(body, "modelId") ?? readString18(body, "model");
|
|
24056
24685
|
if (!modelId) {
|
|
24057
24686
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
24058
24687
|
}
|
|
@@ -24087,7 +24716,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24087
24716
|
await authenticateRequest(ctx, paths);
|
|
24088
24717
|
const language = readPreferredLanguage(ctx);
|
|
24089
24718
|
const body = await readJsonBody(ctx.req);
|
|
24090
|
-
const title =
|
|
24719
|
+
const title = readString18(body, "title") ?? readString18(body, "name") ?? readString18(body, "display_name");
|
|
24091
24720
|
if (!title) {
|
|
24092
24721
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
24093
24722
|
}
|
|
@@ -24281,7 +24910,7 @@ function registerConversationRoutes(router, options) {
|
|
|
24281
24910
|
async (ctx) => {
|
|
24282
24911
|
await authenticateRequest(ctx, paths);
|
|
24283
24912
|
const body = await readJsonBody(ctx.req);
|
|
24284
|
-
const scope =
|
|
24913
|
+
const scope = readString18(body, "scope") ?? "always";
|
|
24285
24914
|
ctx.body = {
|
|
24286
24915
|
ok: true,
|
|
24287
24916
|
...await conversations.resolveApproval({
|
|
@@ -24389,7 +25018,15 @@ function registerConversationRoutes(router, options) {
|
|
|
24389
25018
|
}
|
|
24390
25019
|
);
|
|
24391
25020
|
}
|
|
24392
|
-
async function prepareConversationListRead(conversations, logger, auth) {
|
|
25021
|
+
async function prepareConversationListRead(conversations, logger, auth, options = {}) {
|
|
25022
|
+
if (options.syncHermesSessions) {
|
|
25023
|
+
await conversations.syncHermesSessions().catch((error) => {
|
|
25024
|
+
void logger.warn("hermes_session_sync_failed", {
|
|
25025
|
+
source: "conversation_list_read",
|
|
25026
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25027
|
+
});
|
|
25028
|
+
});
|
|
25029
|
+
}
|
|
24393
25030
|
await conversations.backfillCronOwnership({
|
|
24394
25031
|
accountId: auth.accountId,
|
|
24395
25032
|
appInstanceId: auth.appInstanceId
|
|
@@ -24405,6 +25042,13 @@ async function prepareConversationListRead(conversations, logger, auth) {
|
|
|
24405
25042
|
});
|
|
24406
25043
|
});
|
|
24407
25044
|
}
|
|
25045
|
+
function readConversationListCursor(query) {
|
|
25046
|
+
return readQueryString(query.cursor) ?? readQueryString(query.after) ?? readQueryString(query.page_cursor);
|
|
25047
|
+
}
|
|
25048
|
+
function readConversationListForce(query) {
|
|
25049
|
+
const raw = Array.isArray(query.force) ? query.force[0] : query.force;
|
|
25050
|
+
return readBoolean3(raw) === true;
|
|
25051
|
+
}
|
|
24408
25052
|
function localizeConversationListPage(page, language) {
|
|
24409
25053
|
return {
|
|
24410
25054
|
...page,
|
|
@@ -24492,7 +25136,7 @@ function resolveConversationEventCursor(input) {
|
|
|
24492
25136
|
return Math.max(queryAfter, headerAfter);
|
|
24493
25137
|
}
|
|
24494
25138
|
function readConversationClearPlanTargetStatus(body) {
|
|
24495
|
-
const raw =
|
|
25139
|
+
const raw = readString18(body, "target_status") ?? readString18(body, "targetStatus") ?? "active";
|
|
24496
25140
|
if (raw === "active" || raw === "archived") {
|
|
24497
25141
|
return raw;
|
|
24498
25142
|
}
|
|
@@ -24618,11 +25262,11 @@ function isSseRequestContext(ctx) {
|
|
|
24618
25262
|
}
|
|
24619
25263
|
return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
|
|
24620
25264
|
}
|
|
24621
|
-
function isSseRequestPath(
|
|
24622
|
-
if (!
|
|
25265
|
+
function isSseRequestPath(path32) {
|
|
25266
|
+
if (!path32) {
|
|
24623
25267
|
return false;
|
|
24624
25268
|
}
|
|
24625
|
-
return
|
|
25269
|
+
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
25270
|
}
|
|
24627
25271
|
function isExpectedClientDisconnectError2(error, options = {}) {
|
|
24628
25272
|
if (!(error instanceof Error)) {
|
|
@@ -24723,6 +25367,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24723
25367
|
router.get("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24724
25368
|
await authenticateRequest(ctx, paths);
|
|
24725
25369
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25370
|
+
assertCronJobId(ctx.params.jobId);
|
|
24726
25371
|
ctx.set("cache-control", "no-store");
|
|
24727
25372
|
const job = await getHermesCronJob(ctx.params.jobId, {
|
|
24728
25373
|
logger,
|
|
@@ -24736,9 +25381,46 @@ function registerCronJobRoutes(router, options) {
|
|
|
24736
25381
|
)
|
|
24737
25382
|
};
|
|
24738
25383
|
});
|
|
25384
|
+
router.get("/api/v1/profiles/:name/cron-jobs/:jobId/history", async (ctx) => {
|
|
25385
|
+
await authenticateRequest(ctx, paths);
|
|
25386
|
+
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25387
|
+
assertCronJobId(ctx.params.jobId);
|
|
25388
|
+
ctx.set("cache-control", "no-store");
|
|
25389
|
+
await getHermesCronJob(ctx.params.jobId, {
|
|
25390
|
+
logger,
|
|
25391
|
+
profileName: profile.name
|
|
25392
|
+
});
|
|
25393
|
+
const historyPage = await listHermesCronJobHistory({
|
|
25394
|
+
profileName: profile.name,
|
|
25395
|
+
jobId: ctx.params.jobId,
|
|
25396
|
+
limit: readPositiveInteger2(ctx.query.limit),
|
|
25397
|
+
offset: readPositiveInteger2(ctx.query.offset)
|
|
25398
|
+
});
|
|
25399
|
+
const history = historyPage.entries.map((entry) => ({
|
|
25400
|
+
id: entry.id,
|
|
25401
|
+
run_at: entry.runAt,
|
|
25402
|
+
content: entry.content,
|
|
25403
|
+
failed: entry.failed,
|
|
25404
|
+
status: entry.status,
|
|
25405
|
+
job_name: entry.jobName ?? null,
|
|
25406
|
+
truncated: entry.truncated
|
|
25407
|
+
}));
|
|
25408
|
+
ctx.body = {
|
|
25409
|
+
ok: true,
|
|
25410
|
+
profile,
|
|
25411
|
+
history,
|
|
25412
|
+
pagination: {
|
|
25413
|
+
total: historyPage.total,
|
|
25414
|
+
offset: historyPage.offset,
|
|
25415
|
+
limit: historyPage.limit,
|
|
25416
|
+
has_more: historyPage.hasMore
|
|
25417
|
+
}
|
|
25418
|
+
};
|
|
25419
|
+
});
|
|
24739
25420
|
router.patch("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24740
25421
|
const auth = await authenticateRequest(ctx, paths);
|
|
24741
25422
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25423
|
+
assertCronJobId(ctx.params.jobId);
|
|
24742
25424
|
const body = await readJsonBody(ctx.req);
|
|
24743
25425
|
const input = readCronJobUpdateInput(body);
|
|
24744
25426
|
const deliverTouched = readOptionalCronText(body, "deliver").present;
|
|
@@ -24771,6 +25453,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24771
25453
|
router.delete("/api/v1/profiles/:name/cron-jobs/:jobId", async (ctx) => {
|
|
24772
25454
|
await authenticateRequest(ctx, paths);
|
|
24773
25455
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25456
|
+
assertCronJobId(ctx.params.jobId);
|
|
24774
25457
|
await deleteHermesCronJob(ctx.params.jobId, {
|
|
24775
25458
|
logger,
|
|
24776
25459
|
profileName: profile.name
|
|
@@ -24781,6 +25464,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24781
25464
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/pause", async (ctx) => {
|
|
24782
25465
|
await authenticateRequest(ctx, paths);
|
|
24783
25466
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25467
|
+
assertCronJobId(ctx.params.jobId);
|
|
24784
25468
|
const job = await runHermesCronJobAction(ctx.params.jobId, "pause", {
|
|
24785
25469
|
logger,
|
|
24786
25470
|
profileName: profile.name
|
|
@@ -24796,6 +25480,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24796
25480
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/resume", async (ctx) => {
|
|
24797
25481
|
await authenticateRequest(ctx, paths);
|
|
24798
25482
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25483
|
+
assertCronJobId(ctx.params.jobId);
|
|
24799
25484
|
const job = await runHermesCronJobAction(ctx.params.jobId, "resume", {
|
|
24800
25485
|
logger,
|
|
24801
25486
|
profileName: profile.name
|
|
@@ -24811,6 +25496,7 @@ function registerCronJobRoutes(router, options) {
|
|
|
24811
25496
|
router.post("/api/v1/profiles/:name/cron-jobs/:jobId/run", async (ctx) => {
|
|
24812
25497
|
await authenticateRequest(ctx, paths);
|
|
24813
25498
|
const profile = await getHermesProfileStatus(ctx.params.name, paths);
|
|
25499
|
+
assertCronJobId(ctx.params.jobId);
|
|
24814
25500
|
const job = await runHermesCronJobAction(ctx.params.jobId, "run", {
|
|
24815
25501
|
logger,
|
|
24816
25502
|
profileName: profile.name
|
|
@@ -24844,7 +25530,7 @@ function toHermesCronJobInput(input) {
|
|
|
24844
25530
|
};
|
|
24845
25531
|
}
|
|
24846
25532
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
24847
|
-
const jobId =
|
|
25533
|
+
const jobId = readString18(input.job, "id") ?? readString18(input.job, "job_id");
|
|
24848
25534
|
if (!jobId) {
|
|
24849
25535
|
return input.job;
|
|
24850
25536
|
}
|
|
@@ -24863,9 +25549,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
24863
25549
|
}
|
|
24864
25550
|
function readCronJobCreateInput(body) {
|
|
24865
25551
|
const input = {};
|
|
24866
|
-
const name =
|
|
24867
|
-
const prompt =
|
|
24868
|
-
const schedule =
|
|
25552
|
+
const name = readString18(body, "name") ?? readString18(body, "title");
|
|
25553
|
+
const prompt = readString18(body, "prompt") ?? readString18(body, "description") ?? readString18(body, "task");
|
|
25554
|
+
const schedule = readString18(body, "schedule");
|
|
24869
25555
|
if (!name) {
|
|
24870
25556
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
24871
25557
|
}
|
|
@@ -24886,7 +25572,7 @@ function readCronJobCreateInput(body) {
|
|
|
24886
25572
|
input.name = name;
|
|
24887
25573
|
input.prompt = prompt;
|
|
24888
25574
|
input.schedule = schedule;
|
|
24889
|
-
input.deliver =
|
|
25575
|
+
input.deliver = readString18(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
24890
25576
|
const skills = readOptionalCronSkills(body);
|
|
24891
25577
|
if (skills) {
|
|
24892
25578
|
input.skills = skills;
|
|
@@ -25006,6 +25692,12 @@ function attachCronJobProfile(job, profile) {
|
|
|
25006
25692
|
}
|
|
25007
25693
|
};
|
|
25008
25694
|
}
|
|
25695
|
+
function assertCronJobId(jobId) {
|
|
25696
|
+
if (/^[a-f0-9]{12}$/u.test(jobId)) {
|
|
25697
|
+
return;
|
|
25698
|
+
}
|
|
25699
|
+
throw new LinkHttpError(400, "cron_job_id_invalid", "Invalid cron job id");
|
|
25700
|
+
}
|
|
25009
25701
|
|
|
25010
25702
|
// src/http/routes/gateway-reload.ts
|
|
25011
25703
|
async function reloadGatewayAfterModelConfigChange(result, options) {
|
|
@@ -25213,9 +25905,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
25213
25905
|
});
|
|
25214
25906
|
}
|
|
25215
25907
|
function readModelConfigInput(body) {
|
|
25216
|
-
const id =
|
|
25217
|
-
const provider =
|
|
25218
|
-
const baseUrl =
|
|
25908
|
+
const id = readString18(body, "id") ?? readString18(body, "model_id") ?? readString18(body, "modelId");
|
|
25909
|
+
const provider = readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey");
|
|
25910
|
+
const baseUrl = readString18(body, "base_url") ?? readString18(body, "baseUrl");
|
|
25219
25911
|
if (!id || !provider || !baseUrl) {
|
|
25220
25912
|
throw new LinkHttpError(
|
|
25221
25913
|
400,
|
|
@@ -25225,21 +25917,21 @@ function readModelConfigInput(body) {
|
|
|
25225
25917
|
}
|
|
25226
25918
|
return {
|
|
25227
25919
|
id,
|
|
25228
|
-
originalModelId:
|
|
25229
|
-
originalProvider:
|
|
25230
|
-
originalBaseUrl:
|
|
25231
|
-
originalApiMode:
|
|
25920
|
+
originalModelId: readString18(body, "original_model_id") ?? readString18(body, "originalModelId") ?? readString18(body, "original_id") ?? void 0,
|
|
25921
|
+
originalProvider: readString18(body, "original_provider") ?? readString18(body, "originalProvider") ?? readString18(body, "original_provider_key") ?? readString18(body, "originalProviderKey") ?? void 0,
|
|
25922
|
+
originalBaseUrl: readString18(body, "original_base_url") ?? readString18(body, "originalBaseUrl") ?? void 0,
|
|
25923
|
+
originalApiMode: readString18(body, "original_api_mode") ?? readString18(body, "originalApiMode") ?? void 0,
|
|
25232
25924
|
provider,
|
|
25233
|
-
providerName:
|
|
25925
|
+
providerName: readString18(body, "provider_name") ?? readString18(body, "providerName") ?? void 0,
|
|
25234
25926
|
baseUrl,
|
|
25235
|
-
apiKey:
|
|
25236
|
-
apiMode:
|
|
25927
|
+
apiKey: readString18(body, "api_key") ?? readString18(body, "apiKey") ?? void 0,
|
|
25928
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0,
|
|
25237
25929
|
contextLength: readPositiveInteger2(
|
|
25238
25930
|
body.context_length ?? body.contextLength
|
|
25239
25931
|
),
|
|
25240
|
-
keyEnv:
|
|
25932
|
+
keyEnv: readString18(body, "key_env") ?? readString18(body, "keyEnv") ?? void 0,
|
|
25241
25933
|
setDefault: readBoolean3(body.set_default ?? body.setDefault),
|
|
25242
|
-
reasoningEffort:
|
|
25934
|
+
reasoningEffort: readString18(body, "reasoning_effort") ?? readString18(body, "reasoningEffort") ?? void 0,
|
|
25243
25935
|
supportsVision: readNullableBoolean(
|
|
25244
25936
|
body.supports_vision ?? body.supportsVision
|
|
25245
25937
|
)
|
|
@@ -25247,28 +25939,28 @@ function readModelConfigInput(body) {
|
|
|
25247
25939
|
}
|
|
25248
25940
|
function readModelDefaultsInput(body) {
|
|
25249
25941
|
return {
|
|
25250
|
-
taskModelId:
|
|
25251
|
-
taskModelProvider:
|
|
25252
|
-
taskModelBaseUrl:
|
|
25253
|
-
taskModelApiMode:
|
|
25254
|
-
compressionModelId:
|
|
25255
|
-
compressionModelProvider:
|
|
25256
|
-
compressionModelBaseUrl:
|
|
25257
|
-
compressionModelApiMode:
|
|
25258
|
-
reasoningEffort:
|
|
25259
|
-
imageInputMode:
|
|
25942
|
+
taskModelId: readString18(body, "task_model_id") ?? readString18(body, "taskModelId") ?? readString18(body, "default_model_id") ?? readString18(body, "defaultModelId") ?? void 0,
|
|
25943
|
+
taskModelProvider: readString18(body, "task_model_provider") ?? readString18(body, "taskModelProvider") ?? readString18(body, "default_model_provider") ?? readString18(body, "defaultModelProvider") ?? void 0,
|
|
25944
|
+
taskModelBaseUrl: readString18(body, "task_model_base_url") ?? readString18(body, "taskModelBaseUrl") ?? readString18(body, "default_model_base_url") ?? readString18(body, "defaultModelBaseUrl") ?? void 0,
|
|
25945
|
+
taskModelApiMode: readString18(body, "task_model_api_mode") ?? readString18(body, "taskModelApiMode") ?? readString18(body, "default_model_api_mode") ?? readString18(body, "defaultModelApiMode") ?? void 0,
|
|
25946
|
+
compressionModelId: readString18(body, "compression_model_id") ?? readString18(body, "compressionModelId") ?? void 0,
|
|
25947
|
+
compressionModelProvider: readString18(body, "compression_model_provider") ?? readString18(body, "compressionModelProvider") ?? void 0,
|
|
25948
|
+
compressionModelBaseUrl: readString18(body, "compression_model_base_url") ?? readString18(body, "compressionModelBaseUrl") ?? void 0,
|
|
25949
|
+
compressionModelApiMode: readString18(body, "compression_model_api_mode") ?? readString18(body, "compressionModelApiMode") ?? void 0,
|
|
25950
|
+
reasoningEffort: readString18(body, "reasoning_effort") ?? readString18(body, "reasoningEffort") ?? readString18(body, "default_reasoning_effort") ?? readString18(body, "defaultReasoningEffort") ?? void 0,
|
|
25951
|
+
imageInputMode: readString18(body, "image_input_mode") ?? readString18(body, "imageInputMode") ?? void 0
|
|
25260
25952
|
};
|
|
25261
25953
|
}
|
|
25262
25954
|
function readModelDeleteInput(body) {
|
|
25263
|
-
const id =
|
|
25955
|
+
const id = readString18(body, "model_id") ?? readString18(body, "modelId");
|
|
25264
25956
|
if (!id) {
|
|
25265
25957
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
25266
25958
|
}
|
|
25267
25959
|
return {
|
|
25268
25960
|
id,
|
|
25269
|
-
provider:
|
|
25270
|
-
baseUrl:
|
|
25271
|
-
apiMode:
|
|
25961
|
+
provider: readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey") ?? void 0,
|
|
25962
|
+
baseUrl: readString18(body, "base_url") ?? readString18(body, "baseUrl") ?? void 0,
|
|
25963
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0
|
|
25272
25964
|
};
|
|
25273
25965
|
}
|
|
25274
25966
|
function readNullableBoolean(value) {
|
|
@@ -25291,8 +25983,8 @@ function readNullableBoolean(value) {
|
|
|
25291
25983
|
return void 0;
|
|
25292
25984
|
}
|
|
25293
25985
|
function readModelConfigImportInput(body) {
|
|
25294
|
-
const sourceProfileName =
|
|
25295
|
-
const modelId =
|
|
25986
|
+
const sourceProfileName = readString18(body, "source_profile") ?? readString18(body, "sourceProfile") ?? readString18(body, "source_profile_name") ?? readString18(body, "sourceProfileName");
|
|
25987
|
+
const modelId = readString18(body, "model_id") ?? readString18(body, "modelId") ?? readString18(body, "id");
|
|
25296
25988
|
if (!sourceProfileName || !modelId) {
|
|
25297
25989
|
throw new LinkHttpError(
|
|
25298
25990
|
400,
|
|
@@ -25303,9 +25995,9 @@ function readModelConfigImportInput(body) {
|
|
|
25303
25995
|
return {
|
|
25304
25996
|
sourceProfileName,
|
|
25305
25997
|
modelId,
|
|
25306
|
-
provider:
|
|
25307
|
-
baseUrl:
|
|
25308
|
-
apiMode:
|
|
25998
|
+
provider: readString18(body, "provider") ?? readString18(body, "provider_key") ?? readString18(body, "providerKey") ?? void 0,
|
|
25999
|
+
baseUrl: readString18(body, "base_url") ?? readString18(body, "baseUrl") ?? void 0,
|
|
26000
|
+
apiMode: readString18(body, "api_mode") ?? readString18(body, "apiMode") ?? void 0,
|
|
25309
26001
|
setDefault: readBoolean3(body.set_default ?? body.setDefault)
|
|
25310
26002
|
};
|
|
25311
26003
|
}
|
|
@@ -25396,20 +26088,20 @@ function errorMessage3(error) {
|
|
|
25396
26088
|
}
|
|
25397
26089
|
|
|
25398
26090
|
// src/hermes/profile-identity.ts
|
|
25399
|
-
import { readFile as readFile15, stat as
|
|
25400
|
-
import
|
|
26091
|
+
import { readFile as readFile15, stat as stat16 } from "fs/promises";
|
|
26092
|
+
import path23 from "path";
|
|
25401
26093
|
var MAX_SOUL_MD_LENGTH = 2e4;
|
|
25402
26094
|
async function readHermesProfileIdentity(profileName, paths) {
|
|
25403
26095
|
await assertProfileExists2(profileName, paths);
|
|
25404
26096
|
const soulPath = resolveSoulPath(profileName);
|
|
25405
26097
|
const content = await readFile15(soulPath, "utf8").catch((error) => {
|
|
25406
|
-
if (
|
|
26098
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
25407
26099
|
return null;
|
|
25408
26100
|
}
|
|
25409
26101
|
throw error;
|
|
25410
26102
|
});
|
|
25411
|
-
const fileStat = content === null ? null : await
|
|
25412
|
-
if (
|
|
26103
|
+
const fileStat = content === null ? null : await stat16(soulPath).catch((error) => {
|
|
26104
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
25413
26105
|
return null;
|
|
25414
26106
|
}
|
|
25415
26107
|
throw error;
|
|
@@ -25454,9 +26146,9 @@ async function assertProfileExists2(profileName, paths) {
|
|
|
25454
26146
|
}
|
|
25455
26147
|
}
|
|
25456
26148
|
function resolveSoulPath(profileName) {
|
|
25457
|
-
return
|
|
26149
|
+
return path23.join(resolveHermesProfileDir(profileName), "SOUL.md");
|
|
25458
26150
|
}
|
|
25459
|
-
function
|
|
26151
|
+
function isNodeError18(error, code) {
|
|
25460
26152
|
return error instanceof Error && "code" in error && error.code === code;
|
|
25461
26153
|
}
|
|
25462
26154
|
|
|
@@ -25465,18 +26157,18 @@ import { spawn as spawn2 } from "child_process";
|
|
|
25465
26157
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
25466
26158
|
import {
|
|
25467
26159
|
cp,
|
|
25468
|
-
mkdir as
|
|
26160
|
+
mkdir as mkdir12,
|
|
25469
26161
|
readFile as readFile17,
|
|
25470
|
-
rm as
|
|
25471
|
-
stat as
|
|
26162
|
+
rm as rm7,
|
|
26163
|
+
stat as stat18
|
|
25472
26164
|
} from "fs/promises";
|
|
25473
|
-
import
|
|
26165
|
+
import path25 from "path";
|
|
25474
26166
|
import YAML5 from "yaml";
|
|
25475
26167
|
|
|
25476
26168
|
// src/hermes/link-skill.ts
|
|
25477
|
-
import { readFile as readFile16, stat as
|
|
26169
|
+
import { readFile as readFile16, stat as stat17 } from "fs/promises";
|
|
25478
26170
|
import os5 from "os";
|
|
25479
|
-
import
|
|
26171
|
+
import path24 from "path";
|
|
25480
26172
|
import YAML4 from "yaml";
|
|
25481
26173
|
var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
|
|
25482
26174
|
var HERMES_LINK_SKILL_DIR = "hermes-link";
|
|
@@ -25561,7 +26253,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
|
|
|
25561
26253
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
25562
26254
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
25563
26255
|
const externalDir = resolveHermesLinkSkillExternalDir(paths);
|
|
25564
|
-
const skillPath =
|
|
26256
|
+
const skillPath = path24.join(
|
|
25565
26257
|
externalDir,
|
|
25566
26258
|
HERMES_LINK_SKILL_DIR,
|
|
25567
26259
|
HERMES_LINK_SKILL_FILE
|
|
@@ -25643,11 +26335,11 @@ function withDefaultProfilePlaceholder2(profiles) {
|
|
|
25643
26335
|
];
|
|
25644
26336
|
}
|
|
25645
26337
|
function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
|
|
25646
|
-
return
|
|
26338
|
+
return path24.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
|
|
25647
26339
|
}
|
|
25648
26340
|
async function writeHermesLinkSkill(skillPath) {
|
|
25649
26341
|
const existing = await readFile16(skillPath, "utf8").catch((error) => {
|
|
25650
|
-
if (
|
|
26342
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25651
26343
|
return null;
|
|
25652
26344
|
}
|
|
25653
26345
|
throw error;
|
|
@@ -25706,7 +26398,7 @@ async function ensureProfileUsesExternalSkillDir(profile, externalDir) {
|
|
|
25706
26398
|
async function readHermesConfigDocument3(configPath) {
|
|
25707
26399
|
const existingRaw = await readFile16(configPath, "utf8").catch(
|
|
25708
26400
|
(error) => {
|
|
25709
|
-
if (
|
|
26401
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25710
26402
|
return null;
|
|
25711
26403
|
}
|
|
25712
26404
|
throw error;
|
|
@@ -25738,11 +26430,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
|
|
|
25738
26430
|
const seen = new Set(
|
|
25739
26431
|
entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
|
|
25740
26432
|
);
|
|
25741
|
-
const normalizedExternalDir =
|
|
26433
|
+
const normalizedExternalDir = path24.resolve(externalDir);
|
|
25742
26434
|
return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
|
|
25743
26435
|
}
|
|
25744
26436
|
function externalDirsInclude(current, externalDir, hermesHome) {
|
|
25745
|
-
const normalizedExternalDir =
|
|
26437
|
+
const normalizedExternalDir = path24.resolve(externalDir);
|
|
25746
26438
|
return readExternalDirEntries(current).some(
|
|
25747
26439
|
(entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
|
|
25748
26440
|
);
|
|
@@ -25753,14 +26445,14 @@ function readExternalDirEntries(value) {
|
|
|
25753
26445
|
}
|
|
25754
26446
|
function resolveExternalDirEntry(entry, hermesHome) {
|
|
25755
26447
|
const expanded = expandHome(expandEnvVars(entry));
|
|
25756
|
-
return
|
|
26448
|
+
return path24.resolve(path24.isAbsolute(expanded) ? expanded : path24.join(hermesHome, expanded));
|
|
25757
26449
|
}
|
|
25758
26450
|
function expandHome(value) {
|
|
25759
26451
|
if (value === "~") {
|
|
25760
26452
|
return os5.homedir();
|
|
25761
26453
|
}
|
|
25762
|
-
if (value.startsWith(`~${
|
|
25763
|
-
return
|
|
26454
|
+
if (value.startsWith(`~${path24.sep}`) || value.startsWith("~/")) {
|
|
26455
|
+
return path24.join(os5.homedir(), value.slice(2));
|
|
25764
26456
|
}
|
|
25765
26457
|
return value;
|
|
25766
26458
|
}
|
|
@@ -25771,8 +26463,8 @@ function expandEnvVars(value) {
|
|
|
25771
26463
|
);
|
|
25772
26464
|
}
|
|
25773
26465
|
async function pathIsDirectory2(filePath) {
|
|
25774
|
-
return
|
|
25775
|
-
if (
|
|
26466
|
+
return stat17(filePath).then((value) => value.isDirectory()).catch((error) => {
|
|
26467
|
+
if (isNodeError19(error, "ENOENT")) {
|
|
25776
26468
|
return false;
|
|
25777
26469
|
}
|
|
25778
26470
|
throw error;
|
|
@@ -25793,7 +26485,7 @@ function toRecord16(value) {
|
|
|
25793
26485
|
function isRecord4(value) {
|
|
25794
26486
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25795
26487
|
}
|
|
25796
|
-
function
|
|
26488
|
+
function isNodeError19(error, code) {
|
|
25797
26489
|
return error instanceof Error && "code" in error && error.code === code;
|
|
25798
26490
|
}
|
|
25799
26491
|
|
|
@@ -25848,7 +26540,7 @@ async function startHermesProfileCreation(input, options) {
|
|
|
25848
26540
|
identity_error: null,
|
|
25849
26541
|
identity_template_id: normalized.identityTemplateId
|
|
25850
26542
|
};
|
|
25851
|
-
await
|
|
26543
|
+
await mkdir12(options.paths.runDir, { recursive: true, mode: 448 });
|
|
25852
26544
|
await writeProfileCreationState(options.paths, started);
|
|
25853
26545
|
await writer.write(`
|
|
25854
26546
|
=== profile creation started ${startedAt} ===
|
|
@@ -26312,9 +27004,9 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
26312
27004
|
return keys;
|
|
26313
27005
|
}
|
|
26314
27006
|
async function writeEnvValues(profileName, values) {
|
|
26315
|
-
const envPath =
|
|
27007
|
+
const envPath = path25.join(resolveHermesProfileDir(profileName), ".env");
|
|
26316
27008
|
const existingRaw = await readFile17(envPath, "utf8").catch((error) => {
|
|
26317
|
-
if (
|
|
27009
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26318
27010
|
return "";
|
|
26319
27011
|
}
|
|
26320
27012
|
throw error;
|
|
@@ -26349,12 +27041,12 @@ async function writeEnvValues(profileName, values) {
|
|
|
26349
27041
|
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
26350
27042
|
}
|
|
26351
27043
|
async function copySkills(sourceProfile, targetProfile) {
|
|
26352
|
-
const sourceSkills =
|
|
26353
|
-
const targetSkills =
|
|
27044
|
+
const sourceSkills = path25.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
27045
|
+
const targetSkills = path25.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
26354
27046
|
if (!await pathExists2(sourceSkills)) {
|
|
26355
27047
|
return;
|
|
26356
27048
|
}
|
|
26357
|
-
await
|
|
27049
|
+
await rm7(targetSkills, { recursive: true, force: true });
|
|
26358
27050
|
await cp(sourceSkills, targetSkills, {
|
|
26359
27051
|
recursive: true,
|
|
26360
27052
|
force: true,
|
|
@@ -26375,7 +27067,7 @@ function copyProperty(source, target, key) {
|
|
|
26375
27067
|
async function readYamlConfig(configPath) {
|
|
26376
27068
|
const existingRaw = await readFile17(configPath, "utf8").catch(
|
|
26377
27069
|
(error) => {
|
|
26378
|
-
if (
|
|
27070
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26379
27071
|
return null;
|
|
26380
27072
|
}
|
|
26381
27073
|
throw error;
|
|
@@ -26402,7 +27094,7 @@ async function failProfileCreation(input) {
|
|
|
26402
27094
|
await input.writer.write(`
|
|
26403
27095
|
Rolling back ${input.rollbackProfileName}...
|
|
26404
27096
|
`);
|
|
26405
|
-
await
|
|
27097
|
+
await rm7(resolveHermesProfileDir(input.rollbackProfileName), {
|
|
26406
27098
|
recursive: true,
|
|
26407
27099
|
force: true
|
|
26408
27100
|
}).catch(() => void 0);
|
|
@@ -26445,24 +27137,24 @@ async function readProfileCreationLogLines(paths) {
|
|
|
26445
27137
|
);
|
|
26446
27138
|
}
|
|
26447
27139
|
function profileCreationStatePath(paths) {
|
|
26448
|
-
return
|
|
27140
|
+
return path25.join(paths.runDir, "profile-create-state.json");
|
|
26449
27141
|
}
|
|
26450
27142
|
function profileCreationLogPath(paths) {
|
|
26451
|
-
return
|
|
27143
|
+
return path25.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
26452
27144
|
}
|
|
26453
27145
|
async function clearProfileCreationLogFiles(paths) {
|
|
26454
27146
|
const primary = profileCreationLogPath(paths);
|
|
26455
27147
|
await Promise.all([
|
|
26456
|
-
|
|
27148
|
+
rm7(primary, { force: true }).catch(() => void 0),
|
|
26457
27149
|
...Array.from(
|
|
26458
27150
|
{ length: PROFILE_CREATE_LOG_MAX_FILES },
|
|
26459
|
-
(_, index) =>
|
|
27151
|
+
(_, index) => rm7(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
26460
27152
|
)
|
|
26461
27153
|
]);
|
|
26462
27154
|
}
|
|
26463
27155
|
async function pathExists2(targetPath) {
|
|
26464
|
-
return await
|
|
26465
|
-
if (
|
|
27156
|
+
return await stat18(targetPath).then(() => true).catch((error) => {
|
|
27157
|
+
if (isNodeError20(error, "ENOENT")) {
|
|
26466
27158
|
return false;
|
|
26467
27159
|
}
|
|
26468
27160
|
throw error;
|
|
@@ -26509,7 +27201,7 @@ function formatEnvValue2(value) {
|
|
|
26509
27201
|
function escapeRegExp3(value) {
|
|
26510
27202
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
26511
27203
|
}
|
|
26512
|
-
function
|
|
27204
|
+
function isNodeError20(error, code) {
|
|
26513
27205
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
26514
27206
|
}
|
|
26515
27207
|
|
|
@@ -26594,16 +27286,16 @@ function readProfilePermissionsInput(body) {
|
|
|
26594
27286
|
const approvals = readOptionalObject(body, "approvals");
|
|
26595
27287
|
if (approvals) {
|
|
26596
27288
|
input.approvals = {
|
|
26597
|
-
mode:
|
|
27289
|
+
mode: readString18(approvals, "mode") ?? readString18(approvals, "approval_mode") ?? readString18(approvals, "approvalMode") ?? void 0,
|
|
26598
27290
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
26599
|
-
cronMode:
|
|
27291
|
+
cronMode: readString18(approvals, "cron_mode") ?? readString18(approvals, "cronMode") ?? void 0
|
|
26600
27292
|
};
|
|
26601
27293
|
}
|
|
26602
27294
|
const terminal = readOptionalObject(body, "terminal");
|
|
26603
27295
|
if (terminal) {
|
|
26604
27296
|
input.terminal = {
|
|
26605
|
-
backend:
|
|
26606
|
-
cwd:
|
|
27297
|
+
backend: readString18(terminal, "backend") ?? void 0,
|
|
27298
|
+
cwd: readString18(terminal, "cwd") ?? void 0,
|
|
26607
27299
|
containerCpu: readPositiveInteger2(
|
|
26608
27300
|
terminal.container_cpu ?? terminal.containerCpu
|
|
26609
27301
|
),
|
|
@@ -26720,9 +27412,9 @@ import {
|
|
|
26720
27412
|
access as access3,
|
|
26721
27413
|
readdir as readdir10,
|
|
26722
27414
|
readFile as readFile18,
|
|
26723
|
-
stat as
|
|
27415
|
+
stat as stat19
|
|
26724
27416
|
} from "fs/promises";
|
|
26725
|
-
import
|
|
27417
|
+
import path26 from "path";
|
|
26726
27418
|
import YAML6 from "yaml";
|
|
26727
27419
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
26728
27420
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -26866,14 +27558,23 @@ async function resetHermesMemoryStore(profileName, target) {
|
|
|
26866
27558
|
return readHermesProfileMemory(profileName);
|
|
26867
27559
|
}
|
|
26868
27560
|
async function saveHermesMemorySettings(profileName, patch) {
|
|
26869
|
-
const
|
|
26870
|
-
|
|
26871
|
-
|
|
26872
|
-
|
|
26873
|
-
|
|
26874
|
-
);
|
|
27561
|
+
const hasLimitPatch = patch.memoryCharLimit !== void 0 || patch.userCharLimit !== void 0;
|
|
27562
|
+
const hasProviderPatch = Object.keys(patch).some(
|
|
27563
|
+
(key) => key !== "memoryCharLimit" && key !== "userCharLimit"
|
|
27564
|
+
);
|
|
27565
|
+
if (hasProviderPatch) {
|
|
27566
|
+
const provider = await readActiveMemoryProvider(profileName);
|
|
27567
|
+
if (!provider) {
|
|
27568
|
+
throw new HermesMemoryError(
|
|
27569
|
+
"memory_settings_builtin_only",
|
|
27570
|
+
"\u5F53\u524D Profile \u4F7F\u7528 built-in memory\uFF0C\u6CA1\u6709\u53EF\u4FDD\u5B58\u7684\u5916\u90E8 provider \u8BBE\u7F6E\u3002"
|
|
27571
|
+
);
|
|
27572
|
+
}
|
|
27573
|
+
await saveProviderSettings(profileName, provider, patch);
|
|
27574
|
+
}
|
|
27575
|
+
if (hasLimitPatch) {
|
|
27576
|
+
await patchHermesMemoryLimits(profileName, patch);
|
|
26875
27577
|
}
|
|
26876
|
-
await saveProviderSettings(profileName, provider, patch);
|
|
26877
27578
|
return readHermesProfileMemory(profileName);
|
|
26878
27579
|
}
|
|
26879
27580
|
async function saveHermesMemoryProviderSettings(profileName, provider, patch) {
|
|
@@ -26910,9 +27611,9 @@ async function testHindsightProviderSettings(profileName, patch) {
|
|
|
26910
27611
|
const mode = normalizeHindsightMode(
|
|
26911
27612
|
patch.mode ?? config.mode ?? env.HINDSIGHT_MODE
|
|
26912
27613
|
);
|
|
26913
|
-
const apiUrl =
|
|
26914
|
-
const bankId =
|
|
26915
|
-
const apiKey =
|
|
27614
|
+
const apiUrl = readString19(patch.apiUrl) ?? readString19(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
|
|
27615
|
+
const bankId = readString19(patch.bankId) ?? readString19(config.bank_id) ?? "hermes";
|
|
27616
|
+
const apiKey = readString19(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key);
|
|
26916
27617
|
const baseUrl = normalizeHttpUrl(apiUrl);
|
|
26917
27618
|
if (!baseUrl) {
|
|
26918
27619
|
return {
|
|
@@ -27044,7 +27745,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
27044
27745
|
});
|
|
27045
27746
|
await patchJsonProviderConfig(
|
|
27046
27747
|
profileName,
|
|
27047
|
-
|
|
27748
|
+
path26.join("hindsight", "config.json"),
|
|
27048
27749
|
{
|
|
27049
27750
|
mode: patch.mode,
|
|
27050
27751
|
api_url: patch.apiUrl,
|
|
@@ -27194,7 +27895,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
27194
27895
|
const configPath = resolveHermesConfigPath(profileName);
|
|
27195
27896
|
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27196
27897
|
(error) => {
|
|
27197
|
-
if (
|
|
27898
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27198
27899
|
return null;
|
|
27199
27900
|
}
|
|
27200
27901
|
throw error;
|
|
@@ -27214,14 +27915,46 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
27214
27915
|
document.contents = document.createNode(config);
|
|
27215
27916
|
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
27216
27917
|
}
|
|
27918
|
+
async function patchHermesMemoryLimits(profileName, patch) {
|
|
27919
|
+
const configPath = resolveHermesConfigPath(profileName);
|
|
27920
|
+
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27921
|
+
(error) => {
|
|
27922
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27923
|
+
return null;
|
|
27924
|
+
}
|
|
27925
|
+
throw error;
|
|
27926
|
+
}
|
|
27927
|
+
);
|
|
27928
|
+
const document = existingRaw ? YAML6.parseDocument(existingRaw) : new YAML6.Document({});
|
|
27929
|
+
const config = toRecord18(document.toJSON());
|
|
27930
|
+
const memory = toRecord18(config.memory);
|
|
27931
|
+
if (patch.memoryCharLimit !== void 0) {
|
|
27932
|
+
memory.memory_char_limit = patch.memoryCharLimit;
|
|
27933
|
+
}
|
|
27934
|
+
if (patch.userCharLimit !== void 0) {
|
|
27935
|
+
memory.user_char_limit = patch.userCharLimit;
|
|
27936
|
+
}
|
|
27937
|
+
config.memory = memory;
|
|
27938
|
+
if (existingRaw) {
|
|
27939
|
+
await atomicWriteFilePreservingMetadata(
|
|
27940
|
+
`${configPath}.bak.${Date.now()}`,
|
|
27941
|
+
existingRaw,
|
|
27942
|
+
{
|
|
27943
|
+
metadataSourcePath: configPath
|
|
27944
|
+
}
|
|
27945
|
+
);
|
|
27946
|
+
}
|
|
27947
|
+
document.contents = document.createNode(config);
|
|
27948
|
+
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
27949
|
+
}
|
|
27217
27950
|
function resolveMemoryDir(profileName) {
|
|
27218
|
-
return
|
|
27951
|
+
return path26.join(resolveHermesProfileDir(profileName), "memories");
|
|
27219
27952
|
}
|
|
27220
27953
|
async function readMemoryStore(profileName, target, limits) {
|
|
27221
27954
|
const filePath = memoryFilePath(profileName, target);
|
|
27222
27955
|
const entries = await readMemoryEntries(filePath);
|
|
27223
|
-
const fileStat = await
|
|
27224
|
-
if (
|
|
27956
|
+
const fileStat = await stat19(filePath).catch((error) => {
|
|
27957
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27225
27958
|
return null;
|
|
27226
27959
|
}
|
|
27227
27960
|
throw error;
|
|
@@ -27250,7 +27983,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
27250
27983
|
}
|
|
27251
27984
|
async function readMemoryEntries(filePath) {
|
|
27252
27985
|
const raw = await readFile18(filePath, "utf8").catch((error) => {
|
|
27253
|
-
if (
|
|
27986
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27254
27987
|
return "";
|
|
27255
27988
|
}
|
|
27256
27989
|
throw error;
|
|
@@ -27276,7 +28009,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
27276
28009
|
);
|
|
27277
28010
|
}
|
|
27278
28011
|
function memoryFilePath(profileName, target) {
|
|
27279
|
-
return
|
|
28012
|
+
return path26.join(
|
|
27280
28013
|
resolveMemoryDir(profileName),
|
|
27281
28014
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
27282
28015
|
);
|
|
@@ -27336,7 +28069,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
27336
28069
|
configurable: true,
|
|
27337
28070
|
configured: true,
|
|
27338
28071
|
configurationIssue: null,
|
|
27339
|
-
providerConfigPath:
|
|
28072
|
+
providerConfigPath: path26.join(
|
|
27340
28073
|
resolveHermesProfileDir(profileName),
|
|
27341
28074
|
"<provider>.json"
|
|
27342
28075
|
),
|
|
@@ -27414,7 +28147,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27414
28147
|
const config2 = await readJsonObject(
|
|
27415
28148
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
27416
28149
|
);
|
|
27417
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
28150
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString19(config2.apiKey)) || isConfiguredEnvValue(readString19(config2.api_key)) || isConfiguredEnvValue(readString19(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
27418
28151
|
configured: false,
|
|
27419
28152
|
issue: "Honcho \u9700\u8981\u5148\u586B\u5199 API Key\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
27420
28153
|
};
|
|
@@ -27423,7 +28156,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27423
28156
|
const config2 = await readJsonObject(
|
|
27424
28157
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
27425
28158
|
);
|
|
27426
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
28159
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString19(config2.api_key)) ? { configured: true, issue: null } : {
|
|
27427
28160
|
configured: false,
|
|
27428
28161
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u9875\u586B\u5199 API Key\uFF0CLink \u4F1A\u5199\u5165\u5F53\u524D Profile \u7684 .env\u3002"
|
|
27429
28162
|
};
|
|
@@ -27465,7 +28198,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27465
28198
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
27466
28199
|
);
|
|
27467
28200
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
27468
|
-
const apiKey =
|
|
28201
|
+
const apiKey = readString19(config.apiKey) ?? readString19(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
27469
28202
|
if (mode === "cloud") {
|
|
27470
28203
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
27471
28204
|
configured: false,
|
|
@@ -27473,15 +28206,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27473
28206
|
};
|
|
27474
28207
|
}
|
|
27475
28208
|
if (mode === "local_external") {
|
|
27476
|
-
const apiUrl =
|
|
28209
|
+
const apiUrl = readString19(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
27477
28210
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
27478
28211
|
configured: false,
|
|
27479
28212
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
27480
28213
|
};
|
|
27481
28214
|
}
|
|
27482
28215
|
if (mode === "local_embedded") {
|
|
27483
|
-
const llmProvider =
|
|
27484
|
-
const llmModel =
|
|
28216
|
+
const llmProvider = readString19(config.llm_provider) ?? "openai";
|
|
28217
|
+
const llmModel = readString19(config.llm_model);
|
|
27485
28218
|
if (!llmModel) {
|
|
27486
28219
|
return {
|
|
27487
28220
|
configured: false,
|
|
@@ -27489,7 +28222,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27489
28222
|
};
|
|
27490
28223
|
}
|
|
27491
28224
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
27492
|
-
|
|
28225
|
+
readString19(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
27493
28226
|
)) {
|
|
27494
28227
|
return {
|
|
27495
28228
|
configured: false,
|
|
@@ -27497,7 +28230,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
27497
28230
|
};
|
|
27498
28231
|
}
|
|
27499
28232
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
27500
|
-
|
|
28233
|
+
readString19(config.llmApiKey) ?? readString19(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
27501
28234
|
)) {
|
|
27502
28235
|
return {
|
|
27503
28236
|
configured: false,
|
|
@@ -27553,8 +28286,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27553
28286
|
secretSetting(
|
|
27554
28287
|
"apiKey",
|
|
27555
28288
|
"API Key",
|
|
27556
|
-
env.HONCHO_API_KEY ??
|
|
27557
|
-
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
28289
|
+
env.HONCHO_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28290
|
+
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27558
28291
|
),
|
|
27559
28292
|
stringSetting("workspace", "Workspace", config.workspace ?? "hermes"),
|
|
27560
28293
|
stringSetting("peerName", "\u7528\u6237 Peer", config.peerName ?? ""),
|
|
@@ -27596,8 +28329,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27596
28329
|
secretSetting(
|
|
27597
28330
|
"apiKey",
|
|
27598
28331
|
"API Key",
|
|
27599
|
-
env.MEM0_API_KEY ??
|
|
27600
|
-
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
28332
|
+
env.MEM0_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28333
|
+
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27601
28334
|
),
|
|
27602
28335
|
stringSetting("userId", "User ID", config.user_id ?? "hermes-user"),
|
|
27603
28336
|
stringSetting("agentId", "Agent ID", config.agent_id ?? "hermes"),
|
|
@@ -27682,8 +28415,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27682
28415
|
secretSetting(
|
|
27683
28416
|
"apiKey",
|
|
27684
28417
|
"Hindsight API Key",
|
|
27685
|
-
env.HINDSIGHT_API_KEY ??
|
|
27686
|
-
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(
|
|
28418
|
+
env.HINDSIGHT_API_KEY ?? readString19(config.apiKey) ?? readString19(config.api_key),
|
|
28419
|
+
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString19(config.apiKey)) || isConfiguredEnvValue(readString19(config.api_key))
|
|
27687
28420
|
),
|
|
27688
28421
|
stringSetting(
|
|
27689
28422
|
"bankId",
|
|
@@ -27705,8 +28438,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27705
28438
|
secretSetting(
|
|
27706
28439
|
"llmApiKey",
|
|
27707
28440
|
"LLM API Key",
|
|
27708
|
-
env.HINDSIGHT_LLM_API_KEY ??
|
|
27709
|
-
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(
|
|
28441
|
+
env.HINDSIGHT_LLM_API_KEY ?? readString19(config.llmApiKey) ?? readString19(config.llm_api_key),
|
|
28442
|
+
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString19(config.llmApiKey)) || isConfiguredEnvValue(readString19(config.llm_api_key))
|
|
27710
28443
|
),
|
|
27711
28444
|
booleanSetting("autoRecall", "\u81EA\u52A8\u56DE\u5FC6", config.auto_recall ?? true),
|
|
27712
28445
|
booleanSetting("autoRetain", "\u81EA\u52A8\u6C89\u6DC0", config.auto_retain ?? true),
|
|
@@ -27730,7 +28463,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27730
28463
|
stringSetting(
|
|
27731
28464
|
"dbPath",
|
|
27732
28465
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
27733
|
-
config.db_path ??
|
|
28466
|
+
config.db_path ?? path26.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
27734
28467
|
),
|
|
27735
28468
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
27736
28469
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -27766,7 +28499,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27766
28499
|
stringSetting(
|
|
27767
28500
|
"workingDirectory",
|
|
27768
28501
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
27769
|
-
|
|
28502
|
+
path26.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
27770
28503
|
false
|
|
27771
28504
|
)
|
|
27772
28505
|
];
|
|
@@ -27775,16 +28508,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
27775
28508
|
}
|
|
27776
28509
|
function memoryProviderConfigPath(profileName, provider) {
|
|
27777
28510
|
if (provider === "honcho") {
|
|
27778
|
-
return
|
|
28511
|
+
return path26.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
27779
28512
|
}
|
|
27780
28513
|
if (provider === "mem0") {
|
|
27781
|
-
return
|
|
28514
|
+
return path26.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
27782
28515
|
}
|
|
27783
28516
|
if (provider === "supermemory") {
|
|
27784
|
-
return
|
|
28517
|
+
return path26.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
27785
28518
|
}
|
|
27786
28519
|
if (provider === "hindsight") {
|
|
27787
|
-
return
|
|
28520
|
+
return path26.join(
|
|
27788
28521
|
resolveHermesProfileDir(profileName),
|
|
27789
28522
|
"hindsight",
|
|
27790
28523
|
"config.json"
|
|
@@ -27793,13 +28526,13 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
27793
28526
|
return null;
|
|
27794
28527
|
}
|
|
27795
28528
|
function customProviderConfigPath(profileName, provider) {
|
|
27796
|
-
return
|
|
28529
|
+
return path26.join(
|
|
27797
28530
|
resolveHermesProfileDir(profileName),
|
|
27798
28531
|
`${normalizeCustomProviderId(provider)}.json`
|
|
27799
28532
|
);
|
|
27800
28533
|
}
|
|
27801
28534
|
function customProviderRegistryPath(profileName) {
|
|
27802
|
-
return
|
|
28535
|
+
return path26.join(
|
|
27803
28536
|
resolveHermesProfileDir(profileName),
|
|
27804
28537
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
27805
28538
|
);
|
|
@@ -27807,7 +28540,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
27807
28540
|
async function readCustomProviderRegistry(profileName) {
|
|
27808
28541
|
const raw = await readFile18(customProviderRegistryPath(profileName), "utf8").catch(
|
|
27809
28542
|
(error) => {
|
|
27810
|
-
if (
|
|
28543
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27811
28544
|
return "";
|
|
27812
28545
|
}
|
|
27813
28546
|
throw error;
|
|
@@ -27825,11 +28558,11 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
27825
28558
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
27826
28559
|
}
|
|
27827
28560
|
const record = toRecord18(item);
|
|
27828
|
-
const id = normalizeCustomProviderId(
|
|
28561
|
+
const id = normalizeCustomProviderId(readString19(record.id) ?? "");
|
|
27829
28562
|
return {
|
|
27830
28563
|
id,
|
|
27831
|
-
label:
|
|
27832
|
-
description:
|
|
28564
|
+
label: readString19(record.label) ?? id,
|
|
28565
|
+
description: readString19(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
27833
28566
|
};
|
|
27834
28567
|
}).filter((item) => item.id);
|
|
27835
28568
|
} catch {
|
|
@@ -27851,10 +28584,10 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
27851
28584
|
);
|
|
27852
28585
|
}
|
|
27853
28586
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
27854
|
-
const pluginsDir =
|
|
28587
|
+
const pluginsDir = path26.join(resolveHermesProfileDir(profileName), "plugins");
|
|
27855
28588
|
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
27856
28589
|
(error) => {
|
|
27857
|
-
if (
|
|
28590
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27858
28591
|
return [];
|
|
27859
28592
|
}
|
|
27860
28593
|
throw error;
|
|
@@ -27871,21 +28604,21 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
27871
28604
|
} catch {
|
|
27872
28605
|
continue;
|
|
27873
28606
|
}
|
|
27874
|
-
const providerDir =
|
|
28607
|
+
const providerDir = path26.join(pluginsDir, entry.name);
|
|
27875
28608
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
27876
28609
|
continue;
|
|
27877
28610
|
}
|
|
27878
28611
|
const meta = await readPluginMetadata(providerDir);
|
|
27879
28612
|
descriptors.push({
|
|
27880
28613
|
id: providerId,
|
|
27881
|
-
label:
|
|
27882
|
-
description:
|
|
28614
|
+
label: readString19(meta.name) ?? providerId,
|
|
28615
|
+
description: readString19(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
27883
28616
|
});
|
|
27884
28617
|
}
|
|
27885
28618
|
return descriptors;
|
|
27886
28619
|
}
|
|
27887
28620
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
27888
|
-
const providerDir =
|
|
28621
|
+
const providerDir = path26.join(
|
|
27889
28622
|
resolveHermesProfileDir(profileName),
|
|
27890
28623
|
"plugins",
|
|
27891
28624
|
normalizeCustomProviderId(provider)
|
|
@@ -27893,9 +28626,9 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
27893
28626
|
return isMemoryProviderPluginDir(providerDir);
|
|
27894
28627
|
}
|
|
27895
28628
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
27896
|
-
const source = await readFile18(
|
|
28629
|
+
const source = await readFile18(path26.join(providerDir, "__init__.py"), "utf8").catch(
|
|
27897
28630
|
(error) => {
|
|
27898
|
-
if (
|
|
28631
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27899
28632
|
return "";
|
|
27900
28633
|
}
|
|
27901
28634
|
throw error;
|
|
@@ -27905,9 +28638,9 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
27905
28638
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
27906
28639
|
}
|
|
27907
28640
|
async function readPluginMetadata(providerDir) {
|
|
27908
|
-
const raw = await readFile18(
|
|
28641
|
+
const raw = await readFile18(path26.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
27909
28642
|
(error) => {
|
|
27910
|
-
if (
|
|
28643
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27911
28644
|
return "";
|
|
27912
28645
|
}
|
|
27913
28646
|
throw error;
|
|
@@ -27917,10 +28650,10 @@ async function readPluginMetadata(providerDir) {
|
|
|
27917
28650
|
}
|
|
27918
28651
|
async function resolveByteRoverCli() {
|
|
27919
28652
|
const candidates = [
|
|
27920
|
-
...(process.env.PATH ?? "").split(
|
|
27921
|
-
|
|
28653
|
+
...(process.env.PATH ?? "").split(path26.delimiter).filter(Boolean).map((dir) => path26.join(dir, "brv")),
|
|
28654
|
+
path26.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
27922
28655
|
"/usr/local/bin/brv",
|
|
27923
|
-
|
|
28656
|
+
path26.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
27924
28657
|
].filter(Boolean);
|
|
27925
28658
|
for (const candidate of candidates) {
|
|
27926
28659
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -27933,7 +28666,7 @@ async function resolveByteRoverCli() {
|
|
|
27933
28666
|
async function readHolographicProviderConfig(profileName) {
|
|
27934
28667
|
const raw = await readFile18(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
27935
28668
|
(error) => {
|
|
27936
|
-
if (
|
|
28669
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27937
28670
|
return "";
|
|
27938
28671
|
}
|
|
27939
28672
|
throw error;
|
|
@@ -27947,7 +28680,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
27947
28680
|
const configPath = resolveHermesConfigPath(profileName);
|
|
27948
28681
|
const existingRaw = await readFile18(configPath, "utf8").catch(
|
|
27949
28682
|
(error) => {
|
|
27950
|
-
if (
|
|
28683
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27951
28684
|
return null;
|
|
27952
28685
|
}
|
|
27953
28686
|
throw error;
|
|
@@ -27981,9 +28714,9 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
27981
28714
|
if (entries.length === 0) {
|
|
27982
28715
|
return;
|
|
27983
28716
|
}
|
|
27984
|
-
const envPath =
|
|
28717
|
+
const envPath = path26.join(resolveHermesProfileDir(profileName), ".env");
|
|
27985
28718
|
const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
|
|
27986
|
-
if (
|
|
28719
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
27987
28720
|
return "";
|
|
27988
28721
|
}
|
|
27989
28722
|
throw error;
|
|
@@ -28041,7 +28774,7 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
28041
28774
|
].includes(key);
|
|
28042
28775
|
}
|
|
28043
28776
|
function normalizeHindsightMode(value) {
|
|
28044
|
-
const mode =
|
|
28777
|
+
const mode = readString19(value) ?? "cloud";
|
|
28045
28778
|
return mode === "local" ? "local_embedded" : mode;
|
|
28046
28779
|
}
|
|
28047
28780
|
function normalizeHttpUrl(value) {
|
|
@@ -28110,27 +28843,27 @@ function parseJsonObject2(text) {
|
|
|
28110
28843
|
}
|
|
28111
28844
|
}
|
|
28112
28845
|
function readHindsightError(json) {
|
|
28113
|
-
const detail =
|
|
28846
|
+
const detail = readString19(json.detail) ?? readString19(json.error);
|
|
28114
28847
|
return detail ? `\uFF1A${detail}` : "";
|
|
28115
28848
|
}
|
|
28116
28849
|
function hindsightSemanticIssue(pathName, json) {
|
|
28117
28850
|
if (pathName === "/health") {
|
|
28118
|
-
const status =
|
|
28851
|
+
const status = readString19(json.status);
|
|
28119
28852
|
return status && ["healthy", "ok"].includes(status.toLowerCase()) ? null : `\u5065\u5EB7\u72B6\u6001\u5F02\u5E38\uFF1A${status ?? "unknown"}`;
|
|
28120
28853
|
}
|
|
28121
28854
|
return null;
|
|
28122
28855
|
}
|
|
28123
28856
|
function summarizeHindsightProbe(pathName, json) {
|
|
28124
28857
|
if (pathName === "/health") {
|
|
28125
|
-
const status =
|
|
28126
|
-
const database =
|
|
28858
|
+
const status = readString19(json.status) ?? "ok";
|
|
28859
|
+
const database = readString19(json.database);
|
|
28127
28860
|
return database ? `${status}, database ${database}` : status;
|
|
28128
28861
|
}
|
|
28129
28862
|
if (pathName === "/version") {
|
|
28130
|
-
const version =
|
|
28863
|
+
const version = readString19(json.api_version);
|
|
28131
28864
|
return version ? `API ${version}` : "version endpoint reachable";
|
|
28132
28865
|
}
|
|
28133
|
-
const bankId =
|
|
28866
|
+
const bankId = readString19(json.bank_id);
|
|
28134
28867
|
return bankId ? `bank ${bankId} reachable` : "bank config reachable";
|
|
28135
28868
|
}
|
|
28136
28869
|
async function readActiveMemoryProvider(profileName) {
|
|
@@ -28138,21 +28871,21 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
28138
28871
|
resolveHermesConfigPath(profileName),
|
|
28139
28872
|
"utf8"
|
|
28140
28873
|
).catch((error) => {
|
|
28141
|
-
if (
|
|
28874
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28142
28875
|
return "";
|
|
28143
28876
|
}
|
|
28144
28877
|
throw error;
|
|
28145
28878
|
});
|
|
28146
28879
|
const config = raw ? toRecord18(YAML6.parse(raw)) : {};
|
|
28147
28880
|
const memory = toRecord18(config.memory);
|
|
28148
|
-
const provider =
|
|
28881
|
+
const provider = readString19(memory.provider);
|
|
28149
28882
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
28150
28883
|
return null;
|
|
28151
28884
|
}
|
|
28152
28885
|
return provider;
|
|
28153
28886
|
}
|
|
28154
28887
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
28155
|
-
const configPath =
|
|
28888
|
+
const configPath = path26.join(
|
|
28156
28889
|
resolveHermesProfileDir(profileName),
|
|
28157
28890
|
relativePath
|
|
28158
28891
|
);
|
|
@@ -28171,7 +28904,7 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
28171
28904
|
}
|
|
28172
28905
|
async function readJsonObject(filePath) {
|
|
28173
28906
|
const raw = await readFile18(filePath, "utf8").catch((error) => {
|
|
28174
|
-
if (
|
|
28907
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28175
28908
|
return "{}";
|
|
28176
28909
|
}
|
|
28177
28910
|
throw error;
|
|
@@ -28181,7 +28914,7 @@ async function readJsonObject(filePath) {
|
|
|
28181
28914
|
} catch {
|
|
28182
28915
|
throw new HermesMemoryError(
|
|
28183
28916
|
"memory_provider_config_invalid",
|
|
28184
|
-
`${
|
|
28917
|
+
`${path26.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
28185
28918
|
);
|
|
28186
28919
|
}
|
|
28187
28920
|
}
|
|
@@ -28202,7 +28935,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
28202
28935
|
return {
|
|
28203
28936
|
key,
|
|
28204
28937
|
label,
|
|
28205
|
-
value:
|
|
28938
|
+
value: readString19(value) ?? "",
|
|
28206
28939
|
editable,
|
|
28207
28940
|
kind: "string"
|
|
28208
28941
|
};
|
|
@@ -28214,7 +28947,7 @@ function secretSetting(key, label, value, configured) {
|
|
|
28214
28947
|
value: "",
|
|
28215
28948
|
editable: true,
|
|
28216
28949
|
kind: "secret",
|
|
28217
|
-
configured: configured || isConfiguredEnvValue(
|
|
28950
|
+
configured: configured || isConfiguredEnvValue(readString19(value))
|
|
28218
28951
|
};
|
|
28219
28952
|
}
|
|
28220
28953
|
function textSetting(key, label, value, editable = true) {
|
|
@@ -28227,7 +28960,7 @@ function textSetting(key, label, value, editable = true) {
|
|
|
28227
28960
|
};
|
|
28228
28961
|
}
|
|
28229
28962
|
function selectSetting(key, label, value, options, editable = true) {
|
|
28230
|
-
const stringValue =
|
|
28963
|
+
const stringValue = readString19(value) ?? options[0] ?? null;
|
|
28231
28964
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
28232
28965
|
}
|
|
28233
28966
|
async function readMemoryLimits(profileName) {
|
|
@@ -28235,7 +28968,7 @@ async function readMemoryLimits(profileName) {
|
|
|
28235
28968
|
resolveHermesConfigPath(profileName),
|
|
28236
28969
|
"utf8"
|
|
28237
28970
|
).catch((error) => {
|
|
28238
|
-
if (
|
|
28971
|
+
if (isNodeError21(error, "ENOENT")) {
|
|
28239
28972
|
return "";
|
|
28240
28973
|
}
|
|
28241
28974
|
throw error;
|
|
@@ -28300,7 +29033,7 @@ function hashString(value) {
|
|
|
28300
29033
|
function toRecord18(value) {
|
|
28301
29034
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
28302
29035
|
}
|
|
28303
|
-
function
|
|
29036
|
+
function readString19(value) {
|
|
28304
29037
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
28305
29038
|
}
|
|
28306
29039
|
function readPositiveInteger3(value) {
|
|
@@ -28328,7 +29061,7 @@ function formatEnvValue3(value) {
|
|
|
28328
29061
|
function escapeRegExp4(value) {
|
|
28329
29062
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
28330
29063
|
}
|
|
28331
|
-
function
|
|
29064
|
+
function isNodeError21(error, code) {
|
|
28332
29065
|
return error instanceof Error && "code" in error && error.code === code;
|
|
28333
29066
|
}
|
|
28334
29067
|
|
|
@@ -28404,7 +29137,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
28404
29137
|
try {
|
|
28405
29138
|
ctx.body = await saveHermesMemorySettings(
|
|
28406
29139
|
ctx.params.name,
|
|
28407
|
-
readMemorySettingsPatch(body)
|
|
29140
|
+
readMemorySettingsPatch(body, { includeLimits: true })
|
|
28408
29141
|
);
|
|
28409
29142
|
} catch (error) {
|
|
28410
29143
|
throw toMemoryHttpError(error);
|
|
@@ -28459,7 +29192,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
28459
29192
|
);
|
|
28460
29193
|
}
|
|
28461
29194
|
function readMemoryTarget(body) {
|
|
28462
|
-
const raw =
|
|
29195
|
+
const raw = readString18(body, "target");
|
|
28463
29196
|
if (raw === "memory" || raw === "user") {
|
|
28464
29197
|
return raw;
|
|
28465
29198
|
}
|
|
@@ -28470,7 +29203,7 @@ function readMemoryTarget(body) {
|
|
|
28470
29203
|
);
|
|
28471
29204
|
}
|
|
28472
29205
|
function readMemoryResetTarget(body) {
|
|
28473
|
-
const raw =
|
|
29206
|
+
const raw = readString18(body, "target") ?? "all";
|
|
28474
29207
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
28475
29208
|
return raw;
|
|
28476
29209
|
}
|
|
@@ -28481,7 +29214,7 @@ function readMemoryResetTarget(body) {
|
|
|
28481
29214
|
);
|
|
28482
29215
|
}
|
|
28483
29216
|
function readRequiredMemoryContent(body) {
|
|
28484
|
-
const content =
|
|
29217
|
+
const content = readString18(body, "content") ?? readString18(body, "text");
|
|
28485
29218
|
if (!content) {
|
|
28486
29219
|
throw new LinkHttpError(
|
|
28487
29220
|
400,
|
|
@@ -28492,7 +29225,7 @@ function readRequiredMemoryContent(body) {
|
|
|
28492
29225
|
return content;
|
|
28493
29226
|
}
|
|
28494
29227
|
function readRequiredMemoryMatch(body) {
|
|
28495
|
-
const oldText =
|
|
29228
|
+
const oldText = readString18(body, "old_text") ?? readString18(body, "oldText") ?? readString18(body, "match");
|
|
28496
29229
|
if (!oldText) {
|
|
28497
29230
|
throw new LinkHttpError(
|
|
28498
29231
|
400,
|
|
@@ -28503,7 +29236,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
28503
29236
|
return oldText;
|
|
28504
29237
|
}
|
|
28505
29238
|
function readRequiredMemoryProvider(body) {
|
|
28506
|
-
const provider =
|
|
29239
|
+
const provider = readString18(body, "provider") ?? readString18(body, "provider_id") ?? readString18(body, "providerId");
|
|
28507
29240
|
if (!provider) {
|
|
28508
29241
|
throw new LinkHttpError(
|
|
28509
29242
|
400,
|
|
@@ -28513,9 +29246,27 @@ function readRequiredMemoryProvider(body) {
|
|
|
28513
29246
|
}
|
|
28514
29247
|
return provider;
|
|
28515
29248
|
}
|
|
28516
|
-
function readMemorySettingsPatch(body) {
|
|
29249
|
+
function readMemorySettingsPatch(body, options = {}) {
|
|
28517
29250
|
const input = {};
|
|
28518
|
-
|
|
29251
|
+
if (options.includeLimits) {
|
|
29252
|
+
const memoryCharLimit = readOptionalPositiveInteger(
|
|
29253
|
+
body,
|
|
29254
|
+
"memory_char_limit",
|
|
29255
|
+
"memoryCharLimit"
|
|
29256
|
+
);
|
|
29257
|
+
if (memoryCharLimit !== void 0) {
|
|
29258
|
+
input.memoryCharLimit = memoryCharLimit;
|
|
29259
|
+
}
|
|
29260
|
+
const userCharLimit = readOptionalPositiveInteger(
|
|
29261
|
+
body,
|
|
29262
|
+
"user_char_limit",
|
|
29263
|
+
"userCharLimit"
|
|
29264
|
+
);
|
|
29265
|
+
if (userCharLimit !== void 0) {
|
|
29266
|
+
input.userCharLimit = userCharLimit;
|
|
29267
|
+
}
|
|
29268
|
+
}
|
|
29269
|
+
const mode = readString18(body, "mode");
|
|
28519
29270
|
if (mode) {
|
|
28520
29271
|
input.mode = mode;
|
|
28521
29272
|
}
|
|
@@ -28531,7 +29282,7 @@ function readMemorySettingsPatch(body) {
|
|
|
28531
29282
|
if (bankId !== void 0) {
|
|
28532
29283
|
input.bankId = bankId;
|
|
28533
29284
|
}
|
|
28534
|
-
const llmProvider =
|
|
29285
|
+
const llmProvider = readString18(body, "llm_provider") ?? readString18(body, "llmProvider");
|
|
28535
29286
|
if (llmProvider) {
|
|
28536
29287
|
input.llmProvider = llmProvider;
|
|
28537
29288
|
}
|
|
@@ -28567,11 +29318,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28567
29318
|
if (autoRetain !== void 0) {
|
|
28568
29319
|
input.autoRetain = autoRetain;
|
|
28569
29320
|
}
|
|
28570
|
-
const memoryMode =
|
|
29321
|
+
const memoryMode = readString18(body, "memory_mode") ?? readString18(body, "memoryMode");
|
|
28571
29322
|
if (memoryMode) {
|
|
28572
29323
|
input.memoryMode = memoryMode;
|
|
28573
29324
|
}
|
|
28574
|
-
const recallBudget =
|
|
29325
|
+
const recallBudget = readString18(body, "recall_budget") ?? readString18(body, "recallBudget");
|
|
28575
29326
|
if (recallBudget) {
|
|
28576
29327
|
input.recallBudget = recallBudget;
|
|
28577
29328
|
}
|
|
@@ -28587,11 +29338,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28587
29338
|
if (profileFrequency !== void 0) {
|
|
28588
29339
|
input.profileFrequency = profileFrequency;
|
|
28589
29340
|
}
|
|
28590
|
-
const captureMode =
|
|
29341
|
+
const captureMode = readString18(body, "capture_mode") ?? readString18(body, "captureMode");
|
|
28591
29342
|
if (captureMode) {
|
|
28592
29343
|
input.captureMode = captureMode;
|
|
28593
29344
|
}
|
|
28594
|
-
const searchMode =
|
|
29345
|
+
const searchMode = readString18(body, "search_mode") ?? readString18(body, "searchMode");
|
|
28595
29346
|
if (searchMode) {
|
|
28596
29347
|
input.searchMode = searchMode;
|
|
28597
29348
|
}
|
|
@@ -28625,11 +29376,11 @@ function readMemorySettingsPatch(body) {
|
|
|
28625
29376
|
if (aiPeer !== void 0) {
|
|
28626
29377
|
input.aiPeer = aiPeer;
|
|
28627
29378
|
}
|
|
28628
|
-
const recallMode =
|
|
29379
|
+
const recallMode = readString18(body, "recall_mode") ?? readString18(body, "recallMode");
|
|
28629
29380
|
if (recallMode) {
|
|
28630
29381
|
input.recallMode = recallMode;
|
|
28631
29382
|
}
|
|
28632
|
-
const writeFrequency =
|
|
29383
|
+
const writeFrequency = readString18(body, "write_frequency") ?? readString18(body, "writeFrequency");
|
|
28633
29384
|
if (writeFrequency) {
|
|
28634
29385
|
input.writeFrequency = writeFrequency;
|
|
28635
29386
|
}
|
|
@@ -28637,7 +29388,7 @@ function readMemorySettingsPatch(body) {
|
|
|
28637
29388
|
if (saveMessages !== void 0) {
|
|
28638
29389
|
input.saveMessages = saveMessages;
|
|
28639
29390
|
}
|
|
28640
|
-
const sessionStrategy =
|
|
29391
|
+
const sessionStrategy = readString18(body, "session_strategy") ?? readString18(body, "sessionStrategy");
|
|
28641
29392
|
if (sessionStrategy) {
|
|
28642
29393
|
input.sessionStrategy = sessionStrategy;
|
|
28643
29394
|
}
|
|
@@ -28735,6 +29486,23 @@ function readOptionalNumber(value, key) {
|
|
|
28735
29486
|
}
|
|
28736
29487
|
return numberValue;
|
|
28737
29488
|
}
|
|
29489
|
+
function readOptionalPositiveInteger(body, ...keys) {
|
|
29490
|
+
for (const key of keys) {
|
|
29491
|
+
if (!Object.prototype.hasOwnProperty.call(body, key)) {
|
|
29492
|
+
continue;
|
|
29493
|
+
}
|
|
29494
|
+
const value = readPositiveInteger2(body[key]);
|
|
29495
|
+
if (value === void 0) {
|
|
29496
|
+
throw new LinkHttpError(
|
|
29497
|
+
400,
|
|
29498
|
+
"memory_settings_invalid",
|
|
29499
|
+
`${key} must be a positive integer`
|
|
29500
|
+
);
|
|
29501
|
+
}
|
|
29502
|
+
return value;
|
|
29503
|
+
}
|
|
29504
|
+
return void 0;
|
|
29505
|
+
}
|
|
28738
29506
|
function readOptionalString(body, ...keys) {
|
|
28739
29507
|
for (const key of keys) {
|
|
28740
29508
|
if (Object.prototype.hasOwnProperty.call(body, key)) {
|
|
@@ -28770,7 +29538,7 @@ function toMemoryHttpError(error) {
|
|
|
28770
29538
|
|
|
28771
29539
|
// src/hermes/skills.ts
|
|
28772
29540
|
import { readFile as readFile19, readdir as readdir11 } from "fs/promises";
|
|
28773
|
-
import
|
|
29541
|
+
import path27 from "path";
|
|
28774
29542
|
import YAML7 from "yaml";
|
|
28775
29543
|
var HermesSkillNotFoundError = class extends Error {
|
|
28776
29544
|
constructor(skillName) {
|
|
@@ -28784,7 +29552,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
28784
29552
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
28785
29553
|
const profile = await readExistingProfile(profileName, paths);
|
|
28786
29554
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
28787
|
-
const skillsRoot =
|
|
29555
|
+
const skillsRoot = path27.join(profileDir, "skills");
|
|
28788
29556
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
28789
29557
|
findSkillFiles(skillsRoot),
|
|
28790
29558
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -28863,7 +29631,7 @@ async function findSkillFiles(root) {
|
|
|
28863
29631
|
async function collectSkillFiles(directory, results) {
|
|
28864
29632
|
const entries = await readdir11(directory, { withFileTypes: true }).catch(
|
|
28865
29633
|
(error) => {
|
|
28866
|
-
if (
|
|
29634
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28867
29635
|
return [];
|
|
28868
29636
|
}
|
|
28869
29637
|
throw error;
|
|
@@ -28875,7 +29643,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
28875
29643
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
28876
29644
|
continue;
|
|
28877
29645
|
}
|
|
28878
|
-
const entryPath =
|
|
29646
|
+
const entryPath = path27.join(directory, entry.name);
|
|
28879
29647
|
if (entry.isDirectory()) {
|
|
28880
29648
|
await collectSkillFiles(entryPath, results);
|
|
28881
29649
|
continue;
|
|
@@ -28888,7 +29656,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
28888
29656
|
async function readSkillMetadata(input) {
|
|
28889
29657
|
const raw = await readFile19(input.skillFile, "utf8").catch(
|
|
28890
29658
|
(error) => {
|
|
28891
|
-
if (
|
|
29659
|
+
if (isNodeError22(error, "ENOENT") || isNodeError22(error, "EACCES")) {
|
|
28892
29660
|
return null;
|
|
28893
29661
|
}
|
|
28894
29662
|
throw error;
|
|
@@ -28897,16 +29665,16 @@ async function readSkillMetadata(input) {
|
|
|
28897
29665
|
if (raw === null) {
|
|
28898
29666
|
return null;
|
|
28899
29667
|
}
|
|
28900
|
-
const skillDir =
|
|
29668
|
+
const skillDir = path27.dirname(input.skillFile);
|
|
28901
29669
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
28902
29670
|
const name = normalizeSkillName(
|
|
28903
|
-
|
|
29671
|
+
readString20(frontmatter.name) ?? path27.basename(skillDir)
|
|
28904
29672
|
);
|
|
28905
29673
|
if (!name) {
|
|
28906
29674
|
return null;
|
|
28907
29675
|
}
|
|
28908
29676
|
const description = normalizeDescription(
|
|
28909
|
-
|
|
29677
|
+
readString20(frontmatter.description) ?? firstBodyDescription(body)
|
|
28910
29678
|
);
|
|
28911
29679
|
const provenance = input.provenance.get(name) ?? {
|
|
28912
29680
|
source: "local",
|
|
@@ -28919,7 +29687,7 @@ async function readSkillMetadata(input) {
|
|
|
28919
29687
|
enabled: !input.disabled.has(name),
|
|
28920
29688
|
source: provenance.source,
|
|
28921
29689
|
trust: provenance.trust,
|
|
28922
|
-
relativePath:
|
|
29690
|
+
relativePath: path27.relative(input.skillsRoot, skillDir)
|
|
28923
29691
|
};
|
|
28924
29692
|
}
|
|
28925
29693
|
function parseSkillDocument(raw) {
|
|
@@ -28940,8 +29708,8 @@ function parseSkillDocument(raw) {
|
|
|
28940
29708
|
}
|
|
28941
29709
|
}
|
|
28942
29710
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
28943
|
-
const relative =
|
|
28944
|
-
const parts = relative.split(
|
|
29711
|
+
const relative = path27.relative(skillsRoot, skillFile);
|
|
29712
|
+
const parts = relative.split(path27.sep).filter(Boolean);
|
|
28945
29713
|
return parts.length >= 3 ? parts[0] : null;
|
|
28946
29714
|
}
|
|
28947
29715
|
function firstBodyDescription(body) {
|
|
@@ -28965,7 +29733,7 @@ function normalizeDescription(value) {
|
|
|
28965
29733
|
}
|
|
28966
29734
|
async function readDisabledSkillNames(configPath) {
|
|
28967
29735
|
const raw = await readFile19(configPath, "utf8").catch((error) => {
|
|
28968
|
-
if (
|
|
29736
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28969
29737
|
return "";
|
|
28970
29738
|
}
|
|
28971
29739
|
throw error;
|
|
@@ -28988,9 +29756,9 @@ async function readSkillProvenance(root) {
|
|
|
28988
29756
|
return provenance;
|
|
28989
29757
|
}
|
|
28990
29758
|
async function readBundledSkillNames(root) {
|
|
28991
|
-
const raw = await readFile19(
|
|
29759
|
+
const raw = await readFile19(path27.join(root, ".bundled_manifest"), "utf8").catch(
|
|
28992
29760
|
(error) => {
|
|
28993
|
-
if (
|
|
29761
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
28994
29762
|
return "";
|
|
28995
29763
|
}
|
|
28996
29764
|
throw error;
|
|
@@ -29011,9 +29779,9 @@ async function readBundledSkillNames(root) {
|
|
|
29011
29779
|
return names;
|
|
29012
29780
|
}
|
|
29013
29781
|
async function readHubInstalledSkills(root) {
|
|
29014
|
-
const raw = await readFile19(
|
|
29782
|
+
const raw = await readFile19(path27.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
29015
29783
|
(error) => {
|
|
29016
|
-
if (
|
|
29784
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
29017
29785
|
return "";
|
|
29018
29786
|
}
|
|
29019
29787
|
throw error;
|
|
@@ -29033,8 +29801,8 @@ async function readHubInstalledSkills(root) {
|
|
|
29033
29801
|
for (const [name, rawEntry] of Object.entries(installed2)) {
|
|
29034
29802
|
const entry = toRecord19(rawEntry);
|
|
29035
29803
|
result.set(normalizeSkillName(name), {
|
|
29036
|
-
source:
|
|
29037
|
-
trust:
|
|
29804
|
+
source: readString20(entry.source) ?? "hub",
|
|
29805
|
+
trust: readString20(entry.trust_level) ?? null
|
|
29038
29806
|
});
|
|
29039
29807
|
}
|
|
29040
29808
|
return result;
|
|
@@ -29085,7 +29853,7 @@ function compareCategoryNames(left, right) {
|
|
|
29085
29853
|
async function readHermesConfigDocument4(configPath) {
|
|
29086
29854
|
const existingRaw = await readFile19(configPath, "utf8").catch(
|
|
29087
29855
|
(error) => {
|
|
29088
|
-
if (
|
|
29856
|
+
if (isNodeError22(error, "ENOENT")) {
|
|
29089
29857
|
return null;
|
|
29090
29858
|
}
|
|
29091
29859
|
throw error;
|
|
@@ -29118,7 +29886,7 @@ function readStringList4(value) {
|
|
|
29118
29886
|
}
|
|
29119
29887
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
29120
29888
|
}
|
|
29121
|
-
function
|
|
29889
|
+
function readString20(value) {
|
|
29122
29890
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
29123
29891
|
}
|
|
29124
29892
|
function toRecord19(value) {
|
|
@@ -29132,7 +29900,7 @@ function ensureRecord5(target, key) {
|
|
|
29132
29900
|
target[key] = current;
|
|
29133
29901
|
return current;
|
|
29134
29902
|
}
|
|
29135
|
-
function
|
|
29903
|
+
function isNodeError22(error, code) {
|
|
29136
29904
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
29137
29905
|
}
|
|
29138
29906
|
|
|
@@ -29489,7 +30257,7 @@ function registerRunRoutes(router, options) {
|
|
|
29489
30257
|
router.post("/api/v1/runs", async (ctx) => {
|
|
29490
30258
|
await authenticateRequest(ctx, paths);
|
|
29491
30259
|
const body = await readJsonBody(ctx.req);
|
|
29492
|
-
const input =
|
|
30260
|
+
const input = readString18(body, "input");
|
|
29493
30261
|
if (!input) {
|
|
29494
30262
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
29495
30263
|
}
|
|
@@ -29497,12 +30265,12 @@ function registerRunRoutes(router, options) {
|
|
|
29497
30265
|
ctx.body = await createHermesRun(
|
|
29498
30266
|
{
|
|
29499
30267
|
input,
|
|
29500
|
-
instructions:
|
|
30268
|
+
instructions: readString18(body, "instructions") ?? void 0,
|
|
29501
30269
|
conversation_history: readConversationHistory(
|
|
29502
30270
|
body.conversation_history ?? body.conversationHistory
|
|
29503
30271
|
),
|
|
29504
|
-
session_id:
|
|
29505
|
-
session_key:
|
|
30272
|
+
session_id: readString18(body, "session_id") ?? readString18(body, "sessionId") ?? void 0,
|
|
30273
|
+
session_key: readString18(body, "session_key") ?? readString18(body, "sessionKey") ?? void 0
|
|
29506
30274
|
},
|
|
29507
30275
|
{ logger, profileName: readOptionalProfileName(body) }
|
|
29508
30276
|
);
|
|
@@ -29673,8 +30441,8 @@ function readModelList(payload) {
|
|
|
29673
30441
|
// src/hermes/updates.ts
|
|
29674
30442
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
29675
30443
|
import { spawn as spawn3 } from "child_process";
|
|
29676
|
-
import { mkdir as
|
|
29677
|
-
import
|
|
30444
|
+
import { mkdir as mkdir13, readFile as readFile20, rm as rm8 } from "fs/promises";
|
|
30445
|
+
import path28 from "path";
|
|
29678
30446
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
29679
30447
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
29680
30448
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -29737,7 +30505,7 @@ async function startHermesUpdate(options) {
|
|
|
29737
30505
|
signal: null,
|
|
29738
30506
|
error: null
|
|
29739
30507
|
};
|
|
29740
|
-
await
|
|
30508
|
+
await mkdir13(options.paths.runDir, { recursive: true, mode: 448 });
|
|
29741
30509
|
await writer.write(`
|
|
29742
30510
|
=== hermes update started ${startedAt} ===
|
|
29743
30511
|
`);
|
|
@@ -29911,20 +30679,20 @@ function normalizeServerReleaseSnapshot(payload) {
|
|
|
29911
30679
|
const remote = toNullableRecord(snapshot.remote);
|
|
29912
30680
|
return {
|
|
29913
30681
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
29914
|
-
cacheState:
|
|
29915
|
-
issue:
|
|
30682
|
+
cacheState: readString21(snapshot, "cache_state") ?? readString21(snapshot, "cacheState"),
|
|
30683
|
+
issue: readString21(snapshot, "issue")
|
|
29916
30684
|
};
|
|
29917
30685
|
}
|
|
29918
30686
|
function normalizeServerRelease(payload) {
|
|
29919
|
-
const tag =
|
|
29920
|
-
const name =
|
|
30687
|
+
const tag = readString21(payload, "tag");
|
|
30688
|
+
const name = readString21(payload, "name");
|
|
29921
30689
|
return {
|
|
29922
|
-
version:
|
|
30690
|
+
version: readString21(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
29923
30691
|
tag,
|
|
29924
30692
|
name,
|
|
29925
|
-
releaseUrl:
|
|
29926
|
-
publishedAt:
|
|
29927
|
-
fetchedAt:
|
|
30693
|
+
releaseUrl: readString21(payload, "releaseUrl") ?? readString21(payload, "release_url"),
|
|
30694
|
+
publishedAt: readString21(payload, "publishedAt") ?? readString21(payload, "published_at"),
|
|
30695
|
+
fetchedAt: readString21(payload, "fetchedAt") ?? readString21(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
29928
30696
|
};
|
|
29929
30697
|
}
|
|
29930
30698
|
async function readReleaseCache(paths) {
|
|
@@ -29952,21 +30720,21 @@ async function readUpdateLogLines(paths) {
|
|
|
29952
30720
|
);
|
|
29953
30721
|
}
|
|
29954
30722
|
function releaseCachePath(paths) {
|
|
29955
|
-
return
|
|
30723
|
+
return path28.join(paths.indexesDir, "hermes-release-check.json");
|
|
29956
30724
|
}
|
|
29957
30725
|
function updateStatePath(paths) {
|
|
29958
|
-
return
|
|
30726
|
+
return path28.join(paths.runDir, "hermes-update-state.json");
|
|
29959
30727
|
}
|
|
29960
30728
|
function updateLogPath(paths) {
|
|
29961
|
-
return
|
|
30729
|
+
return path28.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
29962
30730
|
}
|
|
29963
30731
|
async function clearUpdateLogFiles(paths) {
|
|
29964
30732
|
const primary = updateLogPath(paths);
|
|
29965
30733
|
await Promise.all([
|
|
29966
|
-
|
|
30734
|
+
rm8(primary, { force: true }).catch(() => void 0),
|
|
29967
30735
|
...Array.from(
|
|
29968
30736
|
{ length: UPDATE_LOG_MAX_FILES },
|
|
29969
|
-
(_, index) =>
|
|
30737
|
+
(_, index) => rm8(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
29970
30738
|
)
|
|
29971
30739
|
]);
|
|
29972
30740
|
}
|
|
@@ -30050,7 +30818,7 @@ function isRecentRunningState2(state) {
|
|
|
30050
30818
|
const startedAt = Date.parse(state.started_at);
|
|
30051
30819
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
30052
30820
|
}
|
|
30053
|
-
function
|
|
30821
|
+
function readString21(payload, key) {
|
|
30054
30822
|
const value = payload[key];
|
|
30055
30823
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
30056
30824
|
}
|
|
@@ -30058,17 +30826,17 @@ function readString20(payload, key) {
|
|
|
30058
30826
|
// src/link/updates.ts
|
|
30059
30827
|
import { spawn as spawn5 } from "child_process";
|
|
30060
30828
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
30061
|
-
import { mkdir as
|
|
30062
|
-
import
|
|
30829
|
+
import { mkdir as mkdir16, readFile as readFile22, rm as rm11 } from "fs/promises";
|
|
30830
|
+
import path30 from "path";
|
|
30063
30831
|
|
|
30064
30832
|
// src/daemon/process.ts
|
|
30065
30833
|
import { spawn as spawn4 } from "child_process";
|
|
30066
|
-
import { mkdir as
|
|
30067
|
-
import
|
|
30834
|
+
import { mkdir as mkdir15, readFile as readFile21, rm as rm10, writeFile as writeFile4 } from "fs/promises";
|
|
30835
|
+
import path29 from "path";
|
|
30068
30836
|
|
|
30069
30837
|
// src/daemon/service.ts
|
|
30070
30838
|
import { createServer } from "http";
|
|
30071
|
-
import { mkdir as
|
|
30839
|
+
import { mkdir as mkdir14, rm as rm9, writeFile as writeFile3 } from "fs/promises";
|
|
30072
30840
|
|
|
30073
30841
|
// src/relay/control-client.ts
|
|
30074
30842
|
import WebSocket from "ws";
|
|
@@ -31113,11 +31881,11 @@ async function mergeLastReportedPublicRoutes(paths, snapshotInput) {
|
|
|
31113
31881
|
const state = await readNetworkReportState(paths);
|
|
31114
31882
|
return {
|
|
31115
31883
|
...snapshotInput,
|
|
31116
|
-
publicIpv4s:
|
|
31884
|
+
publicIpv4s: uniqueStrings2([
|
|
31117
31885
|
...snapshotInput.publicIpv4s,
|
|
31118
31886
|
...state.lastReportedPublicIpv4s
|
|
31119
31887
|
]).slice(0, 2),
|
|
31120
|
-
publicIpv6s:
|
|
31888
|
+
publicIpv6s: uniqueStrings2([
|
|
31121
31889
|
...snapshotInput.publicIpv6s,
|
|
31122
31890
|
...state.lastReportedPublicIpv6s
|
|
31123
31891
|
]).slice(0, 2)
|
|
@@ -31201,15 +31969,15 @@ function normalizeLanIps(value) {
|
|
|
31201
31969
|
];
|
|
31202
31970
|
}
|
|
31203
31971
|
function sameNetworkSnapshot(left, right) {
|
|
31204
|
-
return
|
|
31972
|
+
return sameStringList2(left.lanIps, right.lanIps) && sameStringList2(left.publicIpv4s, right.publicIpv4s) && sameStringList2(left.publicIpv6s, right.publicIpv6s);
|
|
31205
31973
|
}
|
|
31206
|
-
function
|
|
31974
|
+
function sameStringList2(left, right) {
|
|
31207
31975
|
if (left.length !== right.length) {
|
|
31208
31976
|
return false;
|
|
31209
31977
|
}
|
|
31210
31978
|
return left.every((value, index) => value === right[index]);
|
|
31211
31979
|
}
|
|
31212
|
-
function
|
|
31980
|
+
function uniqueStrings2(values) {
|
|
31213
31981
|
return [...new Set(values)];
|
|
31214
31982
|
}
|
|
31215
31983
|
function formatUtcDay(date) {
|
|
@@ -31844,7 +32612,7 @@ async function startLinkService(options = {}) {
|
|
|
31844
32612
|
await logger.info("service_stopped");
|
|
31845
32613
|
await logger.flush();
|
|
31846
32614
|
if (options.writePidFile) {
|
|
31847
|
-
await
|
|
32615
|
+
await rm9(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
31848
32616
|
}
|
|
31849
32617
|
}
|
|
31850
32618
|
};
|
|
@@ -31894,7 +32662,7 @@ function pidFilePath(paths = resolveRuntimePaths()) {
|
|
|
31894
32662
|
return `${paths.runDir}/hermeslink.pid`;
|
|
31895
32663
|
}
|
|
31896
32664
|
async function writePidFile(paths) {
|
|
31897
|
-
await
|
|
32665
|
+
await mkdir14(paths.runDir, { recursive: true, mode: 448 });
|
|
31898
32666
|
await writeFile3(pidFilePath(paths), `${process.pid}
|
|
31899
32667
|
`, { mode: 384 });
|
|
31900
32668
|
}
|
|
@@ -31989,8 +32757,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
31989
32757
|
return status;
|
|
31990
32758
|
}
|
|
31991
32759
|
}
|
|
31992
|
-
await
|
|
31993
|
-
await
|
|
32760
|
+
await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
|
|
32761
|
+
await mkdir15(paths.runDir, { recursive: true, mode: 448 });
|
|
31994
32762
|
const scriptPath = currentCliScriptPath();
|
|
31995
32763
|
const child = spawn4(process.execPath, [scriptPath, "daemon-supervisor"], {
|
|
31996
32764
|
detached: true,
|
|
@@ -32008,10 +32776,10 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32008
32776
|
return await getDaemonStatus(paths);
|
|
32009
32777
|
}
|
|
32010
32778
|
async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
32011
|
-
await
|
|
32779
|
+
await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
|
|
32012
32780
|
const log = createRotatingTextLogWriter({
|
|
32013
32781
|
paths,
|
|
32014
|
-
fileName:
|
|
32782
|
+
fileName: path29.basename(daemonLogFile(paths))
|
|
32015
32783
|
});
|
|
32016
32784
|
const scriptPath = currentCliScriptPath();
|
|
32017
32785
|
const write = (chunk) => {
|
|
@@ -32138,7 +32906,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32138
32906
|
try {
|
|
32139
32907
|
process.kill(status.pid, "SIGTERM");
|
|
32140
32908
|
} catch {
|
|
32141
|
-
await
|
|
32909
|
+
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
32142
32910
|
return await getDaemonStatus(paths);
|
|
32143
32911
|
}
|
|
32144
32912
|
for (let index = 0; index < 20; index += 1) {
|
|
@@ -32160,7 +32928,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
32160
32928
|
}
|
|
32161
32929
|
}
|
|
32162
32930
|
if (!isProcessAlive3(status.pid) || !await pidBackedServiceIsReachable(paths)) {
|
|
32163
|
-
await
|
|
32931
|
+
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
32164
32932
|
}
|
|
32165
32933
|
return await getDaemonStatus(paths);
|
|
32166
32934
|
}
|
|
@@ -32168,7 +32936,7 @@ async function getDaemonStatus(paths = resolveRuntimePaths()) {
|
|
|
32168
32936
|
const pidFile = pidFilePath(paths);
|
|
32169
32937
|
const pid = await readPid(pidFile);
|
|
32170
32938
|
if (pid && !isProcessAlive3(pid)) {
|
|
32171
|
-
await
|
|
32939
|
+
await rm10(pidFile, { force: true }).catch(() => void 0);
|
|
32172
32940
|
return {
|
|
32173
32941
|
running: false,
|
|
32174
32942
|
pid: null,
|
|
@@ -32309,10 +33077,10 @@ function terminateChild(child, previousForceKillTimer) {
|
|
|
32309
33077
|
}
|
|
32310
33078
|
}
|
|
32311
33079
|
function supervisorStopIntentPath(paths) {
|
|
32312
|
-
return
|
|
33080
|
+
return path29.join(paths.runDir, "supervisor-stop-intent.json");
|
|
32313
33081
|
}
|
|
32314
33082
|
async function writeSupervisorStopIntent(paths, pid) {
|
|
32315
|
-
await
|
|
33083
|
+
await mkdir15(paths.runDir, { recursive: true, mode: 448 });
|
|
32316
33084
|
await writeFile4(
|
|
32317
33085
|
supervisorStopIntentPath(paths),
|
|
32318
33086
|
`${JSON.stringify({ pid, created_at: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
@@ -32328,14 +33096,14 @@ async function consumeSupervisorStopIntent(paths, pid) {
|
|
|
32328
33096
|
}
|
|
32329
33097
|
const payload = parseSupervisorStopIntent(raw);
|
|
32330
33098
|
if (!isValidSupervisorStopIntent(payload)) {
|
|
32331
|
-
await
|
|
33099
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32332
33100
|
return false;
|
|
32333
33101
|
}
|
|
32334
33102
|
if (payload.pid !== pid) {
|
|
32335
|
-
await
|
|
33103
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32336
33104
|
return false;
|
|
32337
33105
|
}
|
|
32338
|
-
await
|
|
33106
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32339
33107
|
return true;
|
|
32340
33108
|
}
|
|
32341
33109
|
async function clearExpiredSupervisorStopIntent(paths) {
|
|
@@ -32346,7 +33114,7 @@ async function clearExpiredSupervisorStopIntent(paths) {
|
|
|
32346
33114
|
}
|
|
32347
33115
|
const payload = parseSupervisorStopIntent(raw);
|
|
32348
33116
|
if (!isValidSupervisorStopIntent(payload)) {
|
|
32349
|
-
await
|
|
33117
|
+
await rm10(filePath, { force: true }).catch(() => void 0);
|
|
32350
33118
|
}
|
|
32351
33119
|
}
|
|
32352
33120
|
function parseSupervisorStopIntent(raw) {
|
|
@@ -32481,7 +33249,7 @@ async function startLinkUpdate(options) {
|
|
|
32481
33249
|
error: null,
|
|
32482
33250
|
manual_command: manualCommand
|
|
32483
33251
|
};
|
|
32484
|
-
await
|
|
33252
|
+
await mkdir16(options.paths.runDir, { recursive: true, mode: 448 });
|
|
32485
33253
|
await writer.write(
|
|
32486
33254
|
`
|
|
32487
33255
|
=== link update started ${startedAt} target=${targetVersion} ===
|
|
@@ -32789,16 +33557,16 @@ function normalizeServerSnapshot(payload) {
|
|
|
32789
33557
|
if (!policy) {
|
|
32790
33558
|
return {
|
|
32791
33559
|
remote: null,
|
|
32792
|
-
issue:
|
|
33560
|
+
issue: readString22(snapshot, "issue")
|
|
32793
33561
|
};
|
|
32794
33562
|
}
|
|
32795
33563
|
const release = toNullableRecord2(snapshot.release);
|
|
32796
|
-
const currentVersion =
|
|
32797
|
-
const minSafeVersion =
|
|
33564
|
+
const currentVersion = readString22(policy, "current_version") ?? readString22(policy, "currentVersion");
|
|
33565
|
+
const minSafeVersion = readString22(policy, "min_safe_version") ?? readString22(policy, "minSafeVersion");
|
|
32798
33566
|
if (!currentVersion) {
|
|
32799
33567
|
return {
|
|
32800
33568
|
remote: null,
|
|
32801
|
-
issue:
|
|
33569
|
+
issue: readString22(snapshot, "issue")
|
|
32802
33570
|
};
|
|
32803
33571
|
}
|
|
32804
33572
|
return {
|
|
@@ -32806,10 +33574,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
32806
33574
|
current_version: currentVersion,
|
|
32807
33575
|
min_safe_version: minSafeVersion,
|
|
32808
33576
|
target_version: currentVersion,
|
|
32809
|
-
release_url: release ?
|
|
32810
|
-
published_at: release ?
|
|
33577
|
+
release_url: release ? readString22(release, "release_url") ?? readString22(release, "releaseUrl") : null,
|
|
33578
|
+
published_at: release ? readString22(release, "published_at") ?? readString22(release, "publishedAt") : null
|
|
32811
33579
|
},
|
|
32812
|
-
issue:
|
|
33580
|
+
issue: readString22(snapshot, "issue")
|
|
32813
33581
|
};
|
|
32814
33582
|
}
|
|
32815
33583
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher, channel) {
|
|
@@ -32872,7 +33640,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
|
|
|
32872
33640
|
};
|
|
32873
33641
|
}
|
|
32874
33642
|
function buildUnixInstallCommand(installerUrl) {
|
|
32875
|
-
const nodeBinDir =
|
|
33643
|
+
const nodeBinDir = path30.dirname(process.execPath);
|
|
32876
33644
|
const fetchScript = [
|
|
32877
33645
|
quoteShellToken(process.execPath),
|
|
32878
33646
|
"--input-type=module",
|
|
@@ -33142,18 +33910,18 @@ async function readUpdateLogLines2(paths) {
|
|
|
33142
33910
|
);
|
|
33143
33911
|
}
|
|
33144
33912
|
function updateStatePath2(paths) {
|
|
33145
|
-
return
|
|
33913
|
+
return path30.join(paths.runDir, "link-update-state.json");
|
|
33146
33914
|
}
|
|
33147
33915
|
function updateLogPath2(paths) {
|
|
33148
|
-
return
|
|
33916
|
+
return path30.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
33149
33917
|
}
|
|
33150
33918
|
async function clearUpdateLogFiles2(paths) {
|
|
33151
33919
|
const primary = updateLogPath2(paths);
|
|
33152
33920
|
await Promise.all([
|
|
33153
|
-
|
|
33921
|
+
rm11(primary, { force: true }).catch(() => void 0),
|
|
33154
33922
|
...Array.from(
|
|
33155
33923
|
{ length: UPDATE_LOG_MAX_FILES2 },
|
|
33156
|
-
(_, index) =>
|
|
33924
|
+
(_, index) => rm11(`${primary}.${index + 1}`, { force: true }).catch(() => void 0)
|
|
33157
33925
|
)
|
|
33158
33926
|
]);
|
|
33159
33927
|
}
|
|
@@ -33223,14 +33991,14 @@ function toRecord22(value) {
|
|
|
33223
33991
|
function toNullableRecord2(value) {
|
|
33224
33992
|
return typeof value === "object" && value !== null ? value : null;
|
|
33225
33993
|
}
|
|
33226
|
-
function
|
|
33994
|
+
function readString22(payload, key) {
|
|
33227
33995
|
const value = payload[key];
|
|
33228
33996
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
33229
33997
|
}
|
|
33230
33998
|
|
|
33231
33999
|
// src/pairing/pairing.ts
|
|
33232
|
-
import
|
|
33233
|
-
import { rm as
|
|
34000
|
+
import path31 from "path";
|
|
34001
|
+
import { rm as rm12 } from "fs/promises";
|
|
33234
34002
|
|
|
33235
34003
|
// src/relay/bootstrap.ts
|
|
33236
34004
|
var RelayNetworkError = class extends Error {
|
|
@@ -33492,7 +34260,7 @@ async function readPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
|
33492
34260
|
};
|
|
33493
34261
|
}
|
|
33494
34262
|
async function clearPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
33495
|
-
await
|
|
34263
|
+
await rm12(pairingClaimPath(sessionId, paths), { force: true }).catch(() => void 0);
|
|
33496
34264
|
}
|
|
33497
34265
|
async function claimPairing(input) {
|
|
33498
34266
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
@@ -33569,10 +34337,10 @@ async function loadRequiredIdentity2(paths) {
|
|
|
33569
34337
|
}
|
|
33570
34338
|
return identity;
|
|
33571
34339
|
}
|
|
33572
|
-
async function postServerJson(serverBaseUrl,
|
|
34340
|
+
async function postServerJson(serverBaseUrl, path32, body, options) {
|
|
33573
34341
|
let response;
|
|
33574
34342
|
try {
|
|
33575
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
34343
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path32}`, {
|
|
33576
34344
|
method: "POST",
|
|
33577
34345
|
headers: {
|
|
33578
34346
|
accept: "application/json",
|
|
@@ -33620,10 +34388,10 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
33620
34388
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
33621
34389
|
};
|
|
33622
34390
|
}
|
|
33623
|
-
async function patchServerJson(serverBaseUrl,
|
|
34391
|
+
async function patchServerJson(serverBaseUrl, path32, token, body, options) {
|
|
33624
34392
|
let response;
|
|
33625
34393
|
try {
|
|
33626
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
34394
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path32}`, {
|
|
33627
34395
|
method: "PATCH",
|
|
33628
34396
|
headers: {
|
|
33629
34397
|
accept: "application/json",
|
|
@@ -33671,10 +34439,10 @@ function createPairingNetworkError(input) {
|
|
|
33671
34439
|
);
|
|
33672
34440
|
}
|
|
33673
34441
|
function pairingClaimPath(sessionId, paths) {
|
|
33674
|
-
return
|
|
34442
|
+
return path31.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
33675
34443
|
}
|
|
33676
34444
|
function pairingSessionPath(sessionId, paths) {
|
|
33677
|
-
return
|
|
34445
|
+
return path31.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
33678
34446
|
}
|
|
33679
34447
|
function qrPreferredUrls(routes) {
|
|
33680
34448
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -33754,8 +34522,8 @@ function registerSystemRoutes(router, options) {
|
|
|
33754
34522
|
});
|
|
33755
34523
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
33756
34524
|
const body = await readJsonBody(ctx.req);
|
|
33757
|
-
const sessionId =
|
|
33758
|
-
const claimToken =
|
|
34525
|
+
const sessionId = readString18(body, "session_id") ?? readString18(body, "sessionId");
|
|
34526
|
+
const claimToken = readString18(body, "claim_token") ?? readString18(body, "claimToken");
|
|
33759
34527
|
if (!sessionId || !claimToken) {
|
|
33760
34528
|
throw new LinkHttpError(
|
|
33761
34529
|
400,
|
|
@@ -33766,10 +34534,10 @@ function registerSystemRoutes(router, options) {
|
|
|
33766
34534
|
const claimed = await claimPairing({
|
|
33767
34535
|
sessionId,
|
|
33768
34536
|
claimToken,
|
|
33769
|
-
deviceLabel:
|
|
33770
|
-
devicePlatform:
|
|
33771
|
-
deviceModel:
|
|
33772
|
-
appInstanceId:
|
|
34537
|
+
deviceLabel: readString18(body, "device_label") ?? readString18(body, "deviceLabel") ?? "HermesPilot App",
|
|
34538
|
+
devicePlatform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform") ?? "unknown",
|
|
34539
|
+
deviceModel: readString18(body, "device_model") ?? readString18(body, "deviceModel"),
|
|
34540
|
+
appInstanceId: readString18(body, "app_instance_id") ?? readString18(body, "appInstanceId"),
|
|
33773
34541
|
paths
|
|
33774
34542
|
});
|
|
33775
34543
|
ctx.body = claimed;
|
|
@@ -33849,9 +34617,9 @@ function registerSystemRoutes(router, options) {
|
|
|
33849
34617
|
const body = await readJsonBody(ctx.req);
|
|
33850
34618
|
const session = await createDeviceSession(
|
|
33851
34619
|
{
|
|
33852
|
-
label:
|
|
33853
|
-
platform:
|
|
33854
|
-
model:
|
|
34620
|
+
label: readString18(body, "device_label") ?? readString18(body, "deviceLabel") ?? "HermesPilot App",
|
|
34621
|
+
platform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform") ?? "unknown",
|
|
34622
|
+
model: readString18(body, "device_model") ?? readString18(body, "deviceModel"),
|
|
33855
34623
|
appInstanceId: auth.appInstanceId
|
|
33856
34624
|
},
|
|
33857
34625
|
paths
|
|
@@ -33880,7 +34648,7 @@ function registerSystemRoutes(router, options) {
|
|
|
33880
34648
|
});
|
|
33881
34649
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
33882
34650
|
const body = await readJsonBody(ctx.req);
|
|
33883
|
-
const refreshToken =
|
|
34651
|
+
const refreshToken = readString18(body, "refresh_token") ?? readString18(body, "refreshToken");
|
|
33884
34652
|
if (!refreshToken) {
|
|
33885
34653
|
throw new LinkHttpError(
|
|
33886
34654
|
400,
|
|
@@ -33891,10 +34659,10 @@ function registerSystemRoutes(router, options) {
|
|
|
33891
34659
|
const session = await refreshDeviceSession(
|
|
33892
34660
|
refreshToken,
|
|
33893
34661
|
{
|
|
33894
|
-
appInstanceId:
|
|
33895
|
-
label:
|
|
33896
|
-
platform:
|
|
33897
|
-
model:
|
|
34662
|
+
appInstanceId: readString18(body, "app_instance_id") ?? readString18(body, "appInstanceId"),
|
|
34663
|
+
label: readString18(body, "device_label") ?? readString18(body, "deviceLabel"),
|
|
34664
|
+
platform: readString18(body, "device_platform") ?? readString18(body, "devicePlatform"),
|
|
34665
|
+
model: readString18(body, "device_model") ?? readString18(body, "deviceModel")
|
|
33898
34666
|
},
|
|
33899
34667
|
paths
|
|
33900
34668
|
);
|
|
@@ -33913,7 +34681,7 @@ function registerSystemRoutes(router, options) {
|
|
|
33913
34681
|
});
|
|
33914
34682
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
33915
34683
|
const body = await readJsonBody(ctx.req);
|
|
33916
|
-
const refreshToken =
|
|
34684
|
+
const refreshToken = readString18(body, "refresh_token") ?? readString18(body, "refreshToken");
|
|
33917
34685
|
if (refreshToken) {
|
|
33918
34686
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
33919
34687
|
}
|
|
@@ -34139,7 +34907,7 @@ function registerSystemRoutes(router, options) {
|
|
|
34139
34907
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
34140
34908
|
const auth = await authenticateRequest(ctx, paths);
|
|
34141
34909
|
const body = await readJsonBody(ctx.req);
|
|
34142
|
-
const label =
|
|
34910
|
+
const label = readString18(body, "label") ?? readString18(body, "device_label");
|
|
34143
34911
|
if (!label) {
|
|
34144
34912
|
throw new LinkHttpError(
|
|
34145
34913
|
400,
|
|
@@ -34183,7 +34951,7 @@ function isActiveCronJob(job) {
|
|
|
34183
34951
|
if (!enabled) {
|
|
34184
34952
|
return false;
|
|
34185
34953
|
}
|
|
34186
|
-
const state =
|
|
34954
|
+
const state = readString18(job, "state")?.toLowerCase();
|
|
34187
34955
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
34188
34956
|
}
|
|
34189
34957
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -34277,8 +35045,8 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
34277
35045
|
ctx.body = await startLinkUpdate({
|
|
34278
35046
|
paths,
|
|
34279
35047
|
logger,
|
|
34280
|
-
channel:
|
|
34281
|
-
targetVersion:
|
|
35048
|
+
channel: readString18(body, "channel"),
|
|
35049
|
+
targetVersion: readString18(body, "target_version") ?? readString18(body, "targetVersion")
|
|
34282
35050
|
});
|
|
34283
35051
|
});
|
|
34284
35052
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -34308,7 +35076,7 @@ import QRCode from "qrcode";
|
|
|
34308
35076
|
function registerPairingRoutes(router, options) {
|
|
34309
35077
|
const { paths } = options;
|
|
34310
35078
|
router.get("/pair", async (ctx) => {
|
|
34311
|
-
const sessionId =
|
|
35079
|
+
const sessionId = readString18(ctx.query, "session_id");
|
|
34312
35080
|
if (!sessionId) {
|
|
34313
35081
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
34314
35082
|
}
|
|
@@ -34333,7 +35101,7 @@ function registerPairingRoutes(router, options) {
|
|
|
34333
35101
|
ctx.body = page;
|
|
34334
35102
|
});
|
|
34335
35103
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
34336
|
-
const sessionId =
|
|
35104
|
+
const sessionId = readString18(ctx.query, "session_id");
|
|
34337
35105
|
if (!sessionId) {
|
|
34338
35106
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
34339
35107
|
}
|
|
@@ -34784,7 +35552,7 @@ function registerInternalRoutes(router, options) {
|
|
|
34784
35552
|
router.post("/internal/deliver", async (ctx) => {
|
|
34785
35553
|
assertLoopbackRequest(ctx.req);
|
|
34786
35554
|
const body = await readJsonBody(ctx.req);
|
|
34787
|
-
const stagingDir =
|
|
35555
|
+
const stagingDir = readString18(body, "staging_dir") ?? readString18(body, "stagingDir");
|
|
34788
35556
|
if (!stagingDir) {
|
|
34789
35557
|
throw new LinkHttpError(
|
|
34790
35558
|
400,
|
|
@@ -34797,6 +35565,19 @@ function registerInternalRoutes(router, options) {
|
|
|
34797
35565
|
...await options.conversations.deliverStagedFiles(stagingDir)
|
|
34798
35566
|
};
|
|
34799
35567
|
});
|
|
35568
|
+
router.post("/internal/deliver-tool", async (ctx) => {
|
|
35569
|
+
assertLoopbackRequest(ctx.req);
|
|
35570
|
+
const body = await readJsonBody(ctx.req);
|
|
35571
|
+
ctx.body = {
|
|
35572
|
+
ok: true,
|
|
35573
|
+
...await options.conversations.deliverFilesFromTool({
|
|
35574
|
+
profile: readString18(body, "profile") ?? readString18(body, "profile_name") ?? readString18(body, "profileName") ?? void 0,
|
|
35575
|
+
taskId: readString18(body, "task_id") ?? readString18(body, "taskId") ?? void 0,
|
|
35576
|
+
sessionId: readString18(body, "session_id") ?? readString18(body, "sessionId") ?? void 0,
|
|
35577
|
+
files: body.files ?? body.file ?? body.path
|
|
35578
|
+
})
|
|
35579
|
+
};
|
|
35580
|
+
});
|
|
34800
35581
|
}
|
|
34801
35582
|
function assertLoopbackRequest(request) {
|
|
34802
35583
|
const address = request.socket.remoteAddress;
|