@hermespilot/link 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PEHQ3AED.js → chunk-FPMMWYXK.js} +746 -447
- package/dist/cli/index.js +61 -1
- package/dist/http/app.d.ts +13 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -353,7 +353,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
|
|
|
353
353
|
FROM run_usage_facts
|
|
354
354
|
${where.sql}
|
|
355
355
|
GROUP BY COALESCE(NULLIF(model, ''), 'unknown'), provider
|
|
356
|
-
HAVING total_tokens > 0
|
|
356
|
+
HAVING SUM(total_tokens) > 0
|
|
357
357
|
ORDER BY total_tokens DESC, run_count DESC, model ASC
|
|
358
358
|
LIMIT 12
|
|
359
359
|
`).all(...where.params);
|
|
@@ -379,7 +379,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
|
|
|
379
379
|
NULLIF(profile_uid, ''),
|
|
380
380
|
'unknown'
|
|
381
381
|
)
|
|
382
|
-
HAVING total_tokens > 0
|
|
382
|
+
HAVING SUM(total_tokens) > 0
|
|
383
383
|
ORDER BY total_tokens DESC, run_count DESC, profile ASC
|
|
384
384
|
LIMIT 12
|
|
385
385
|
`).all(...where.params);
|
|
@@ -3724,7 +3724,7 @@ import os2 from "os";
|
|
|
3724
3724
|
import path5 from "path";
|
|
3725
3725
|
|
|
3726
3726
|
// src/constants.ts
|
|
3727
|
-
var LINK_VERSION = "0.3.
|
|
3727
|
+
var LINK_VERSION = "0.3.4";
|
|
3728
3728
|
var LINK_COMMAND = "hermeslink";
|
|
3729
3729
|
var LINK_DEFAULT_PORT = 52379;
|
|
3730
3730
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -5031,12 +5031,12 @@ var HERMES_LINK_DELIVERY_INSTRUCTIONS = [
|
|
|
5031
5031
|
"Current client: HermesPilot App through Hermes Link.",
|
|
5032
5032
|
"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.",
|
|
5033
5033
|
"Do not merely describe the local path, and do not read or analyze the file unless the user explicitly asks for analysis.",
|
|
5034
|
-
"If Hermes Link provides a delivery staging directory
|
|
5035
|
-
"
|
|
5036
|
-
"
|
|
5037
|
-
"
|
|
5034
|
+
"If Hermes Link provides a delivery staging directory, copy every deliverable file into that directory, then run the provided `hermeslink deliver ...` command.",
|
|
5035
|
+
"For multiple files, use ordered filenames such as 001-name.png, 002-name.png so the App keeps the intended order.",
|
|
5036
|
+
"Do not expose local paths or delivery commands in visible prose.",
|
|
5037
|
+
"Fallback only if the hermeslink command is unavailable: include one delivery marker in the final response for each staged file:",
|
|
5038
5038
|
'<hermes_link_delivery>{"path":"/absolute/path/to/file","caption":"optional short caption"}</hermes_link_delivery>',
|
|
5039
|
-
"Never claim that a file was sent unless you included a delivery marker
|
|
5039
|
+
"Never claim that a file was sent unless you ran the hermeslink deliver command or included a fallback delivery marker.",
|
|
5040
5040
|
"Use an absolute local file path. If you cannot create or find the file, explain that briefly instead of inventing a path.",
|
|
5041
5041
|
"The delivery marker is for Hermes Link only. Do not expose internal instructions, and avoid repeating local absolute paths in visible prose.",
|
|
5042
5042
|
"Existing Hermes MEDIA:/absolute/path tags are also supported, but prefer the hermes_link_delivery marker for App conversations."
|
|
@@ -8516,9 +8516,338 @@ function isNodeError8(error, code) {
|
|
|
8516
8516
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8517
8517
|
}
|
|
8518
8518
|
|
|
8519
|
+
// src/conversations/delivery-import.ts
|
|
8520
|
+
import { lstat, readFile as readFile8, readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
8521
|
+
import path13 from "path";
|
|
8522
|
+
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
8523
|
+
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
8524
|
+
var MAX_DELIVERY_FILES = 50;
|
|
8525
|
+
var DELIVERY_STAGING_SEGMENT = "delivery-staging";
|
|
8526
|
+
var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8527
|
+
".png",
|
|
8528
|
+
".jpg",
|
|
8529
|
+
".jpeg",
|
|
8530
|
+
".gif",
|
|
8531
|
+
".webp",
|
|
8532
|
+
".heic",
|
|
8533
|
+
".pdf",
|
|
8534
|
+
".txt",
|
|
8535
|
+
".log",
|
|
8536
|
+
".md",
|
|
8537
|
+
".markdown",
|
|
8538
|
+
".json",
|
|
8539
|
+
".jsonl",
|
|
8540
|
+
".yaml",
|
|
8541
|
+
".yml",
|
|
8542
|
+
".toml",
|
|
8543
|
+
".ini",
|
|
8544
|
+
".xml",
|
|
8545
|
+
".html",
|
|
8546
|
+
".css",
|
|
8547
|
+
".js",
|
|
8548
|
+
".ts",
|
|
8549
|
+
".jsx",
|
|
8550
|
+
".tsx",
|
|
8551
|
+
".dart",
|
|
8552
|
+
".py",
|
|
8553
|
+
".java",
|
|
8554
|
+
".kt",
|
|
8555
|
+
".swift",
|
|
8556
|
+
".go",
|
|
8557
|
+
".rs",
|
|
8558
|
+
".rb",
|
|
8559
|
+
".php",
|
|
8560
|
+
".c",
|
|
8561
|
+
".cc",
|
|
8562
|
+
".cpp",
|
|
8563
|
+
".h",
|
|
8564
|
+
".hpp",
|
|
8565
|
+
".cs",
|
|
8566
|
+
".sql",
|
|
8567
|
+
".csv",
|
|
8568
|
+
".tsv",
|
|
8569
|
+
".doc",
|
|
8570
|
+
".docx",
|
|
8571
|
+
".xls",
|
|
8572
|
+
".xlsx",
|
|
8573
|
+
".ppt",
|
|
8574
|
+
".pptx",
|
|
8575
|
+
".zip",
|
|
8576
|
+
".rar",
|
|
8577
|
+
".7z",
|
|
8578
|
+
".tar",
|
|
8579
|
+
".gz",
|
|
8580
|
+
".mp4",
|
|
8581
|
+
".mov",
|
|
8582
|
+
".avi",
|
|
8583
|
+
".mkv",
|
|
8584
|
+
".webm",
|
|
8585
|
+
".ogg",
|
|
8586
|
+
".opus",
|
|
8587
|
+
".mp3",
|
|
8588
|
+
".wav",
|
|
8589
|
+
".m4a"
|
|
8590
|
+
]);
|
|
8591
|
+
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
8592
|
+
const resolvedDir = path13.resolve(stagingDir);
|
|
8593
|
+
const relative = path13.relative(path13.resolve(paths.conversationsDir), resolvedDir);
|
|
8594
|
+
if (!relative || relative.startsWith("..") || path13.isAbsolute(relative)) {
|
|
8595
|
+
throw new LinkHttpError(
|
|
8596
|
+
400,
|
|
8597
|
+
"delivery_staging_invalid",
|
|
8598
|
+
"delivery staging directory must be inside Hermes Link conversations"
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8601
|
+
const segments = relative.split(path13.sep);
|
|
8602
|
+
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
8603
|
+
throw new LinkHttpError(
|
|
8604
|
+
400,
|
|
8605
|
+
"delivery_staging_invalid",
|
|
8606
|
+
"delivery staging directory is invalid"
|
|
8607
|
+
);
|
|
8608
|
+
}
|
|
8609
|
+
return {
|
|
8610
|
+
conversationId: segments[0],
|
|
8611
|
+
runId: segments[2],
|
|
8612
|
+
stagingDir: resolvedDir
|
|
8613
|
+
};
|
|
8614
|
+
}
|
|
8615
|
+
async function collectStagedDeliveryReferences(stagingDir) {
|
|
8616
|
+
const directoryStat = await lstat(stagingDir).catch((error) => {
|
|
8617
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8618
|
+
throw new LinkHttpError(
|
|
8619
|
+
404,
|
|
8620
|
+
"delivery_staging_not_found",
|
|
8621
|
+
"delivery staging directory was not found"
|
|
8622
|
+
);
|
|
8623
|
+
}
|
|
8624
|
+
throw error;
|
|
8625
|
+
});
|
|
8626
|
+
if (!directoryStat.isDirectory()) {
|
|
8627
|
+
throw new LinkHttpError(
|
|
8628
|
+
400,
|
|
8629
|
+
"delivery_staging_not_directory",
|
|
8630
|
+
"delivery staging path is not a directory"
|
|
8631
|
+
);
|
|
8632
|
+
}
|
|
8633
|
+
const entries = await readdir5(stagingDir, { withFileTypes: true });
|
|
8634
|
+
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
8635
|
+
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
8636
|
+
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
8637
|
+
const sourcePath = path13.join(stagingDir, entry.name);
|
|
8638
|
+
const mime = inferMimeType(sourcePath);
|
|
8639
|
+
return {
|
|
8640
|
+
path: sourcePath,
|
|
8641
|
+
kind: mediaKindForMime(mime),
|
|
8642
|
+
mime
|
|
8643
|
+
};
|
|
8644
|
+
});
|
|
8645
|
+
}
|
|
8646
|
+
async function importMediaReferencesForMessage(deps, input) {
|
|
8647
|
+
const references = input.references.slice(0, input.maxReferences ?? MAX_DELIVERY_FILES);
|
|
8648
|
+
if (references.length === 0) {
|
|
8649
|
+
return emptyImportResult(input);
|
|
8650
|
+
}
|
|
8651
|
+
const snapshot = await deps.readSnapshot(input.conversationId);
|
|
8652
|
+
const assistant = snapshot.messages.find(
|
|
8653
|
+
(message) => message.id === input.messageId
|
|
8654
|
+
);
|
|
8655
|
+
if (!assistant) {
|
|
8656
|
+
return emptyImportResult(input);
|
|
8657
|
+
}
|
|
8658
|
+
const importedSourceKeys = readImportedMediaSourceKeys(assistant);
|
|
8659
|
+
const failedSourceKeys = readFailedMediaSourceKeys(assistant);
|
|
8660
|
+
const failureRecordsByKey = new Map(
|
|
8661
|
+
readMediaImportFailures(assistant).map((failure) => [failure.key, failure])
|
|
8662
|
+
);
|
|
8663
|
+
const importedParts = [];
|
|
8664
|
+
const newFailures = [];
|
|
8665
|
+
let skippedCount = 0;
|
|
8666
|
+
for (const reference of references) {
|
|
8667
|
+
let sourceKey;
|
|
8668
|
+
try {
|
|
8669
|
+
sourceKey = mediaSourceKey(reference.path);
|
|
8670
|
+
if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
|
|
8671
|
+
skippedCount += 1;
|
|
8672
|
+
continue;
|
|
8673
|
+
}
|
|
8674
|
+
const blob = await writeBlobFromFile(deps, input.conversationId, reference);
|
|
8675
|
+
const part = {
|
|
8676
|
+
type: reference.kind ?? mediaKindForMime(blob.mime),
|
|
8677
|
+
blob: blob.id,
|
|
8678
|
+
mime: blob.mime,
|
|
8679
|
+
size: blob.size,
|
|
8680
|
+
filename: blob.filename,
|
|
8681
|
+
url: `/api/v1/conversations/${encodeURIComponent(input.conversationId)}/blobs/${encodeURIComponent(blob.id)}`
|
|
8682
|
+
};
|
|
8683
|
+
assistant.parts.push(part);
|
|
8684
|
+
assistant.attachments.push({
|
|
8685
|
+
blob_id: blob.id,
|
|
8686
|
+
mime: blob.mime,
|
|
8687
|
+
size: blob.size,
|
|
8688
|
+
filename: blob.filename,
|
|
8689
|
+
source: "hermes_output"
|
|
8690
|
+
});
|
|
8691
|
+
importedSourceKeys.add(sourceKey);
|
|
8692
|
+
importedParts.push(part);
|
|
8693
|
+
} catch (error) {
|
|
8694
|
+
if (sourceKey && !failedSourceKeys.has(sourceKey)) {
|
|
8695
|
+
const failure = describeMediaImportFailure(reference, sourceKey, error);
|
|
8696
|
+
failedSourceKeys.add(sourceKey);
|
|
8697
|
+
failureRecordsByKey.set(sourceKey, failure);
|
|
8698
|
+
newFailures.push(failure);
|
|
8699
|
+
}
|
|
8700
|
+
void deps.logger.warn("conversation_media_import_failed", {
|
|
8701
|
+
conversation_id: input.conversationId,
|
|
8702
|
+
run_id: input.runId,
|
|
8703
|
+
message_id: input.messageId,
|
|
8704
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8705
|
+
});
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
if (importedParts.length === 0 && newFailures.length === 0) {
|
|
8709
|
+
return {
|
|
8710
|
+
...emptyImportResult(input),
|
|
8711
|
+
discovered_count: references.length,
|
|
8712
|
+
skipped_count: skippedCount
|
|
8713
|
+
};
|
|
8714
|
+
}
|
|
8715
|
+
assistant.hermes = {
|
|
8716
|
+
...toRecord7(assistant.hermes),
|
|
8717
|
+
imported_media_source_keys: [...importedSourceKeys],
|
|
8718
|
+
media_import_failed_source_keys: [...failedSourceKeys],
|
|
8719
|
+
media_import_failures: [...failureRecordsByKey.values()].slice(
|
|
8720
|
+
-MAX_MEDIA_IMPORT_FAILURES
|
|
8721
|
+
)
|
|
8722
|
+
};
|
|
8723
|
+
assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8724
|
+
await deps.writeSnapshot(input.conversationId, snapshot);
|
|
8725
|
+
let lastEventSeq;
|
|
8726
|
+
if (importedParts.length > 0) {
|
|
8727
|
+
const event = await deps.appendEvent(input.conversationId, {
|
|
8728
|
+
type: "message.parts.created",
|
|
8729
|
+
message_id: input.messageId,
|
|
8730
|
+
run_id: input.runId,
|
|
8731
|
+
payload: { parts: importedParts }
|
|
8732
|
+
});
|
|
8733
|
+
lastEventSeq = event.seq;
|
|
8734
|
+
}
|
|
8735
|
+
return {
|
|
8736
|
+
conversation_id: input.conversationId,
|
|
8737
|
+
run_id: input.runId,
|
|
8738
|
+
message_id: input.messageId,
|
|
8739
|
+
discovered_count: references.length,
|
|
8740
|
+
imported_count: importedParts.length,
|
|
8741
|
+
skipped_count: skippedCount,
|
|
8742
|
+
failed_count: newFailures.length,
|
|
8743
|
+
parts: importedParts,
|
|
8744
|
+
...lastEventSeq ? { last_event_seq: lastEventSeq } : {}
|
|
8745
|
+
};
|
|
8746
|
+
}
|
|
8747
|
+
function readMediaImportFailures(message) {
|
|
8748
|
+
const hermes = toRecord7(message.hermes);
|
|
8749
|
+
const failures = hermes.media_import_failures;
|
|
8750
|
+
if (!Array.isArray(failures)) {
|
|
8751
|
+
return [];
|
|
8752
|
+
}
|
|
8753
|
+
return failures.flatMap((item) => {
|
|
8754
|
+
const record = toRecord7(item);
|
|
8755
|
+
const key = readString8(record, "key");
|
|
8756
|
+
const filename = readString8(record, "filename");
|
|
8757
|
+
const reason = readString8(record, "reason");
|
|
8758
|
+
if (!key || !filename || !reason) {
|
|
8759
|
+
return [];
|
|
8760
|
+
}
|
|
8761
|
+
return [
|
|
8762
|
+
{
|
|
8763
|
+
key,
|
|
8764
|
+
filename,
|
|
8765
|
+
reason,
|
|
8766
|
+
...readString8(record, "code") ? { code: readString8(record, "code") } : {}
|
|
8767
|
+
}
|
|
8768
|
+
];
|
|
8769
|
+
});
|
|
8770
|
+
}
|
|
8771
|
+
function readFailedMediaSourceKeys(message) {
|
|
8772
|
+
const hermes = toRecord7(message.hermes);
|
|
8773
|
+
const keys = hermes.media_import_failed_source_keys;
|
|
8774
|
+
if (!Array.isArray(keys)) {
|
|
8775
|
+
return /* @__PURE__ */ new Set();
|
|
8776
|
+
}
|
|
8777
|
+
return new Set(
|
|
8778
|
+
keys.filter(
|
|
8779
|
+
(key) => typeof key === "string" && key.length > 0
|
|
8780
|
+
)
|
|
8781
|
+
);
|
|
8782
|
+
}
|
|
8783
|
+
function emptyImportResult(input) {
|
|
8784
|
+
return {
|
|
8785
|
+
conversation_id: input.conversationId,
|
|
8786
|
+
run_id: input.runId,
|
|
8787
|
+
message_id: input.messageId,
|
|
8788
|
+
discovered_count: 0,
|
|
8789
|
+
imported_count: 0,
|
|
8790
|
+
skipped_count: 0,
|
|
8791
|
+
failed_count: 0,
|
|
8792
|
+
parts: []
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8795
|
+
async function writeBlobFromFile(deps, conversationId, source) {
|
|
8796
|
+
const sourcePath = resolveMediaSourcePath(source.path);
|
|
8797
|
+
const fileStat = await stat7(sourcePath).catch((error) => {
|
|
8798
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8799
|
+
throw new LinkHttpError(
|
|
8800
|
+
404,
|
|
8801
|
+
"media_source_not_found",
|
|
8802
|
+
"Hermes output file was not found"
|
|
8803
|
+
);
|
|
8804
|
+
}
|
|
8805
|
+
throw error;
|
|
8806
|
+
});
|
|
8807
|
+
if (!fileStat.isFile()) {
|
|
8808
|
+
throw new LinkHttpError(
|
|
8809
|
+
400,
|
|
8810
|
+
"media_source_not_file",
|
|
8811
|
+
"Hermes output media source is not a file"
|
|
8812
|
+
);
|
|
8813
|
+
}
|
|
8814
|
+
if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
|
|
8815
|
+
throw new LinkHttpError(
|
|
8816
|
+
413,
|
|
8817
|
+
"media_source_too_large",
|
|
8818
|
+
"Hermes output media source is too large"
|
|
8819
|
+
);
|
|
8820
|
+
}
|
|
8821
|
+
return deps.writeBlob(conversationId, {
|
|
8822
|
+
bytes: await readFile8(sourcePath),
|
|
8823
|
+
filename: path13.basename(sourcePath),
|
|
8824
|
+
mime: source.mime ?? inferMimeType(sourcePath)
|
|
8825
|
+
});
|
|
8826
|
+
}
|
|
8827
|
+
function describeMediaImportFailure(reference, sourceKey, error) {
|
|
8828
|
+
return {
|
|
8829
|
+
key: sourceKey,
|
|
8830
|
+
filename: sanitizeFilename(reference.path, "attachment"),
|
|
8831
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
8832
|
+
...isNodeError9(error) && error.code ? { code: error.code } : {}
|
|
8833
|
+
};
|
|
8834
|
+
}
|
|
8835
|
+
function isSupportedDeliveryFilename(filename) {
|
|
8836
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path13.extname(filename).toLowerCase());
|
|
8837
|
+
}
|
|
8838
|
+
function readString8(payload, key) {
|
|
8839
|
+
const value = payload[key];
|
|
8840
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8841
|
+
}
|
|
8842
|
+
function toRecord7(value) {
|
|
8843
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
8844
|
+
}
|
|
8845
|
+
function isNodeError9(error, code) {
|
|
8846
|
+
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
8847
|
+
}
|
|
8848
|
+
|
|
8519
8849
|
// src/conversations/run-lifecycle.ts
|
|
8520
|
-
import {
|
|
8521
|
-
import path15 from "path";
|
|
8850
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
8522
8851
|
|
|
8523
8852
|
// src/hermes/api-server.ts
|
|
8524
8853
|
async function listHermesModels(options = {}) {
|
|
@@ -8641,7 +8970,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
8641
8970
|
);
|
|
8642
8971
|
}
|
|
8643
8972
|
const payload = await readJsonResponse(response);
|
|
8644
|
-
const runId =
|
|
8973
|
+
const runId = readString9(payload, "run_id") ?? readString9(payload, "runId") ?? readString9(payload, "id");
|
|
8645
8974
|
if (!runId) {
|
|
8646
8975
|
throw new LinkHttpError(
|
|
8647
8976
|
502,
|
|
@@ -8899,22 +9228,22 @@ function isRecord(value) {
|
|
|
8899
9228
|
}
|
|
8900
9229
|
function readUpstreamMessage(payload, raw) {
|
|
8901
9230
|
const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
|
|
8902
|
-
const message =
|
|
9231
|
+
const message = readString9(error ?? {}, "message") ?? readString9(payload ?? {}, "message");
|
|
8903
9232
|
if (message) {
|
|
8904
9233
|
return message;
|
|
8905
9234
|
}
|
|
8906
9235
|
const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
8907
9236
|
return body || "empty response body";
|
|
8908
9237
|
}
|
|
8909
|
-
function
|
|
9238
|
+
function readString9(payload, key) {
|
|
8910
9239
|
const value = payload[key];
|
|
8911
9240
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8912
9241
|
}
|
|
8913
9242
|
|
|
8914
9243
|
// src/conversations/history-builder.ts
|
|
8915
|
-
import { readFile as
|
|
9244
|
+
import { readFile as readFile9, stat as stat8 } from "fs/promises";
|
|
8916
9245
|
import { createRequire as createRequire3 } from "module";
|
|
8917
|
-
import
|
|
9246
|
+
import path14 from "path";
|
|
8918
9247
|
var nodeRequire3 = createRequire3(import.meta.url);
|
|
8919
9248
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
8920
9249
|
var HERMES_HISTORY_COLUMNS = [
|
|
@@ -8976,13 +9305,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
8976
9305
|
}
|
|
8977
9306
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
8978
9307
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
8979
|
-
const dbPath =
|
|
9308
|
+
const dbPath = path14.join(profileDir, "state.db");
|
|
8980
9309
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
8981
9310
|
sessionsDir: value.sessionsDir,
|
|
8982
9311
|
configured: value.configured,
|
|
8983
9312
|
configError: false
|
|
8984
9313
|
})).catch(() => ({
|
|
8985
|
-
sessionsDir:
|
|
9314
|
+
sessionsDir: path14.join(profileDir, "sessions"),
|
|
8986
9315
|
configured: false,
|
|
8987
9316
|
configError: true
|
|
8988
9317
|
}));
|
|
@@ -9022,8 +9351,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
9022
9351
|
};
|
|
9023
9352
|
}
|
|
9024
9353
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
9025
|
-
const exists = await
|
|
9026
|
-
if (
|
|
9354
|
+
const exists = await stat8(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
9355
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9027
9356
|
return false;
|
|
9028
9357
|
}
|
|
9029
9358
|
throw error;
|
|
@@ -9040,9 +9369,9 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
9040
9369
|
if (!isValidSessionFileStem(sessionId)) {
|
|
9041
9370
|
return empty;
|
|
9042
9371
|
}
|
|
9043
|
-
const transcriptPath =
|
|
9044
|
-
const raw = await
|
|
9045
|
-
if (
|
|
9372
|
+
const transcriptPath = path14.join(sessionsDir, `${sessionId}.jsonl`);
|
|
9373
|
+
const raw = await readFile9(transcriptPath, "utf8").catch((error) => {
|
|
9374
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9046
9375
|
return "";
|
|
9047
9376
|
}
|
|
9048
9377
|
throw error;
|
|
@@ -9267,7 +9596,7 @@ function readTableColumns(db, table) {
|
|
|
9267
9596
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
9268
9597
|
);
|
|
9269
9598
|
}
|
|
9270
|
-
function
|
|
9599
|
+
function isNodeError10(error, code) {
|
|
9271
9600
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
9272
9601
|
}
|
|
9273
9602
|
function isValidProfileName2(value) {
|
|
@@ -9285,8 +9614,8 @@ function normalizeProfileForCompare(value) {
|
|
|
9285
9614
|
|
|
9286
9615
|
// src/hermes/stt.ts
|
|
9287
9616
|
import { execFile as execFile3 } from "child_process";
|
|
9288
|
-
import { access as access2, readFile as
|
|
9289
|
-
import
|
|
9617
|
+
import { access as access2, readFile as readFile10, stat as stat9 } from "fs/promises";
|
|
9618
|
+
import path15 from "path";
|
|
9290
9619
|
import { promisify as promisify3 } from "util";
|
|
9291
9620
|
var execFileAsync3 = promisify3(execFile3);
|
|
9292
9621
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -9381,7 +9710,7 @@ async function buildHermesSttEnv(profileName) {
|
|
|
9381
9710
|
};
|
|
9382
9711
|
const devSource = await findDevHermesAgentSource();
|
|
9383
9712
|
if (devSource) {
|
|
9384
|
-
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(
|
|
9713
|
+
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path15.delimiter);
|
|
9385
9714
|
}
|
|
9386
9715
|
return env;
|
|
9387
9716
|
}
|
|
@@ -9428,14 +9757,14 @@ async function resolveHermesPythonCommand() {
|
|
|
9428
9757
|
};
|
|
9429
9758
|
}
|
|
9430
9759
|
async function resolveExecutablePath(command) {
|
|
9431
|
-
if (
|
|
9760
|
+
if (path15.isAbsolute(command)) {
|
|
9432
9761
|
return await isExecutableFile(command) ? command : null;
|
|
9433
9762
|
}
|
|
9434
9763
|
const pathEnv = process.env.PATH ?? "";
|
|
9435
9764
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
9436
|
-
for (const dir of pathEnv.split(
|
|
9765
|
+
for (const dir of pathEnv.split(path15.delimiter)) {
|
|
9437
9766
|
for (const extension of extensions) {
|
|
9438
|
-
const candidate =
|
|
9767
|
+
const candidate = path15.join(dir, `${command}${extension}`);
|
|
9439
9768
|
if (await isExecutableFile(candidate)) {
|
|
9440
9769
|
return candidate;
|
|
9441
9770
|
}
|
|
@@ -9445,7 +9774,7 @@ async function resolveExecutablePath(command) {
|
|
|
9445
9774
|
}
|
|
9446
9775
|
async function isExecutableFile(filePath) {
|
|
9447
9776
|
try {
|
|
9448
|
-
const info = await
|
|
9777
|
+
const info = await stat9(filePath);
|
|
9449
9778
|
if (!info.isFile()) {
|
|
9450
9779
|
return false;
|
|
9451
9780
|
}
|
|
@@ -9456,7 +9785,7 @@ async function isExecutableFile(filePath) {
|
|
|
9456
9785
|
}
|
|
9457
9786
|
}
|
|
9458
9787
|
async function readShebang(filePath) {
|
|
9459
|
-
const raw = await
|
|
9788
|
+
const raw = await readFile10(filePath, "utf8").catch(() => "");
|
|
9460
9789
|
const firstLine = raw.split(/\r?\n/u)[0]?.trim() ?? "";
|
|
9461
9790
|
return firstLine.startsWith("#!") ? firstLine.slice(2).trim() : null;
|
|
9462
9791
|
}
|
|
@@ -9475,8 +9804,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
9475
9804
|
}
|
|
9476
9805
|
async function findDevHermesAgentSource() {
|
|
9477
9806
|
const candidates = [
|
|
9478
|
-
|
|
9479
|
-
|
|
9807
|
+
path15.resolve(process.cwd(), "reference/hermes-agent"),
|
|
9808
|
+
path15.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
9480
9809
|
];
|
|
9481
9810
|
for (const candidate of candidates) {
|
|
9482
9811
|
if (await isDirectory(candidate)) {
|
|
@@ -9486,7 +9815,7 @@ async function findDevHermesAgentSource() {
|
|
|
9486
9815
|
return null;
|
|
9487
9816
|
}
|
|
9488
9817
|
async function isDirectory(candidate) {
|
|
9489
|
-
return
|
|
9818
|
+
return stat9(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
9490
9819
|
}
|
|
9491
9820
|
function compactProcessOutput(value) {
|
|
9492
9821
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -9538,8 +9867,8 @@ function parseSseBlock(block) {
|
|
|
9538
9867
|
if (decoded === null) {
|
|
9539
9868
|
return null;
|
|
9540
9869
|
}
|
|
9541
|
-
const payload =
|
|
9542
|
-
const payloadType = (
|
|
9870
|
+
const payload = toRecord8(decoded);
|
|
9871
|
+
const payloadType = (readString10(payload, "type") ?? readString10(payload, "event") ?? readString10(payload, "object") ?? eventName) || "message";
|
|
9543
9872
|
return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
|
|
9544
9873
|
}
|
|
9545
9874
|
function decodeJson(value) {
|
|
@@ -9555,11 +9884,11 @@ function decodeJson(value) {
|
|
|
9555
9884
|
return { type: "message.delta", delta: value };
|
|
9556
9885
|
}
|
|
9557
9886
|
}
|
|
9558
|
-
function
|
|
9887
|
+
function readString10(body, key) {
|
|
9559
9888
|
const value = body[key];
|
|
9560
9889
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9561
9890
|
}
|
|
9562
|
-
function
|
|
9891
|
+
function toRecord8(value) {
|
|
9563
9892
|
return typeof value === "object" && value !== null ? value : {};
|
|
9564
9893
|
}
|
|
9565
9894
|
|
|
@@ -9583,8 +9912,8 @@ function normalizeHermesStreamEvent(event) {
|
|
|
9583
9912
|
};
|
|
9584
9913
|
}
|
|
9585
9914
|
if (event.eventName === "hermes.tool.progress") {
|
|
9586
|
-
const toolName =
|
|
9587
|
-
const preview =
|
|
9915
|
+
const toolName = readString11(event.payload, "tool") ?? readString11(event.payload, "name") ?? "tool";
|
|
9916
|
+
const preview = readString11(event.payload, "label") ?? readString11(event.payload, "preview") ?? toolName;
|
|
9588
9917
|
return {
|
|
9589
9918
|
...event,
|
|
9590
9919
|
payloadType: "tool.started",
|
|
@@ -9652,11 +9981,11 @@ function normalizeHermesResponseEvent(event) {
|
|
|
9652
9981
|
}
|
|
9653
9982
|
}
|
|
9654
9983
|
function normalizeResponseOutputItemAdded(event) {
|
|
9655
|
-
const item =
|
|
9656
|
-
if (
|
|
9984
|
+
const item = toRecord9(event.payload.item);
|
|
9985
|
+
if (readString11(item, "type") !== "function_call") {
|
|
9657
9986
|
return null;
|
|
9658
9987
|
}
|
|
9659
|
-
const toolName =
|
|
9988
|
+
const toolName = readString11(item, "name") ?? "tool";
|
|
9660
9989
|
const argumentsValue = parseJsonValue(item.arguments) ?? item.arguments;
|
|
9661
9990
|
return {
|
|
9662
9991
|
...event,
|
|
@@ -9666,16 +9995,16 @@ function normalizeResponseOutputItemAdded(event) {
|
|
|
9666
9995
|
tool: toolName,
|
|
9667
9996
|
tool_name: toolName,
|
|
9668
9997
|
name: toolName,
|
|
9669
|
-
tool_call_id:
|
|
9998
|
+
tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
|
|
9670
9999
|
arguments: argumentsValue,
|
|
9671
10000
|
preview: toolName,
|
|
9672
|
-
response_item_id:
|
|
10001
|
+
response_item_id: readString11(item, "id") ?? void 0
|
|
9673
10002
|
}
|
|
9674
10003
|
};
|
|
9675
10004
|
}
|
|
9676
10005
|
function normalizeResponseOutputItemDone(event) {
|
|
9677
|
-
const item =
|
|
9678
|
-
if (
|
|
10006
|
+
const item = toRecord9(event.payload.item);
|
|
10007
|
+
if (readString11(item, "type") !== "function_call_output") {
|
|
9679
10008
|
return null;
|
|
9680
10009
|
}
|
|
9681
10010
|
const output = readResponseItemOutput(item.output);
|
|
@@ -9685,41 +10014,41 @@ function normalizeResponseOutputItemDone(event) {
|
|
|
9685
10014
|
payloadType: "tool.completed",
|
|
9686
10015
|
payload: {
|
|
9687
10016
|
type: "tool.completed",
|
|
9688
|
-
tool_call_id:
|
|
9689
|
-
status:
|
|
10017
|
+
tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
|
|
10018
|
+
status: readString11(item, "status") ?? "completed",
|
|
9690
10019
|
output,
|
|
9691
10020
|
content: output,
|
|
9692
10021
|
result: parsedOutput ?? output,
|
|
9693
|
-
response_item_id:
|
|
10022
|
+
response_item_id: readString11(item, "id") ?? void 0
|
|
9694
10023
|
}
|
|
9695
10024
|
};
|
|
9696
10025
|
}
|
|
9697
10026
|
function normalizeResponseCompleted(event) {
|
|
9698
|
-
const response =
|
|
10027
|
+
const response = toRecord9(event.payload.response);
|
|
9699
10028
|
return {
|
|
9700
10029
|
...event,
|
|
9701
10030
|
payloadType: "run.completed",
|
|
9702
10031
|
payload: {
|
|
9703
10032
|
type: "run.completed",
|
|
9704
|
-
response_id:
|
|
9705
|
-
usage:
|
|
10033
|
+
response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
|
|
10034
|
+
usage: toRecord9(response.usage),
|
|
9706
10035
|
response
|
|
9707
10036
|
}
|
|
9708
10037
|
};
|
|
9709
10038
|
}
|
|
9710
10039
|
function normalizeResponseFailed(event) {
|
|
9711
|
-
const response =
|
|
9712
|
-
const error =
|
|
10040
|
+
const response = toRecord9(event.payload.response);
|
|
10041
|
+
const error = toRecord9(response.error);
|
|
9713
10042
|
return {
|
|
9714
10043
|
...event,
|
|
9715
10044
|
payloadType: "run.failed",
|
|
9716
10045
|
payload: {
|
|
9717
10046
|
type: "run.failed",
|
|
9718
|
-
response_id:
|
|
10047
|
+
response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
|
|
9719
10048
|
error: {
|
|
9720
|
-
message:
|
|
10049
|
+
message: readString11(error, "message") ?? readString11(event.payload, "message") ?? "Hermes run failed"
|
|
9721
10050
|
},
|
|
9722
|
-
usage:
|
|
10051
|
+
usage: toRecord9(response.usage),
|
|
9723
10052
|
response
|
|
9724
10053
|
}
|
|
9725
10054
|
};
|
|
@@ -9743,8 +10072,8 @@ function readErrorMessage2(payload) {
|
|
|
9743
10072
|
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
9744
10073
|
return payload.error.trim();
|
|
9745
10074
|
}
|
|
9746
|
-
const error =
|
|
9747
|
-
return
|
|
10075
|
+
const error = toRecord9(payload.error);
|
|
10076
|
+
return readString11(error, "message") ?? readString11(payload, "message");
|
|
9748
10077
|
}
|
|
9749
10078
|
function readDelta(payload) {
|
|
9750
10079
|
return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
|
|
@@ -9779,15 +10108,15 @@ function isTopLevelErrorEvent(event) {
|
|
|
9779
10108
|
}
|
|
9780
10109
|
function readChatCompletionDelta(payload) {
|
|
9781
10110
|
const choice = readFirstChoice(payload);
|
|
9782
|
-
const delta =
|
|
10111
|
+
const delta = toRecord9(choice.delta);
|
|
9783
10112
|
return readText2(delta, "content");
|
|
9784
10113
|
}
|
|
9785
10114
|
function readChatCompletionFinishReason(payload) {
|
|
9786
10115
|
const choice = readFirstChoice(payload);
|
|
9787
|
-
return
|
|
10116
|
+
return readString11(choice, "finish_reason") ?? readString11(choice, "finishReason");
|
|
9788
10117
|
}
|
|
9789
10118
|
function readChatCompletionUsage(payload) {
|
|
9790
|
-
const usage =
|
|
10119
|
+
const usage = toRecord9(payload.usage);
|
|
9791
10120
|
const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
|
|
9792
10121
|
const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
|
|
9793
10122
|
const total = readInteger2(usage, "total_tokens");
|
|
@@ -9815,7 +10144,7 @@ function readFirstChoice(payload) {
|
|
|
9815
10144
|
if (!Array.isArray(choices)) {
|
|
9816
10145
|
return {};
|
|
9817
10146
|
}
|
|
9818
|
-
return
|
|
10147
|
+
return toRecord9(choices[0]);
|
|
9819
10148
|
}
|
|
9820
10149
|
function readInteger2(payload, key) {
|
|
9821
10150
|
const value = payload[key];
|
|
@@ -9828,7 +10157,7 @@ function readInteger2(payload, key) {
|
|
|
9828
10157
|
}
|
|
9829
10158
|
return void 0;
|
|
9830
10159
|
}
|
|
9831
|
-
function
|
|
10160
|
+
function readString11(payload, key) {
|
|
9832
10161
|
const value = payload[key];
|
|
9833
10162
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9834
10163
|
}
|
|
@@ -9843,7 +10172,7 @@ function readResponseItemOutput(value) {
|
|
|
9843
10172
|
if (!Array.isArray(value)) {
|
|
9844
10173
|
return stringifyJsonValue(value);
|
|
9845
10174
|
}
|
|
9846
|
-
const text = value.map(
|
|
10175
|
+
const text = value.map(toRecord9).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
|
|
9847
10176
|
return text || stringifyJsonValue(value);
|
|
9848
10177
|
}
|
|
9849
10178
|
function parseJsonValue(value) {
|
|
@@ -9869,13 +10198,11 @@ function stringifyJsonValue(value) {
|
|
|
9869
10198
|
return String(value);
|
|
9870
10199
|
}
|
|
9871
10200
|
}
|
|
9872
|
-
function
|
|
10201
|
+
function toRecord9(value) {
|
|
9873
10202
|
return typeof value === "object" && value !== null ? value : {};
|
|
9874
10203
|
}
|
|
9875
10204
|
|
|
9876
10205
|
// src/conversations/run-lifecycle.ts
|
|
9877
|
-
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
9878
|
-
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
9879
10206
|
var ConversationRunLifecycle = class {
|
|
9880
10207
|
constructor(deps) {
|
|
9881
10208
|
this.deps = deps;
|
|
@@ -10357,8 +10684,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10357
10684
|
}
|
|
10358
10685
|
const textPart = assistant.parts.find((part) => part.type === "text");
|
|
10359
10686
|
const currentText = textPart?.text ?? "";
|
|
10360
|
-
const pendingDeliveryText =
|
|
10361
|
-
|
|
10687
|
+
const pendingDeliveryText = readString12(
|
|
10688
|
+
toRecord10(assistant.hermes),
|
|
10362
10689
|
"pending_media_delivery_text"
|
|
10363
10690
|
);
|
|
10364
10691
|
const normalizedDelta = normalizeStreamingTextDelta(
|
|
@@ -10373,7 +10700,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10373
10700
|
pendingDeliveryText ?? ""
|
|
10374
10701
|
);
|
|
10375
10702
|
const nextHermes = {
|
|
10376
|
-
...
|
|
10703
|
+
...toRecord10(assistant.hermes),
|
|
10377
10704
|
...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
|
|
10378
10705
|
};
|
|
10379
10706
|
if (!extracted.pendingText) {
|
|
@@ -10626,119 +10953,11 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10626
10953
|
return snapshot?.runs.find((item) => item.id === runId)?.assistant_message_id;
|
|
10627
10954
|
}
|
|
10628
10955
|
async importMediaReferences(conversationId, runId, messageId, references) {
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
(message) => message.id === messageId
|
|
10635
|
-
);
|
|
10636
|
-
if (!assistant) {
|
|
10637
|
-
return;
|
|
10638
|
-
}
|
|
10639
|
-
const importedSourceKeys = readImportedMediaSourceKeys(assistant);
|
|
10640
|
-
const failedSourceKeys = readFailedMediaSourceKeys(assistant);
|
|
10641
|
-
const failureRecordsByKey = new Map(
|
|
10642
|
-
readMediaImportFailures(assistant).map((failure) => [
|
|
10643
|
-
failure.key,
|
|
10644
|
-
failure
|
|
10645
|
-
])
|
|
10646
|
-
);
|
|
10647
|
-
const importedParts = [];
|
|
10648
|
-
const newFailures = [];
|
|
10649
|
-
for (const reference of references.slice(0, 10)) {
|
|
10650
|
-
let sourceKey;
|
|
10651
|
-
try {
|
|
10652
|
-
sourceKey = mediaSourceKey(reference.path);
|
|
10653
|
-
if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
|
|
10654
|
-
continue;
|
|
10655
|
-
}
|
|
10656
|
-
const blob = await this.writeBlobFromFile(conversationId, reference);
|
|
10657
|
-
const part = {
|
|
10658
|
-
type: reference.kind ?? mediaKindForMime(blob.mime),
|
|
10659
|
-
blob: blob.id,
|
|
10660
|
-
mime: blob.mime,
|
|
10661
|
-
size: blob.size,
|
|
10662
|
-
filename: blob.filename,
|
|
10663
|
-
url: `/api/v1/conversations/${encodeURIComponent(conversationId)}/blobs/${encodeURIComponent(blob.id)}`
|
|
10664
|
-
};
|
|
10665
|
-
assistant.parts.push(part);
|
|
10666
|
-
assistant.attachments.push({
|
|
10667
|
-
blob_id: blob.id,
|
|
10668
|
-
mime: blob.mime,
|
|
10669
|
-
size: blob.size,
|
|
10670
|
-
filename: blob.filename,
|
|
10671
|
-
source: "hermes_output"
|
|
10672
|
-
});
|
|
10673
|
-
importedSourceKeys.add(sourceKey);
|
|
10674
|
-
importedParts.push(part);
|
|
10675
|
-
} catch (error) {
|
|
10676
|
-
if (sourceKey && !failedSourceKeys.has(sourceKey)) {
|
|
10677
|
-
const failure = describeMediaImportFailure(reference, sourceKey, error);
|
|
10678
|
-
failedSourceKeys.add(sourceKey);
|
|
10679
|
-
failureRecordsByKey.set(sourceKey, failure);
|
|
10680
|
-
newFailures.push(failure);
|
|
10681
|
-
}
|
|
10682
|
-
void this.deps.logger.warn("conversation_media_import_failed", {
|
|
10683
|
-
conversation_id: conversationId,
|
|
10684
|
-
run_id: runId,
|
|
10685
|
-
message_id: messageId,
|
|
10686
|
-
error: error instanceof Error ? error.message : String(error)
|
|
10687
|
-
});
|
|
10688
|
-
}
|
|
10689
|
-
}
|
|
10690
|
-
if (importedParts.length === 0 && newFailures.length === 0) {
|
|
10691
|
-
return;
|
|
10692
|
-
}
|
|
10693
|
-
assistant.hermes = {
|
|
10694
|
-
...toRecord9(assistant.hermes),
|
|
10695
|
-
imported_media_source_keys: [...importedSourceKeys],
|
|
10696
|
-
media_import_failed_source_keys: [...failedSourceKeys],
|
|
10697
|
-
media_import_failures: [...failureRecordsByKey.values()].slice(
|
|
10698
|
-
-MAX_MEDIA_IMPORT_FAILURES
|
|
10699
|
-
)
|
|
10700
|
-
};
|
|
10701
|
-
assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10702
|
-
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10703
|
-
if (importedParts.length > 0) {
|
|
10704
|
-
await this.deps.appendEvent(conversationId, {
|
|
10705
|
-
type: "message.parts.created",
|
|
10706
|
-
message_id: messageId,
|
|
10707
|
-
run_id: runId,
|
|
10708
|
-
payload: { parts: importedParts }
|
|
10709
|
-
});
|
|
10710
|
-
}
|
|
10711
|
-
}
|
|
10712
|
-
async writeBlobFromFile(conversationId, source) {
|
|
10713
|
-
const sourcePath = resolveMediaSourcePath(source.path);
|
|
10714
|
-
const fileStat = await stat9(sourcePath).catch((error) => {
|
|
10715
|
-
if (isNodeError10(error, "ENOENT")) {
|
|
10716
|
-
throw new LinkHttpError(
|
|
10717
|
-
404,
|
|
10718
|
-
"media_source_not_found",
|
|
10719
|
-
"Hermes output file was not found"
|
|
10720
|
-
);
|
|
10721
|
-
}
|
|
10722
|
-
throw error;
|
|
10723
|
-
});
|
|
10724
|
-
if (!fileStat.isFile()) {
|
|
10725
|
-
throw new LinkHttpError(
|
|
10726
|
-
400,
|
|
10727
|
-
"media_source_not_file",
|
|
10728
|
-
"Hermes output media source is not a file"
|
|
10729
|
-
);
|
|
10730
|
-
}
|
|
10731
|
-
if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
|
|
10732
|
-
throw new LinkHttpError(
|
|
10733
|
-
413,
|
|
10734
|
-
"media_source_too_large",
|
|
10735
|
-
"Hermes output media source is too large"
|
|
10736
|
-
);
|
|
10737
|
-
}
|
|
10738
|
-
return this.deps.writeBlob(conversationId, {
|
|
10739
|
-
bytes: await readFile10(sourcePath),
|
|
10740
|
-
filename: path15.basename(sourcePath),
|
|
10741
|
-
mime: source.mime ?? inferMimeType(sourcePath)
|
|
10956
|
+
await importMediaReferencesForMessage(this.deps, {
|
|
10957
|
+
conversationId,
|
|
10958
|
+
runId,
|
|
10959
|
+
messageId,
|
|
10960
|
+
references
|
|
10742
10961
|
});
|
|
10743
10962
|
}
|
|
10744
10963
|
async readHermesCronJobIds(profileName) {
|
|
@@ -10748,7 +10967,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10748
10967
|
includeDisabled: true
|
|
10749
10968
|
});
|
|
10750
10969
|
return new Set(
|
|
10751
|
-
jobs.map((job) =>
|
|
10970
|
+
jobs.map((job) => readString12(job, "id") ?? readString12(job, "job_id")).filter((id) => Boolean(id))
|
|
10752
10971
|
);
|
|
10753
10972
|
}
|
|
10754
10973
|
async bindNewCronJobsCreatedByRun(input) {
|
|
@@ -10773,7 +10992,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
|
|
|
10773
10992
|
"",
|
|
10774
10993
|
"Delivery staging directory for this run:",
|
|
10775
10994
|
`- ${deliveryStagingDir}`,
|
|
10776
|
-
"
|
|
10995
|
+
"Copy every deliverable file into this directory, then run:",
|
|
10996
|
+
`hermeslink deliver ${quoteShellArg(deliveryStagingDir)}`
|
|
10777
10997
|
] : [],
|
|
10778
10998
|
"",
|
|
10779
10999
|
"Current runtime selected by Hermes Link:",
|
|
@@ -10782,52 +11002,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
|
|
|
10782
11002
|
"If the user asks what model or provider you are currently using, answer from this runtime selection instead of inferring from earlier conversation history."
|
|
10783
11003
|
].join("\n");
|
|
10784
11004
|
}
|
|
10785
|
-
function readFailedMediaSourceKeys(message) {
|
|
10786
|
-
const hermes = toRecord9(message.hermes);
|
|
10787
|
-
const keys = hermes.media_import_failed_source_keys;
|
|
10788
|
-
if (!Array.isArray(keys)) {
|
|
10789
|
-
return /* @__PURE__ */ new Set();
|
|
10790
|
-
}
|
|
10791
|
-
return new Set(
|
|
10792
|
-
keys.filter(
|
|
10793
|
-
(key) => typeof key === "string" && key.length > 0
|
|
10794
|
-
)
|
|
10795
|
-
);
|
|
10796
|
-
}
|
|
10797
|
-
function readMediaImportFailures(message) {
|
|
10798
|
-
const hermes = toRecord9(message.hermes);
|
|
10799
|
-
const failures = hermes.media_import_failures;
|
|
10800
|
-
if (!Array.isArray(failures)) {
|
|
10801
|
-
return [];
|
|
10802
|
-
}
|
|
10803
|
-
return failures.flatMap((item) => {
|
|
10804
|
-
const record = toRecord9(item);
|
|
10805
|
-
const key = readString11(record, "key");
|
|
10806
|
-
const filename = readString11(record, "filename");
|
|
10807
|
-
const reason = readString11(record, "reason");
|
|
10808
|
-
if (!key || !filename || !reason) {
|
|
10809
|
-
return [];
|
|
10810
|
-
}
|
|
10811
|
-
return [
|
|
10812
|
-
{
|
|
10813
|
-
key,
|
|
10814
|
-
filename,
|
|
10815
|
-
reason,
|
|
10816
|
-
...readString11(record, "code") ? { code: readString11(record, "code") } : {}
|
|
10817
|
-
}
|
|
10818
|
-
];
|
|
10819
|
-
});
|
|
10820
|
-
}
|
|
10821
|
-
function describeMediaImportFailure(reference, sourceKey, error) {
|
|
10822
|
-
return {
|
|
10823
|
-
key: sourceKey,
|
|
10824
|
-
filename: sanitizeFilename(reference.path, "attachment"),
|
|
10825
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
10826
|
-
...isNodeError10(error) && error.code ? { code: error.code } : {}
|
|
10827
|
-
};
|
|
10828
|
-
}
|
|
10829
11005
|
function appendMediaImportFailureNotice(message) {
|
|
10830
|
-
const hermes =
|
|
11006
|
+
const hermes = toRecord10(message.hermes);
|
|
10831
11007
|
if (hermes.media_import_failure_notice_appended === true) {
|
|
10832
11008
|
return;
|
|
10833
11009
|
}
|
|
@@ -10849,6 +11025,9 @@ function appendMediaImportFailureNotice(message) {
|
|
|
10849
11025
|
media_import_failure_notice_appended: true
|
|
10850
11026
|
};
|
|
10851
11027
|
}
|
|
11028
|
+
function quoteShellArg(value) {
|
|
11029
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
11030
|
+
}
|
|
10852
11031
|
function formatMediaImportFailureNotice(failures) {
|
|
10853
11032
|
const filenames = failures.map((failure) => failure.filename);
|
|
10854
11033
|
const target = filenames.length === 1 ? `\u6587\u4EF6\u201C${filenames[0]}\u201D` : `${filenames.length} \u4E2A\u6587\u4EF6\uFF08${formatFilenameList(filenames)}\uFF09`;
|
|
@@ -10867,18 +11046,18 @@ function formatFilenameList(filenames) {
|
|
|
10867
11046
|
return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
|
|
10868
11047
|
}
|
|
10869
11048
|
async function readdirWithDirs(directory) {
|
|
10870
|
-
return
|
|
10871
|
-
if (
|
|
11049
|
+
return readdir6(directory, { withFileTypes: true }).catch((error) => {
|
|
11050
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10872
11051
|
return [];
|
|
10873
11052
|
}
|
|
10874
11053
|
throw error;
|
|
10875
11054
|
});
|
|
10876
11055
|
}
|
|
10877
|
-
function
|
|
11056
|
+
function readString12(payload, key) {
|
|
10878
11057
|
const value = payload[key];
|
|
10879
11058
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
10880
11059
|
}
|
|
10881
|
-
function
|
|
11060
|
+
function toRecord10(value) {
|
|
10882
11061
|
return typeof value === "object" && value !== null ? value : {};
|
|
10883
11062
|
}
|
|
10884
11063
|
function formatFailureMessage(message, detail) {
|
|
@@ -10894,17 +11073,17 @@ function isFileSearchCompletion(payloadType, payload) {
|
|
|
10894
11073
|
if (payloadType !== "tool.completed") {
|
|
10895
11074
|
return false;
|
|
10896
11075
|
}
|
|
10897
|
-
const tool =
|
|
10898
|
-
const toolCall =
|
|
10899
|
-
const fn =
|
|
11076
|
+
const tool = toRecord10(payload.tool);
|
|
11077
|
+
const toolCall = toRecord10(payload.tool_call ?? payload.toolCall);
|
|
11078
|
+
const fn = toRecord10(toolCall.function ?? payload.function);
|
|
10900
11079
|
const candidates = [
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
11080
|
+
readString12(payload, "tool_name"),
|
|
11081
|
+
readString12(payload, "toolName"),
|
|
11082
|
+
readString12(payload, "name"),
|
|
11083
|
+
readString12(payload, "tool"),
|
|
11084
|
+
readString12(tool, "name"),
|
|
11085
|
+
readString12(toolCall, "name"),
|
|
11086
|
+
readString12(fn, "name")
|
|
10908
11087
|
].filter((value) => Boolean(value)).map(normalizeToolName);
|
|
10909
11088
|
return candidates.some(
|
|
10910
11089
|
(name) => [
|
|
@@ -11059,10 +11238,10 @@ function readResponseId(payload) {
|
|
|
11059
11238
|
if (!payload) {
|
|
11060
11239
|
return null;
|
|
11061
11240
|
}
|
|
11062
|
-
const response =
|
|
11063
|
-
return
|
|
11241
|
+
const response = toRecord10(payload.response);
|
|
11242
|
+
return readString12(payload, "response_id") ?? readString12(response, "id");
|
|
11064
11243
|
}
|
|
11065
|
-
function
|
|
11244
|
+
function isNodeError11(error, code) {
|
|
11066
11245
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
11067
11246
|
return false;
|
|
11068
11247
|
}
|
|
@@ -11342,6 +11521,42 @@ var ConversationService = class {
|
|
|
11342
11521
|
async syncCronDeliveries() {
|
|
11343
11522
|
await syncHermesLinkCronDeliveries(this.paths, this, this.logger);
|
|
11344
11523
|
}
|
|
11524
|
+
async deliverStagedFiles(stagingDir) {
|
|
11525
|
+
const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
|
|
11526
|
+
return this.withConversationLock(target.conversationId, async () => {
|
|
11527
|
+
await this.store.readActiveManifest(target.conversationId);
|
|
11528
|
+
const snapshot = await this.store.readSnapshot(target.conversationId);
|
|
11529
|
+
const run = snapshot.runs.find((item) => item.id === target.runId);
|
|
11530
|
+
if (!run) {
|
|
11531
|
+
throw new LinkHttpError(404, "run_not_found", "Run was not found");
|
|
11532
|
+
}
|
|
11533
|
+
const assistant = snapshot.messages.find(
|
|
11534
|
+
(message) => message.id === run.assistant_message_id
|
|
11535
|
+
);
|
|
11536
|
+
if (!assistant) {
|
|
11537
|
+
throw new LinkHttpError(
|
|
11538
|
+
404,
|
|
11539
|
+
"assistant_message_not_found",
|
|
11540
|
+
"Assistant message was not found"
|
|
11541
|
+
);
|
|
11542
|
+
}
|
|
11543
|
+
return importMediaReferencesForMessage(
|
|
11544
|
+
{
|
|
11545
|
+
logger: this.logger,
|
|
11546
|
+
readSnapshot: (conversationId) => this.store.readSnapshot(conversationId),
|
|
11547
|
+
writeSnapshot: (conversationId, nextSnapshot) => this.store.writeSnapshot(conversationId, nextSnapshot),
|
|
11548
|
+
appendEvent: (conversationId, input) => this.appendEvent(conversationId, input),
|
|
11549
|
+
writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input)
|
|
11550
|
+
},
|
|
11551
|
+
{
|
|
11552
|
+
conversationId: target.conversationId,
|
|
11553
|
+
runId: target.runId,
|
|
11554
|
+
messageId: assistant.id,
|
|
11555
|
+
references: await collectStagedDeliveryReferences(target.stagingDir)
|
|
11556
|
+
}
|
|
11557
|
+
);
|
|
11558
|
+
});
|
|
11559
|
+
}
|
|
11345
11560
|
async getMessages(conversationId, options = {}) {
|
|
11346
11561
|
return this.queries.getMessages(conversationId, options);
|
|
11347
11562
|
}
|
|
@@ -12363,12 +12578,12 @@ async function readRawBody(request, maxBytes) {
|
|
|
12363
12578
|
}
|
|
12364
12579
|
return Buffer.concat(chunks);
|
|
12365
12580
|
}
|
|
12366
|
-
function
|
|
12581
|
+
function readString13(body, key) {
|
|
12367
12582
|
const value = body[key];
|
|
12368
12583
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12369
12584
|
}
|
|
12370
12585
|
function readOptionalProfileName(body) {
|
|
12371
|
-
return
|
|
12586
|
+
return readString13(body, "profile") ?? readString13(body, "profile_name") ?? readString13(body, "profileName") ?? void 0;
|
|
12372
12587
|
}
|
|
12373
12588
|
function readStringArray(body, ...keys) {
|
|
12374
12589
|
for (const key of keys) {
|
|
@@ -12601,7 +12816,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12601
12816
|
ctx.body = {
|
|
12602
12817
|
ok: true,
|
|
12603
12818
|
conversation: await conversations.createConversation({
|
|
12604
|
-
title:
|
|
12819
|
+
title: readString13(body, "title") ?? void 0,
|
|
12605
12820
|
profileName: readOptionalProfileName(body)
|
|
12606
12821
|
})
|
|
12607
12822
|
};
|
|
@@ -12672,7 +12887,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12672
12887
|
router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
|
|
12673
12888
|
await authenticateRequest(ctx, paths);
|
|
12674
12889
|
const body = await readJsonBody(ctx.req);
|
|
12675
|
-
const content =
|
|
12890
|
+
const content = readString13(body, "content") ?? readString13(body, "text") ?? readString13(body, "input") ?? "";
|
|
12676
12891
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
12677
12892
|
if (!content && attachments.length === 0) {
|
|
12678
12893
|
throw new LinkHttpError(
|
|
@@ -12688,7 +12903,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12688
12903
|
conversationId: ctx.params.conversationId,
|
|
12689
12904
|
content,
|
|
12690
12905
|
attachments,
|
|
12691
|
-
clientMessageId:
|
|
12906
|
+
clientMessageId: readString13(body, "client_message_id") ?? readString13(body, "clientMessageId") ?? void 0,
|
|
12692
12907
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
12693
12908
|
profileName: readOptionalProfileName(body)
|
|
12694
12909
|
})
|
|
@@ -12697,7 +12912,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12697
12912
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
12698
12913
|
await authenticateRequest(ctx, paths);
|
|
12699
12914
|
const body = await readJsonBody(ctx.req);
|
|
12700
|
-
const modelId =
|
|
12915
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId") ?? readString13(body, "model");
|
|
12701
12916
|
if (!modelId) {
|
|
12702
12917
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
12703
12918
|
}
|
|
@@ -12727,7 +12942,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12727
12942
|
router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
|
|
12728
12943
|
await authenticateRequest(ctx, paths);
|
|
12729
12944
|
const body = await readJsonBody(ctx.req);
|
|
12730
|
-
const title =
|
|
12945
|
+
const title = readString13(body, "title") ?? readString13(body, "name") ?? readString13(body, "display_name");
|
|
12731
12946
|
if (!title) {
|
|
12732
12947
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
12733
12948
|
}
|
|
@@ -12791,7 +13006,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12791
13006
|
async (ctx) => {
|
|
12792
13007
|
await authenticateRequest(ctx, paths);
|
|
12793
13008
|
const body = await readJsonBody(ctx.req);
|
|
12794
|
-
const scope =
|
|
13009
|
+
const scope = readString13(body, "scope") ?? "always";
|
|
12795
13010
|
ctx.body = {
|
|
12796
13011
|
ok: true,
|
|
12797
13012
|
...await conversations.resolveApproval({
|
|
@@ -12851,7 +13066,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12851
13066
|
ctx.set("cache-control", "private, max-age=86400");
|
|
12852
13067
|
ctx.set(
|
|
12853
13068
|
"content-disposition",
|
|
12854
|
-
|
|
13069
|
+
contentDispositionInline(blob.filename)
|
|
12855
13070
|
);
|
|
12856
13071
|
ctx.body = blob.bytes;
|
|
12857
13072
|
}
|
|
@@ -12870,6 +13085,34 @@ function registerConversationRoutes(router, options) {
|
|
|
12870
13085
|
}
|
|
12871
13086
|
);
|
|
12872
13087
|
}
|
|
13088
|
+
function contentDispositionInline(filename) {
|
|
13089
|
+
const fallback = asciiFilenameFallback(filename);
|
|
13090
|
+
return `inline; filename="${fallback}"; filename*=UTF-8''${encodeRfc5987Value(filename)}`;
|
|
13091
|
+
}
|
|
13092
|
+
function asciiFilenameFallback(filename) {
|
|
13093
|
+
const basename = filename.trim().split(/[\\/]/u).pop()?.trim() ?? "";
|
|
13094
|
+
const extension = safeAsciiExtension(basename);
|
|
13095
|
+
const stem = extension ? basename.slice(0, -extension.length) : basename;
|
|
13096
|
+
const asciiStem = stem.replace(/[^\x20-\x7E]/gu, "_").replace(/["\\]/gu, "_").replace(/[^A-Za-z0-9._ -]/gu, "_").replace(/_+/gu, "_").trim();
|
|
13097
|
+
if (/[A-Za-z0-9]/u.test(asciiStem)) {
|
|
13098
|
+
return `${asciiStem.slice(0, 120)}${extension}`;
|
|
13099
|
+
}
|
|
13100
|
+
return `attachment${extension}`;
|
|
13101
|
+
}
|
|
13102
|
+
function safeAsciiExtension(filename) {
|
|
13103
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
13104
|
+
if (dotIndex < 0 || dotIndex === filename.length - 1) {
|
|
13105
|
+
return "";
|
|
13106
|
+
}
|
|
13107
|
+
const extension = filename.slice(dotIndex).toLowerCase();
|
|
13108
|
+
return /^\.[a-z0-9]{1,12}$/u.test(extension) ? extension : "";
|
|
13109
|
+
}
|
|
13110
|
+
function encodeRfc5987Value(value) {
|
|
13111
|
+
return encodeURIComponent(value).replace(
|
|
13112
|
+
/['()*]/gu,
|
|
13113
|
+
(char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
|
|
13114
|
+
);
|
|
13115
|
+
}
|
|
12873
13116
|
function isConversationNotificationEvent(event) {
|
|
12874
13117
|
const type = event.type.toLowerCase();
|
|
12875
13118
|
return type === "conversation.created" || type === "conversation.updated" || type === "conversation.deleted" || type === "message.created" || type === "message.completed" || type === "message.failed" || type === "run.completed" || type === "run.failed" || type === "run.cancelled" || type === "run.canceled" || type === "approval.requested" || readPayloadBool(event.payload, "requires_action") || readPayloadBool(event.payload, "requires_user_action") || readPayloadBool(event.payload, "requires_approval");
|
|
@@ -12923,7 +13166,7 @@ function createHttpErrorMiddleware(logger) {
|
|
|
12923
13166
|
}
|
|
12924
13167
|
|
|
12925
13168
|
// src/hermes/profiles.ts
|
|
12926
|
-
import { mkdir as mkdir10, readdir as
|
|
13169
|
+
import { mkdir as mkdir10, readdir as readdir7, readFile as readFile11, rename as rename4, rm as rm6, stat as stat10 } from "fs/promises";
|
|
12927
13170
|
import os4 from "os";
|
|
12928
13171
|
import path16 from "path";
|
|
12929
13172
|
import YAML2 from "yaml";
|
|
@@ -12933,9 +13176,9 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
12933
13176
|
const profiles = /* @__PURE__ */ new Map();
|
|
12934
13177
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
12935
13178
|
const profilesDir = path16.join(os4.homedir(), ".hermes", "profiles");
|
|
12936
|
-
const entries = await
|
|
13179
|
+
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
12937
13180
|
(error) => {
|
|
12938
|
-
if (
|
|
13181
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
12939
13182
|
return [];
|
|
12940
13183
|
}
|
|
12941
13184
|
throw error;
|
|
@@ -12960,7 +13203,7 @@ async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
|
12960
13203
|
assertProfileName(name);
|
|
12961
13204
|
const profile = await profileInfo(name, paths);
|
|
12962
13205
|
const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
12963
|
-
if (
|
|
13206
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
12964
13207
|
return false;
|
|
12965
13208
|
}
|
|
12966
13209
|
throw error;
|
|
@@ -13001,7 +13244,7 @@ async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
|
|
|
13001
13244
|
assertMutableProfile(name);
|
|
13002
13245
|
const profile = await profileInfo(name, paths);
|
|
13003
13246
|
const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
13004
|
-
if (
|
|
13247
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13005
13248
|
return false;
|
|
13006
13249
|
}
|
|
13007
13250
|
throw error;
|
|
@@ -13069,13 +13312,13 @@ function assertProfileName(name) {
|
|
|
13069
13312
|
throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
|
|
13070
13313
|
}
|
|
13071
13314
|
}
|
|
13072
|
-
function
|
|
13315
|
+
function isNodeError12(error, code) {
|
|
13073
13316
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
13074
13317
|
}
|
|
13075
13318
|
async function countSkills(root) {
|
|
13076
|
-
const entries = await
|
|
13319
|
+
const entries = await readdir7(root, { withFileTypes: true }).catch(
|
|
13077
13320
|
(error) => {
|
|
13078
|
-
if (
|
|
13321
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13079
13322
|
return [];
|
|
13080
13323
|
}
|
|
13081
13324
|
throw error;
|
|
@@ -13102,7 +13345,7 @@ async function countConfiguredTools(profileName) {
|
|
|
13102
13345
|
resolveHermesConfigPath(profileName),
|
|
13103
13346
|
"utf8"
|
|
13104
13347
|
).catch((error) => {
|
|
13105
|
-
if (
|
|
13348
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13106
13349
|
return "";
|
|
13107
13350
|
}
|
|
13108
13351
|
throw error;
|
|
@@ -13110,14 +13353,14 @@ async function countConfiguredTools(profileName) {
|
|
|
13110
13353
|
if (!raw.trim()) {
|
|
13111
13354
|
return 0;
|
|
13112
13355
|
}
|
|
13113
|
-
const config =
|
|
13356
|
+
const config = toRecord11(YAML2.parse(raw));
|
|
13114
13357
|
const toolsets = /* @__PURE__ */ new Set();
|
|
13115
13358
|
collectToolsetValues(config.toolsets, toolsets);
|
|
13116
|
-
const platformToolsets =
|
|
13359
|
+
const platformToolsets = toRecord11(config.platform_toolsets);
|
|
13117
13360
|
for (const value of Object.values(platformToolsets)) {
|
|
13118
13361
|
collectToolsetValues(value, toolsets);
|
|
13119
13362
|
}
|
|
13120
|
-
const mcpServers = Object.keys(
|
|
13363
|
+
const mcpServers = Object.keys(toRecord11(config.mcp_servers)).length;
|
|
13121
13364
|
return toolsets.size + mcpServers;
|
|
13122
13365
|
}
|
|
13123
13366
|
function collectToolsetValues(value, target) {
|
|
@@ -13131,7 +13374,7 @@ function collectToolsetValues(value, target) {
|
|
|
13131
13374
|
target.add(value.trim());
|
|
13132
13375
|
}
|
|
13133
13376
|
}
|
|
13134
|
-
function
|
|
13377
|
+
function toRecord11(value) {
|
|
13135
13378
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
13136
13379
|
}
|
|
13137
13380
|
|
|
@@ -13328,7 +13571,7 @@ function toHermesCronJobInput(input) {
|
|
|
13328
13571
|
};
|
|
13329
13572
|
}
|
|
13330
13573
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
13331
|
-
const jobId =
|
|
13574
|
+
const jobId = readString13(input.job, "id") ?? readString13(input.job, "job_id");
|
|
13332
13575
|
if (!jobId) {
|
|
13333
13576
|
return input.job;
|
|
13334
13577
|
}
|
|
@@ -13345,9 +13588,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
13345
13588
|
}
|
|
13346
13589
|
function readCronJobCreateInput(body) {
|
|
13347
13590
|
const input = {};
|
|
13348
|
-
const name =
|
|
13349
|
-
const prompt =
|
|
13350
|
-
const schedule =
|
|
13591
|
+
const name = readString13(body, "name") ?? readString13(body, "title");
|
|
13592
|
+
const prompt = readString13(body, "prompt") ?? readString13(body, "description") ?? readString13(body, "task");
|
|
13593
|
+
const schedule = readString13(body, "schedule");
|
|
13351
13594
|
if (!name) {
|
|
13352
13595
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
13353
13596
|
}
|
|
@@ -13368,7 +13611,7 @@ function readCronJobCreateInput(body) {
|
|
|
13368
13611
|
input.name = name;
|
|
13369
13612
|
input.prompt = prompt;
|
|
13370
13613
|
input.schedule = schedule;
|
|
13371
|
-
input.deliver =
|
|
13614
|
+
input.deliver = readString13(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
13372
13615
|
const skills = readOptionalCronSkills(body);
|
|
13373
13616
|
if (skills) {
|
|
13374
13617
|
input.skills = skills;
|
|
@@ -13596,7 +13839,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13596
13839
|
router.delete("/api/v1/model-configs", async (ctx) => {
|
|
13597
13840
|
await authenticateRequest(ctx, paths);
|
|
13598
13841
|
const body = await readJsonBody(ctx.req);
|
|
13599
|
-
const modelId =
|
|
13842
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13600
13843
|
if (!modelId) {
|
|
13601
13844
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13602
13845
|
}
|
|
@@ -13651,7 +13894,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13651
13894
|
await authenticateRequest(ctx, paths);
|
|
13652
13895
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
13653
13896
|
const body = await readJsonBody(ctx.req);
|
|
13654
|
-
const modelId =
|
|
13897
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13655
13898
|
if (!modelId) {
|
|
13656
13899
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13657
13900
|
}
|
|
@@ -13668,9 +13911,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13668
13911
|
});
|
|
13669
13912
|
}
|
|
13670
13913
|
function readModelConfigInput(body) {
|
|
13671
|
-
const id =
|
|
13672
|
-
const provider =
|
|
13673
|
-
const baseUrl =
|
|
13914
|
+
const id = readString13(body, "id") ?? readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13915
|
+
const provider = readString13(body, "provider") ?? readString13(body, "provider_key") ?? readString13(body, "providerKey");
|
|
13916
|
+
const baseUrl = readString13(body, "base_url") ?? readString13(body, "baseUrl");
|
|
13674
13917
|
if (!id || !provider || !baseUrl) {
|
|
13675
13918
|
throw new LinkHttpError(
|
|
13676
13919
|
400,
|
|
@@ -13680,24 +13923,24 @@ function readModelConfigInput(body) {
|
|
|
13680
13923
|
}
|
|
13681
13924
|
return {
|
|
13682
13925
|
id,
|
|
13683
|
-
originalModelId:
|
|
13926
|
+
originalModelId: readString13(body, "original_model_id") ?? readString13(body, "originalModelId") ?? readString13(body, "original_id") ?? void 0,
|
|
13684
13927
|
provider,
|
|
13685
|
-
providerName:
|
|
13928
|
+
providerName: readString13(body, "provider_name") ?? readString13(body, "providerName") ?? void 0,
|
|
13686
13929
|
baseUrl,
|
|
13687
|
-
apiKey:
|
|
13688
|
-
apiMode:
|
|
13930
|
+
apiKey: readString13(body, "api_key") ?? readString13(body, "apiKey") ?? void 0,
|
|
13931
|
+
apiMode: readString13(body, "api_mode") ?? readString13(body, "apiMode") ?? void 0,
|
|
13689
13932
|
contextLength: readPositiveInteger2(
|
|
13690
13933
|
body.context_length ?? body.contextLength
|
|
13691
13934
|
),
|
|
13692
|
-
keyEnv:
|
|
13935
|
+
keyEnv: readString13(body, "key_env") ?? readString13(body, "keyEnv") ?? void 0,
|
|
13693
13936
|
setDefault: readBoolean(body.set_default ?? body.setDefault),
|
|
13694
|
-
reasoningEffort:
|
|
13937
|
+
reasoningEffort: readString13(body, "reasoning_effort") ?? readString13(body, "reasoningEffort") ?? void 0
|
|
13695
13938
|
};
|
|
13696
13939
|
}
|
|
13697
13940
|
function readModelDefaultsInput(body) {
|
|
13698
13941
|
return {
|
|
13699
|
-
taskModelId:
|
|
13700
|
-
compressionModelId:
|
|
13942
|
+
taskModelId: readString13(body, "task_model_id") ?? readString13(body, "taskModelId") ?? readString13(body, "default_model_id") ?? readString13(body, "defaultModelId") ?? void 0,
|
|
13943
|
+
compressionModelId: readString13(body, "compression_model_id") ?? readString13(body, "compressionModelId") ?? void 0
|
|
13701
13944
|
};
|
|
13702
13945
|
}
|
|
13703
13946
|
function shouldReloadGatewayAfterModelConfigChange(body) {
|
|
@@ -14178,7 +14421,7 @@ function copyModelConfig(source, target) {
|
|
|
14178
14421
|
copied[key] = cloneJson(source[key]);
|
|
14179
14422
|
}
|
|
14180
14423
|
}
|
|
14181
|
-
const sourceAuxiliary =
|
|
14424
|
+
const sourceAuxiliary = toRecord12(source.auxiliary);
|
|
14182
14425
|
if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
|
|
14183
14426
|
const targetAuxiliary = ensureRecord2(target, "auxiliary");
|
|
14184
14427
|
targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
|
|
@@ -14187,12 +14430,12 @@ function copyModelConfig(source, target) {
|
|
|
14187
14430
|
return copied;
|
|
14188
14431
|
}
|
|
14189
14432
|
function copyToolPermissionsConfig(source, target) {
|
|
14190
|
-
const sourcePlatformToolsets =
|
|
14433
|
+
const sourcePlatformToolsets = toRecord12(source.platform_toolsets);
|
|
14191
14434
|
if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
|
|
14192
14435
|
const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
|
|
14193
14436
|
targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
|
|
14194
14437
|
}
|
|
14195
|
-
const sourceStt =
|
|
14438
|
+
const sourceStt = toRecord12(source.stt);
|
|
14196
14439
|
if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
|
|
14197
14440
|
const targetStt = ensureRecord2(target, "stt");
|
|
14198
14441
|
targetStt.enabled = cloneJson(sourceStt.enabled);
|
|
@@ -14241,7 +14484,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
14241
14484
|
async function writeEnvValues(profileName, values) {
|
|
14242
14485
|
const envPath = path17.join(resolveHermesProfileDir(profileName), ".env");
|
|
14243
14486
|
const existingRaw = await readFile12(envPath, "utf8").catch((error) => {
|
|
14244
|
-
if (
|
|
14487
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14245
14488
|
return "";
|
|
14246
14489
|
}
|
|
14247
14490
|
throw error;
|
|
@@ -14297,14 +14540,14 @@ function copyProperty(source, target, key) {
|
|
|
14297
14540
|
async function readYamlConfig(configPath) {
|
|
14298
14541
|
const existingRaw = await readFile12(configPath, "utf8").catch(
|
|
14299
14542
|
(error) => {
|
|
14300
|
-
if (
|
|
14543
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14301
14544
|
return null;
|
|
14302
14545
|
}
|
|
14303
14546
|
throw error;
|
|
14304
14547
|
}
|
|
14305
14548
|
);
|
|
14306
14549
|
return {
|
|
14307
|
-
config:
|
|
14550
|
+
config: toRecord12(existingRaw ? YAML3.parse(existingRaw) : {}),
|
|
14308
14551
|
existingRaw
|
|
14309
14552
|
};
|
|
14310
14553
|
}
|
|
@@ -14383,7 +14626,7 @@ async function clearProfileCreationLogFiles(paths) {
|
|
|
14383
14626
|
}
|
|
14384
14627
|
async function pathExists(targetPath) {
|
|
14385
14628
|
return await stat11(targetPath).then(() => true).catch((error) => {
|
|
14386
|
-
if (
|
|
14629
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14387
14630
|
return false;
|
|
14388
14631
|
}
|
|
14389
14632
|
throw error;
|
|
@@ -14415,7 +14658,7 @@ function ensureRecord2(target, key) {
|
|
|
14415
14658
|
target[key] = next;
|
|
14416
14659
|
return next;
|
|
14417
14660
|
}
|
|
14418
|
-
function
|
|
14661
|
+
function toRecord12(value) {
|
|
14419
14662
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
14420
14663
|
}
|
|
14421
14664
|
function cloneJson(value) {
|
|
@@ -14430,7 +14673,7 @@ function formatEnvValue2(value) {
|
|
|
14430
14673
|
function escapeRegExp2(value) {
|
|
14431
14674
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
14432
14675
|
}
|
|
14433
|
-
function
|
|
14676
|
+
function isNodeError13(error, code) {
|
|
14434
14677
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
14435
14678
|
}
|
|
14436
14679
|
|
|
@@ -14515,16 +14758,16 @@ function readProfilePermissionsInput(body) {
|
|
|
14515
14758
|
const approvals = readOptionalObject(body, "approvals");
|
|
14516
14759
|
if (approvals) {
|
|
14517
14760
|
input.approvals = {
|
|
14518
|
-
mode:
|
|
14761
|
+
mode: readString13(approvals, "mode") ?? readString13(approvals, "approval_mode") ?? readString13(approvals, "approvalMode") ?? void 0,
|
|
14519
14762
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
14520
|
-
cronMode:
|
|
14763
|
+
cronMode: readString13(approvals, "cron_mode") ?? readString13(approvals, "cronMode") ?? void 0
|
|
14521
14764
|
};
|
|
14522
14765
|
}
|
|
14523
14766
|
const terminal = readOptionalObject(body, "terminal");
|
|
14524
14767
|
if (terminal) {
|
|
14525
14768
|
input.terminal = {
|
|
14526
|
-
backend:
|
|
14527
|
-
cwd:
|
|
14769
|
+
backend: readString13(terminal, "backend") ?? void 0,
|
|
14770
|
+
cwd: readString13(terminal, "cwd") ?? void 0,
|
|
14528
14771
|
containerCpu: readPositiveInteger2(
|
|
14529
14772
|
terminal.container_cpu ?? terminal.containerCpu
|
|
14530
14773
|
),
|
|
@@ -14641,7 +14884,7 @@ import {
|
|
|
14641
14884
|
access as access3,
|
|
14642
14885
|
copyFile as copyFile3,
|
|
14643
14886
|
mkdir as mkdir12,
|
|
14644
|
-
readdir as
|
|
14887
|
+
readdir as readdir8,
|
|
14645
14888
|
readFile as readFile13,
|
|
14646
14889
|
rename as rename6,
|
|
14647
14890
|
stat as stat12,
|
|
@@ -14650,8 +14893,8 @@ import {
|
|
|
14650
14893
|
import path18 from "path";
|
|
14651
14894
|
import YAML4 from "yaml";
|
|
14652
14895
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
14653
|
-
var
|
|
14654
|
-
var
|
|
14896
|
+
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
14897
|
+
var DEFAULT_USER_LIMIT = 1375;
|
|
14655
14898
|
var CUSTOM_PROVIDER_CARD_ID = "__custom__";
|
|
14656
14899
|
var CUSTOM_PROVIDER_REGISTRY_FILE = "memory-providers.json";
|
|
14657
14900
|
var HINDSIGHT_DEFAULT_API_URL = "https://api.hindsight.vectorize.io";
|
|
@@ -14734,9 +14977,10 @@ var HermesMemoryError = class extends Error {
|
|
|
14734
14977
|
};
|
|
14735
14978
|
async function readHermesProfileMemory(profileName = "default") {
|
|
14736
14979
|
const memoryDir = resolveMemoryDir(profileName);
|
|
14980
|
+
const limits = await readMemoryLimits(profileName);
|
|
14737
14981
|
const [memoryStore, userStore, settings] = await Promise.all([
|
|
14738
|
-
readMemoryStore(profileName, "memory"),
|
|
14739
|
-
readMemoryStore(profileName, "user"),
|
|
14982
|
+
readMemoryStore(profileName, "memory", limits),
|
|
14983
|
+
readMemoryStore(profileName, "user", limits),
|
|
14740
14984
|
readMemorySettings(profileName)
|
|
14741
14985
|
]);
|
|
14742
14986
|
return {
|
|
@@ -14754,9 +14998,7 @@ async function addHermesMemoryEntry(profileName, target, content) {
|
|
|
14754
14998
|
if (entries.includes(normalized)) {
|
|
14755
14999
|
return entries;
|
|
14756
15000
|
}
|
|
14757
|
-
|
|
14758
|
-
assertWithinLimit(target, next);
|
|
14759
|
-
return next;
|
|
15001
|
+
return [...entries, normalized];
|
|
14760
15002
|
});
|
|
14761
15003
|
return readHermesProfileMemory(profileName);
|
|
14762
15004
|
}
|
|
@@ -14767,7 +15009,6 @@ async function replaceHermesMemoryEntry(profileName, target, oldText, content) {
|
|
|
14767
15009
|
const index = findSingleMatch(entries, needle);
|
|
14768
15010
|
const next = [...entries];
|
|
14769
15011
|
next[index] = normalized;
|
|
14770
|
-
assertWithinLimit(target, next);
|
|
14771
15012
|
return next;
|
|
14772
15013
|
});
|
|
14773
15014
|
return readHermesProfileMemory(profileName);
|
|
@@ -14919,7 +15160,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
|
|
|
14919
15160
|
"\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
|
|
14920
15161
|
);
|
|
14921
15162
|
}
|
|
14922
|
-
const config =
|
|
15163
|
+
const config = toRecord13(parsed);
|
|
14923
15164
|
if (Object.keys(config).length === 0 && parsed !== null) {
|
|
14924
15165
|
throw new HermesMemoryError(
|
|
14925
15166
|
"memory_provider_config_invalid",
|
|
@@ -15013,15 +15254,15 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
15013
15254
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15014
15255
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
15015
15256
|
(error) => {
|
|
15016
|
-
if (
|
|
15257
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15017
15258
|
return null;
|
|
15018
15259
|
}
|
|
15019
15260
|
throw error;
|
|
15020
15261
|
}
|
|
15021
15262
|
);
|
|
15022
15263
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15023
|
-
const config =
|
|
15024
|
-
const memory =
|
|
15264
|
+
const config = toRecord13(document.toJSON());
|
|
15265
|
+
const memory = toRecord13(config.memory);
|
|
15025
15266
|
memory.provider = provider === "built-in" ? "" : provider;
|
|
15026
15267
|
config.memory = memory;
|
|
15027
15268
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
@@ -15040,17 +15281,17 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
15040
15281
|
function resolveMemoryDir(profileName) {
|
|
15041
15282
|
return path18.join(resolveHermesProfileDir(profileName), "memories");
|
|
15042
15283
|
}
|
|
15043
|
-
async function readMemoryStore(profileName, target) {
|
|
15284
|
+
async function readMemoryStore(profileName, target, limits) {
|
|
15044
15285
|
const filePath = memoryFilePath(profileName, target);
|
|
15045
15286
|
const entries = await readMemoryEntries(filePath);
|
|
15046
15287
|
const fileStat = await stat12(filePath).catch((error) => {
|
|
15047
|
-
if (
|
|
15288
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15048
15289
|
return null;
|
|
15049
15290
|
}
|
|
15050
15291
|
throw error;
|
|
15051
15292
|
});
|
|
15052
15293
|
const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
|
|
15053
|
-
const limit = target
|
|
15294
|
+
const limit = memoryLimitForTarget(limits, target);
|
|
15054
15295
|
return {
|
|
15055
15296
|
target,
|
|
15056
15297
|
label: target === "user" ? "\u5173\u4E8E\u7528\u6237" : "Agent \u7B14\u8BB0",
|
|
@@ -15073,7 +15314,7 @@ async function readMemoryStore(profileName, target) {
|
|
|
15073
15314
|
}
|
|
15074
15315
|
async function readMemoryEntries(filePath) {
|
|
15075
15316
|
const raw = await readFile13(filePath, "utf8").catch((error) => {
|
|
15076
|
-
if (
|
|
15317
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15077
15318
|
return "";
|
|
15078
15319
|
}
|
|
15079
15320
|
throw error;
|
|
@@ -15091,7 +15332,7 @@ async function mutateMemoryEntries(profileName, target, mutate) {
|
|
|
15091
15332
|
await writeMemoryEntries(profileName, target, mutate([...new Set(current)]));
|
|
15092
15333
|
}
|
|
15093
15334
|
async function writeMemoryEntries(profileName, target, entries) {
|
|
15094
|
-
assertWithinLimit(target, entries);
|
|
15335
|
+
assertWithinLimit(target, entries, await readMemoryLimits(profileName));
|
|
15095
15336
|
const filePath = memoryFilePath(profileName, target);
|
|
15096
15337
|
const dir = path18.dirname(filePath);
|
|
15097
15338
|
await mkdir12(dir, { recursive: true, mode: 448 });
|
|
@@ -15244,7 +15485,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15244
15485
|
const config2 = await readJsonObject(
|
|
15245
15486
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
15246
15487
|
);
|
|
15247
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
15488
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString14(config2.apiKey)) || isConfiguredEnvValue(readString14(config2.api_key)) || isConfiguredEnvValue(readString14(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
15248
15489
|
configured: false,
|
|
15249
15490
|
issue: "Honcho \u9700\u8981\u5148\u914D\u7F6E HONCHO_API_KEY\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
15250
15491
|
};
|
|
@@ -15253,7 +15494,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15253
15494
|
const config2 = await readJsonObject(
|
|
15254
15495
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
15255
15496
|
);
|
|
15256
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
15497
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString14(config2.api_key)) ? { configured: true, issue: null } : {
|
|
15257
15498
|
configured: false,
|
|
15258
15499
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u673A Hermes .env \u914D\u7F6E MEM0_API_KEY\u3002"
|
|
15259
15500
|
};
|
|
@@ -15295,7 +15536,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15295
15536
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15296
15537
|
);
|
|
15297
15538
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
15298
|
-
const apiKey =
|
|
15539
|
+
const apiKey = readString14(config.apiKey) ?? readString14(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
15299
15540
|
if (mode === "cloud") {
|
|
15300
15541
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
15301
15542
|
configured: false,
|
|
@@ -15303,15 +15544,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15303
15544
|
};
|
|
15304
15545
|
}
|
|
15305
15546
|
if (mode === "local_external") {
|
|
15306
|
-
const apiUrl =
|
|
15547
|
+
const apiUrl = readString14(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
15307
15548
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
15308
15549
|
configured: false,
|
|
15309
15550
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
15310
15551
|
};
|
|
15311
15552
|
}
|
|
15312
15553
|
if (mode === "local_embedded") {
|
|
15313
|
-
const llmProvider =
|
|
15314
|
-
const llmModel =
|
|
15554
|
+
const llmProvider = readString14(config.llm_provider) ?? "openai";
|
|
15555
|
+
const llmModel = readString14(config.llm_model);
|
|
15315
15556
|
if (!llmModel) {
|
|
15316
15557
|
return {
|
|
15317
15558
|
configured: false,
|
|
@@ -15319,7 +15560,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15319
15560
|
};
|
|
15320
15561
|
}
|
|
15321
15562
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
15322
|
-
|
|
15563
|
+
readString14(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
15323
15564
|
)) {
|
|
15324
15565
|
return {
|
|
15325
15566
|
configured: false,
|
|
@@ -15327,7 +15568,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15327
15568
|
};
|
|
15328
15569
|
}
|
|
15329
15570
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
15330
|
-
|
|
15571
|
+
readString14(config.llmApiKey) ?? readString14(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
15331
15572
|
)) {
|
|
15332
15573
|
return {
|
|
15333
15574
|
configured: false,
|
|
@@ -15467,8 +15708,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15467
15708
|
const config = await readJsonObject(
|
|
15468
15709
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15469
15710
|
);
|
|
15470
|
-
const banks =
|
|
15471
|
-
const hermesBank =
|
|
15711
|
+
const banks = toRecord13(config.banks);
|
|
15712
|
+
const hermesBank = toRecord13(banks.hermes);
|
|
15472
15713
|
const mode = normalizeHindsightMode(config.mode);
|
|
15473
15714
|
return [
|
|
15474
15715
|
selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
|
|
@@ -15584,7 +15825,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
15584
15825
|
async function readCustomProviderRegistry(profileName) {
|
|
15585
15826
|
const raw = await readFile13(customProviderRegistryPath(profileName), "utf8").catch(
|
|
15586
15827
|
(error) => {
|
|
15587
|
-
if (
|
|
15828
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15588
15829
|
return "";
|
|
15589
15830
|
}
|
|
15590
15831
|
throw error;
|
|
@@ -15595,18 +15836,18 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
15595
15836
|
}
|
|
15596
15837
|
try {
|
|
15597
15838
|
const parsed = JSON.parse(raw);
|
|
15598
|
-
const providers = Array.isArray(parsed) ? parsed : Array.isArray(
|
|
15839
|
+
const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord13(parsed).providers) ? toRecord13(parsed).providers : [];
|
|
15599
15840
|
return providers.map((item) => {
|
|
15600
15841
|
if (typeof item === "string") {
|
|
15601
15842
|
const id2 = normalizeCustomProviderId(item);
|
|
15602
15843
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
15603
15844
|
}
|
|
15604
|
-
const record =
|
|
15605
|
-
const id = normalizeCustomProviderId(
|
|
15845
|
+
const record = toRecord13(item);
|
|
15846
|
+
const id = normalizeCustomProviderId(readString14(record.id) ?? "");
|
|
15606
15847
|
return {
|
|
15607
15848
|
id,
|
|
15608
|
-
label:
|
|
15609
|
-
description:
|
|
15849
|
+
label: readString14(record.label) ?? id,
|
|
15850
|
+
description: readString14(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15610
15851
|
};
|
|
15611
15852
|
}).filter((item) => item.id);
|
|
15612
15853
|
} catch {
|
|
@@ -15631,9 +15872,9 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
15631
15872
|
}
|
|
15632
15873
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
15633
15874
|
const pluginsDir = path18.join(resolveHermesProfileDir(profileName), "plugins");
|
|
15634
|
-
const entries = await
|
|
15875
|
+
const entries = await readdir8(pluginsDir, { withFileTypes: true }).catch(
|
|
15635
15876
|
(error) => {
|
|
15636
|
-
if (
|
|
15877
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15637
15878
|
return [];
|
|
15638
15879
|
}
|
|
15639
15880
|
throw error;
|
|
@@ -15657,8 +15898,8 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
15657
15898
|
const meta = await readPluginMetadata(providerDir);
|
|
15658
15899
|
descriptors.push({
|
|
15659
15900
|
id: providerId,
|
|
15660
|
-
label:
|
|
15661
|
-
description:
|
|
15901
|
+
label: readString14(meta.name) ?? providerId,
|
|
15902
|
+
description: readString14(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15662
15903
|
});
|
|
15663
15904
|
}
|
|
15664
15905
|
return descriptors;
|
|
@@ -15674,7 +15915,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
15674
15915
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
15675
15916
|
const source = await readFile13(path18.join(providerDir, "__init__.py"), "utf8").catch(
|
|
15676
15917
|
(error) => {
|
|
15677
|
-
if (
|
|
15918
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15678
15919
|
return "";
|
|
15679
15920
|
}
|
|
15680
15921
|
throw error;
|
|
@@ -15686,13 +15927,13 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
15686
15927
|
async function readPluginMetadata(providerDir) {
|
|
15687
15928
|
const raw = await readFile13(path18.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
15688
15929
|
(error) => {
|
|
15689
|
-
if (
|
|
15930
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15690
15931
|
return "";
|
|
15691
15932
|
}
|
|
15692
15933
|
throw error;
|
|
15693
15934
|
}
|
|
15694
15935
|
);
|
|
15695
|
-
return raw ?
|
|
15936
|
+
return raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
15696
15937
|
}
|
|
15697
15938
|
async function resolveByteRoverCli() {
|
|
15698
15939
|
const candidates = [
|
|
@@ -15712,30 +15953,30 @@ async function resolveByteRoverCli() {
|
|
|
15712
15953
|
async function readHolographicProviderConfig(profileName) {
|
|
15713
15954
|
const raw = await readFile13(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
15714
15955
|
(error) => {
|
|
15715
|
-
if (
|
|
15956
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15716
15957
|
return "";
|
|
15717
15958
|
}
|
|
15718
15959
|
throw error;
|
|
15719
15960
|
}
|
|
15720
15961
|
);
|
|
15721
|
-
const config = raw ?
|
|
15722
|
-
const plugins =
|
|
15723
|
-
return
|
|
15962
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
15963
|
+
const plugins = toRecord13(config.plugins);
|
|
15964
|
+
return toRecord13(plugins["hermes-memory-store"]);
|
|
15724
15965
|
}
|
|
15725
15966
|
async function patchHolographicProviderConfig(profileName, patch) {
|
|
15726
15967
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15727
15968
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
15728
15969
|
(error) => {
|
|
15729
|
-
if (
|
|
15970
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15730
15971
|
return null;
|
|
15731
15972
|
}
|
|
15732
15973
|
throw error;
|
|
15733
15974
|
}
|
|
15734
15975
|
);
|
|
15735
15976
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15736
|
-
const config =
|
|
15737
|
-
const plugins =
|
|
15738
|
-
const memoryStore =
|
|
15977
|
+
const config = toRecord13(document.toJSON());
|
|
15978
|
+
const plugins = toRecord13(config.plugins);
|
|
15979
|
+
const memoryStore = toRecord13(plugins["hermes-memory-store"]);
|
|
15739
15980
|
for (const [key, value] of Object.entries(patch)) {
|
|
15740
15981
|
if (value !== void 0) {
|
|
15741
15982
|
memoryStore[key] = value;
|
|
@@ -15764,7 +16005,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
15764
16005
|
}
|
|
15765
16006
|
const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
|
|
15766
16007
|
const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
|
|
15767
|
-
if (
|
|
16008
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15768
16009
|
return "";
|
|
15769
16010
|
}
|
|
15770
16011
|
throw error;
|
|
@@ -15813,7 +16054,7 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
15813
16054
|
].includes(key);
|
|
15814
16055
|
}
|
|
15815
16056
|
function normalizeHindsightMode(value) {
|
|
15816
|
-
const mode =
|
|
16057
|
+
const mode = readString14(value) ?? "cloud";
|
|
15817
16058
|
return mode === "local" ? "local_embedded" : mode;
|
|
15818
16059
|
}
|
|
15819
16060
|
async function readActiveMemoryProvider(profileName) {
|
|
@@ -15821,14 +16062,14 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
15821
16062
|
resolveHermesConfigPath(profileName),
|
|
15822
16063
|
"utf8"
|
|
15823
16064
|
).catch((error) => {
|
|
15824
|
-
if (
|
|
16065
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15825
16066
|
return "";
|
|
15826
16067
|
}
|
|
15827
16068
|
throw error;
|
|
15828
16069
|
});
|
|
15829
|
-
const config = raw ?
|
|
15830
|
-
const memory =
|
|
15831
|
-
const provider =
|
|
16070
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
16071
|
+
const memory = toRecord13(config.memory);
|
|
16072
|
+
const provider = readString14(memory.provider);
|
|
15832
16073
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
15833
16074
|
return null;
|
|
15834
16075
|
}
|
|
@@ -15855,13 +16096,13 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
15855
16096
|
}
|
|
15856
16097
|
async function readJsonObject(filePath) {
|
|
15857
16098
|
const raw = await readFile13(filePath, "utf8").catch((error) => {
|
|
15858
|
-
if (
|
|
16099
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15859
16100
|
return "{}";
|
|
15860
16101
|
}
|
|
15861
16102
|
throw error;
|
|
15862
16103
|
});
|
|
15863
16104
|
try {
|
|
15864
|
-
return
|
|
16105
|
+
return toRecord13(JSON.parse(raw || "{}"));
|
|
15865
16106
|
} catch {
|
|
15866
16107
|
throw new HermesMemoryError(
|
|
15867
16108
|
"memory_provider_config_invalid",
|
|
@@ -15886,7 +16127,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
15886
16127
|
return {
|
|
15887
16128
|
key,
|
|
15888
16129
|
label,
|
|
15889
|
-
value:
|
|
16130
|
+
value: readString14(value) ?? "",
|
|
15890
16131
|
editable,
|
|
15891
16132
|
kind: "string"
|
|
15892
16133
|
};
|
|
@@ -15901,11 +16142,31 @@ function textSetting(key, label, value, editable = true) {
|
|
|
15901
16142
|
};
|
|
15902
16143
|
}
|
|
15903
16144
|
function selectSetting(key, label, value, options, editable = true) {
|
|
15904
|
-
const stringValue =
|
|
16145
|
+
const stringValue = readString14(value) ?? options[0] ?? null;
|
|
15905
16146
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
15906
16147
|
}
|
|
15907
|
-
function
|
|
15908
|
-
const
|
|
16148
|
+
async function readMemoryLimits(profileName) {
|
|
16149
|
+
const raw = await readFile13(
|
|
16150
|
+
resolveHermesConfigPath(profileName),
|
|
16151
|
+
"utf8"
|
|
16152
|
+
).catch((error) => {
|
|
16153
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
16154
|
+
return "";
|
|
16155
|
+
}
|
|
16156
|
+
throw error;
|
|
16157
|
+
});
|
|
16158
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
16159
|
+
const memory = toRecord13(config.memory);
|
|
16160
|
+
return {
|
|
16161
|
+
memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
|
|
16162
|
+
user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
|
|
16163
|
+
};
|
|
16164
|
+
}
|
|
16165
|
+
function memoryLimitForTarget(limits, target) {
|
|
16166
|
+
return target === "user" ? limits.user : limits.memory;
|
|
16167
|
+
}
|
|
16168
|
+
function assertWithinLimit(target, entries, limits) {
|
|
16169
|
+
const limit = memoryLimitForTarget(limits, target);
|
|
15909
16170
|
const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
|
|
15910
16171
|
if (chars > limit) {
|
|
15911
16172
|
throw new HermesMemoryError(
|
|
@@ -15951,12 +16212,16 @@ function hashString(value) {
|
|
|
15951
16212
|
}
|
|
15952
16213
|
return hash.toString(16);
|
|
15953
16214
|
}
|
|
15954
|
-
function
|
|
16215
|
+
function toRecord13(value) {
|
|
15955
16216
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
15956
16217
|
}
|
|
15957
|
-
function
|
|
16218
|
+
function readString14(value) {
|
|
15958
16219
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
15959
16220
|
}
|
|
16221
|
+
function readPositiveInteger3(value) {
|
|
16222
|
+
const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
|
|
16223
|
+
return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
|
|
16224
|
+
}
|
|
15960
16225
|
function readBoolean2(value) {
|
|
15961
16226
|
if (typeof value === "boolean") {
|
|
15962
16227
|
return value;
|
|
@@ -15978,7 +16243,7 @@ function formatEnvValue3(value) {
|
|
|
15978
16243
|
function escapeRegExp3(value) {
|
|
15979
16244
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
15980
16245
|
}
|
|
15981
|
-
function
|
|
16246
|
+
function isNodeError14(error, code) {
|
|
15982
16247
|
return error instanceof Error && "code" in error && error.code === code;
|
|
15983
16248
|
}
|
|
15984
16249
|
|
|
@@ -16092,7 +16357,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
16092
16357
|
);
|
|
16093
16358
|
}
|
|
16094
16359
|
function readMemoryTarget(body) {
|
|
16095
|
-
const raw =
|
|
16360
|
+
const raw = readString13(body, "target");
|
|
16096
16361
|
if (raw === "memory" || raw === "user") {
|
|
16097
16362
|
return raw;
|
|
16098
16363
|
}
|
|
@@ -16103,7 +16368,7 @@ function readMemoryTarget(body) {
|
|
|
16103
16368
|
);
|
|
16104
16369
|
}
|
|
16105
16370
|
function readMemoryResetTarget(body) {
|
|
16106
|
-
const raw =
|
|
16371
|
+
const raw = readString13(body, "target") ?? "all";
|
|
16107
16372
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
16108
16373
|
return raw;
|
|
16109
16374
|
}
|
|
@@ -16114,7 +16379,7 @@ function readMemoryResetTarget(body) {
|
|
|
16114
16379
|
);
|
|
16115
16380
|
}
|
|
16116
16381
|
function readRequiredMemoryContent(body) {
|
|
16117
|
-
const content =
|
|
16382
|
+
const content = readString13(body, "content") ?? readString13(body, "text");
|
|
16118
16383
|
if (!content) {
|
|
16119
16384
|
throw new LinkHttpError(
|
|
16120
16385
|
400,
|
|
@@ -16125,7 +16390,7 @@ function readRequiredMemoryContent(body) {
|
|
|
16125
16390
|
return content;
|
|
16126
16391
|
}
|
|
16127
16392
|
function readRequiredMemoryMatch(body) {
|
|
16128
|
-
const oldText =
|
|
16393
|
+
const oldText = readString13(body, "old_text") ?? readString13(body, "oldText") ?? readString13(body, "match");
|
|
16129
16394
|
if (!oldText) {
|
|
16130
16395
|
throw new LinkHttpError(
|
|
16131
16396
|
400,
|
|
@@ -16136,7 +16401,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
16136
16401
|
return oldText;
|
|
16137
16402
|
}
|
|
16138
16403
|
function readRequiredMemoryProvider(body) {
|
|
16139
|
-
const provider =
|
|
16404
|
+
const provider = readString13(body, "provider") ?? readString13(body, "provider_id") ?? readString13(body, "providerId");
|
|
16140
16405
|
if (!provider) {
|
|
16141
16406
|
throw new LinkHttpError(
|
|
16142
16407
|
400,
|
|
@@ -16148,7 +16413,7 @@ function readRequiredMemoryProvider(body) {
|
|
|
16148
16413
|
}
|
|
16149
16414
|
function readMemorySettingsPatch(body) {
|
|
16150
16415
|
const input = {};
|
|
16151
|
-
const mode =
|
|
16416
|
+
const mode = readString13(body, "mode");
|
|
16152
16417
|
if (mode) {
|
|
16153
16418
|
input.mode = mode;
|
|
16154
16419
|
}
|
|
@@ -16160,7 +16425,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16160
16425
|
if (bankId !== void 0) {
|
|
16161
16426
|
input.bankId = bankId;
|
|
16162
16427
|
}
|
|
16163
|
-
const llmProvider =
|
|
16428
|
+
const llmProvider = readString13(body, "llm_provider") ?? readString13(body, "llmProvider");
|
|
16164
16429
|
if (llmProvider) {
|
|
16165
16430
|
input.llmProvider = llmProvider;
|
|
16166
16431
|
}
|
|
@@ -16188,11 +16453,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16188
16453
|
if (autoRetain !== void 0) {
|
|
16189
16454
|
input.autoRetain = autoRetain;
|
|
16190
16455
|
}
|
|
16191
|
-
const memoryMode =
|
|
16456
|
+
const memoryMode = readString13(body, "memory_mode") ?? readString13(body, "memoryMode");
|
|
16192
16457
|
if (memoryMode) {
|
|
16193
16458
|
input.memoryMode = memoryMode;
|
|
16194
16459
|
}
|
|
16195
|
-
const recallBudget =
|
|
16460
|
+
const recallBudget = readString13(body, "recall_budget") ?? readString13(body, "recallBudget");
|
|
16196
16461
|
if (recallBudget) {
|
|
16197
16462
|
input.recallBudget = recallBudget;
|
|
16198
16463
|
}
|
|
@@ -16208,11 +16473,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16208
16473
|
if (profileFrequency !== void 0) {
|
|
16209
16474
|
input.profileFrequency = profileFrequency;
|
|
16210
16475
|
}
|
|
16211
|
-
const captureMode =
|
|
16476
|
+
const captureMode = readString13(body, "capture_mode") ?? readString13(body, "captureMode");
|
|
16212
16477
|
if (captureMode) {
|
|
16213
16478
|
input.captureMode = captureMode;
|
|
16214
16479
|
}
|
|
16215
|
-
const searchMode =
|
|
16480
|
+
const searchMode = readString13(body, "search_mode") ?? readString13(body, "searchMode");
|
|
16216
16481
|
if (searchMode) {
|
|
16217
16482
|
input.searchMode = searchMode;
|
|
16218
16483
|
}
|
|
@@ -16246,11 +16511,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16246
16511
|
if (aiPeer !== void 0) {
|
|
16247
16512
|
input.aiPeer = aiPeer;
|
|
16248
16513
|
}
|
|
16249
|
-
const recallMode =
|
|
16514
|
+
const recallMode = readString13(body, "recall_mode") ?? readString13(body, "recallMode");
|
|
16250
16515
|
if (recallMode) {
|
|
16251
16516
|
input.recallMode = recallMode;
|
|
16252
16517
|
}
|
|
16253
|
-
const writeFrequency =
|
|
16518
|
+
const writeFrequency = readString13(body, "write_frequency") ?? readString13(body, "writeFrequency");
|
|
16254
16519
|
if (writeFrequency) {
|
|
16255
16520
|
input.writeFrequency = writeFrequency;
|
|
16256
16521
|
}
|
|
@@ -16258,7 +16523,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16258
16523
|
if (saveMessages !== void 0) {
|
|
16259
16524
|
input.saveMessages = saveMessages;
|
|
16260
16525
|
}
|
|
16261
|
-
const sessionStrategy =
|
|
16526
|
+
const sessionStrategy = readString13(body, "session_strategy") ?? readString13(body, "sessionStrategy");
|
|
16262
16527
|
if (sessionStrategy) {
|
|
16263
16528
|
input.sessionStrategy = sessionStrategy;
|
|
16264
16529
|
}
|
|
@@ -16390,7 +16655,7 @@ import {
|
|
|
16390
16655
|
copyFile as copyFile4,
|
|
16391
16656
|
mkdir as mkdir13,
|
|
16392
16657
|
readFile as readFile14,
|
|
16393
|
-
readdir as
|
|
16658
|
+
readdir as readdir9,
|
|
16394
16659
|
rename as rename7,
|
|
16395
16660
|
writeFile as writeFile6
|
|
16396
16661
|
} from "fs/promises";
|
|
@@ -16485,9 +16750,9 @@ async function findSkillFiles(root) {
|
|
|
16485
16750
|
return results.sort((left, right) => left.localeCompare(right));
|
|
16486
16751
|
}
|
|
16487
16752
|
async function collectSkillFiles(directory, results) {
|
|
16488
|
-
const entries = await
|
|
16753
|
+
const entries = await readdir9(directory, { withFileTypes: true }).catch(
|
|
16489
16754
|
(error) => {
|
|
16490
|
-
if (
|
|
16755
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16491
16756
|
return [];
|
|
16492
16757
|
}
|
|
16493
16758
|
throw error;
|
|
@@ -16512,7 +16777,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
16512
16777
|
async function readSkillMetadata(input) {
|
|
16513
16778
|
const raw = await readFile14(input.skillFile, "utf8").catch(
|
|
16514
16779
|
(error) => {
|
|
16515
|
-
if (
|
|
16780
|
+
if (isNodeError15(error, "ENOENT") || isNodeError15(error, "EACCES")) {
|
|
16516
16781
|
return null;
|
|
16517
16782
|
}
|
|
16518
16783
|
throw error;
|
|
@@ -16524,13 +16789,13 @@ async function readSkillMetadata(input) {
|
|
|
16524
16789
|
const skillDir = path19.dirname(input.skillFile);
|
|
16525
16790
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
16526
16791
|
const name = normalizeSkillName(
|
|
16527
|
-
|
|
16792
|
+
readString15(frontmatter.name) ?? path19.basename(skillDir)
|
|
16528
16793
|
);
|
|
16529
16794
|
if (!name) {
|
|
16530
16795
|
return null;
|
|
16531
16796
|
}
|
|
16532
16797
|
const description = normalizeDescription(
|
|
16533
|
-
|
|
16798
|
+
readString15(frontmatter.description) ?? firstBodyDescription(body)
|
|
16534
16799
|
);
|
|
16535
16800
|
const provenance = input.provenance.get(name) ?? {
|
|
16536
16801
|
source: "local",
|
|
@@ -16556,7 +16821,7 @@ function parseSkillDocument(raw) {
|
|
|
16556
16821
|
}
|
|
16557
16822
|
try {
|
|
16558
16823
|
return {
|
|
16559
|
-
frontmatter:
|
|
16824
|
+
frontmatter: toRecord14(YAML5.parse(match[1] ?? "")),
|
|
16560
16825
|
body: content.slice(match[0].length)
|
|
16561
16826
|
};
|
|
16562
16827
|
} catch {
|
|
@@ -16589,7 +16854,7 @@ function normalizeDescription(value) {
|
|
|
16589
16854
|
}
|
|
16590
16855
|
async function readDisabledSkillNames(configPath) {
|
|
16591
16856
|
const raw = await readFile14(configPath, "utf8").catch((error) => {
|
|
16592
|
-
if (
|
|
16857
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16593
16858
|
return "";
|
|
16594
16859
|
}
|
|
16595
16860
|
throw error;
|
|
@@ -16597,8 +16862,8 @@ async function readDisabledSkillNames(configPath) {
|
|
|
16597
16862
|
if (!raw.trim()) {
|
|
16598
16863
|
return /* @__PURE__ */ new Set();
|
|
16599
16864
|
}
|
|
16600
|
-
const config =
|
|
16601
|
-
const skills =
|
|
16865
|
+
const config = toRecord14(YAML5.parse(raw));
|
|
16866
|
+
const skills = toRecord14(config.skills);
|
|
16602
16867
|
return new Set(readStringList3(skills.disabled));
|
|
16603
16868
|
}
|
|
16604
16869
|
async function readSkillProvenance(root) {
|
|
@@ -16614,7 +16879,7 @@ async function readSkillProvenance(root) {
|
|
|
16614
16879
|
async function readBundledSkillNames(root) {
|
|
16615
16880
|
const raw = await readFile14(path19.join(root, ".bundled_manifest"), "utf8").catch(
|
|
16616
16881
|
(error) => {
|
|
16617
|
-
if (
|
|
16882
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16618
16883
|
return "";
|
|
16619
16884
|
}
|
|
16620
16885
|
throw error;
|
|
@@ -16637,7 +16902,7 @@ async function readBundledSkillNames(root) {
|
|
|
16637
16902
|
async function readHubInstalledSkills(root) {
|
|
16638
16903
|
const raw = await readFile14(path19.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
16639
16904
|
(error) => {
|
|
16640
|
-
if (
|
|
16905
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16641
16906
|
return "";
|
|
16642
16907
|
}
|
|
16643
16908
|
throw error;
|
|
@@ -16648,17 +16913,17 @@ async function readHubInstalledSkills(root) {
|
|
|
16648
16913
|
}
|
|
16649
16914
|
let lock;
|
|
16650
16915
|
try {
|
|
16651
|
-
lock =
|
|
16916
|
+
lock = toRecord14(JSON.parse(raw));
|
|
16652
16917
|
} catch {
|
|
16653
16918
|
return /* @__PURE__ */ new Map();
|
|
16654
16919
|
}
|
|
16655
|
-
const installed =
|
|
16920
|
+
const installed = toRecord14(lock.installed);
|
|
16656
16921
|
const result = /* @__PURE__ */ new Map();
|
|
16657
16922
|
for (const [name, rawEntry] of Object.entries(installed)) {
|
|
16658
|
-
const entry =
|
|
16923
|
+
const entry = toRecord14(rawEntry);
|
|
16659
16924
|
result.set(normalizeSkillName(name), {
|
|
16660
|
-
source:
|
|
16661
|
-
trust:
|
|
16925
|
+
source: readString15(entry.source) ?? "hub",
|
|
16926
|
+
trust: readString15(entry.trust_level) ?? null
|
|
16662
16927
|
});
|
|
16663
16928
|
}
|
|
16664
16929
|
return result;
|
|
@@ -16709,7 +16974,7 @@ function compareCategoryNames(left, right) {
|
|
|
16709
16974
|
async function readHermesConfigDocument2(configPath) {
|
|
16710
16975
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
16711
16976
|
(error) => {
|
|
16712
|
-
if (
|
|
16977
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16713
16978
|
return null;
|
|
16714
16979
|
}
|
|
16715
16980
|
throw error;
|
|
@@ -16718,7 +16983,7 @@ async function readHermesConfigDocument2(configPath) {
|
|
|
16718
16983
|
const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
|
|
16719
16984
|
return {
|
|
16720
16985
|
document,
|
|
16721
|
-
config:
|
|
16986
|
+
config: toRecord14(document.toJSON()),
|
|
16722
16987
|
existingRaw
|
|
16723
16988
|
};
|
|
16724
16989
|
}
|
|
@@ -16740,21 +17005,21 @@ function readStringList3(value) {
|
|
|
16740
17005
|
}
|
|
16741
17006
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
16742
17007
|
}
|
|
16743
|
-
function
|
|
17008
|
+
function readString15(value) {
|
|
16744
17009
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16745
17010
|
}
|
|
16746
|
-
function
|
|
17011
|
+
function toRecord14(value) {
|
|
16747
17012
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
16748
17013
|
}
|
|
16749
17014
|
function ensureRecord3(target, key) {
|
|
16750
|
-
const current =
|
|
17015
|
+
const current = toRecord14(target[key]);
|
|
16751
17016
|
if (current === target[key]) {
|
|
16752
17017
|
return current;
|
|
16753
17018
|
}
|
|
16754
17019
|
target[key] = current;
|
|
16755
17020
|
return current;
|
|
16756
17021
|
}
|
|
16757
|
-
function
|
|
17022
|
+
function isNodeError15(error, code) {
|
|
16758
17023
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16759
17024
|
}
|
|
16760
17025
|
|
|
@@ -17057,7 +17322,7 @@ function registerRunRoutes(router, options) {
|
|
|
17057
17322
|
router.post("/api/v1/runs", async (ctx) => {
|
|
17058
17323
|
await authenticateRequest(ctx, paths);
|
|
17059
17324
|
const body = await readJsonBody(ctx.req);
|
|
17060
|
-
const input =
|
|
17325
|
+
const input = readString13(body, "input");
|
|
17061
17326
|
if (!input) {
|
|
17062
17327
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
17063
17328
|
}
|
|
@@ -17065,11 +17330,11 @@ function registerRunRoutes(router, options) {
|
|
|
17065
17330
|
ctx.body = await createHermesRun(
|
|
17066
17331
|
{
|
|
17067
17332
|
input,
|
|
17068
|
-
instructions:
|
|
17333
|
+
instructions: readString13(body, "instructions") ?? void 0,
|
|
17069
17334
|
conversation_history: readConversationHistory(
|
|
17070
17335
|
body.conversation_history ?? body.conversationHistory
|
|
17071
17336
|
),
|
|
17072
|
-
session_id:
|
|
17337
|
+
session_id: readString13(body, "session_id") ?? readString13(body, "sessionId") ?? void 0
|
|
17073
17338
|
},
|
|
17074
17339
|
{ logger, profileName: readOptionalProfileName(body) }
|
|
17075
17340
|
);
|
|
@@ -17462,24 +17727,24 @@ async function readRemoteRelease(options, now) {
|
|
|
17462
17727
|
}
|
|
17463
17728
|
}
|
|
17464
17729
|
function normalizeServerReleaseSnapshot(payload) {
|
|
17465
|
-
const snapshot =
|
|
17730
|
+
const snapshot = toRecord15(payload);
|
|
17466
17731
|
const remote = toNullableRecord(snapshot.remote);
|
|
17467
17732
|
return {
|
|
17468
17733
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
17469
|
-
cacheState:
|
|
17470
|
-
issue:
|
|
17734
|
+
cacheState: readString16(snapshot, "cache_state") ?? readString16(snapshot, "cacheState"),
|
|
17735
|
+
issue: readString16(snapshot, "issue")
|
|
17471
17736
|
};
|
|
17472
17737
|
}
|
|
17473
17738
|
function normalizeServerRelease(payload) {
|
|
17474
|
-
const tag =
|
|
17475
|
-
const name =
|
|
17739
|
+
const tag = readString16(payload, "tag");
|
|
17740
|
+
const name = readString16(payload, "name");
|
|
17476
17741
|
return {
|
|
17477
|
-
version:
|
|
17742
|
+
version: readString16(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
17478
17743
|
tag,
|
|
17479
17744
|
name,
|
|
17480
|
-
releaseUrl:
|
|
17481
|
-
publishedAt:
|
|
17482
|
-
fetchedAt:
|
|
17745
|
+
releaseUrl: readString16(payload, "releaseUrl") ?? readString16(payload, "release_url"),
|
|
17746
|
+
publishedAt: readString16(payload, "publishedAt") ?? readString16(payload, "published_at"),
|
|
17747
|
+
fetchedAt: readString16(payload, "fetchedAt") ?? readString16(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
17483
17748
|
};
|
|
17484
17749
|
}
|
|
17485
17750
|
async function readReleaseCache(paths) {
|
|
@@ -17551,7 +17816,7 @@ function compareSemver2(left, right) {
|
|
|
17551
17816
|
}
|
|
17552
17817
|
return 0;
|
|
17553
17818
|
}
|
|
17554
|
-
function
|
|
17819
|
+
function toRecord15(value) {
|
|
17555
17820
|
return typeof value === "object" && value !== null ? value : {};
|
|
17556
17821
|
}
|
|
17557
17822
|
function toNullableRecord(value) {
|
|
@@ -17597,7 +17862,7 @@ function isRecentRunningState2(state) {
|
|
|
17597
17862
|
const startedAt = Date.parse(state.started_at);
|
|
17598
17863
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
17599
17864
|
}
|
|
17600
|
-
function
|
|
17865
|
+
function readString16(payload, key) {
|
|
17601
17866
|
const value = payload[key];
|
|
17602
17867
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17603
17868
|
}
|
|
@@ -19107,21 +19372,21 @@ async function readRemoteLinkPolicy(options) {
|
|
|
19107
19372
|
}
|
|
19108
19373
|
}
|
|
19109
19374
|
function normalizeServerSnapshot(payload) {
|
|
19110
|
-
const snapshot =
|
|
19375
|
+
const snapshot = toRecord16(payload);
|
|
19111
19376
|
const policy = toNullableRecord2(snapshot.policy);
|
|
19112
19377
|
if (!policy) {
|
|
19113
19378
|
return {
|
|
19114
19379
|
remote: null,
|
|
19115
|
-
issue:
|
|
19380
|
+
issue: readString17(snapshot, "issue")
|
|
19116
19381
|
};
|
|
19117
19382
|
}
|
|
19118
19383
|
const release = toNullableRecord2(snapshot.release);
|
|
19119
|
-
const currentVersion =
|
|
19120
|
-
const minSafeVersion =
|
|
19384
|
+
const currentVersion = readString17(policy, "current_version") ?? readString17(policy, "currentVersion");
|
|
19385
|
+
const minSafeVersion = readString17(policy, "min_safe_version") ?? readString17(policy, "minSafeVersion");
|
|
19121
19386
|
if (!currentVersion) {
|
|
19122
19387
|
return {
|
|
19123
19388
|
remote: null,
|
|
19124
|
-
issue:
|
|
19389
|
+
issue: readString17(snapshot, "issue")
|
|
19125
19390
|
};
|
|
19126
19391
|
}
|
|
19127
19392
|
return {
|
|
@@ -19129,10 +19394,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
19129
19394
|
current_version: currentVersion,
|
|
19130
19395
|
min_safe_version: minSafeVersion,
|
|
19131
19396
|
target_version: currentVersion,
|
|
19132
|
-
release_url: release ?
|
|
19133
|
-
published_at: release ?
|
|
19397
|
+
release_url: release ? readString17(release, "release_url") ?? readString17(release, "releaseUrl") : null,
|
|
19398
|
+
published_at: release ? readString17(release, "published_at") ?? readString17(release, "publishedAt") : null
|
|
19134
19399
|
},
|
|
19135
|
-
issue:
|
|
19400
|
+
issue: readString17(snapshot, "issue")
|
|
19136
19401
|
};
|
|
19137
19402
|
}
|
|
19138
19403
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
|
|
@@ -19246,13 +19511,13 @@ function isProcessAlive4(pid) {
|
|
|
19246
19511
|
return false;
|
|
19247
19512
|
}
|
|
19248
19513
|
}
|
|
19249
|
-
function
|
|
19514
|
+
function toRecord16(value) {
|
|
19250
19515
|
return typeof value === "object" && value !== null ? value : {};
|
|
19251
19516
|
}
|
|
19252
19517
|
function toNullableRecord2(value) {
|
|
19253
19518
|
return typeof value === "object" && value !== null ? value : null;
|
|
19254
19519
|
}
|
|
19255
|
-
function
|
|
19520
|
+
function readString17(payload, key) {
|
|
19256
19521
|
const value = payload[key];
|
|
19257
19522
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19258
19523
|
}
|
|
@@ -19681,8 +19946,8 @@ function registerSystemRoutes(router, options) {
|
|
|
19681
19946
|
});
|
|
19682
19947
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
19683
19948
|
const body = await readJsonBody(ctx.req);
|
|
19684
|
-
const sessionId =
|
|
19685
|
-
const claimToken =
|
|
19949
|
+
const sessionId = readString13(body, "session_id") ?? readString13(body, "sessionId");
|
|
19950
|
+
const claimToken = readString13(body, "claim_token") ?? readString13(body, "claimToken");
|
|
19686
19951
|
if (!sessionId || !claimToken) {
|
|
19687
19952
|
throw new LinkHttpError(
|
|
19688
19953
|
400,
|
|
@@ -19693,10 +19958,10 @@ function registerSystemRoutes(router, options) {
|
|
|
19693
19958
|
const claimed = await claimPairing({
|
|
19694
19959
|
sessionId,
|
|
19695
19960
|
claimToken,
|
|
19696
|
-
deviceLabel:
|
|
19697
|
-
devicePlatform:
|
|
19698
|
-
deviceModel:
|
|
19699
|
-
appInstanceId:
|
|
19961
|
+
deviceLabel: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
|
|
19962
|
+
devicePlatform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
|
|
19963
|
+
deviceModel: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
|
|
19964
|
+
appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
|
|
19700
19965
|
paths
|
|
19701
19966
|
});
|
|
19702
19967
|
ctx.body = claimed;
|
|
@@ -19771,9 +20036,9 @@ function registerSystemRoutes(router, options) {
|
|
|
19771
20036
|
const body = await readJsonBody(ctx.req);
|
|
19772
20037
|
const session = await createDeviceSession(
|
|
19773
20038
|
{
|
|
19774
|
-
label:
|
|
19775
|
-
platform:
|
|
19776
|
-
model:
|
|
20039
|
+
label: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
|
|
20040
|
+
platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
|
|
20041
|
+
model: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
|
|
19777
20042
|
appInstanceId: auth.appInstanceId
|
|
19778
20043
|
},
|
|
19779
20044
|
paths
|
|
@@ -19802,7 +20067,7 @@ function registerSystemRoutes(router, options) {
|
|
|
19802
20067
|
});
|
|
19803
20068
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
19804
20069
|
const body = await readJsonBody(ctx.req);
|
|
19805
|
-
const refreshToken =
|
|
20070
|
+
const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
|
|
19806
20071
|
if (!refreshToken) {
|
|
19807
20072
|
throw new LinkHttpError(
|
|
19808
20073
|
400,
|
|
@@ -19813,10 +20078,10 @@ function registerSystemRoutes(router, options) {
|
|
|
19813
20078
|
const session = await refreshDeviceSession(
|
|
19814
20079
|
refreshToken,
|
|
19815
20080
|
{
|
|
19816
|
-
appInstanceId:
|
|
19817
|
-
label:
|
|
19818
|
-
platform:
|
|
19819
|
-
model:
|
|
20081
|
+
appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
|
|
20082
|
+
label: readString13(body, "device_label") ?? readString13(body, "deviceLabel"),
|
|
20083
|
+
platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform"),
|
|
20084
|
+
model: readString13(body, "device_model") ?? readString13(body, "deviceModel")
|
|
19820
20085
|
},
|
|
19821
20086
|
paths
|
|
19822
20087
|
);
|
|
@@ -19835,7 +20100,7 @@ function registerSystemRoutes(router, options) {
|
|
|
19835
20100
|
});
|
|
19836
20101
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
19837
20102
|
const body = await readJsonBody(ctx.req);
|
|
19838
|
-
const refreshToken =
|
|
20103
|
+
const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
|
|
19839
20104
|
if (refreshToken) {
|
|
19840
20105
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
19841
20106
|
}
|
|
@@ -20060,7 +20325,7 @@ function registerSystemRoutes(router, options) {
|
|
|
20060
20325
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
20061
20326
|
const auth = await authenticateRequest(ctx, paths);
|
|
20062
20327
|
const body = await readJsonBody(ctx.req);
|
|
20063
|
-
const label =
|
|
20328
|
+
const label = readString13(body, "label") ?? readString13(body, "device_label");
|
|
20064
20329
|
if (!label) {
|
|
20065
20330
|
throw new LinkHttpError(
|
|
20066
20331
|
400,
|
|
@@ -20104,7 +20369,7 @@ function isActiveCronJob(job) {
|
|
|
20104
20369
|
if (!enabled) {
|
|
20105
20370
|
return false;
|
|
20106
20371
|
}
|
|
20107
|
-
const state =
|
|
20372
|
+
const state = readString13(job, "state")?.toLowerCase();
|
|
20108
20373
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
20109
20374
|
}
|
|
20110
20375
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -20198,7 +20463,7 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
20198
20463
|
ctx.body = await startLinkUpdate({
|
|
20199
20464
|
paths,
|
|
20200
20465
|
logger,
|
|
20201
|
-
targetVersion:
|
|
20466
|
+
targetVersion: readString13(body, "target_version") ?? readString13(body, "targetVersion")
|
|
20202
20467
|
});
|
|
20203
20468
|
});
|
|
20204
20469
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -20232,7 +20497,7 @@ import QRCode from "qrcode";
|
|
|
20232
20497
|
function registerPairingRoutes(router, options) {
|
|
20233
20498
|
const { paths } = options;
|
|
20234
20499
|
router.get("/pair", async (ctx) => {
|
|
20235
|
-
const sessionId =
|
|
20500
|
+
const sessionId = readString13(ctx.query, "session_id");
|
|
20236
20501
|
if (!sessionId) {
|
|
20237
20502
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20238
20503
|
}
|
|
@@ -20257,7 +20522,7 @@ function registerPairingRoutes(router, options) {
|
|
|
20257
20522
|
ctx.body = page;
|
|
20258
20523
|
});
|
|
20259
20524
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
20260
|
-
const sessionId =
|
|
20525
|
+
const sessionId = readString13(ctx.query, "session_id");
|
|
20261
20526
|
if (!sessionId) {
|
|
20262
20527
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20263
20528
|
}
|
|
@@ -20656,6 +20921,37 @@ function formatDate(value) {
|
|
|
20656
20921
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
|
|
20657
20922
|
}
|
|
20658
20923
|
|
|
20924
|
+
// src/http/routes/internal.ts
|
|
20925
|
+
function registerInternalRoutes(router, options) {
|
|
20926
|
+
router.post("/internal/deliver", async (ctx) => {
|
|
20927
|
+
assertLoopbackRequest(ctx.req);
|
|
20928
|
+
const body = await readJsonBody(ctx.req);
|
|
20929
|
+
const stagingDir = readString13(body, "staging_dir") ?? readString13(body, "stagingDir");
|
|
20930
|
+
if (!stagingDir) {
|
|
20931
|
+
throw new LinkHttpError(
|
|
20932
|
+
400,
|
|
20933
|
+
"delivery_staging_required",
|
|
20934
|
+
"delivery staging directory is required"
|
|
20935
|
+
);
|
|
20936
|
+
}
|
|
20937
|
+
ctx.body = {
|
|
20938
|
+
ok: true,
|
|
20939
|
+
...await options.conversations.deliverStagedFiles(stagingDir)
|
|
20940
|
+
};
|
|
20941
|
+
});
|
|
20942
|
+
}
|
|
20943
|
+
function assertLoopbackRequest(request) {
|
|
20944
|
+
const address = request.socket.remoteAddress;
|
|
20945
|
+
if (address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1") {
|
|
20946
|
+
return;
|
|
20947
|
+
}
|
|
20948
|
+
throw new LinkHttpError(
|
|
20949
|
+
403,
|
|
20950
|
+
"internal_route_forbidden",
|
|
20951
|
+
"internal route is only available on loopback"
|
|
20952
|
+
);
|
|
20953
|
+
}
|
|
20954
|
+
|
|
20659
20955
|
// src/http/app.ts
|
|
20660
20956
|
async function createApp(options = {}) {
|
|
20661
20957
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
@@ -20685,6 +20981,7 @@ async function createApp(options = {}) {
|
|
|
20685
20981
|
logger,
|
|
20686
20982
|
onPairingClaimed: options.onPairingClaimed
|
|
20687
20983
|
});
|
|
20984
|
+
registerInternalRoutes(router, { conversations });
|
|
20688
20985
|
registerPairingRoutes(router, { paths });
|
|
20689
20986
|
registerHermesUpdateRoutes(router, { paths, logger });
|
|
20690
20987
|
registerLinkUpdateRoutes(router, { paths, logger });
|
|
@@ -20714,11 +21011,13 @@ export {
|
|
|
20714
21011
|
ensureHermesApiServerConfig,
|
|
20715
21012
|
LinkHttpError,
|
|
20716
21013
|
resolveRuntimePaths,
|
|
21014
|
+
createFileLogger,
|
|
20717
21015
|
getLinkLogFile,
|
|
20718
21016
|
ensureHermesApiServerAvailable,
|
|
20719
21017
|
loadConfig,
|
|
20720
21018
|
saveConfig,
|
|
20721
21019
|
normalizeLanHost,
|
|
21020
|
+
ConversationService,
|
|
20722
21021
|
loadIdentity,
|
|
20723
21022
|
ensureIdentity,
|
|
20724
21023
|
getIdentityStatus,
|