@hermespilot/link 0.3.4 → 0.3.5
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-FPMMWYXK.js → chunk-ZQO7TU7G.js} +1713 -488
- package/dist/cli/index.js +1 -1
- package/dist/http/app.d.ts +18 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import Router from "@koa/router";
|
|
|
4
4
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
|
-
import { randomUUID as
|
|
7
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
8
8
|
|
|
9
9
|
// src/database/link-database.ts
|
|
10
10
|
import { mkdir } from "fs/promises";
|
|
@@ -3637,7 +3637,7 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
3637
3637
|
mtimeMs: fileStat.mtimeMs
|
|
3638
3638
|
});
|
|
3639
3639
|
}
|
|
3640
|
-
return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path:
|
|
3640
|
+
return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path: path25, mtime }) => ({ path: path25, mtime }));
|
|
3641
3641
|
}
|
|
3642
3642
|
async function readCronOutput(outputPath) {
|
|
3643
3643
|
const content = await readFile3(outputPath, "utf8");
|
|
@@ -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.5";
|
|
3728
3728
|
var LINK_COMMAND = "hermeslink";
|
|
3729
3729
|
var LINK_DEFAULT_PORT = 52379;
|
|
3730
3730
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -8485,40 +8485,1198 @@ var ConversationStore = class {
|
|
|
8485
8485
|
}
|
|
8486
8486
|
return manifest;
|
|
8487
8487
|
}
|
|
8488
|
-
async isConversationActive(conversationId) {
|
|
8489
|
-
const manifest = await this.readManifest(conversationId).catch(() => null);
|
|
8490
|
-
return manifest?.status === "active";
|
|
8488
|
+
async isConversationActive(conversationId) {
|
|
8489
|
+
const manifest = await this.readManifest(conversationId).catch(() => null);
|
|
8490
|
+
return manifest?.status === "active";
|
|
8491
|
+
}
|
|
8492
|
+
removeConversationAttachments(conversationId) {
|
|
8493
|
+
return rm5(path12.join(this.conversationDir(conversationId), "attachments"), {
|
|
8494
|
+
recursive: true,
|
|
8495
|
+
force: true
|
|
8496
|
+
});
|
|
8497
|
+
}
|
|
8498
|
+
conversationDir(conversationId) {
|
|
8499
|
+
assertValidConversationId(conversationId);
|
|
8500
|
+
return path12.join(this.paths.conversationsDir, conversationId);
|
|
8501
|
+
}
|
|
8502
|
+
manifestPath(conversationId) {
|
|
8503
|
+
return path12.join(this.conversationDir(conversationId), "manifest.json");
|
|
8504
|
+
}
|
|
8505
|
+
snapshotPath(conversationId) {
|
|
8506
|
+
return path12.join(this.conversationDir(conversationId), "snapshot.json");
|
|
8507
|
+
}
|
|
8508
|
+
eventsPath(conversationId) {
|
|
8509
|
+
return path12.join(this.conversationDir(conversationId), "events.ndjson");
|
|
8510
|
+
}
|
|
8511
|
+
};
|
|
8512
|
+
function createEmptySnapshot2() {
|
|
8513
|
+
return { schema_version: 1, messages: [], runs: [] };
|
|
8514
|
+
}
|
|
8515
|
+
function isNodeError8(error, code) {
|
|
8516
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8517
|
+
}
|
|
8518
|
+
|
|
8519
|
+
// src/conversations/hermes-session-sync.ts
|
|
8520
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
8521
|
+
import { readdir as readdir5, readFile as readFile8, stat as stat7 } from "fs/promises";
|
|
8522
|
+
import { createRequire as createRequire3 } from "module";
|
|
8523
|
+
import os4 from "os";
|
|
8524
|
+
import path13 from "path";
|
|
8525
|
+
var nodeRequire3 = createRequire3(import.meta.url);
|
|
8526
|
+
var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
8527
|
+
var DEFAULT_PROFILE_NAME = "default";
|
|
8528
|
+
var MAX_IMPORTABLE_SESSIONS = 100;
|
|
8529
|
+
var HIDDEN_SESSION_SOURCES = /* @__PURE__ */ new Set(["tool"]);
|
|
8530
|
+
var HERMES_IMPORT_PROJECTION_VERSION = "turn_blocks_v2";
|
|
8531
|
+
var MESSAGE_COLUMNS = [
|
|
8532
|
+
"id",
|
|
8533
|
+
"session_id",
|
|
8534
|
+
"role",
|
|
8535
|
+
"content",
|
|
8536
|
+
"tool_call_id",
|
|
8537
|
+
"tool_calls",
|
|
8538
|
+
"tool_name",
|
|
8539
|
+
"timestamp",
|
|
8540
|
+
"token_count",
|
|
8541
|
+
"finish_reason",
|
|
8542
|
+
"reasoning",
|
|
8543
|
+
"reasoning_content",
|
|
8544
|
+
"reasoning_details",
|
|
8545
|
+
"codex_reasoning_items"
|
|
8546
|
+
];
|
|
8547
|
+
async function syncHermesSessionsIntoConversations(paths, logger, options = {}) {
|
|
8548
|
+
const maxImports = options.maxImports ?? MAX_IMPORTABLE_SESSIONS;
|
|
8549
|
+
const store = new ConversationStore(paths);
|
|
8550
|
+
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
8551
|
+
const profileNames = await discoverHermesProfileNames();
|
|
8552
|
+
const result = {
|
|
8553
|
+
scanned_profiles: profileNames.length,
|
|
8554
|
+
scanned_sessions: 0,
|
|
8555
|
+
eligible_sessions: 0,
|
|
8556
|
+
imported_count: 0,
|
|
8557
|
+
reprojected_count: 0,
|
|
8558
|
+
skipped_existing: 0,
|
|
8559
|
+
skipped_hidden: 0,
|
|
8560
|
+
skipped_deleted: 0,
|
|
8561
|
+
skipped_over_limit: 0,
|
|
8562
|
+
errors: []
|
|
8563
|
+
};
|
|
8564
|
+
const candidates = [];
|
|
8565
|
+
for (const profileName of profileNames) {
|
|
8566
|
+
const profileDir = resolveHermesProfileDir(profileName);
|
|
8567
|
+
const dbPath = path13.join(profileDir, "state.db");
|
|
8568
|
+
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
8569
|
+
result.errors.push({
|
|
8570
|
+
profile: profileName,
|
|
8571
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8572
|
+
});
|
|
8573
|
+
return [];
|
|
8574
|
+
});
|
|
8575
|
+
result.scanned_sessions += sessions.length;
|
|
8576
|
+
for (const session of sessions) {
|
|
8577
|
+
if (isDeletedSession(session)) {
|
|
8578
|
+
result.skipped_deleted += 1;
|
|
8579
|
+
continue;
|
|
8580
|
+
}
|
|
8581
|
+
if (isHiddenSession(session)) {
|
|
8582
|
+
result.skipped_hidden += 1;
|
|
8583
|
+
continue;
|
|
8584
|
+
}
|
|
8585
|
+
result.eligible_sessions += 1;
|
|
8586
|
+
candidates.push({ profileName, profileDir, dbPath, session });
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
8589
|
+
candidates.sort((left, right) => {
|
|
8590
|
+
const rightTime = readNumber2(right.session.last_active) ?? 0;
|
|
8591
|
+
const leftTime = readNumber2(left.session.last_active) ?? 0;
|
|
8592
|
+
return rightTime - leftTime;
|
|
8593
|
+
});
|
|
8594
|
+
const importableCandidates = candidates.slice(0, maxImports);
|
|
8595
|
+
result.skipped_over_limit = Math.max(0, candidates.length - maxImports);
|
|
8596
|
+
for (const candidate of importableCandidates) {
|
|
8597
|
+
if (knownHermesSessions.ids.has(candidate.session.id)) {
|
|
8598
|
+
result.skipped_existing += 1;
|
|
8599
|
+
const reprojected = await reprojectExistingHermesConversation({
|
|
8600
|
+
paths,
|
|
8601
|
+
store,
|
|
8602
|
+
candidate,
|
|
8603
|
+
conversationIds: knownHermesSessions.conversationIdsBySessionId.get(
|
|
8604
|
+
candidate.session.id
|
|
8605
|
+
) ?? []
|
|
8606
|
+
}).catch((error) => {
|
|
8607
|
+
result.errors.push({
|
|
8608
|
+
profile: candidate.profileName,
|
|
8609
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8610
|
+
});
|
|
8611
|
+
return false;
|
|
8612
|
+
});
|
|
8613
|
+
if (reprojected) {
|
|
8614
|
+
result.reprojected_count += 1;
|
|
8615
|
+
}
|
|
8616
|
+
continue;
|
|
8617
|
+
}
|
|
8618
|
+
const imported = await importHermesSession({
|
|
8619
|
+
paths,
|
|
8620
|
+
store,
|
|
8621
|
+
candidate,
|
|
8622
|
+
existingHermesSessionIds: knownHermesSessions.ids
|
|
8623
|
+
}).catch((error) => {
|
|
8624
|
+
result.errors.push({
|
|
8625
|
+
profile: candidate.profileName,
|
|
8626
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8627
|
+
});
|
|
8628
|
+
return false;
|
|
8629
|
+
});
|
|
8630
|
+
if (imported) {
|
|
8631
|
+
result.imported_count += 1;
|
|
8632
|
+
}
|
|
8633
|
+
}
|
|
8634
|
+
if (result.imported_count > 0 || result.reprojected_count > 0 || result.errors.length > 0) {
|
|
8635
|
+
void logger.info("hermes_session_sync_completed", { ...result });
|
|
8636
|
+
} else {
|
|
8637
|
+
void logger.debug("hermes_session_sync_completed", { ...result });
|
|
8638
|
+
}
|
|
8639
|
+
return result;
|
|
8640
|
+
}
|
|
8641
|
+
async function importHermesSession(input) {
|
|
8642
|
+
const { paths, store, candidate, existingHermesSessionIds } = input;
|
|
8643
|
+
const profile = await resolveConversationProfileTarget(
|
|
8644
|
+
paths,
|
|
8645
|
+
candidate.profileName
|
|
8646
|
+
);
|
|
8647
|
+
const sessionId = candidate.session.id;
|
|
8648
|
+
const messages = await readHermesSessionMessages(candidate);
|
|
8649
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8650
|
+
const createdAt = isoFromHermesTime(candidate.session.started_at) ?? now;
|
|
8651
|
+
const updatedAt = isoFromHermesTime(candidate.session.last_active) ?? isoFromHermesTime(messages.at(-1)?.timestamp) ?? createdAt;
|
|
8652
|
+
const conversationId = createConversationId();
|
|
8653
|
+
const snapshot = {
|
|
8654
|
+
schema_version: 1,
|
|
8655
|
+
messages: toLinkMessages({
|
|
8656
|
+
conversationId,
|
|
8657
|
+
profileName: profile.profileName,
|
|
8658
|
+
profileUid: profile.profileUid,
|
|
8659
|
+
profileDisplayName: profile.profileDisplayName,
|
|
8660
|
+
sessionId,
|
|
8661
|
+
messages
|
|
8662
|
+
}),
|
|
8663
|
+
runs: []
|
|
8664
|
+
};
|
|
8665
|
+
const title = readString8(candidate.session, "title") ?? firstUserText(snapshot);
|
|
8666
|
+
const manifest = {
|
|
8667
|
+
id: conversationId,
|
|
8668
|
+
schema_version: 1,
|
|
8669
|
+
kind: "direct",
|
|
8670
|
+
title: normalizeTitle(title),
|
|
8671
|
+
title_source: title ? "hermes" : "default",
|
|
8672
|
+
status: "active",
|
|
8673
|
+
hermes_session_id: sessionId,
|
|
8674
|
+
hermes_session_ids: [sessionId],
|
|
8675
|
+
profile_uid: profile.profileUid,
|
|
8676
|
+
profile_name_snapshot: profile.profileName,
|
|
8677
|
+
profile: profile.profileName,
|
|
8678
|
+
created_at: createdAt,
|
|
8679
|
+
updated_at: updatedAt,
|
|
8680
|
+
last_event_seq: 0
|
|
8681
|
+
};
|
|
8682
|
+
await store.createConversation(manifest, snapshot);
|
|
8683
|
+
await store.appendEvent(conversationId, {
|
|
8684
|
+
type: "conversation.created",
|
|
8685
|
+
payload: {
|
|
8686
|
+
imported_from: "hermes",
|
|
8687
|
+
hermes_session_id: sessionId,
|
|
8688
|
+
profile: {
|
|
8689
|
+
uid: profile.profileUid,
|
|
8690
|
+
name: profile.profileName,
|
|
8691
|
+
display_name: profile.profileDisplayName,
|
|
8692
|
+
avatar_url: profile.profileAvatarUrl
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
});
|
|
8696
|
+
for (const message of snapshot.messages) {
|
|
8697
|
+
await store.appendEvent(conversationId, {
|
|
8698
|
+
type: "message.created",
|
|
8699
|
+
message_id: message.id,
|
|
8700
|
+
payload: { message, imported_from: "hermes" },
|
|
8701
|
+
raw: message.raw
|
|
8702
|
+
});
|
|
8703
|
+
}
|
|
8704
|
+
const stats = buildConversationStats(
|
|
8705
|
+
await store.readManifest(conversationId),
|
|
8706
|
+
snapshot
|
|
8707
|
+
);
|
|
8708
|
+
await store.writeManifest({
|
|
8709
|
+
...await store.readManifest(conversationId),
|
|
8710
|
+
stats
|
|
8711
|
+
});
|
|
8712
|
+
await upsertConversationStats(
|
|
8713
|
+
paths,
|
|
8714
|
+
toStatsIndexRecord(await store.readManifest(conversationId), stats)
|
|
8715
|
+
);
|
|
8716
|
+
existingHermesSessionIds.add(sessionId);
|
|
8717
|
+
return true;
|
|
8718
|
+
}
|
|
8719
|
+
async function reprojectExistingHermesConversation(input) {
|
|
8720
|
+
let changed = false;
|
|
8721
|
+
for (const conversationId of input.conversationIds) {
|
|
8722
|
+
const manifest = await input.store.readManifest(conversationId).catch(() => null);
|
|
8723
|
+
if (!manifest || manifest.status !== "active") {
|
|
8724
|
+
continue;
|
|
8725
|
+
}
|
|
8726
|
+
const snapshot = await input.store.readSnapshot(conversationId).catch(() => null);
|
|
8727
|
+
if (!snapshot) {
|
|
8728
|
+
continue;
|
|
8729
|
+
}
|
|
8730
|
+
const prefix = collectImportedHermesPrefix(snapshot);
|
|
8731
|
+
if (!prefix?.needsUpgrade || prefix.messages.length === 0) {
|
|
8732
|
+
continue;
|
|
8733
|
+
}
|
|
8734
|
+
const profile = await resolveConversationProfileTarget(
|
|
8735
|
+
input.paths,
|
|
8736
|
+
manifest.profile_name_snapshot ?? manifest.profile ?? input.candidate.profileName
|
|
8737
|
+
);
|
|
8738
|
+
const nextSnapshot = {
|
|
8739
|
+
...snapshot,
|
|
8740
|
+
messages: [
|
|
8741
|
+
...toLinkMessages({
|
|
8742
|
+
conversationId,
|
|
8743
|
+
profileName: profile.profileName,
|
|
8744
|
+
profileUid: profile.profileUid,
|
|
8745
|
+
profileDisplayName: profile.profileDisplayName,
|
|
8746
|
+
sessionId: input.candidate.session.id,
|
|
8747
|
+
messages: prefix.messages
|
|
8748
|
+
}),
|
|
8749
|
+
...snapshot.messages.slice(prefix.endIndex)
|
|
8750
|
+
]
|
|
8751
|
+
};
|
|
8752
|
+
const stats = buildConversationStats(manifest, nextSnapshot);
|
|
8753
|
+
await input.store.writeSnapshot(conversationId, nextSnapshot);
|
|
8754
|
+
await input.store.writeManifest({ ...manifest, stats });
|
|
8755
|
+
await upsertConversationStats(
|
|
8756
|
+
input.paths,
|
|
8757
|
+
toStatsIndexRecord({ ...manifest, stats }, stats)
|
|
8758
|
+
);
|
|
8759
|
+
changed = true;
|
|
8760
|
+
}
|
|
8761
|
+
return changed;
|
|
8762
|
+
}
|
|
8763
|
+
function collectImportedHermesPrefix(snapshot) {
|
|
8764
|
+
const rows = [];
|
|
8765
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8766
|
+
let needsProjectionVersion = false;
|
|
8767
|
+
let hasToolMetadata = false;
|
|
8768
|
+
let endIndex = 0;
|
|
8769
|
+
for (; endIndex < snapshot.messages.length; endIndex += 1) {
|
|
8770
|
+
const message = snapshot.messages[endIndex];
|
|
8771
|
+
if (!isHermesImportedMessage(message)) {
|
|
8772
|
+
break;
|
|
8773
|
+
}
|
|
8774
|
+
if (message.hermes?.import_projection !== HERMES_IMPORT_PROJECTION_VERSION) {
|
|
8775
|
+
needsProjectionVersion = true;
|
|
8776
|
+
}
|
|
8777
|
+
if (message.role === "tool") {
|
|
8778
|
+
hasToolMetadata = true;
|
|
8779
|
+
}
|
|
8780
|
+
for (const row of readHermesRawMessageRows(message.raw)) {
|
|
8781
|
+
appendHermesRowOnce(rows, seen, row);
|
|
8782
|
+
if (hasHermesToolMetadata(row)) {
|
|
8783
|
+
hasToolMetadata = true;
|
|
8784
|
+
}
|
|
8785
|
+
}
|
|
8786
|
+
for (const event of message.agent_events ?? []) {
|
|
8787
|
+
for (const row of readHermesRowsFromAgentEvent(event)) {
|
|
8788
|
+
appendHermesRowOnce(rows, seen, row);
|
|
8789
|
+
if (hasHermesToolMetadata(row)) {
|
|
8790
|
+
hasToolMetadata = true;
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
}
|
|
8795
|
+
if (rows.length === 0 || endIndex === 0) {
|
|
8796
|
+
return null;
|
|
8797
|
+
}
|
|
8798
|
+
return {
|
|
8799
|
+
endIndex,
|
|
8800
|
+
messages: rows,
|
|
8801
|
+
needsUpgrade: needsProjectionVersion && hasToolMetadata
|
|
8802
|
+
};
|
|
8803
|
+
}
|
|
8804
|
+
function isHermesImportedMessage(message) {
|
|
8805
|
+
return message.hermes?.imported_from === "hermes" || message.raw?.format === "hermes-message" || message.raw?.format === "hermes-message-group";
|
|
8806
|
+
}
|
|
8807
|
+
function readHermesRowsFromAgentEvent(event) {
|
|
8808
|
+
if (event.raw?.format !== "hermes-message" && event.raw?.format !== "hermes-message-group") {
|
|
8809
|
+
return [];
|
|
8810
|
+
}
|
|
8811
|
+
const payload = toRecord7(event.raw.payload);
|
|
8812
|
+
const message = toRecord7(payload.message);
|
|
8813
|
+
if (normalizeMessageRole(readString8(message, "role") ?? void 0) === "tool") {
|
|
8814
|
+
return [message];
|
|
8815
|
+
}
|
|
8816
|
+
return readHermesRawMessageRows(event.raw).filter(
|
|
8817
|
+
(row) => Boolean(readString8(row, "role"))
|
|
8818
|
+
);
|
|
8819
|
+
}
|
|
8820
|
+
function appendHermesRowOnce(rows, seen, row) {
|
|
8821
|
+
const key = hermesRowKey(row, rows.length);
|
|
8822
|
+
if (seen.has(key)) {
|
|
8823
|
+
return;
|
|
8824
|
+
}
|
|
8825
|
+
seen.add(key);
|
|
8826
|
+
rows.push(row);
|
|
8827
|
+
}
|
|
8828
|
+
function hermesRowKey(row, fallbackIndex) {
|
|
8829
|
+
if (row.id !== void 0 && row.id !== null) {
|
|
8830
|
+
return `id:${row.id}`;
|
|
8831
|
+
}
|
|
8832
|
+
return `fallback:${fallbackIndex}:${row.role ?? ""}:${row.timestamp ?? ""}:${normalizeContent(row.content)}`;
|
|
8833
|
+
}
|
|
8834
|
+
function hasHermesToolMetadata(row) {
|
|
8835
|
+
return normalizeMessageRole(row.role) === "tool" || readHermesToolCalls(row).length > 0 || Boolean(readString8(row, "tool_call_id")) || Boolean(readString8(row, "tool_name"));
|
|
8836
|
+
}
|
|
8837
|
+
function toLinkMessages(input) {
|
|
8838
|
+
const linkMessages = [];
|
|
8839
|
+
let pendingToolCalls = [];
|
|
8840
|
+
const toolCallsById = /* @__PURE__ */ new Map();
|
|
8841
|
+
let currentAssistant = null;
|
|
8842
|
+
const finishAssistantTurn = () => {
|
|
8843
|
+
if (!currentAssistant) {
|
|
8844
|
+
return;
|
|
8845
|
+
}
|
|
8846
|
+
pendingToolCalls.forEach((pending, index) => {
|
|
8847
|
+
const event = projectHermesToolCallWithoutOutputEvent({
|
|
8848
|
+
conversationId: input.conversationId,
|
|
8849
|
+
messageId: pending.message.id,
|
|
8850
|
+
pending,
|
|
8851
|
+
index
|
|
8852
|
+
});
|
|
8853
|
+
if (event) {
|
|
8854
|
+
attachAgentEventToMessage(pending.message, event, event.created_at);
|
|
8855
|
+
}
|
|
8856
|
+
});
|
|
8857
|
+
pendingToolCalls = [];
|
|
8858
|
+
toolCallsById.clear();
|
|
8859
|
+
currentAssistant = null;
|
|
8860
|
+
};
|
|
8861
|
+
input.messages.forEach((message, index) => {
|
|
8862
|
+
const role = normalizeMessageRole(message.role);
|
|
8863
|
+
if (role === "tool") {
|
|
8864
|
+
const pending = consumePendingToolCall({
|
|
8865
|
+
toolMessage: message,
|
|
8866
|
+
pendingToolCalls,
|
|
8867
|
+
toolCallsById
|
|
8868
|
+
});
|
|
8869
|
+
let target = pending?.message ?? currentAssistant;
|
|
8870
|
+
let createdSynthetic = false;
|
|
8871
|
+
if (!target) {
|
|
8872
|
+
target = createSyntheticToolAssistantMessage({
|
|
8873
|
+
...input,
|
|
8874
|
+
message,
|
|
8875
|
+
index
|
|
8876
|
+
});
|
|
8877
|
+
linkMessages.push(target);
|
|
8878
|
+
currentAssistant = target;
|
|
8879
|
+
createdSynthetic = true;
|
|
8880
|
+
}
|
|
8881
|
+
if (!createdSynthetic) {
|
|
8882
|
+
appendHermesRawMessage(target, message);
|
|
8883
|
+
rememberHermesMessageId(target, message);
|
|
8884
|
+
}
|
|
8885
|
+
const event = projectHermesToolCompletedEvent({
|
|
8886
|
+
conversationId: input.conversationId,
|
|
8887
|
+
messageId: target.id,
|
|
8888
|
+
sourceMessage: message,
|
|
8889
|
+
pending,
|
|
8890
|
+
index
|
|
8891
|
+
});
|
|
8892
|
+
if (event) {
|
|
8893
|
+
attachAgentEventToMessage(target, event, event.created_at);
|
|
8894
|
+
}
|
|
8895
|
+
return;
|
|
8896
|
+
}
|
|
8897
|
+
if (role === "assistant") {
|
|
8898
|
+
if (!currentAssistant) {
|
|
8899
|
+
currentAssistant = toLinkMessage({
|
|
8900
|
+
conversationId: input.conversationId,
|
|
8901
|
+
profileName: input.profileName,
|
|
8902
|
+
profileUid: input.profileUid,
|
|
8903
|
+
profileDisplayName: input.profileDisplayName,
|
|
8904
|
+
sessionId: input.sessionId,
|
|
8905
|
+
message,
|
|
8906
|
+
index
|
|
8907
|
+
});
|
|
8908
|
+
linkMessages.push(currentAssistant);
|
|
8909
|
+
} else {
|
|
8910
|
+
appendHermesRawMessage(currentAssistant, message);
|
|
8911
|
+
rememberHermesMessageId(currentAssistant, message);
|
|
8912
|
+
appendAssistantTextToMessage({
|
|
8913
|
+
message: currentAssistant,
|
|
8914
|
+
text: normalizeContent(message.content),
|
|
8915
|
+
updatedAt: isoFromHermesTime(message.timestamp) ?? currentAssistant.updated_at
|
|
8916
|
+
});
|
|
8917
|
+
}
|
|
8918
|
+
for (const toolCall of readHermesToolCalls(message)) {
|
|
8919
|
+
const pending = { message: currentAssistant, toolCall };
|
|
8920
|
+
pendingToolCalls.push(pending);
|
|
8921
|
+
if (toolCall.id) {
|
|
8922
|
+
toolCallsById.set(toolCall.id, pending);
|
|
8923
|
+
}
|
|
8924
|
+
const event = projectHermesToolStartedEvent({
|
|
8925
|
+
conversationId: input.conversationId,
|
|
8926
|
+
messageId: currentAssistant.id,
|
|
8927
|
+
sourceMessage: message,
|
|
8928
|
+
toolCall,
|
|
8929
|
+
index
|
|
8930
|
+
});
|
|
8931
|
+
if (event) {
|
|
8932
|
+
attachAgentEventToMessage(currentAssistant, event, event.created_at);
|
|
8933
|
+
}
|
|
8934
|
+
}
|
|
8935
|
+
return;
|
|
8936
|
+
}
|
|
8937
|
+
finishAssistantTurn();
|
|
8938
|
+
const linkMessage = toLinkMessage({
|
|
8939
|
+
conversationId: input.conversationId,
|
|
8940
|
+
profileName: input.profileName,
|
|
8941
|
+
profileUid: input.profileUid,
|
|
8942
|
+
profileDisplayName: input.profileDisplayName,
|
|
8943
|
+
sessionId: input.sessionId,
|
|
8944
|
+
message,
|
|
8945
|
+
index
|
|
8946
|
+
});
|
|
8947
|
+
linkMessages.push(linkMessage);
|
|
8948
|
+
});
|
|
8949
|
+
finishAssistantTurn();
|
|
8950
|
+
return linkMessages;
|
|
8951
|
+
}
|
|
8952
|
+
function consumePendingToolCall(input) {
|
|
8953
|
+
const toolCallId = readString8(input.toolMessage, "tool_call_id");
|
|
8954
|
+
const toolName = readString8(input.toolMessage, "tool_name");
|
|
8955
|
+
let pending = toolCallId ? input.toolCallsById.get(toolCallId) : void 0;
|
|
8956
|
+
if (!pending && toolName) {
|
|
8957
|
+
pending = input.pendingToolCalls.find(
|
|
8958
|
+
(item) => item.toolCall.name === toolName
|
|
8959
|
+
);
|
|
8960
|
+
}
|
|
8961
|
+
if (!pending && !toolCallId) {
|
|
8962
|
+
pending = input.pendingToolCalls[0];
|
|
8963
|
+
}
|
|
8964
|
+
if (!pending) {
|
|
8965
|
+
return void 0;
|
|
8966
|
+
}
|
|
8967
|
+
const index = input.pendingToolCalls.indexOf(pending);
|
|
8968
|
+
if (index >= 0) {
|
|
8969
|
+
input.pendingToolCalls.splice(index, 1);
|
|
8970
|
+
}
|
|
8971
|
+
if (pending.toolCall.id) {
|
|
8972
|
+
input.toolCallsById.delete(pending.toolCall.id);
|
|
8973
|
+
}
|
|
8974
|
+
return pending;
|
|
8975
|
+
}
|
|
8976
|
+
function createSyntheticToolAssistantMessage(input) {
|
|
8977
|
+
const message = toLinkMessage({
|
|
8978
|
+
conversationId: input.conversationId,
|
|
8979
|
+
profileName: input.profileName,
|
|
8980
|
+
profileUid: input.profileUid,
|
|
8981
|
+
profileDisplayName: input.profileDisplayName,
|
|
8982
|
+
sessionId: input.sessionId,
|
|
8983
|
+
message: {
|
|
8984
|
+
...input.message,
|
|
8985
|
+
role: "assistant",
|
|
8986
|
+
content: null
|
|
8987
|
+
},
|
|
8988
|
+
index: input.index
|
|
8989
|
+
});
|
|
8990
|
+
message.raw = {
|
|
8991
|
+
format: "hermes-message",
|
|
8992
|
+
payload: input.message
|
|
8993
|
+
};
|
|
8994
|
+
return message;
|
|
8995
|
+
}
|
|
8996
|
+
function readHermesToolCalls(message) {
|
|
8997
|
+
const decoded = parseJsonValue(message.tool_calls) ?? message.tool_calls;
|
|
8998
|
+
const values = Array.isArray(decoded) ? decoded : decoded ? [decoded] : [];
|
|
8999
|
+
return values.map((value) => normalizeHermesToolCall(value)).filter(
|
|
9000
|
+
(toolCall) => Boolean(toolCall)
|
|
9001
|
+
);
|
|
9002
|
+
}
|
|
9003
|
+
function normalizeHermesToolCall(value) {
|
|
9004
|
+
const record = toRecord7(value);
|
|
9005
|
+
if (Object.keys(record).length === 0) {
|
|
9006
|
+
return null;
|
|
9007
|
+
}
|
|
9008
|
+
const fn = toRecord7(record.function);
|
|
9009
|
+
const id = readString8(record, "id") ?? readString8(record, "call_id") ?? readString8(record, "tool_call_id") ?? readString8(fn, "id") ?? void 0;
|
|
9010
|
+
const name = readString8(fn, "name") ?? readString8(record, "name") ?? readString8(record, "tool_name") ?? readString8(record, "tool") ?? "tool";
|
|
9011
|
+
const rawArguments = fn.arguments ?? record.arguments ?? record.args ?? record.input;
|
|
9012
|
+
return {
|
|
9013
|
+
...id ? { id } : {},
|
|
9014
|
+
name,
|
|
9015
|
+
...rawArguments === void 0 ? {} : { arguments: parseJsonValue(rawArguments) ?? rawArguments },
|
|
9016
|
+
raw: value
|
|
9017
|
+
};
|
|
9018
|
+
}
|
|
9019
|
+
function projectHermesToolStartedEvent(input) {
|
|
9020
|
+
const createdAt = isoFromHermesTime(input.sourceMessage.timestamp) ?? new Date(Date.now() + input.index).toISOString();
|
|
9021
|
+
return projectHermesAgentEvent({
|
|
9022
|
+
conversationId: input.conversationId,
|
|
9023
|
+
messageId: input.messageId,
|
|
9024
|
+
type: "tool.started",
|
|
9025
|
+
createdAt,
|
|
9026
|
+
seq: input.index + 1,
|
|
9027
|
+
payload: {
|
|
9028
|
+
type: "tool.started",
|
|
9029
|
+
tool: input.toolCall.name,
|
|
9030
|
+
tool_name: input.toolCall.name,
|
|
9031
|
+
name: input.toolCall.name,
|
|
9032
|
+
...input.toolCall.id ? { tool_call_id: input.toolCall.id, id: input.toolCall.id } : {},
|
|
9033
|
+
...input.toolCall.arguments === void 0 ? {} : { arguments: input.toolCall.arguments },
|
|
9034
|
+
tool_call: input.toolCall.raw
|
|
9035
|
+
},
|
|
9036
|
+
raw: {
|
|
9037
|
+
format: "hermes-message",
|
|
9038
|
+
payload: {
|
|
9039
|
+
message: input.sourceMessage,
|
|
9040
|
+
tool_call: input.toolCall.raw
|
|
9041
|
+
}
|
|
9042
|
+
}
|
|
9043
|
+
});
|
|
9044
|
+
}
|
|
9045
|
+
function projectHermesToolCompletedEvent(input) {
|
|
9046
|
+
const createdAt = isoFromHermesTime(input.sourceMessage.timestamp) ?? new Date(Date.now() + input.index).toISOString();
|
|
9047
|
+
const output = normalizeContent(input.sourceMessage.content);
|
|
9048
|
+
const parsedOutput = parseJsonValue(output);
|
|
9049
|
+
const toolCallId = readString8(input.sourceMessage, "tool_call_id") ?? input.pending?.toolCall.id;
|
|
9050
|
+
const toolName = readString8(input.sourceMessage, "tool_name") ?? input.pending?.toolCall.name ?? "tool";
|
|
9051
|
+
return projectHermesAgentEvent({
|
|
9052
|
+
conversationId: input.conversationId,
|
|
9053
|
+
messageId: input.messageId,
|
|
9054
|
+
type: "tool.completed",
|
|
9055
|
+
createdAt,
|
|
9056
|
+
seq: input.index + 1,
|
|
9057
|
+
payload: {
|
|
9058
|
+
type: "tool.completed",
|
|
9059
|
+
tool: toolName,
|
|
9060
|
+
tool_name: toolName,
|
|
9061
|
+
name: toolName,
|
|
9062
|
+
...toolCallId ? { tool_call_id: toolCallId, id: toolCallId } : {},
|
|
9063
|
+
...input.pending?.toolCall.arguments === void 0 ? {} : { arguments: input.pending.toolCall.arguments },
|
|
9064
|
+
output,
|
|
9065
|
+
content: output,
|
|
9066
|
+
result: parsedOutput ?? output,
|
|
9067
|
+
...input.pending ? { tool_call: input.pending.toolCall.raw } : {}
|
|
9068
|
+
},
|
|
9069
|
+
raw: {
|
|
9070
|
+
format: "hermes-message",
|
|
9071
|
+
payload: {
|
|
9072
|
+
message: input.sourceMessage,
|
|
9073
|
+
...input.pending ? { tool_call: input.pending.toolCall.raw } : {}
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
});
|
|
9077
|
+
}
|
|
9078
|
+
function projectHermesToolCallWithoutOutputEvent(input) {
|
|
9079
|
+
return projectHermesAgentEvent({
|
|
9080
|
+
conversationId: input.conversationId,
|
|
9081
|
+
messageId: input.messageId,
|
|
9082
|
+
type: "tool.completed",
|
|
9083
|
+
createdAt: input.pending.message.updated_at,
|
|
9084
|
+
seq: input.index + 1,
|
|
9085
|
+
payload: {
|
|
9086
|
+
type: "tool.completed",
|
|
9087
|
+
tool: input.pending.toolCall.name,
|
|
9088
|
+
tool_name: input.pending.toolCall.name,
|
|
9089
|
+
name: input.pending.toolCall.name,
|
|
9090
|
+
...input.pending.toolCall.id ? {
|
|
9091
|
+
tool_call_id: input.pending.toolCall.id,
|
|
9092
|
+
id: input.pending.toolCall.id
|
|
9093
|
+
} : {},
|
|
9094
|
+
...input.pending.toolCall.arguments === void 0 ? {} : { arguments: input.pending.toolCall.arguments },
|
|
9095
|
+
tool_call: input.pending.toolCall.raw
|
|
9096
|
+
},
|
|
9097
|
+
raw: {
|
|
9098
|
+
format: "hermes-tool-call",
|
|
9099
|
+
payload: {
|
|
9100
|
+
tool_call: input.pending.toolCall.raw,
|
|
9101
|
+
note: "tool_output_not_found"
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
});
|
|
9105
|
+
}
|
|
9106
|
+
function projectHermesAgentEvent(input) {
|
|
9107
|
+
const event = {
|
|
9108
|
+
seq: input.seq,
|
|
9109
|
+
type: input.type,
|
|
9110
|
+
conversation_id: input.conversationId,
|
|
9111
|
+
message_id: input.messageId,
|
|
9112
|
+
created_at: input.createdAt,
|
|
9113
|
+
payload: input.payload,
|
|
9114
|
+
raw: input.raw
|
|
9115
|
+
};
|
|
9116
|
+
return projectConversationAgentEvent(event);
|
|
9117
|
+
}
|
|
9118
|
+
function attachAgentEventToMessage(message, event, updatedAt) {
|
|
9119
|
+
ensureTextBlockForMessage(message);
|
|
9120
|
+
message.agent_events = upsertAgentEventProjection(
|
|
9121
|
+
message.agent_events ?? [],
|
|
9122
|
+
event
|
|
9123
|
+
);
|
|
9124
|
+
appendAgentEventBlock(message, event, updatedAt);
|
|
9125
|
+
message.updated_at = latestTimestamp(message.updated_at, updatedAt);
|
|
9126
|
+
}
|
|
9127
|
+
function appendAssistantTextToMessage(input) {
|
|
9128
|
+
const text = input.text;
|
|
9129
|
+
if (!text) {
|
|
9130
|
+
return;
|
|
9131
|
+
}
|
|
9132
|
+
appendTextPart(input.message, text);
|
|
9133
|
+
if (input.message.blocks?.length) {
|
|
9134
|
+
appendTextBlock(input.message, text, input.updatedAt);
|
|
9135
|
+
}
|
|
9136
|
+
input.message.updated_at = latestTimestamp(
|
|
9137
|
+
input.message.updated_at,
|
|
9138
|
+
input.updatedAt
|
|
9139
|
+
);
|
|
9140
|
+
}
|
|
9141
|
+
function appendTextPart(message, text) {
|
|
9142
|
+
const existing = message.parts.find((part) => part.type === "text");
|
|
9143
|
+
if (existing) {
|
|
9144
|
+
existing.text = joinImportedText(existing.text ?? "", text);
|
|
9145
|
+
return;
|
|
9146
|
+
}
|
|
9147
|
+
message.parts.push({ type: "text", text });
|
|
9148
|
+
}
|
|
9149
|
+
function appendTextBlock(message, text, updatedAt) {
|
|
9150
|
+
const blocks = [...message.blocks ?? []];
|
|
9151
|
+
const last = blocks.at(-1);
|
|
9152
|
+
if (last?.type === "text") {
|
|
9153
|
+
blocks[blocks.length - 1] = {
|
|
9154
|
+
...last,
|
|
9155
|
+
text: joinImportedText(last.text, text),
|
|
9156
|
+
updated_at: updatedAt
|
|
9157
|
+
};
|
|
9158
|
+
} else {
|
|
9159
|
+
blocks.push({
|
|
9160
|
+
id: `text_${blocks.length + 1}`,
|
|
9161
|
+
type: "text",
|
|
9162
|
+
text,
|
|
9163
|
+
created_at: updatedAt,
|
|
9164
|
+
updated_at: updatedAt
|
|
9165
|
+
});
|
|
9166
|
+
}
|
|
9167
|
+
message.blocks = blocks;
|
|
9168
|
+
}
|
|
9169
|
+
function ensureTextBlockForMessage(message) {
|
|
9170
|
+
if (message.blocks?.length) {
|
|
9171
|
+
return;
|
|
9172
|
+
}
|
|
9173
|
+
const text = message.parts.filter((part) => part.type === "text" && part.text).map((part) => part.text).join("");
|
|
9174
|
+
if (!text) {
|
|
9175
|
+
return;
|
|
9176
|
+
}
|
|
9177
|
+
message.blocks = [
|
|
9178
|
+
{
|
|
9179
|
+
id: "text_1",
|
|
9180
|
+
type: "text",
|
|
9181
|
+
text,
|
|
9182
|
+
created_at: message.created_at,
|
|
9183
|
+
updated_at: message.updated_at
|
|
9184
|
+
}
|
|
9185
|
+
];
|
|
9186
|
+
}
|
|
9187
|
+
function appendAgentEventBlock(message, event, updatedAt) {
|
|
9188
|
+
const blocks = [...message.blocks ?? []];
|
|
9189
|
+
const matchingIndex = blocks.findIndex((block) => {
|
|
9190
|
+
if (block.type !== "agent_events") {
|
|
9191
|
+
return false;
|
|
9192
|
+
}
|
|
9193
|
+
return upsertAgentEventProjection(block.events, event).length === block.events.length;
|
|
9194
|
+
});
|
|
9195
|
+
const targetIndex = matchingIndex >= 0 ? matchingIndex : blocks.at(-1)?.type === "agent_events" ? blocks.length - 1 : -1;
|
|
9196
|
+
if (targetIndex >= 0) {
|
|
9197
|
+
const block = blocks[targetIndex];
|
|
9198
|
+
if (block.type === "agent_events") {
|
|
9199
|
+
blocks[targetIndex] = {
|
|
9200
|
+
...block,
|
|
9201
|
+
events: upsertAgentEventProjection(block.events, event),
|
|
9202
|
+
updated_at: updatedAt
|
|
9203
|
+
};
|
|
9204
|
+
}
|
|
9205
|
+
} else {
|
|
9206
|
+
blocks.push({
|
|
9207
|
+
id: `tools_${blocks.length + 1}`,
|
|
9208
|
+
type: "agent_events",
|
|
9209
|
+
events: [event],
|
|
9210
|
+
created_at: updatedAt,
|
|
9211
|
+
updated_at: updatedAt
|
|
9212
|
+
});
|
|
9213
|
+
}
|
|
9214
|
+
message.blocks = blocks;
|
|
9215
|
+
}
|
|
9216
|
+
function latestTimestamp(left, right) {
|
|
9217
|
+
const leftTime = Date.parse(left);
|
|
9218
|
+
const rightTime = Date.parse(right);
|
|
9219
|
+
if (Number.isNaN(leftTime)) {
|
|
9220
|
+
return right;
|
|
9221
|
+
}
|
|
9222
|
+
if (Number.isNaN(rightTime)) {
|
|
9223
|
+
return left;
|
|
9224
|
+
}
|
|
9225
|
+
return leftTime >= rightTime ? left : right;
|
|
9226
|
+
}
|
|
9227
|
+
async function readKnownHermesSessions(store) {
|
|
9228
|
+
const ids = /* @__PURE__ */ new Set();
|
|
9229
|
+
const conversationIdsBySessionId = /* @__PURE__ */ new Map();
|
|
9230
|
+
for (const conversationId of await store.listConversationIds()) {
|
|
9231
|
+
const manifest = await store.readManifest(conversationId).catch(() => null);
|
|
9232
|
+
if (!manifest) {
|
|
9233
|
+
continue;
|
|
9234
|
+
}
|
|
9235
|
+
if (manifest.hermes_session_id) {
|
|
9236
|
+
ids.add(manifest.hermes_session_id);
|
|
9237
|
+
rememberKnownHermesConversation(
|
|
9238
|
+
conversationIdsBySessionId,
|
|
9239
|
+
manifest.hermes_session_id,
|
|
9240
|
+
conversationId
|
|
9241
|
+
);
|
|
9242
|
+
}
|
|
9243
|
+
for (const sessionId of manifest.hermes_session_ids ?? []) {
|
|
9244
|
+
ids.add(sessionId);
|
|
9245
|
+
rememberKnownHermesConversation(
|
|
9246
|
+
conversationIdsBySessionId,
|
|
9247
|
+
sessionId,
|
|
9248
|
+
conversationId
|
|
9249
|
+
);
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
9252
|
+
return { ids, conversationIdsBySessionId };
|
|
9253
|
+
}
|
|
9254
|
+
function rememberKnownHermesConversation(map, sessionId, conversationId) {
|
|
9255
|
+
const current = map.get(sessionId) ?? [];
|
|
9256
|
+
if (!current.includes(conversationId)) {
|
|
9257
|
+
map.set(sessionId, [...current, conversationId]);
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
async function discoverHermesProfileNames() {
|
|
9261
|
+
const names = /* @__PURE__ */ new Set([DEFAULT_PROFILE_NAME]);
|
|
9262
|
+
const profilesDir = path13.join(os4.homedir(), ".hermes", "profiles");
|
|
9263
|
+
const entries = await readdir5(profilesDir, { withFileTypes: true }).catch(
|
|
9264
|
+
(error) => {
|
|
9265
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
9266
|
+
return [];
|
|
9267
|
+
}
|
|
9268
|
+
throw error;
|
|
9269
|
+
}
|
|
9270
|
+
);
|
|
9271
|
+
for (const entry of entries) {
|
|
9272
|
+
if (entry.isDirectory() && PROFILE_NAME_PATTERN3.test(entry.name)) {
|
|
9273
|
+
names.add(entry.name);
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
return [...names].sort((left, right) => {
|
|
9277
|
+
if (left === DEFAULT_PROFILE_NAME) {
|
|
9278
|
+
return -1;
|
|
9279
|
+
}
|
|
9280
|
+
if (right === DEFAULT_PROFILE_NAME) {
|
|
9281
|
+
return 1;
|
|
9282
|
+
}
|
|
9283
|
+
return left.localeCompare(right);
|
|
9284
|
+
});
|
|
9285
|
+
}
|
|
9286
|
+
async function listProfileSessions(dbPath) {
|
|
9287
|
+
if (!await isFile(dbPath)) {
|
|
9288
|
+
return [];
|
|
9289
|
+
}
|
|
9290
|
+
let db = null;
|
|
9291
|
+
try {
|
|
9292
|
+
const { DatabaseSync } = nodeRequire3(
|
|
9293
|
+
"node:sqlite"
|
|
9294
|
+
);
|
|
9295
|
+
db = new DatabaseSync(dbPath, {
|
|
9296
|
+
readOnly: true,
|
|
9297
|
+
timeout: 1e3
|
|
9298
|
+
});
|
|
9299
|
+
const sessionColumns = readTableColumns(db, "sessions");
|
|
9300
|
+
if (!sessionColumns.has("id")) {
|
|
9301
|
+
return [];
|
|
9302
|
+
}
|
|
9303
|
+
const messageColumns = readTableColumns(db, "messages");
|
|
9304
|
+
const selectColumns = [...sessionColumns].map((column) => `s.${quoteIdentifier(column)}`).join(", ");
|
|
9305
|
+
const lastActiveSql = messageColumns.has("timestamp") ? `COALESCE(
|
|
9306
|
+
(SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id),
|
|
9307
|
+
s.started_at,
|
|
9308
|
+
0
|
|
9309
|
+
) AS last_active` : "COALESCE(s.started_at, 0) AS last_active";
|
|
9310
|
+
const rows = db.prepare(
|
|
9311
|
+
`
|
|
9312
|
+
SELECT ${selectColumns}, ${lastActiveSql}
|
|
9313
|
+
FROM sessions s
|
|
9314
|
+
ORDER BY last_active DESC
|
|
9315
|
+
`
|
|
9316
|
+
).all();
|
|
9317
|
+
return projectCompressionTips(rows);
|
|
9318
|
+
} finally {
|
|
9319
|
+
db?.close();
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
function appendHermesRawMessage(message, row) {
|
|
9323
|
+
const rows = readHermesRawMessageRows(message.raw);
|
|
9324
|
+
message.raw = rows.length === 0 ? {
|
|
9325
|
+
format: "hermes-message",
|
|
9326
|
+
payload: row
|
|
9327
|
+
} : {
|
|
9328
|
+
format: "hermes-message-group",
|
|
9329
|
+
payload: { messages: [...rows, row] }
|
|
9330
|
+
};
|
|
9331
|
+
}
|
|
9332
|
+
function readHermesRawMessageRows(raw) {
|
|
9333
|
+
if (!raw) {
|
|
9334
|
+
return [];
|
|
9335
|
+
}
|
|
9336
|
+
if (raw.format === "hermes-message-group") {
|
|
9337
|
+
const payload = toRecord7(raw.payload);
|
|
9338
|
+
return Array.isArray(payload.messages) ? payload.messages.filter(
|
|
9339
|
+
(item) => typeof item === "object" && item !== null
|
|
9340
|
+
) : [];
|
|
9341
|
+
}
|
|
9342
|
+
if (raw.format === "hermes-message") {
|
|
9343
|
+
return typeof raw.payload === "object" && raw.payload !== null ? [raw.payload] : [];
|
|
9344
|
+
}
|
|
9345
|
+
return [];
|
|
9346
|
+
}
|
|
9347
|
+
function rememberHermesMessageId(message, row) {
|
|
9348
|
+
if (row.id === void 0 || row.id === null) {
|
|
9349
|
+
return;
|
|
9350
|
+
}
|
|
9351
|
+
const existing = Array.isArray(message.hermes?.message_ids) ? message.hermes.message_ids : message.hermes?.message_id === void 0 ? [] : [message.hermes.message_id];
|
|
9352
|
+
const id = row.id;
|
|
9353
|
+
message.hermes = {
|
|
9354
|
+
...message.hermes ?? {},
|
|
9355
|
+
message_ids: existing.includes(id) ? existing : [...existing, id]
|
|
9356
|
+
};
|
|
9357
|
+
}
|
|
9358
|
+
function joinImportedText(left, right) {
|
|
9359
|
+
if (!left) {
|
|
9360
|
+
return right;
|
|
9361
|
+
}
|
|
9362
|
+
if (!right) {
|
|
9363
|
+
return left;
|
|
9364
|
+
}
|
|
9365
|
+
if (/\s$/u.test(left) || /^\s/u.test(right)) {
|
|
9366
|
+
return `${left}${right}`;
|
|
9367
|
+
}
|
|
9368
|
+
return `${left}
|
|
9369
|
+
|
|
9370
|
+
${right}`;
|
|
9371
|
+
}
|
|
9372
|
+
function projectCompressionTips(rows) {
|
|
9373
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9374
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
9375
|
+
for (const row of rows) {
|
|
9376
|
+
const id = readString8(row, "id");
|
|
9377
|
+
if (!id) {
|
|
9378
|
+
continue;
|
|
9379
|
+
}
|
|
9380
|
+
byId.set(id, row);
|
|
9381
|
+
const parentId = readString8(row, "parent_session_id");
|
|
9382
|
+
if (parentId) {
|
|
9383
|
+
const children = childrenByParent.get(parentId) ?? [];
|
|
9384
|
+
children.push(row);
|
|
9385
|
+
childrenByParent.set(parentId, children);
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
const projected = [];
|
|
9389
|
+
for (const row of rows) {
|
|
9390
|
+
const id = readString8(row, "id");
|
|
9391
|
+
if (!id || readString8(row, "parent_session_id")) {
|
|
9392
|
+
continue;
|
|
9393
|
+
}
|
|
9394
|
+
let tip = row;
|
|
9395
|
+
const visited = /* @__PURE__ */ new Set([id]);
|
|
9396
|
+
while (readString8(tip, "end_reason") === "compression") {
|
|
9397
|
+
const tipId2 = readString8(tip, "id");
|
|
9398
|
+
if (!tipId2) {
|
|
9399
|
+
break;
|
|
9400
|
+
}
|
|
9401
|
+
const next = (childrenByParent.get(tipId2) ?? []).filter((child) => readString8(child, "id")).sort(
|
|
9402
|
+
(left, right) => (readNumber2(right.last_active) ?? 0) - (readNumber2(left.last_active) ?? 0)
|
|
9403
|
+
)[0];
|
|
9404
|
+
const nextId = next ? readString8(next, "id") : null;
|
|
9405
|
+
if (!next || !nextId || visited.has(nextId)) {
|
|
9406
|
+
break;
|
|
9407
|
+
}
|
|
9408
|
+
tip = next;
|
|
9409
|
+
visited.add(nextId);
|
|
9410
|
+
}
|
|
9411
|
+
const tipId = readString8(tip, "id");
|
|
9412
|
+
if (tipId) {
|
|
9413
|
+
projected.push({
|
|
9414
|
+
...tip,
|
|
9415
|
+
id: tipId,
|
|
9416
|
+
_lineage_root_id: id,
|
|
9417
|
+
started_at: readNumber2(row.started_at) ?? readNumber2(tip.started_at)
|
|
9418
|
+
});
|
|
9419
|
+
}
|
|
9420
|
+
}
|
|
9421
|
+
return projected;
|
|
9422
|
+
}
|
|
9423
|
+
async function readHermesSessionMessages(candidate) {
|
|
9424
|
+
const [dbMessages, jsonlMessages] = await Promise.all([
|
|
9425
|
+
readStateDbMessages(candidate.dbPath, candidate.session.id),
|
|
9426
|
+
readJsonlMessages(candidate.profileName, candidate.session.id)
|
|
9427
|
+
]);
|
|
9428
|
+
return jsonlMessages.length > dbMessages.length ? jsonlMessages : dbMessages;
|
|
9429
|
+
}
|
|
9430
|
+
async function readStateDbMessages(dbPath, sessionId) {
|
|
9431
|
+
if (!await isFile(dbPath)) {
|
|
9432
|
+
return [];
|
|
9433
|
+
}
|
|
9434
|
+
let db = null;
|
|
9435
|
+
try {
|
|
9436
|
+
const { DatabaseSync } = nodeRequire3(
|
|
9437
|
+
"node:sqlite"
|
|
9438
|
+
);
|
|
9439
|
+
db = new DatabaseSync(dbPath, {
|
|
9440
|
+
readOnly: true,
|
|
9441
|
+
timeout: 1e3
|
|
9442
|
+
});
|
|
9443
|
+
const columns = readTableColumns(db, "messages");
|
|
9444
|
+
if (!columns.has("session_id") || !columns.has("role")) {
|
|
9445
|
+
return [];
|
|
9446
|
+
}
|
|
9447
|
+
const selectColumns = MESSAGE_COLUMNS.map(
|
|
9448
|
+
(column) => columns.has(column) ? quoteIdentifier(column) : `NULL AS ${column}`
|
|
9449
|
+
).join(", ");
|
|
9450
|
+
return db.prepare(
|
|
9451
|
+
`
|
|
9452
|
+
SELECT ${selectColumns}
|
|
9453
|
+
FROM messages
|
|
9454
|
+
WHERE session_id = ?
|
|
9455
|
+
ORDER BY timestamp, id
|
|
9456
|
+
`
|
|
9457
|
+
).all(sessionId);
|
|
9458
|
+
} catch {
|
|
9459
|
+
return [];
|
|
9460
|
+
} finally {
|
|
9461
|
+
db?.close();
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
async function readJsonlMessages(profileName, sessionId) {
|
|
9465
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
9466
|
+
return [];
|
|
9467
|
+
}
|
|
9468
|
+
const profileDir = resolveHermesProfileDir(profileName);
|
|
9469
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path13.join(profileDir, "sessions"));
|
|
9470
|
+
const transcriptPath = path13.join(sessionsDir, `${sessionId}.jsonl`);
|
|
9471
|
+
const raw = await readFile8(transcriptPath, "utf8").catch((error) => {
|
|
9472
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
9473
|
+
return "";
|
|
9474
|
+
}
|
|
9475
|
+
throw error;
|
|
9476
|
+
});
|
|
9477
|
+
if (!raw.trim()) {
|
|
9478
|
+
return [];
|
|
9479
|
+
}
|
|
9480
|
+
const rows = [];
|
|
9481
|
+
for (const line of raw.split(/\r?\n/u)) {
|
|
9482
|
+
if (!line.trim()) {
|
|
9483
|
+
continue;
|
|
9484
|
+
}
|
|
9485
|
+
try {
|
|
9486
|
+
const parsed = JSON.parse(line);
|
|
9487
|
+
const normalized = normalizeJsonlMessage(parsed);
|
|
9488
|
+
if (normalized) {
|
|
9489
|
+
rows.push(normalized);
|
|
9490
|
+
}
|
|
9491
|
+
} catch {
|
|
9492
|
+
continue;
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
return rows;
|
|
9496
|
+
}
|
|
9497
|
+
function normalizeJsonlMessage(row) {
|
|
9498
|
+
const role = readString8(row, "role");
|
|
9499
|
+
if (!role) {
|
|
9500
|
+
return null;
|
|
9501
|
+
}
|
|
9502
|
+
const content = normalizeContent(row.content);
|
|
9503
|
+
const timestamp = readNumber2(row.timestamp) ?? readNumber2(row.created_at) ?? readNumber2(row.createdAt);
|
|
9504
|
+
return {
|
|
9505
|
+
...row,
|
|
9506
|
+
role,
|
|
9507
|
+
content,
|
|
9508
|
+
timestamp: timestamp ?? void 0
|
|
9509
|
+
};
|
|
9510
|
+
}
|
|
9511
|
+
function toLinkMessage(input) {
|
|
9512
|
+
const role = normalizeMessageRole(input.message.role);
|
|
9513
|
+
const text = normalizeContent(input.message.content);
|
|
9514
|
+
const createdAt = isoFromHermesTime(input.message.timestamp) ?? new Date(Date.now() + input.index).toISOString();
|
|
9515
|
+
return {
|
|
9516
|
+
id: `msg_${randomUUID6().replaceAll("-", "")}`,
|
|
9517
|
+
schema_version: 1,
|
|
9518
|
+
conversation_id: input.conversationId,
|
|
9519
|
+
role,
|
|
9520
|
+
status: "completed",
|
|
9521
|
+
created_at: createdAt,
|
|
9522
|
+
updated_at: createdAt,
|
|
9523
|
+
sender: senderForRole({
|
|
9524
|
+
role,
|
|
9525
|
+
profileName: input.profileName,
|
|
9526
|
+
profileUid: input.profileUid,
|
|
9527
|
+
profileDisplayName: input.profileDisplayName
|
|
9528
|
+
}),
|
|
9529
|
+
parts: text ? [{ type: "text", text }] : [],
|
|
9530
|
+
attachments: [],
|
|
9531
|
+
hermes: {
|
|
9532
|
+
session_id: input.sessionId,
|
|
9533
|
+
message_id: input.message.id,
|
|
9534
|
+
imported_from: "hermes",
|
|
9535
|
+
import_projection: HERMES_IMPORT_PROJECTION_VERSION
|
|
9536
|
+
},
|
|
9537
|
+
raw: {
|
|
9538
|
+
format: "hermes-message",
|
|
9539
|
+
payload: input.message
|
|
9540
|
+
}
|
|
9541
|
+
};
|
|
9542
|
+
}
|
|
9543
|
+
function senderForRole(input) {
|
|
9544
|
+
switch (input.role) {
|
|
9545
|
+
case "user":
|
|
9546
|
+
return { id: "hermes_user", type: "human", display_name: "Me" };
|
|
9547
|
+
case "assistant":
|
|
9548
|
+
return {
|
|
9549
|
+
id: `agent_${input.profileName}`,
|
|
9550
|
+
type: "agent",
|
|
9551
|
+
display_name: input.profileDisplayName,
|
|
9552
|
+
profile_uid: input.profileUid,
|
|
9553
|
+
profile: input.profileName
|
|
9554
|
+
};
|
|
9555
|
+
case "tool":
|
|
9556
|
+
return { id: "hermes_tool", type: "tool", display_name: "Tool" };
|
|
9557
|
+
case "system":
|
|
9558
|
+
return { id: "hermes_system", type: "system", display_name: "System" };
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
function firstUserText(snapshot) {
|
|
9562
|
+
return snapshot.messages.find((message) => message.role === "user")?.parts.find((part) => part.type === "text")?.text?.slice(0, 80);
|
|
9563
|
+
}
|
|
9564
|
+
function normalizeTitle(value) {
|
|
9565
|
+
const normalized = value?.replace(/\s+/gu, " ").trim();
|
|
9566
|
+
return normalized || DEFAULT_CONVERSATION_TITLE;
|
|
9567
|
+
}
|
|
9568
|
+
function normalizeMessageRole(value) {
|
|
9569
|
+
switch (value?.trim().toLowerCase()) {
|
|
9570
|
+
case "user":
|
|
9571
|
+
return "user";
|
|
9572
|
+
case "assistant":
|
|
9573
|
+
return "assistant";
|
|
9574
|
+
case "tool":
|
|
9575
|
+
return "tool";
|
|
9576
|
+
case "system":
|
|
9577
|
+
return "system";
|
|
9578
|
+
default:
|
|
9579
|
+
return "system";
|
|
9580
|
+
}
|
|
9581
|
+
}
|
|
9582
|
+
function normalizeContent(value) {
|
|
9583
|
+
if (typeof value === "string") {
|
|
9584
|
+
return value;
|
|
9585
|
+
}
|
|
9586
|
+
if (Array.isArray(value)) {
|
|
9587
|
+
return value.map((item) => {
|
|
9588
|
+
if (typeof item === "string") {
|
|
9589
|
+
return item;
|
|
9590
|
+
}
|
|
9591
|
+
if (typeof item === "object" && item !== null) {
|
|
9592
|
+
return readString8(item, "text") ?? "";
|
|
9593
|
+
}
|
|
9594
|
+
return "";
|
|
9595
|
+
}).filter(Boolean).join("");
|
|
9596
|
+
}
|
|
9597
|
+
return "";
|
|
9598
|
+
}
|
|
9599
|
+
function parseJsonValue(value) {
|
|
9600
|
+
if (typeof value !== "string") {
|
|
9601
|
+
return void 0;
|
|
9602
|
+
}
|
|
9603
|
+
const trimmed = value.trim();
|
|
9604
|
+
if (!trimmed) {
|
|
9605
|
+
return void 0;
|
|
8491
9606
|
}
|
|
8492
|
-
|
|
8493
|
-
return
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
});
|
|
9607
|
+
try {
|
|
9608
|
+
return JSON.parse(trimmed);
|
|
9609
|
+
} catch {
|
|
9610
|
+
return void 0;
|
|
8497
9611
|
}
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
9612
|
+
}
|
|
9613
|
+
function toRecord7(value) {
|
|
9614
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
9615
|
+
}
|
|
9616
|
+
function isDeletedSession(session) {
|
|
9617
|
+
return readBoolean(session.deleted) || readBoolean(session.is_deleted) || Boolean(readString8(session, "deleted_at")) || ["deleted", "removed"].includes(readString8(session, "status") ?? "");
|
|
9618
|
+
}
|
|
9619
|
+
function isHiddenSession(session) {
|
|
9620
|
+
const source = readString8(session, "source")?.toLowerCase();
|
|
9621
|
+
const status = readString8(session, "status")?.toLowerCase();
|
|
9622
|
+
const visibility = readString8(session, "visibility")?.toLowerCase();
|
|
9623
|
+
return Boolean(source && HIDDEN_SESSION_SOURCES.has(source)) || readBoolean(session.hidden) || readBoolean(session.archived) || Boolean(readString8(session, "archived_at")) || status === "hidden" || status === "archived" || visibility === "hidden" || visibility === "hide";
|
|
9624
|
+
}
|
|
9625
|
+
function readTableColumns(db, tableName) {
|
|
9626
|
+
try {
|
|
9627
|
+
const rows = db.prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`).all();
|
|
9628
|
+
return new Set(
|
|
9629
|
+
rows.map((row) => typeof row.name === "string" ? row.name : "").filter(Boolean)
|
|
9630
|
+
);
|
|
9631
|
+
} catch {
|
|
9632
|
+
return /* @__PURE__ */ new Set();
|
|
8501
9633
|
}
|
|
8502
|
-
|
|
8503
|
-
|
|
9634
|
+
}
|
|
9635
|
+
function quoteIdentifier(value) {
|
|
9636
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
9637
|
+
}
|
|
9638
|
+
async function isFile(filePath) {
|
|
9639
|
+
return stat7(filePath).then((value) => value.isFile()).catch((error) => {
|
|
9640
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
9641
|
+
return false;
|
|
9642
|
+
}
|
|
9643
|
+
throw error;
|
|
9644
|
+
});
|
|
9645
|
+
}
|
|
9646
|
+
function createConversationId() {
|
|
9647
|
+
return `conv_${randomUUID6().replaceAll("-", "")}`;
|
|
9648
|
+
}
|
|
9649
|
+
function isoFromHermesTime(value) {
|
|
9650
|
+
const numeric = readNumber2(value);
|
|
9651
|
+
if (!numeric || numeric <= 0) {
|
|
9652
|
+
return void 0;
|
|
8504
9653
|
}
|
|
8505
|
-
|
|
8506
|
-
|
|
9654
|
+
const millis = numeric > 1e10 ? numeric : numeric * 1e3;
|
|
9655
|
+
return new Date(millis).toISOString();
|
|
9656
|
+
}
|
|
9657
|
+
function readString8(payload, key) {
|
|
9658
|
+
const value = payload[key];
|
|
9659
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9660
|
+
}
|
|
9661
|
+
function readNumber2(value) {
|
|
9662
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
9663
|
+
}
|
|
9664
|
+
function readBoolean(value) {
|
|
9665
|
+
if (value === true || value === 1) {
|
|
9666
|
+
return true;
|
|
8507
9667
|
}
|
|
8508
|
-
|
|
8509
|
-
return
|
|
9668
|
+
if (typeof value === "string") {
|
|
9669
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
8510
9670
|
}
|
|
8511
|
-
|
|
8512
|
-
function createEmptySnapshot2() {
|
|
8513
|
-
return { schema_version: 1, messages: [], runs: [] };
|
|
9671
|
+
return false;
|
|
8514
9672
|
}
|
|
8515
|
-
function
|
|
9673
|
+
function isNodeError9(error, code) {
|
|
8516
9674
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8517
9675
|
}
|
|
8518
9676
|
|
|
8519
9677
|
// src/conversations/delivery-import.ts
|
|
8520
|
-
import { lstat, readFile as
|
|
8521
|
-
import
|
|
9678
|
+
import { lstat, readFile as readFile9, readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
9679
|
+
import path14 from "path";
|
|
8522
9680
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
8523
9681
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
8524
9682
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -8589,16 +9747,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
8589
9747
|
".m4a"
|
|
8590
9748
|
]);
|
|
8591
9749
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
8592
|
-
const resolvedDir =
|
|
8593
|
-
const relative =
|
|
8594
|
-
if (!relative || relative.startsWith("..") ||
|
|
9750
|
+
const resolvedDir = path14.resolve(stagingDir);
|
|
9751
|
+
const relative = path14.relative(path14.resolve(paths.conversationsDir), resolvedDir);
|
|
9752
|
+
if (!relative || relative.startsWith("..") || path14.isAbsolute(relative)) {
|
|
8595
9753
|
throw new LinkHttpError(
|
|
8596
9754
|
400,
|
|
8597
9755
|
"delivery_staging_invalid",
|
|
8598
9756
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
8599
9757
|
);
|
|
8600
9758
|
}
|
|
8601
|
-
const segments = relative.split(
|
|
9759
|
+
const segments = relative.split(path14.sep);
|
|
8602
9760
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
8603
9761
|
throw new LinkHttpError(
|
|
8604
9762
|
400,
|
|
@@ -8614,7 +9772,7 @@ function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
|
8614
9772
|
}
|
|
8615
9773
|
async function collectStagedDeliveryReferences(stagingDir) {
|
|
8616
9774
|
const directoryStat = await lstat(stagingDir).catch((error) => {
|
|
8617
|
-
if (
|
|
9775
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
8618
9776
|
throw new LinkHttpError(
|
|
8619
9777
|
404,
|
|
8620
9778
|
"delivery_staging_not_found",
|
|
@@ -8630,11 +9788,11 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
8630
9788
|
"delivery staging path is not a directory"
|
|
8631
9789
|
);
|
|
8632
9790
|
}
|
|
8633
|
-
const entries = await
|
|
9791
|
+
const entries = await readdir6(stagingDir, { withFileTypes: true });
|
|
8634
9792
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
8635
9793
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
8636
9794
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
8637
|
-
const sourcePath =
|
|
9795
|
+
const sourcePath = path14.join(stagingDir, entry.name);
|
|
8638
9796
|
const mime = inferMimeType(sourcePath);
|
|
8639
9797
|
return {
|
|
8640
9798
|
path: sourcePath,
|
|
@@ -8713,7 +9871,7 @@ async function importMediaReferencesForMessage(deps, input) {
|
|
|
8713
9871
|
};
|
|
8714
9872
|
}
|
|
8715
9873
|
assistant.hermes = {
|
|
8716
|
-
...
|
|
9874
|
+
...toRecord8(assistant.hermes),
|
|
8717
9875
|
imported_media_source_keys: [...importedSourceKeys],
|
|
8718
9876
|
media_import_failed_source_keys: [...failedSourceKeys],
|
|
8719
9877
|
media_import_failures: [...failureRecordsByKey.values()].slice(
|
|
@@ -8745,16 +9903,16 @@ async function importMediaReferencesForMessage(deps, input) {
|
|
|
8745
9903
|
};
|
|
8746
9904
|
}
|
|
8747
9905
|
function readMediaImportFailures(message) {
|
|
8748
|
-
const hermes =
|
|
9906
|
+
const hermes = toRecord8(message.hermes);
|
|
8749
9907
|
const failures = hermes.media_import_failures;
|
|
8750
9908
|
if (!Array.isArray(failures)) {
|
|
8751
9909
|
return [];
|
|
8752
9910
|
}
|
|
8753
9911
|
return failures.flatMap((item) => {
|
|
8754
|
-
const record =
|
|
8755
|
-
const key =
|
|
8756
|
-
const filename =
|
|
8757
|
-
const reason =
|
|
9912
|
+
const record = toRecord8(item);
|
|
9913
|
+
const key = readString9(record, "key");
|
|
9914
|
+
const filename = readString9(record, "filename");
|
|
9915
|
+
const reason = readString9(record, "reason");
|
|
8758
9916
|
if (!key || !filename || !reason) {
|
|
8759
9917
|
return [];
|
|
8760
9918
|
}
|
|
@@ -8763,13 +9921,13 @@ function readMediaImportFailures(message) {
|
|
|
8763
9921
|
key,
|
|
8764
9922
|
filename,
|
|
8765
9923
|
reason,
|
|
8766
|
-
...
|
|
9924
|
+
...readString9(record, "code") ? { code: readString9(record, "code") } : {}
|
|
8767
9925
|
}
|
|
8768
9926
|
];
|
|
8769
9927
|
});
|
|
8770
9928
|
}
|
|
8771
9929
|
function readFailedMediaSourceKeys(message) {
|
|
8772
|
-
const hermes =
|
|
9930
|
+
const hermes = toRecord8(message.hermes);
|
|
8773
9931
|
const keys = hermes.media_import_failed_source_keys;
|
|
8774
9932
|
if (!Array.isArray(keys)) {
|
|
8775
9933
|
return /* @__PURE__ */ new Set();
|
|
@@ -8794,8 +9952,8 @@ function emptyImportResult(input) {
|
|
|
8794
9952
|
}
|
|
8795
9953
|
async function writeBlobFromFile(deps, conversationId, source) {
|
|
8796
9954
|
const sourcePath = resolveMediaSourcePath(source.path);
|
|
8797
|
-
const fileStat = await
|
|
8798
|
-
if (
|
|
9955
|
+
const fileStat = await stat8(sourcePath).catch((error) => {
|
|
9956
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
8799
9957
|
throw new LinkHttpError(
|
|
8800
9958
|
404,
|
|
8801
9959
|
"media_source_not_found",
|
|
@@ -8819,8 +9977,8 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
8819
9977
|
);
|
|
8820
9978
|
}
|
|
8821
9979
|
return deps.writeBlob(conversationId, {
|
|
8822
|
-
bytes: await
|
|
8823
|
-
filename:
|
|
9980
|
+
bytes: await readFile9(sourcePath),
|
|
9981
|
+
filename: path14.basename(sourcePath),
|
|
8824
9982
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
8825
9983
|
});
|
|
8826
9984
|
}
|
|
@@ -8829,25 +9987,25 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
8829
9987
|
key: sourceKey,
|
|
8830
9988
|
filename: sanitizeFilename(reference.path, "attachment"),
|
|
8831
9989
|
reason: error instanceof Error ? error.message : String(error),
|
|
8832
|
-
...
|
|
9990
|
+
...isNodeError10(error) && error.code ? { code: error.code } : {}
|
|
8833
9991
|
};
|
|
8834
9992
|
}
|
|
8835
9993
|
function isSupportedDeliveryFilename(filename) {
|
|
8836
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
9994
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path14.extname(filename).toLowerCase());
|
|
8837
9995
|
}
|
|
8838
|
-
function
|
|
9996
|
+
function readString9(payload, key) {
|
|
8839
9997
|
const value = payload[key];
|
|
8840
9998
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8841
9999
|
}
|
|
8842
|
-
function
|
|
10000
|
+
function toRecord8(value) {
|
|
8843
10001
|
return typeof value === "object" && value !== null ? value : {};
|
|
8844
10002
|
}
|
|
8845
|
-
function
|
|
10003
|
+
function isNodeError10(error, code) {
|
|
8846
10004
|
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
8847
10005
|
}
|
|
8848
10006
|
|
|
8849
10007
|
// src/conversations/run-lifecycle.ts
|
|
8850
|
-
import { readdir as
|
|
10008
|
+
import { readdir as readdir7 } from "fs/promises";
|
|
8851
10009
|
|
|
8852
10010
|
// src/hermes/api-server.ts
|
|
8853
10011
|
async function listHermesModels(options = {}) {
|
|
@@ -8970,7 +10128,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
8970
10128
|
);
|
|
8971
10129
|
}
|
|
8972
10130
|
const payload = await readJsonResponse(response);
|
|
8973
|
-
const runId =
|
|
10131
|
+
const runId = readString10(payload, "run_id") ?? readString10(payload, "runId") ?? readString10(payload, "id");
|
|
8974
10132
|
if (!runId) {
|
|
8975
10133
|
throw new LinkHttpError(
|
|
8976
10134
|
502,
|
|
@@ -9083,10 +10241,10 @@ async function cancelHermesRun(runId, options = {}) {
|
|
|
9083
10241
|
);
|
|
9084
10242
|
}
|
|
9085
10243
|
}
|
|
9086
|
-
async function callHermesApi(
|
|
10244
|
+
async function callHermesApi(path25, init, options) {
|
|
9087
10245
|
const method = init.method ?? "GET";
|
|
9088
10246
|
const startedAt = Date.now();
|
|
9089
|
-
void options.logger?.debug("hermes_api_request_started", { method, path:
|
|
10247
|
+
void options.logger?.debug("hermes_api_request_started", { method, path: path25 });
|
|
9090
10248
|
const availability = await ensureHermesApiServerAvailable({
|
|
9091
10249
|
fetchImpl: options.fetchImpl,
|
|
9092
10250
|
logger: options.logger,
|
|
@@ -9094,21 +10252,21 @@ async function callHermesApi(path24, init, options) {
|
|
|
9094
10252
|
});
|
|
9095
10253
|
let config = availability.configResult.apiServer;
|
|
9096
10254
|
const fetcher = options.fetchImpl ?? fetch;
|
|
9097
|
-
const request = () => fetchHermesApi(fetcher, config,
|
|
10255
|
+
const request = () => fetchHermesApi(fetcher, config, path25, init, options);
|
|
9098
10256
|
let response;
|
|
9099
10257
|
try {
|
|
9100
10258
|
response = await request();
|
|
9101
10259
|
} catch (error) {
|
|
9102
|
-
logHermesApiError(options.logger, method,
|
|
10260
|
+
logHermesApiError(options.logger, method, path25, startedAt, error);
|
|
9103
10261
|
throw error;
|
|
9104
10262
|
}
|
|
9105
10263
|
if (response.status !== 401) {
|
|
9106
|
-
logHermesApiResponse(options.logger, method,
|
|
10264
|
+
logHermesApiResponse(options.logger, method, path25, startedAt, response);
|
|
9107
10265
|
return response;
|
|
9108
10266
|
}
|
|
9109
10267
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
9110
10268
|
method,
|
|
9111
|
-
path:
|
|
10269
|
+
path: path25,
|
|
9112
10270
|
duration_ms: Date.now() - startedAt
|
|
9113
10271
|
});
|
|
9114
10272
|
const refreshedAvailability = await ensureHermesApiServerAvailable({
|
|
@@ -9121,20 +10279,20 @@ async function callHermesApi(path24, init, options) {
|
|
|
9121
10279
|
try {
|
|
9122
10280
|
response = await request();
|
|
9123
10281
|
} catch (error) {
|
|
9124
|
-
logHermesApiError(options.logger, method,
|
|
10282
|
+
logHermesApiError(options.logger, method, path25, startedAt, error);
|
|
9125
10283
|
throw error;
|
|
9126
10284
|
}
|
|
9127
|
-
logHermesApiResponse(options.logger, method,
|
|
10285
|
+
logHermesApiResponse(options.logger, method, path25, startedAt, response);
|
|
9128
10286
|
return response;
|
|
9129
10287
|
}
|
|
9130
|
-
async function fetchHermesApi(fetcher, config,
|
|
10288
|
+
async function fetchHermesApi(fetcher, config, path25, init, options) {
|
|
9131
10289
|
const headers = new Headers(init.headers);
|
|
9132
10290
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
9133
10291
|
if (config.key) {
|
|
9134
10292
|
headers.set("x-api-key", config.key);
|
|
9135
10293
|
headers.set("authorization", `Bearer ${config.key}`);
|
|
9136
10294
|
}
|
|
9137
|
-
return await fetcher(`http://127.0.0.1:${config.port}${
|
|
10295
|
+
return await fetcher(`http://127.0.0.1:${config.port}${path25}`, {
|
|
9138
10296
|
...init,
|
|
9139
10297
|
headers
|
|
9140
10298
|
}).catch((error) => {
|
|
@@ -9142,7 +10300,7 @@ async function fetchHermesApi(fetcher, config, path24, init, options) {
|
|
|
9142
10300
|
throw error;
|
|
9143
10301
|
}
|
|
9144
10302
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
9145
|
-
path:
|
|
10303
|
+
path: path25,
|
|
9146
10304
|
port: config.port ?? null,
|
|
9147
10305
|
error: error instanceof Error ? error.message : String(error)
|
|
9148
10306
|
});
|
|
@@ -9153,10 +10311,10 @@ async function fetchHermesApi(fetcher, config, path24, init, options) {
|
|
|
9153
10311
|
);
|
|
9154
10312
|
});
|
|
9155
10313
|
}
|
|
9156
|
-
function logHermesApiResponse(logger, method,
|
|
10314
|
+
function logHermesApiResponse(logger, method, path25, startedAt, response) {
|
|
9157
10315
|
const fields = {
|
|
9158
10316
|
method,
|
|
9159
|
-
path:
|
|
10317
|
+
path: path25,
|
|
9160
10318
|
status: response.status,
|
|
9161
10319
|
duration_ms: Date.now() - startedAt
|
|
9162
10320
|
};
|
|
@@ -9176,10 +10334,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
9176
10334
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
9177
10335
|
});
|
|
9178
10336
|
}
|
|
9179
|
-
function logHermesApiError(logger, method,
|
|
10337
|
+
function logHermesApiError(logger, method, path25, startedAt, error) {
|
|
9180
10338
|
void logger?.warn("hermes_api_request_failed", {
|
|
9181
10339
|
method,
|
|
9182
|
-
path:
|
|
10340
|
+
path: path25,
|
|
9183
10341
|
duration_ms: Date.now() - startedAt,
|
|
9184
10342
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
9185
10343
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -9228,23 +10386,23 @@ function isRecord(value) {
|
|
|
9228
10386
|
}
|
|
9229
10387
|
function readUpstreamMessage(payload, raw) {
|
|
9230
10388
|
const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
|
|
9231
|
-
const message =
|
|
10389
|
+
const message = readString10(error ?? {}, "message") ?? readString10(payload ?? {}, "message");
|
|
9232
10390
|
if (message) {
|
|
9233
10391
|
return message;
|
|
9234
10392
|
}
|
|
9235
10393
|
const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
9236
10394
|
return body || "empty response body";
|
|
9237
10395
|
}
|
|
9238
|
-
function
|
|
10396
|
+
function readString10(payload, key) {
|
|
9239
10397
|
const value = payload[key];
|
|
9240
10398
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9241
10399
|
}
|
|
9242
10400
|
|
|
9243
10401
|
// src/conversations/history-builder.ts
|
|
9244
|
-
import { readFile as
|
|
9245
|
-
import { createRequire as
|
|
9246
|
-
import
|
|
9247
|
-
var
|
|
10402
|
+
import { readFile as readFile10, stat as stat9 } from "fs/promises";
|
|
10403
|
+
import { createRequire as createRequire4 } from "module";
|
|
10404
|
+
import path15 from "path";
|
|
10405
|
+
var nodeRequire4 = createRequire4(import.meta.url);
|
|
9248
10406
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
9249
10407
|
var HERMES_HISTORY_COLUMNS = [
|
|
9250
10408
|
"role",
|
|
@@ -9305,13 +10463,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
9305
10463
|
}
|
|
9306
10464
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
9307
10465
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
9308
|
-
const dbPath =
|
|
10466
|
+
const dbPath = path15.join(profileDir, "state.db");
|
|
9309
10467
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
9310
10468
|
sessionsDir: value.sessionsDir,
|
|
9311
10469
|
configured: value.configured,
|
|
9312
10470
|
configError: false
|
|
9313
10471
|
})).catch(() => ({
|
|
9314
|
-
sessionsDir:
|
|
10472
|
+
sessionsDir: path15.join(profileDir, "sessions"),
|
|
9315
10473
|
configured: false,
|
|
9316
10474
|
configError: true
|
|
9317
10475
|
}));
|
|
@@ -9351,8 +10509,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
9351
10509
|
};
|
|
9352
10510
|
}
|
|
9353
10511
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
9354
|
-
const exists = await
|
|
9355
|
-
if (
|
|
10512
|
+
const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
10513
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
9356
10514
|
return false;
|
|
9357
10515
|
}
|
|
9358
10516
|
throw error;
|
|
@@ -9369,9 +10527,9 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
9369
10527
|
if (!isValidSessionFileStem(sessionId)) {
|
|
9370
10528
|
return empty;
|
|
9371
10529
|
}
|
|
9372
|
-
const transcriptPath =
|
|
9373
|
-
const raw = await
|
|
9374
|
-
if (
|
|
10530
|
+
const transcriptPath = path15.join(sessionsDir, `${sessionId}.jsonl`);
|
|
10531
|
+
const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
|
|
10532
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
9375
10533
|
return "";
|
|
9376
10534
|
}
|
|
9377
10535
|
throw error;
|
|
@@ -9406,14 +10564,14 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
9406
10564
|
function readHistoryRows(dbPath, sessionId) {
|
|
9407
10565
|
let db = null;
|
|
9408
10566
|
try {
|
|
9409
|
-
const { DatabaseSync } =
|
|
10567
|
+
const { DatabaseSync } = nodeRequire4(
|
|
9410
10568
|
"node:sqlite"
|
|
9411
10569
|
);
|
|
9412
10570
|
db = new DatabaseSync(dbPath, {
|
|
9413
10571
|
readOnly: true,
|
|
9414
10572
|
timeout: 1e3
|
|
9415
10573
|
});
|
|
9416
|
-
const columns =
|
|
10574
|
+
const columns = readTableColumns2(db, "messages");
|
|
9417
10575
|
if (!columns.has("role") || !columns.has("content")) {
|
|
9418
10576
|
return [];
|
|
9419
10577
|
}
|
|
@@ -9518,7 +10676,7 @@ function normalizeHistoryMessage(role, content) {
|
|
|
9518
10676
|
if (role !== "user" && role !== "assistant") {
|
|
9519
10677
|
return null;
|
|
9520
10678
|
}
|
|
9521
|
-
const text =
|
|
10679
|
+
const text = normalizeContent2(content).trim();
|
|
9522
10680
|
if (!text) {
|
|
9523
10681
|
return null;
|
|
9524
10682
|
}
|
|
@@ -9529,7 +10687,7 @@ function normalizeHistoryRecord(record) {
|
|
|
9529
10687
|
if (!role) {
|
|
9530
10688
|
return null;
|
|
9531
10689
|
}
|
|
9532
|
-
const content =
|
|
10690
|
+
const content = normalizeContent2(record.content);
|
|
9533
10691
|
const message = { role, content };
|
|
9534
10692
|
assignString(message, "tool_call_id", record.tool_call_id);
|
|
9535
10693
|
assignString(message, "tool_name", record.tool_name);
|
|
@@ -9549,7 +10707,7 @@ function normalizeHistoryRecord(record) {
|
|
|
9549
10707
|
}
|
|
9550
10708
|
return message;
|
|
9551
10709
|
}
|
|
9552
|
-
function
|
|
10710
|
+
function normalizeContent2(value) {
|
|
9553
10711
|
if (typeof value === "string") {
|
|
9554
10712
|
return value;
|
|
9555
10713
|
}
|
|
@@ -9591,12 +10749,12 @@ function hasReplayMetadata(message) {
|
|
|
9591
10749
|
message.tool_call_id || message.tool_name || message.tool_calls || message.reasoning || message.reasoning_content || message.reasoning_details || message.codex_reasoning_items
|
|
9592
10750
|
);
|
|
9593
10751
|
}
|
|
9594
|
-
function
|
|
10752
|
+
function readTableColumns2(db, table) {
|
|
9595
10753
|
return new Set(
|
|
9596
10754
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
9597
10755
|
);
|
|
9598
10756
|
}
|
|
9599
|
-
function
|
|
10757
|
+
function isNodeError11(error, code) {
|
|
9600
10758
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
9601
10759
|
}
|
|
9602
10760
|
function isValidProfileName2(value) {
|
|
@@ -9614,8 +10772,8 @@ function normalizeProfileForCompare(value) {
|
|
|
9614
10772
|
|
|
9615
10773
|
// src/hermes/stt.ts
|
|
9616
10774
|
import { execFile as execFile3 } from "child_process";
|
|
9617
|
-
import { access as access2, readFile as
|
|
9618
|
-
import
|
|
10775
|
+
import { access as access2, readFile as readFile11, stat as stat10 } from "fs/promises";
|
|
10776
|
+
import path16 from "path";
|
|
9619
10777
|
import { promisify as promisify3 } from "util";
|
|
9620
10778
|
var execFileAsync3 = promisify3(execFile3);
|
|
9621
10779
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -9710,7 +10868,7 @@ async function buildHermesSttEnv(profileName) {
|
|
|
9710
10868
|
};
|
|
9711
10869
|
const devSource = await findDevHermesAgentSource();
|
|
9712
10870
|
if (devSource) {
|
|
9713
|
-
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(
|
|
10871
|
+
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path16.delimiter);
|
|
9714
10872
|
}
|
|
9715
10873
|
return env;
|
|
9716
10874
|
}
|
|
@@ -9757,14 +10915,14 @@ async function resolveHermesPythonCommand() {
|
|
|
9757
10915
|
};
|
|
9758
10916
|
}
|
|
9759
10917
|
async function resolveExecutablePath(command) {
|
|
9760
|
-
if (
|
|
10918
|
+
if (path16.isAbsolute(command)) {
|
|
9761
10919
|
return await isExecutableFile(command) ? command : null;
|
|
9762
10920
|
}
|
|
9763
10921
|
const pathEnv = process.env.PATH ?? "";
|
|
9764
10922
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
9765
|
-
for (const dir of pathEnv.split(
|
|
10923
|
+
for (const dir of pathEnv.split(path16.delimiter)) {
|
|
9766
10924
|
for (const extension of extensions) {
|
|
9767
|
-
const candidate =
|
|
10925
|
+
const candidate = path16.join(dir, `${command}${extension}`);
|
|
9768
10926
|
if (await isExecutableFile(candidate)) {
|
|
9769
10927
|
return candidate;
|
|
9770
10928
|
}
|
|
@@ -9774,7 +10932,7 @@ async function resolveExecutablePath(command) {
|
|
|
9774
10932
|
}
|
|
9775
10933
|
async function isExecutableFile(filePath) {
|
|
9776
10934
|
try {
|
|
9777
|
-
const info = await
|
|
10935
|
+
const info = await stat10(filePath);
|
|
9778
10936
|
if (!info.isFile()) {
|
|
9779
10937
|
return false;
|
|
9780
10938
|
}
|
|
@@ -9785,7 +10943,7 @@ async function isExecutableFile(filePath) {
|
|
|
9785
10943
|
}
|
|
9786
10944
|
}
|
|
9787
10945
|
async function readShebang(filePath) {
|
|
9788
|
-
const raw = await
|
|
10946
|
+
const raw = await readFile11(filePath, "utf8").catch(() => "");
|
|
9789
10947
|
const firstLine = raw.split(/\r?\n/u)[0]?.trim() ?? "";
|
|
9790
10948
|
return firstLine.startsWith("#!") ? firstLine.slice(2).trim() : null;
|
|
9791
10949
|
}
|
|
@@ -9804,8 +10962,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
9804
10962
|
}
|
|
9805
10963
|
async function findDevHermesAgentSource() {
|
|
9806
10964
|
const candidates = [
|
|
9807
|
-
|
|
9808
|
-
|
|
10965
|
+
path16.resolve(process.cwd(), "reference/hermes-agent"),
|
|
10966
|
+
path16.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
9809
10967
|
];
|
|
9810
10968
|
for (const candidate of candidates) {
|
|
9811
10969
|
if (await isDirectory(candidate)) {
|
|
@@ -9815,7 +10973,7 @@ async function findDevHermesAgentSource() {
|
|
|
9815
10973
|
return null;
|
|
9816
10974
|
}
|
|
9817
10975
|
async function isDirectory(candidate) {
|
|
9818
|
-
return
|
|
10976
|
+
return stat10(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
9819
10977
|
}
|
|
9820
10978
|
function compactProcessOutput(value) {
|
|
9821
10979
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -9867,8 +11025,8 @@ function parseSseBlock(block) {
|
|
|
9867
11025
|
if (decoded === null) {
|
|
9868
11026
|
return null;
|
|
9869
11027
|
}
|
|
9870
|
-
const payload =
|
|
9871
|
-
const payloadType = (
|
|
11028
|
+
const payload = toRecord9(decoded);
|
|
11029
|
+
const payloadType = (readString11(payload, "type") ?? readString11(payload, "event") ?? readString11(payload, "object") ?? eventName) || "message";
|
|
9872
11030
|
return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
|
|
9873
11031
|
}
|
|
9874
11032
|
function decodeJson(value) {
|
|
@@ -9884,11 +11042,11 @@ function decodeJson(value) {
|
|
|
9884
11042
|
return { type: "message.delta", delta: value };
|
|
9885
11043
|
}
|
|
9886
11044
|
}
|
|
9887
|
-
function
|
|
11045
|
+
function readString11(body, key) {
|
|
9888
11046
|
const value = body[key];
|
|
9889
11047
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9890
11048
|
}
|
|
9891
|
-
function
|
|
11049
|
+
function toRecord9(value) {
|
|
9892
11050
|
return typeof value === "object" && value !== null ? value : {};
|
|
9893
11051
|
}
|
|
9894
11052
|
|
|
@@ -9912,8 +11070,8 @@ function normalizeHermesStreamEvent(event) {
|
|
|
9912
11070
|
};
|
|
9913
11071
|
}
|
|
9914
11072
|
if (event.eventName === "hermes.tool.progress") {
|
|
9915
|
-
const toolName =
|
|
9916
|
-
const preview =
|
|
11073
|
+
const toolName = readString12(event.payload, "tool") ?? readString12(event.payload, "name") ?? "tool";
|
|
11074
|
+
const preview = readString12(event.payload, "label") ?? readString12(event.payload, "preview") ?? toolName;
|
|
9917
11075
|
return {
|
|
9918
11076
|
...event,
|
|
9919
11077
|
payloadType: "tool.started",
|
|
@@ -9981,12 +11139,12 @@ function normalizeHermesResponseEvent(event) {
|
|
|
9981
11139
|
}
|
|
9982
11140
|
}
|
|
9983
11141
|
function normalizeResponseOutputItemAdded(event) {
|
|
9984
|
-
const item =
|
|
9985
|
-
if (
|
|
11142
|
+
const item = toRecord10(event.payload.item);
|
|
11143
|
+
if (readString12(item, "type") !== "function_call") {
|
|
9986
11144
|
return null;
|
|
9987
11145
|
}
|
|
9988
|
-
const toolName =
|
|
9989
|
-
const argumentsValue =
|
|
11146
|
+
const toolName = readString12(item, "name") ?? "tool";
|
|
11147
|
+
const argumentsValue = parseJsonValue2(item.arguments) ?? item.arguments;
|
|
9990
11148
|
return {
|
|
9991
11149
|
...event,
|
|
9992
11150
|
payloadType: "tool.started",
|
|
@@ -9995,60 +11153,60 @@ function normalizeResponseOutputItemAdded(event) {
|
|
|
9995
11153
|
tool: toolName,
|
|
9996
11154
|
tool_name: toolName,
|
|
9997
11155
|
name: toolName,
|
|
9998
|
-
tool_call_id:
|
|
11156
|
+
tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
|
|
9999
11157
|
arguments: argumentsValue,
|
|
10000
11158
|
preview: toolName,
|
|
10001
|
-
response_item_id:
|
|
11159
|
+
response_item_id: readString12(item, "id") ?? void 0
|
|
10002
11160
|
}
|
|
10003
11161
|
};
|
|
10004
11162
|
}
|
|
10005
11163
|
function normalizeResponseOutputItemDone(event) {
|
|
10006
|
-
const item =
|
|
10007
|
-
if (
|
|
11164
|
+
const item = toRecord10(event.payload.item);
|
|
11165
|
+
if (readString12(item, "type") !== "function_call_output") {
|
|
10008
11166
|
return null;
|
|
10009
11167
|
}
|
|
10010
11168
|
const output = readResponseItemOutput(item.output);
|
|
10011
|
-
const parsedOutput =
|
|
11169
|
+
const parsedOutput = parseJsonValue2(output);
|
|
10012
11170
|
return {
|
|
10013
11171
|
...event,
|
|
10014
11172
|
payloadType: "tool.completed",
|
|
10015
11173
|
payload: {
|
|
10016
11174
|
type: "tool.completed",
|
|
10017
|
-
tool_call_id:
|
|
10018
|
-
status:
|
|
11175
|
+
tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
|
|
11176
|
+
status: readString12(item, "status") ?? "completed",
|
|
10019
11177
|
output,
|
|
10020
11178
|
content: output,
|
|
10021
11179
|
result: parsedOutput ?? output,
|
|
10022
|
-
response_item_id:
|
|
11180
|
+
response_item_id: readString12(item, "id") ?? void 0
|
|
10023
11181
|
}
|
|
10024
11182
|
};
|
|
10025
11183
|
}
|
|
10026
11184
|
function normalizeResponseCompleted(event) {
|
|
10027
|
-
const response =
|
|
11185
|
+
const response = toRecord10(event.payload.response);
|
|
10028
11186
|
return {
|
|
10029
11187
|
...event,
|
|
10030
11188
|
payloadType: "run.completed",
|
|
10031
11189
|
payload: {
|
|
10032
11190
|
type: "run.completed",
|
|
10033
|
-
response_id:
|
|
10034
|
-
usage:
|
|
11191
|
+
response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
|
|
11192
|
+
usage: toRecord10(response.usage),
|
|
10035
11193
|
response
|
|
10036
11194
|
}
|
|
10037
11195
|
};
|
|
10038
11196
|
}
|
|
10039
11197
|
function normalizeResponseFailed(event) {
|
|
10040
|
-
const response =
|
|
10041
|
-
const error =
|
|
11198
|
+
const response = toRecord10(event.payload.response);
|
|
11199
|
+
const error = toRecord10(response.error);
|
|
10042
11200
|
return {
|
|
10043
11201
|
...event,
|
|
10044
11202
|
payloadType: "run.failed",
|
|
10045
11203
|
payload: {
|
|
10046
11204
|
type: "run.failed",
|
|
10047
|
-
response_id:
|
|
11205
|
+
response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
|
|
10048
11206
|
error: {
|
|
10049
|
-
message:
|
|
11207
|
+
message: readString12(error, "message") ?? readString12(event.payload, "message") ?? "Hermes run failed"
|
|
10050
11208
|
},
|
|
10051
|
-
usage:
|
|
11209
|
+
usage: toRecord10(response.usage),
|
|
10052
11210
|
response
|
|
10053
11211
|
}
|
|
10054
11212
|
};
|
|
@@ -10072,8 +11230,8 @@ function readErrorMessage2(payload) {
|
|
|
10072
11230
|
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
10073
11231
|
return payload.error.trim();
|
|
10074
11232
|
}
|
|
10075
|
-
const error =
|
|
10076
|
-
return
|
|
11233
|
+
const error = toRecord10(payload.error);
|
|
11234
|
+
return readString12(error, "message") ?? readString12(payload, "message");
|
|
10077
11235
|
}
|
|
10078
11236
|
function readDelta(payload) {
|
|
10079
11237
|
return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
|
|
@@ -10108,15 +11266,15 @@ function isTopLevelErrorEvent(event) {
|
|
|
10108
11266
|
}
|
|
10109
11267
|
function readChatCompletionDelta(payload) {
|
|
10110
11268
|
const choice = readFirstChoice(payload);
|
|
10111
|
-
const delta =
|
|
11269
|
+
const delta = toRecord10(choice.delta);
|
|
10112
11270
|
return readText2(delta, "content");
|
|
10113
11271
|
}
|
|
10114
11272
|
function readChatCompletionFinishReason(payload) {
|
|
10115
11273
|
const choice = readFirstChoice(payload);
|
|
10116
|
-
return
|
|
11274
|
+
return readString12(choice, "finish_reason") ?? readString12(choice, "finishReason");
|
|
10117
11275
|
}
|
|
10118
11276
|
function readChatCompletionUsage(payload) {
|
|
10119
|
-
const usage =
|
|
11277
|
+
const usage = toRecord10(payload.usage);
|
|
10120
11278
|
const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
|
|
10121
11279
|
const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
|
|
10122
11280
|
const total = readInteger2(usage, "total_tokens");
|
|
@@ -10144,7 +11302,7 @@ function readFirstChoice(payload) {
|
|
|
10144
11302
|
if (!Array.isArray(choices)) {
|
|
10145
11303
|
return {};
|
|
10146
11304
|
}
|
|
10147
|
-
return
|
|
11305
|
+
return toRecord10(choices[0]);
|
|
10148
11306
|
}
|
|
10149
11307
|
function readInteger2(payload, key) {
|
|
10150
11308
|
const value = payload[key];
|
|
@@ -10157,7 +11315,7 @@ function readInteger2(payload, key) {
|
|
|
10157
11315
|
}
|
|
10158
11316
|
return void 0;
|
|
10159
11317
|
}
|
|
10160
|
-
function
|
|
11318
|
+
function readString12(payload, key) {
|
|
10161
11319
|
const value = payload[key];
|
|
10162
11320
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
10163
11321
|
}
|
|
@@ -10172,10 +11330,10 @@ function readResponseItemOutput(value) {
|
|
|
10172
11330
|
if (!Array.isArray(value)) {
|
|
10173
11331
|
return stringifyJsonValue(value);
|
|
10174
11332
|
}
|
|
10175
|
-
const text = value.map(
|
|
11333
|
+
const text = value.map(toRecord10).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
|
|
10176
11334
|
return text || stringifyJsonValue(value);
|
|
10177
11335
|
}
|
|
10178
|
-
function
|
|
11336
|
+
function parseJsonValue2(value) {
|
|
10179
11337
|
if (typeof value !== "string" || !value.trim()) {
|
|
10180
11338
|
return null;
|
|
10181
11339
|
}
|
|
@@ -10198,7 +11356,7 @@ function stringifyJsonValue(value) {
|
|
|
10198
11356
|
return String(value);
|
|
10199
11357
|
}
|
|
10200
11358
|
}
|
|
10201
|
-
function
|
|
11359
|
+
function toRecord10(value) {
|
|
10202
11360
|
return typeof value === "object" && value !== null ? value : {};
|
|
10203
11361
|
}
|
|
10204
11362
|
|
|
@@ -10615,7 +11773,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10615
11773
|
assistant.agent_events ?? [],
|
|
10616
11774
|
agentEvent
|
|
10617
11775
|
);
|
|
10618
|
-
|
|
11776
|
+
appendAgentEventBlock2(assistant, agentEvent, event.created_at);
|
|
10619
11777
|
assistant.updated_at = event.created_at;
|
|
10620
11778
|
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10621
11779
|
}
|
|
@@ -10684,8 +11842,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10684
11842
|
}
|
|
10685
11843
|
const textPart = assistant.parts.find((part) => part.type === "text");
|
|
10686
11844
|
const currentText = textPart?.text ?? "";
|
|
10687
|
-
const pendingDeliveryText =
|
|
10688
|
-
|
|
11845
|
+
const pendingDeliveryText = readString13(
|
|
11846
|
+
toRecord11(assistant.hermes),
|
|
10689
11847
|
"pending_media_delivery_text"
|
|
10690
11848
|
);
|
|
10691
11849
|
const normalizedDelta = normalizeStreamingTextDelta(
|
|
@@ -10700,7 +11858,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10700
11858
|
pendingDeliveryText ?? ""
|
|
10701
11859
|
);
|
|
10702
11860
|
const nextHermes = {
|
|
10703
|
-
...
|
|
11861
|
+
...toRecord11(assistant.hermes),
|
|
10704
11862
|
...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
|
|
10705
11863
|
};
|
|
10706
11864
|
if (!extracted.pendingText) {
|
|
@@ -10716,7 +11874,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10716
11874
|
assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10717
11875
|
assistant.raw = { format: "hermes-run-event", payload: event.rawPayload };
|
|
10718
11876
|
if (extracted.visibleText) {
|
|
10719
|
-
|
|
11877
|
+
appendTextBlock2(assistant, extracted.visibleText, assistant.updated_at);
|
|
10720
11878
|
}
|
|
10721
11879
|
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10722
11880
|
if (extracted.visibleText) {
|
|
@@ -10967,7 +12125,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10967
12125
|
includeDisabled: true
|
|
10968
12126
|
});
|
|
10969
12127
|
return new Set(
|
|
10970
|
-
jobs.map((job) =>
|
|
12128
|
+
jobs.map((job) => readString13(job, "id") ?? readString13(job, "job_id")).filter((id) => Boolean(id))
|
|
10971
12129
|
);
|
|
10972
12130
|
}
|
|
10973
12131
|
async bindNewCronJobsCreatedByRun(input) {
|
|
@@ -11003,7 +12161,7 @@ function buildRunInstructions(run, deliveryStagingDir) {
|
|
|
11003
12161
|
].join("\n");
|
|
11004
12162
|
}
|
|
11005
12163
|
function appendMediaImportFailureNotice(message) {
|
|
11006
|
-
const hermes =
|
|
12164
|
+
const hermes = toRecord11(message.hermes);
|
|
11007
12165
|
if (hermes.media_import_failure_notice_appended === true) {
|
|
11008
12166
|
return;
|
|
11009
12167
|
}
|
|
@@ -11046,18 +12204,18 @@ function formatFilenameList(filenames) {
|
|
|
11046
12204
|
return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
|
|
11047
12205
|
}
|
|
11048
12206
|
async function readdirWithDirs(directory) {
|
|
11049
|
-
return
|
|
11050
|
-
if (
|
|
12207
|
+
return readdir7(directory, { withFileTypes: true }).catch((error) => {
|
|
12208
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
11051
12209
|
return [];
|
|
11052
12210
|
}
|
|
11053
12211
|
throw error;
|
|
11054
12212
|
});
|
|
11055
12213
|
}
|
|
11056
|
-
function
|
|
12214
|
+
function readString13(payload, key) {
|
|
11057
12215
|
const value = payload[key];
|
|
11058
12216
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
11059
12217
|
}
|
|
11060
|
-
function
|
|
12218
|
+
function toRecord11(value) {
|
|
11061
12219
|
return typeof value === "object" && value !== null ? value : {};
|
|
11062
12220
|
}
|
|
11063
12221
|
function formatFailureMessage(message, detail) {
|
|
@@ -11073,17 +12231,17 @@ function isFileSearchCompletion(payloadType, payload) {
|
|
|
11073
12231
|
if (payloadType !== "tool.completed") {
|
|
11074
12232
|
return false;
|
|
11075
12233
|
}
|
|
11076
|
-
const tool =
|
|
11077
|
-
const toolCall =
|
|
11078
|
-
const fn =
|
|
12234
|
+
const tool = toRecord11(payload.tool);
|
|
12235
|
+
const toolCall = toRecord11(payload.tool_call ?? payload.toolCall);
|
|
12236
|
+
const fn = toRecord11(toolCall.function ?? payload.function);
|
|
11079
12237
|
const candidates = [
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
12238
|
+
readString13(payload, "tool_name"),
|
|
12239
|
+
readString13(payload, "toolName"),
|
|
12240
|
+
readString13(payload, "name"),
|
|
12241
|
+
readString13(payload, "tool"),
|
|
12242
|
+
readString13(tool, "name"),
|
|
12243
|
+
readString13(toolCall, "name"),
|
|
12244
|
+
readString13(fn, "name")
|
|
11087
12245
|
].filter((value) => Boolean(value)).map(normalizeToolName);
|
|
11088
12246
|
return candidates.some(
|
|
11089
12247
|
(name) => [
|
|
@@ -11126,7 +12284,7 @@ function isVoicePart(part) {
|
|
|
11126
12284
|
function normalizeToolName(value) {
|
|
11127
12285
|
return value.trim().toLowerCase().replace(/[\s-]+/gu, "_");
|
|
11128
12286
|
}
|
|
11129
|
-
function
|
|
12287
|
+
function appendTextBlock2(message, delta, updatedAt) {
|
|
11130
12288
|
if (!delta) {
|
|
11131
12289
|
return;
|
|
11132
12290
|
}
|
|
@@ -11149,7 +12307,7 @@ function appendTextBlock(message, delta, updatedAt) {
|
|
|
11149
12307
|
}
|
|
11150
12308
|
message.blocks = blocks;
|
|
11151
12309
|
}
|
|
11152
|
-
function
|
|
12310
|
+
function appendAgentEventBlock2(message, event, updatedAt) {
|
|
11153
12311
|
const blocks = [...message.blocks ?? []];
|
|
11154
12312
|
const matchingIndex = blocks.findIndex((block) => {
|
|
11155
12313
|
if (block.type !== "agent_events") {
|
|
@@ -11238,10 +12396,10 @@ function readResponseId(payload) {
|
|
|
11238
12396
|
if (!payload) {
|
|
11239
12397
|
return null;
|
|
11240
12398
|
}
|
|
11241
|
-
const response =
|
|
11242
|
-
return
|
|
12399
|
+
const response = toRecord11(payload.response);
|
|
12400
|
+
return readString13(payload, "response_id") ?? readString13(response, "id");
|
|
11243
12401
|
}
|
|
11244
|
-
function
|
|
12402
|
+
function isNodeError12(error, code) {
|
|
11245
12403
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
11246
12404
|
return false;
|
|
11247
12405
|
}
|
|
@@ -11330,6 +12488,7 @@ var ConversationService = class {
|
|
|
11330
12488
|
orchestration;
|
|
11331
12489
|
queries;
|
|
11332
12490
|
runLifecycle;
|
|
12491
|
+
hermesSessionSyncPromise = null;
|
|
11333
12492
|
async withConversationLock(conversationId, task) {
|
|
11334
12493
|
const previous = this.conversationLocks.get(conversationId) ?? Promise.resolve();
|
|
11335
12494
|
let release;
|
|
@@ -11390,7 +12549,7 @@ var ConversationService = class {
|
|
|
11390
12549
|
async createConversation(input = {}) {
|
|
11391
12550
|
await this.store.ensureConversationsDir();
|
|
11392
12551
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11393
|
-
const id = `conv_${
|
|
12552
|
+
const id = `conv_${randomUUID7().replaceAll("-", "")}`;
|
|
11394
12553
|
const title = input.title?.trim() || DEFAULT_CONVERSATION_TITLE;
|
|
11395
12554
|
const profile = await resolveConversationProfileTarget(
|
|
11396
12555
|
this.paths,
|
|
@@ -11477,7 +12636,7 @@ var ConversationService = class {
|
|
|
11477
12636
|
manifest.profile_name_snapshot ?? manifest.profile ?? input.profileName
|
|
11478
12637
|
);
|
|
11479
12638
|
const message = {
|
|
11480
|
-
id: `msg_${
|
|
12639
|
+
id: `msg_${randomUUID7().replaceAll("-", "")}`,
|
|
11481
12640
|
schema_version: 1,
|
|
11482
12641
|
conversation_id: manifest.id,
|
|
11483
12642
|
role: "assistant",
|
|
@@ -11521,6 +12680,29 @@ var ConversationService = class {
|
|
|
11521
12680
|
async syncCronDeliveries() {
|
|
11522
12681
|
await syncHermesLinkCronDeliveries(this.paths, this, this.logger);
|
|
11523
12682
|
}
|
|
12683
|
+
async syncHermesSessions() {
|
|
12684
|
+
if (this.hermesSessionSyncPromise) {
|
|
12685
|
+
return this.hermesSessionSyncPromise;
|
|
12686
|
+
}
|
|
12687
|
+
const task = (async () => {
|
|
12688
|
+
const result = await syncHermesSessionsIntoConversations(
|
|
12689
|
+
this.paths,
|
|
12690
|
+
this.logger
|
|
12691
|
+
);
|
|
12692
|
+
if (result.imported_count > 0 || result.reprojected_count > 0) {
|
|
12693
|
+
await this.rebuildStatisticsIndex();
|
|
12694
|
+
}
|
|
12695
|
+
return result;
|
|
12696
|
+
})();
|
|
12697
|
+
this.hermesSessionSyncPromise = task;
|
|
12698
|
+
try {
|
|
12699
|
+
return await task;
|
|
12700
|
+
} finally {
|
|
12701
|
+
if (this.hermesSessionSyncPromise === task) {
|
|
12702
|
+
this.hermesSessionSyncPromise = null;
|
|
12703
|
+
}
|
|
12704
|
+
}
|
|
12705
|
+
}
|
|
11524
12706
|
async deliverStagedFiles(stagingDir) {
|
|
11525
12707
|
const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
|
|
11526
12708
|
return this.withConversationLock(target.conversationId, async () => {
|
|
@@ -12046,7 +13228,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
12046
13228
|
}
|
|
12047
13229
|
|
|
12048
13230
|
// src/identity/identity.ts
|
|
12049
|
-
import { generateKeyPairSync, randomUUID as
|
|
13231
|
+
import { generateKeyPairSync, randomUUID as randomUUID8, sign } from "crypto";
|
|
12050
13232
|
import { mkdir as mkdir9, chmod } from "fs/promises";
|
|
12051
13233
|
import { z } from "zod";
|
|
12052
13234
|
var linkIdentitySchema = z.object({
|
|
@@ -12074,7 +13256,7 @@ async function ensureIdentity(paths = resolveRuntimePaths()) {
|
|
|
12074
13256
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
12075
13257
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12076
13258
|
const identity = {
|
|
12077
|
-
install_id: `install_${
|
|
13259
|
+
install_id: `install_${randomUUID8().replaceAll("-", "")}`,
|
|
12078
13260
|
link_id: null,
|
|
12079
13261
|
public_key_pem: publicKey.export({ type: "spki", format: "pem" }).toString(),
|
|
12080
13262
|
private_key_pem: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
|
|
@@ -12111,7 +13293,7 @@ function getIdentityStatus(identity) {
|
|
|
12111
13293
|
}
|
|
12112
13294
|
|
|
12113
13295
|
// src/security/devices.ts
|
|
12114
|
-
import { randomBytes as randomBytes2, randomUUID as
|
|
13296
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID9, timingSafeEqual, createHash as createHash4 } from "crypto";
|
|
12115
13297
|
var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
|
|
12116
13298
|
var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
12117
13299
|
var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -12129,7 +13311,7 @@ async function createDeviceSession(input, paths = resolveRuntimePaths()) {
|
|
|
12129
13311
|
}
|
|
12130
13312
|
}
|
|
12131
13313
|
const device = {
|
|
12132
|
-
id: `dev_${
|
|
13314
|
+
id: `dev_${randomUUID9().replaceAll("-", "")}`,
|
|
12133
13315
|
label: normalizeDeviceLabel(input.label),
|
|
12134
13316
|
platform: normalizeDevicePlatform(input.platform),
|
|
12135
13317
|
model: normalizeDeviceModel(input.model),
|
|
@@ -12578,12 +13760,12 @@ async function readRawBody(request, maxBytes) {
|
|
|
12578
13760
|
}
|
|
12579
13761
|
return Buffer.concat(chunks);
|
|
12580
13762
|
}
|
|
12581
|
-
function
|
|
13763
|
+
function readString14(body, key) {
|
|
12582
13764
|
const value = body[key];
|
|
12583
13765
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12584
13766
|
}
|
|
12585
13767
|
function readOptionalProfileName(body) {
|
|
12586
|
-
return
|
|
13768
|
+
return readString14(body, "profile") ?? readString14(body, "profile_name") ?? readString14(body, "profileName") ?? void 0;
|
|
12587
13769
|
}
|
|
12588
13770
|
function readStringArray(body, ...keys) {
|
|
12589
13771
|
for (const key of keys) {
|
|
@@ -12621,7 +13803,7 @@ function readPositiveInteger2(value) {
|
|
|
12621
13803
|
}
|
|
12622
13804
|
return void 0;
|
|
12623
13805
|
}
|
|
12624
|
-
function
|
|
13806
|
+
function readBoolean2(value) {
|
|
12625
13807
|
if (typeof value === "boolean") {
|
|
12626
13808
|
return value;
|
|
12627
13809
|
}
|
|
@@ -12724,7 +13906,7 @@ function readMessageAttachments(value) {
|
|
|
12724
13906
|
}
|
|
12725
13907
|
const kind = readAttachmentString(record.kind);
|
|
12726
13908
|
const type = readAttachmentString(record.type);
|
|
12727
|
-
const isVoiceNote =
|
|
13909
|
+
const isVoiceNote = readBoolean2(record.is_voice_note) ?? readBoolean2(record.isVoiceNote);
|
|
12728
13910
|
const durationMs = readPositiveInteger2(record.duration_ms) ?? readPositiveInteger2(record.durationMs);
|
|
12729
13911
|
const waveform = readAttachmentWaveform2(
|
|
12730
13912
|
record.waveform ?? record.waveform_samples ?? record.waveformSamples
|
|
@@ -12816,7 +13998,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12816
13998
|
ctx.body = {
|
|
12817
13999
|
ok: true,
|
|
12818
14000
|
conversation: await conversations.createConversation({
|
|
12819
|
-
title:
|
|
14001
|
+
title: readString14(body, "title") ?? void 0,
|
|
12820
14002
|
profileName: readOptionalProfileName(body)
|
|
12821
14003
|
})
|
|
12822
14004
|
};
|
|
@@ -12887,7 +14069,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12887
14069
|
router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
|
|
12888
14070
|
await authenticateRequest(ctx, paths);
|
|
12889
14071
|
const body = await readJsonBody(ctx.req);
|
|
12890
|
-
const content =
|
|
14072
|
+
const content = readString14(body, "content") ?? readString14(body, "text") ?? readString14(body, "input") ?? "";
|
|
12891
14073
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
12892
14074
|
if (!content && attachments.length === 0) {
|
|
12893
14075
|
throw new LinkHttpError(
|
|
@@ -12903,7 +14085,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12903
14085
|
conversationId: ctx.params.conversationId,
|
|
12904
14086
|
content,
|
|
12905
14087
|
attachments,
|
|
12906
|
-
clientMessageId:
|
|
14088
|
+
clientMessageId: readString14(body, "client_message_id") ?? readString14(body, "clientMessageId") ?? void 0,
|
|
12907
14089
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
12908
14090
|
profileName: readOptionalProfileName(body)
|
|
12909
14091
|
})
|
|
@@ -12912,7 +14094,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12912
14094
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
12913
14095
|
await authenticateRequest(ctx, paths);
|
|
12914
14096
|
const body = await readJsonBody(ctx.req);
|
|
12915
|
-
const modelId =
|
|
14097
|
+
const modelId = readString14(body, "model_id") ?? readString14(body, "modelId") ?? readString14(body, "model");
|
|
12916
14098
|
if (!modelId) {
|
|
12917
14099
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
12918
14100
|
}
|
|
@@ -12942,7 +14124,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12942
14124
|
router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
|
|
12943
14125
|
await authenticateRequest(ctx, paths);
|
|
12944
14126
|
const body = await readJsonBody(ctx.req);
|
|
12945
|
-
const title =
|
|
14127
|
+
const title = readString14(body, "title") ?? readString14(body, "name") ?? readString14(body, "display_name");
|
|
12946
14128
|
if (!title) {
|
|
12947
14129
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
12948
14130
|
}
|
|
@@ -13006,7 +14188,7 @@ function registerConversationRoutes(router, options) {
|
|
|
13006
14188
|
async (ctx) => {
|
|
13007
14189
|
await authenticateRequest(ctx, paths);
|
|
13008
14190
|
const body = await readJsonBody(ctx.req);
|
|
13009
|
-
const scope =
|
|
14191
|
+
const scope = readString14(body, "scope") ?? "always";
|
|
13010
14192
|
ctx.body = {
|
|
13011
14193
|
ok: true,
|
|
13012
14194
|
...await conversations.resolveApproval({
|
|
@@ -13166,26 +14348,26 @@ function createHttpErrorMiddleware(logger) {
|
|
|
13166
14348
|
}
|
|
13167
14349
|
|
|
13168
14350
|
// src/hermes/profiles.ts
|
|
13169
|
-
import { mkdir as mkdir10, readdir as
|
|
13170
|
-
import
|
|
13171
|
-
import
|
|
14351
|
+
import { mkdir as mkdir10, readdir as readdir8, readFile as readFile12, rename as rename4, rm as rm6, stat as stat11 } from "fs/promises";
|
|
14352
|
+
import os5 from "os";
|
|
14353
|
+
import path17 from "path";
|
|
13172
14354
|
import YAML2 from "yaml";
|
|
13173
14355
|
var DEFAULT_PROFILE = "default";
|
|
13174
|
-
var
|
|
14356
|
+
var PROFILE_NAME_PATTERN4 = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
13175
14357
|
async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
13176
14358
|
const profiles = /* @__PURE__ */ new Map();
|
|
13177
14359
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
13178
|
-
const profilesDir =
|
|
13179
|
-
const entries = await
|
|
14360
|
+
const profilesDir = path17.join(os5.homedir(), ".hermes", "profiles");
|
|
14361
|
+
const entries = await readdir8(profilesDir, { withFileTypes: true }).catch(
|
|
13180
14362
|
(error) => {
|
|
13181
|
-
if (
|
|
14363
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
13182
14364
|
return [];
|
|
13183
14365
|
}
|
|
13184
14366
|
throw error;
|
|
13185
14367
|
}
|
|
13186
14368
|
);
|
|
13187
14369
|
for (const entry of entries) {
|
|
13188
|
-
if (entry.isDirectory() &&
|
|
14370
|
+
if (entry.isDirectory() && PROFILE_NAME_PATTERN4.test(entry.name)) {
|
|
13189
14371
|
profiles.set(entry.name, await profileInfo(entry.name, paths));
|
|
13190
14372
|
}
|
|
13191
14373
|
}
|
|
@@ -13202,8 +14384,8 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
13202
14384
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
13203
14385
|
assertProfileName(name);
|
|
13204
14386
|
const profile = await profileInfo(name, paths);
|
|
13205
|
-
const exists = await
|
|
13206
|
-
if (
|
|
14387
|
+
const exists = await stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
14388
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
13207
14389
|
return false;
|
|
13208
14390
|
}
|
|
13209
14391
|
throw error;
|
|
@@ -13243,8 +14425,8 @@ async function updateHermesProfileMetadata(name, metadata, paths = resolveRuntim
|
|
|
13243
14425
|
async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
|
|
13244
14426
|
assertMutableProfile(name);
|
|
13245
14427
|
const profile = await profileInfo(name, paths);
|
|
13246
|
-
const exists = await
|
|
13247
|
-
if (
|
|
14428
|
+
const exists = await stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
14429
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
13248
14430
|
return false;
|
|
13249
14431
|
}
|
|
13250
14432
|
throw error;
|
|
@@ -13270,7 +14452,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
13270
14452
|
return {
|
|
13271
14453
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
13272
14454
|
modelCount: listedModels?.models.length ?? 0,
|
|
13273
|
-
skillCount: await countSkills(
|
|
14455
|
+
skillCount: await countSkills(path17.join(profileDir, "skills")).catch(
|
|
13274
14456
|
() => 0
|
|
13275
14457
|
),
|
|
13276
14458
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -13308,17 +14490,17 @@ function assertMutableProfile(name) {
|
|
|
13308
14490
|
}
|
|
13309
14491
|
}
|
|
13310
14492
|
function assertProfileName(name) {
|
|
13311
|
-
if (!
|
|
14493
|
+
if (!PROFILE_NAME_PATTERN4.test(name)) {
|
|
13312
14494
|
throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
|
|
13313
14495
|
}
|
|
13314
14496
|
}
|
|
13315
|
-
function
|
|
14497
|
+
function isNodeError13(error, code) {
|
|
13316
14498
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
13317
14499
|
}
|
|
13318
14500
|
async function countSkills(root) {
|
|
13319
|
-
const entries = await
|
|
14501
|
+
const entries = await readdir8(root, { withFileTypes: true }).catch(
|
|
13320
14502
|
(error) => {
|
|
13321
|
-
if (
|
|
14503
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
13322
14504
|
return [];
|
|
13323
14505
|
}
|
|
13324
14506
|
throw error;
|
|
@@ -13326,7 +14508,7 @@ async function countSkills(root) {
|
|
|
13326
14508
|
);
|
|
13327
14509
|
let count = 0;
|
|
13328
14510
|
for (const entry of entries) {
|
|
13329
|
-
const entryPath =
|
|
14511
|
+
const entryPath = path17.join(root, entry.name);
|
|
13330
14512
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
13331
14513
|
continue;
|
|
13332
14514
|
}
|
|
@@ -13341,11 +14523,11 @@ async function countSkills(root) {
|
|
|
13341
14523
|
return count;
|
|
13342
14524
|
}
|
|
13343
14525
|
async function countConfiguredTools(profileName) {
|
|
13344
|
-
const raw = await
|
|
14526
|
+
const raw = await readFile12(
|
|
13345
14527
|
resolveHermesConfigPath(profileName),
|
|
13346
14528
|
"utf8"
|
|
13347
14529
|
).catch((error) => {
|
|
13348
|
-
if (
|
|
14530
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
13349
14531
|
return "";
|
|
13350
14532
|
}
|
|
13351
14533
|
throw error;
|
|
@@ -13353,14 +14535,14 @@ async function countConfiguredTools(profileName) {
|
|
|
13353
14535
|
if (!raw.trim()) {
|
|
13354
14536
|
return 0;
|
|
13355
14537
|
}
|
|
13356
|
-
const config =
|
|
14538
|
+
const config = toRecord12(YAML2.parse(raw));
|
|
13357
14539
|
const toolsets = /* @__PURE__ */ new Set();
|
|
13358
14540
|
collectToolsetValues(config.toolsets, toolsets);
|
|
13359
|
-
const platformToolsets =
|
|
14541
|
+
const platformToolsets = toRecord12(config.platform_toolsets);
|
|
13360
14542
|
for (const value of Object.values(platformToolsets)) {
|
|
13361
14543
|
collectToolsetValues(value, toolsets);
|
|
13362
14544
|
}
|
|
13363
|
-
const mcpServers = Object.keys(
|
|
14545
|
+
const mcpServers = Object.keys(toRecord12(config.mcp_servers)).length;
|
|
13364
14546
|
return toolsets.size + mcpServers;
|
|
13365
14547
|
}
|
|
13366
14548
|
function collectToolsetValues(value, target) {
|
|
@@ -13374,7 +14556,7 @@ function collectToolsetValues(value, target) {
|
|
|
13374
14556
|
target.add(value.trim());
|
|
13375
14557
|
}
|
|
13376
14558
|
}
|
|
13377
|
-
function
|
|
14559
|
+
function toRecord12(value) {
|
|
13378
14560
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
13379
14561
|
}
|
|
13380
14562
|
|
|
@@ -13571,7 +14753,7 @@ function toHermesCronJobInput(input) {
|
|
|
13571
14753
|
};
|
|
13572
14754
|
}
|
|
13573
14755
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
13574
|
-
const jobId =
|
|
14756
|
+
const jobId = readString14(input.job, "id") ?? readString14(input.job, "job_id");
|
|
13575
14757
|
if (!jobId) {
|
|
13576
14758
|
return input.job;
|
|
13577
14759
|
}
|
|
@@ -13588,9 +14770,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
13588
14770
|
}
|
|
13589
14771
|
function readCronJobCreateInput(body) {
|
|
13590
14772
|
const input = {};
|
|
13591
|
-
const name =
|
|
13592
|
-
const prompt =
|
|
13593
|
-
const schedule =
|
|
14773
|
+
const name = readString14(body, "name") ?? readString14(body, "title");
|
|
14774
|
+
const prompt = readString14(body, "prompt") ?? readString14(body, "description") ?? readString14(body, "task");
|
|
14775
|
+
const schedule = readString14(body, "schedule");
|
|
13594
14776
|
if (!name) {
|
|
13595
14777
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
13596
14778
|
}
|
|
@@ -13611,7 +14793,7 @@ function readCronJobCreateInput(body) {
|
|
|
13611
14793
|
input.name = name;
|
|
13612
14794
|
input.prompt = prompt;
|
|
13613
14795
|
input.schedule = schedule;
|
|
13614
|
-
input.deliver =
|
|
14796
|
+
input.deliver = readString14(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
13615
14797
|
const skills = readOptionalCronSkills(body);
|
|
13616
14798
|
if (skills) {
|
|
13617
14799
|
input.skills = skills;
|
|
@@ -13669,7 +14851,7 @@ function readCronJobUpdateInput(body) {
|
|
|
13669
14851
|
if (repeat !== void 0) {
|
|
13670
14852
|
input.repeat = repeat;
|
|
13671
14853
|
}
|
|
13672
|
-
const enabled =
|
|
14854
|
+
const enabled = readBoolean2(body.enabled);
|
|
13673
14855
|
if (enabled !== void 0) {
|
|
13674
14856
|
input.enabled = enabled;
|
|
13675
14857
|
}
|
|
@@ -13839,7 +15021,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13839
15021
|
router.delete("/api/v1/model-configs", async (ctx) => {
|
|
13840
15022
|
await authenticateRequest(ctx, paths);
|
|
13841
15023
|
const body = await readJsonBody(ctx.req);
|
|
13842
|
-
const modelId =
|
|
15024
|
+
const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
|
|
13843
15025
|
if (!modelId) {
|
|
13844
15026
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13845
15027
|
}
|
|
@@ -13894,7 +15076,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13894
15076
|
await authenticateRequest(ctx, paths);
|
|
13895
15077
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
13896
15078
|
const body = await readJsonBody(ctx.req);
|
|
13897
|
-
const modelId =
|
|
15079
|
+
const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
|
|
13898
15080
|
if (!modelId) {
|
|
13899
15081
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13900
15082
|
}
|
|
@@ -13911,9 +15093,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13911
15093
|
});
|
|
13912
15094
|
}
|
|
13913
15095
|
function readModelConfigInput(body) {
|
|
13914
|
-
const id =
|
|
13915
|
-
const provider =
|
|
13916
|
-
const baseUrl =
|
|
15096
|
+
const id = readString14(body, "id") ?? readString14(body, "model_id") ?? readString14(body, "modelId");
|
|
15097
|
+
const provider = readString14(body, "provider") ?? readString14(body, "provider_key") ?? readString14(body, "providerKey");
|
|
15098
|
+
const baseUrl = readString14(body, "base_url") ?? readString14(body, "baseUrl");
|
|
13917
15099
|
if (!id || !provider || !baseUrl) {
|
|
13918
15100
|
throw new LinkHttpError(
|
|
13919
15101
|
400,
|
|
@@ -13923,28 +15105,28 @@ function readModelConfigInput(body) {
|
|
|
13923
15105
|
}
|
|
13924
15106
|
return {
|
|
13925
15107
|
id,
|
|
13926
|
-
originalModelId:
|
|
15108
|
+
originalModelId: readString14(body, "original_model_id") ?? readString14(body, "originalModelId") ?? readString14(body, "original_id") ?? void 0,
|
|
13927
15109
|
provider,
|
|
13928
|
-
providerName:
|
|
15110
|
+
providerName: readString14(body, "provider_name") ?? readString14(body, "providerName") ?? void 0,
|
|
13929
15111
|
baseUrl,
|
|
13930
|
-
apiKey:
|
|
13931
|
-
apiMode:
|
|
15112
|
+
apiKey: readString14(body, "api_key") ?? readString14(body, "apiKey") ?? void 0,
|
|
15113
|
+
apiMode: readString14(body, "api_mode") ?? readString14(body, "apiMode") ?? void 0,
|
|
13932
15114
|
contextLength: readPositiveInteger2(
|
|
13933
15115
|
body.context_length ?? body.contextLength
|
|
13934
15116
|
),
|
|
13935
|
-
keyEnv:
|
|
13936
|
-
setDefault:
|
|
13937
|
-
reasoningEffort:
|
|
15117
|
+
keyEnv: readString14(body, "key_env") ?? readString14(body, "keyEnv") ?? void 0,
|
|
15118
|
+
setDefault: readBoolean2(body.set_default ?? body.setDefault),
|
|
15119
|
+
reasoningEffort: readString14(body, "reasoning_effort") ?? readString14(body, "reasoningEffort") ?? void 0
|
|
13938
15120
|
};
|
|
13939
15121
|
}
|
|
13940
15122
|
function readModelDefaultsInput(body) {
|
|
13941
15123
|
return {
|
|
13942
|
-
taskModelId:
|
|
13943
|
-
compressionModelId:
|
|
15124
|
+
taskModelId: readString14(body, "task_model_id") ?? readString14(body, "taskModelId") ?? readString14(body, "default_model_id") ?? readString14(body, "defaultModelId") ?? void 0,
|
|
15125
|
+
compressionModelId: readString14(body, "compression_model_id") ?? readString14(body, "compressionModelId") ?? void 0
|
|
13944
15126
|
};
|
|
13945
15127
|
}
|
|
13946
15128
|
function shouldReloadGatewayAfterModelConfigChange(body) {
|
|
13947
|
-
const explicit =
|
|
15129
|
+
const explicit = readBoolean2(body.reload_gateway ?? body.reloadGateway) ?? (readBoolean2(body.skip_gateway_reload ?? body.skipGatewayReload) === true ? false : void 0);
|
|
13948
15130
|
return explicit ?? true;
|
|
13949
15131
|
}
|
|
13950
15132
|
function markModelConfigAppliedWithoutGatewayReload(result) {
|
|
@@ -14025,19 +15207,19 @@ import {
|
|
|
14025
15207
|
copyFile as copyFile2,
|
|
14026
15208
|
cp,
|
|
14027
15209
|
mkdir as mkdir11,
|
|
14028
|
-
readFile as
|
|
15210
|
+
readFile as readFile13,
|
|
14029
15211
|
rm as rm7,
|
|
14030
|
-
stat as
|
|
15212
|
+
stat as stat12,
|
|
14031
15213
|
writeFile as writeFile4,
|
|
14032
15214
|
rename as rename5
|
|
14033
15215
|
} from "fs/promises";
|
|
14034
|
-
import
|
|
15216
|
+
import path18 from "path";
|
|
14035
15217
|
import YAML3 from "yaml";
|
|
14036
15218
|
var PROFILE_CREATE_LOG_FILE = "profile-create.log";
|
|
14037
15219
|
var PROFILE_CREATE_LOG_MAX_FILES = 3;
|
|
14038
15220
|
var MAX_PROFILE_CREATE_LOG_LINES = 260;
|
|
14039
15221
|
var MAX_OUTPUT_LINE_LENGTH = 1200;
|
|
14040
|
-
var
|
|
15222
|
+
var PROFILE_NAME_PATTERN5 = /^[a-z0-9][a-z0-9_-]{0,63}$/u;
|
|
14041
15223
|
var ALL_COPY_SCOPES = [
|
|
14042
15224
|
"models",
|
|
14043
15225
|
"skills",
|
|
@@ -14289,7 +15471,7 @@ function normalizeOptionalProfileName(value) {
|
|
|
14289
15471
|
if (!trimmed) {
|
|
14290
15472
|
return null;
|
|
14291
15473
|
}
|
|
14292
|
-
if (!
|
|
15474
|
+
if (!PROFILE_NAME_PATTERN5.test(trimmed)) {
|
|
14293
15475
|
throw new LinkHttpError(
|
|
14294
15476
|
400,
|
|
14295
15477
|
"invalid_profile_name",
|
|
@@ -14421,7 +15603,7 @@ function copyModelConfig(source, target) {
|
|
|
14421
15603
|
copied[key] = cloneJson(source[key]);
|
|
14422
15604
|
}
|
|
14423
15605
|
}
|
|
14424
|
-
const sourceAuxiliary =
|
|
15606
|
+
const sourceAuxiliary = toRecord13(source.auxiliary);
|
|
14425
15607
|
if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
|
|
14426
15608
|
const targetAuxiliary = ensureRecord2(target, "auxiliary");
|
|
14427
15609
|
targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
|
|
@@ -14430,12 +15612,12 @@ function copyModelConfig(source, target) {
|
|
|
14430
15612
|
return copied;
|
|
14431
15613
|
}
|
|
14432
15614
|
function copyToolPermissionsConfig(source, target) {
|
|
14433
|
-
const sourcePlatformToolsets =
|
|
15615
|
+
const sourcePlatformToolsets = toRecord13(source.platform_toolsets);
|
|
14434
15616
|
if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
|
|
14435
15617
|
const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
|
|
14436
15618
|
targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
|
|
14437
15619
|
}
|
|
14438
|
-
const sourceStt =
|
|
15620
|
+
const sourceStt = toRecord13(source.stt);
|
|
14439
15621
|
if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
|
|
14440
15622
|
const targetStt = ensureRecord2(target, "stt");
|
|
14441
15623
|
targetStt.enabled = cloneJson(sourceStt.enabled);
|
|
@@ -14482,9 +15664,9 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
14482
15664
|
return keys;
|
|
14483
15665
|
}
|
|
14484
15666
|
async function writeEnvValues(profileName, values) {
|
|
14485
|
-
const envPath =
|
|
14486
|
-
const existingRaw = await
|
|
14487
|
-
if (
|
|
15667
|
+
const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
|
|
15668
|
+
const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
|
|
15669
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14488
15670
|
return "";
|
|
14489
15671
|
}
|
|
14490
15672
|
throw error;
|
|
@@ -14509,7 +15691,7 @@ async function writeEnvValues(profileName, values) {
|
|
|
14509
15691
|
nextLines.push(`${key}=${formatEnvValue2(value)}`);
|
|
14510
15692
|
}
|
|
14511
15693
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
14512
|
-
await mkdir11(
|
|
15694
|
+
await mkdir11(path18.dirname(envPath), { recursive: true, mode: 448 });
|
|
14513
15695
|
if (existingRaw) {
|
|
14514
15696
|
await copyFile2(envPath, `${envPath}.bak.${Date.now()}`);
|
|
14515
15697
|
}
|
|
@@ -14518,8 +15700,8 @@ async function writeEnvValues(profileName, values) {
|
|
|
14518
15700
|
await rename5(tempPath, envPath);
|
|
14519
15701
|
}
|
|
14520
15702
|
async function copySkills(sourceProfile, targetProfile) {
|
|
14521
|
-
const sourceSkills =
|
|
14522
|
-
const targetSkills =
|
|
15703
|
+
const sourceSkills = path18.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
15704
|
+
const targetSkills = path18.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
14523
15705
|
if (!await pathExists(sourceSkills)) {
|
|
14524
15706
|
return;
|
|
14525
15707
|
}
|
|
@@ -14538,21 +15720,21 @@ function copyProperty(source, target, key) {
|
|
|
14538
15720
|
}
|
|
14539
15721
|
}
|
|
14540
15722
|
async function readYamlConfig(configPath) {
|
|
14541
|
-
const existingRaw = await
|
|
15723
|
+
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
14542
15724
|
(error) => {
|
|
14543
|
-
if (
|
|
15725
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14544
15726
|
return null;
|
|
14545
15727
|
}
|
|
14546
15728
|
throw error;
|
|
14547
15729
|
}
|
|
14548
15730
|
);
|
|
14549
15731
|
return {
|
|
14550
|
-
config:
|
|
15732
|
+
config: toRecord13(existingRaw ? YAML3.parse(existingRaw) : {}),
|
|
14551
15733
|
existingRaw
|
|
14552
15734
|
};
|
|
14553
15735
|
}
|
|
14554
15736
|
async function writeYamlConfig(configPath, input) {
|
|
14555
|
-
await mkdir11(
|
|
15737
|
+
await mkdir11(path18.dirname(configPath), { recursive: true, mode: 448 });
|
|
14556
15738
|
if (input.existingRaw) {
|
|
14557
15739
|
await copyFile2(configPath, `${configPath}.bak.${Date.now()}`);
|
|
14558
15740
|
}
|
|
@@ -14600,7 +15782,7 @@ async function writeProfileCreationState(paths, state) {
|
|
|
14600
15782
|
await writeJsonFile(profileCreationStatePath(paths), state);
|
|
14601
15783
|
}
|
|
14602
15784
|
async function readProfileCreationLogLines(paths) {
|
|
14603
|
-
const raw = await
|
|
15785
|
+
const raw = await readFile13(profileCreationLogPath(paths), "utf8").catch(() => "");
|
|
14604
15786
|
if (!raw.trim()) {
|
|
14605
15787
|
return [];
|
|
14606
15788
|
}
|
|
@@ -14609,10 +15791,10 @@ async function readProfileCreationLogLines(paths) {
|
|
|
14609
15791
|
);
|
|
14610
15792
|
}
|
|
14611
15793
|
function profileCreationStatePath(paths) {
|
|
14612
|
-
return
|
|
15794
|
+
return path18.join(paths.runDir, "profile-create-state.json");
|
|
14613
15795
|
}
|
|
14614
15796
|
function profileCreationLogPath(paths) {
|
|
14615
|
-
return
|
|
15797
|
+
return path18.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
14616
15798
|
}
|
|
14617
15799
|
async function clearProfileCreationLogFiles(paths) {
|
|
14618
15800
|
const primary = profileCreationLogPath(paths);
|
|
@@ -14625,8 +15807,8 @@ async function clearProfileCreationLogFiles(paths) {
|
|
|
14625
15807
|
]);
|
|
14626
15808
|
}
|
|
14627
15809
|
async function pathExists(targetPath) {
|
|
14628
|
-
return await
|
|
14629
|
-
if (
|
|
15810
|
+
return await stat12(targetPath).then(() => true).catch((error) => {
|
|
15811
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
14630
15812
|
return false;
|
|
14631
15813
|
}
|
|
14632
15814
|
throw error;
|
|
@@ -14658,7 +15840,7 @@ function ensureRecord2(target, key) {
|
|
|
14658
15840
|
target[key] = next;
|
|
14659
15841
|
return next;
|
|
14660
15842
|
}
|
|
14661
|
-
function
|
|
15843
|
+
function toRecord13(value) {
|
|
14662
15844
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
14663
15845
|
}
|
|
14664
15846
|
function cloneJson(value) {
|
|
@@ -14673,7 +15855,7 @@ function formatEnvValue2(value) {
|
|
|
14673
15855
|
function escapeRegExp2(value) {
|
|
14674
15856
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
14675
15857
|
}
|
|
14676
|
-
function
|
|
15858
|
+
function isNodeError14(error, code) {
|
|
14677
15859
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
14678
15860
|
}
|
|
14679
15861
|
|
|
@@ -14758,16 +15940,16 @@ function readProfilePermissionsInput(body) {
|
|
|
14758
15940
|
const approvals = readOptionalObject(body, "approvals");
|
|
14759
15941
|
if (approvals) {
|
|
14760
15942
|
input.approvals = {
|
|
14761
|
-
mode:
|
|
15943
|
+
mode: readString14(approvals, "mode") ?? readString14(approvals, "approval_mode") ?? readString14(approvals, "approvalMode") ?? void 0,
|
|
14762
15944
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
14763
|
-
cronMode:
|
|
15945
|
+
cronMode: readString14(approvals, "cron_mode") ?? readString14(approvals, "cronMode") ?? void 0
|
|
14764
15946
|
};
|
|
14765
15947
|
}
|
|
14766
15948
|
const terminal = readOptionalObject(body, "terminal");
|
|
14767
15949
|
if (terminal) {
|
|
14768
15950
|
input.terminal = {
|
|
14769
|
-
backend:
|
|
14770
|
-
cwd:
|
|
15951
|
+
backend: readString14(terminal, "backend") ?? void 0,
|
|
15952
|
+
cwd: readString14(terminal, "cwd") ?? void 0,
|
|
14771
15953
|
containerCpu: readPositiveInteger2(
|
|
14772
15954
|
terminal.container_cpu ?? terminal.containerCpu
|
|
14773
15955
|
),
|
|
@@ -14777,7 +15959,7 @@ function readProfilePermissionsInput(body) {
|
|
|
14777
15959
|
containerDisk: readPositiveInteger2(
|
|
14778
15960
|
terminal.container_disk ?? terminal.containerDisk
|
|
14779
15961
|
),
|
|
14780
|
-
containerPersistent:
|
|
15962
|
+
containerPersistent: readBoolean2(
|
|
14781
15963
|
terminal.container_persistent ?? terminal.containerPersistent
|
|
14782
15964
|
)
|
|
14783
15965
|
};
|
|
@@ -14789,7 +15971,7 @@ function readProfilePermissionsInput(body) {
|
|
|
14789
15971
|
toolsets.enabled_toolsets ?? toolsets.enabledToolsets ?? toolsets.enabled,
|
|
14790
15972
|
"toolsets.enabled"
|
|
14791
15973
|
) ?? void 0,
|
|
14792
|
-
mcpEnabled:
|
|
15974
|
+
mcpEnabled: readBoolean2(toolsets.mcp_enabled ?? toolsets.mcpEnabled)
|
|
14793
15975
|
};
|
|
14794
15976
|
}
|
|
14795
15977
|
if (Object.keys(input).length === 0) {
|
|
@@ -14884,13 +16066,13 @@ import {
|
|
|
14884
16066
|
access as access3,
|
|
14885
16067
|
copyFile as copyFile3,
|
|
14886
16068
|
mkdir as mkdir12,
|
|
14887
|
-
readdir as
|
|
14888
|
-
readFile as
|
|
16069
|
+
readdir as readdir9,
|
|
16070
|
+
readFile as readFile14,
|
|
14889
16071
|
rename as rename6,
|
|
14890
|
-
stat as
|
|
16072
|
+
stat as stat13,
|
|
14891
16073
|
writeFile as writeFile5
|
|
14892
16074
|
} from "fs/promises";
|
|
14893
|
-
import
|
|
16075
|
+
import path19 from "path";
|
|
14894
16076
|
import YAML4 from "yaml";
|
|
14895
16077
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
14896
16078
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -15107,7 +16289,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
15107
16289
|
if (provider === "hindsight") {
|
|
15108
16290
|
await patchJsonProviderConfig(
|
|
15109
16291
|
profileName,
|
|
15110
|
-
|
|
16292
|
+
path19.join("hindsight", "config.json"),
|
|
15111
16293
|
{
|
|
15112
16294
|
mode: patch.mode,
|
|
15113
16295
|
api_url: patch.apiUrl,
|
|
@@ -15160,7 +16342,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
|
|
|
15160
16342
|
"\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
|
|
15161
16343
|
);
|
|
15162
16344
|
}
|
|
15163
|
-
const config =
|
|
16345
|
+
const config = toRecord14(parsed);
|
|
15164
16346
|
if (Object.keys(config).length === 0 && parsed !== null) {
|
|
15165
16347
|
throw new HermesMemoryError(
|
|
15166
16348
|
"memory_provider_config_invalid",
|
|
@@ -15201,7 +16383,7 @@ function isSensitiveConfigKey(key) {
|
|
|
15201
16383
|
}
|
|
15202
16384
|
async function writeCustomProviderConfig(profileName, provider, config) {
|
|
15203
16385
|
const configPath = customProviderConfigPath(profileName, provider);
|
|
15204
|
-
await mkdir12(
|
|
16386
|
+
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
15205
16387
|
await writeFile5(configPath, `${JSON.stringify(config, null, 2)}
|
|
15206
16388
|
`, {
|
|
15207
16389
|
encoding: "utf8",
|
|
@@ -15252,21 +16434,21 @@ function normalizeCustomProviderId(provider) {
|
|
|
15252
16434
|
}
|
|
15253
16435
|
async function patchHermesMemoryProvider(profileName, provider) {
|
|
15254
16436
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15255
|
-
const existingRaw = await
|
|
16437
|
+
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
15256
16438
|
(error) => {
|
|
15257
|
-
if (
|
|
16439
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15258
16440
|
return null;
|
|
15259
16441
|
}
|
|
15260
16442
|
throw error;
|
|
15261
16443
|
}
|
|
15262
16444
|
);
|
|
15263
16445
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15264
|
-
const config =
|
|
15265
|
-
const memory =
|
|
16446
|
+
const config = toRecord14(document.toJSON());
|
|
16447
|
+
const memory = toRecord14(config.memory);
|
|
15266
16448
|
memory.provider = provider === "built-in" ? "" : provider;
|
|
15267
16449
|
config.memory = memory;
|
|
15268
16450
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
15269
|
-
await mkdir12(
|
|
16451
|
+
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
15270
16452
|
if (backupPath) {
|
|
15271
16453
|
await copyFile3(configPath, backupPath);
|
|
15272
16454
|
}
|
|
@@ -15279,13 +16461,13 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
15279
16461
|
await rename6(tempPath, configPath);
|
|
15280
16462
|
}
|
|
15281
16463
|
function resolveMemoryDir(profileName) {
|
|
15282
|
-
return
|
|
16464
|
+
return path19.join(resolveHermesProfileDir(profileName), "memories");
|
|
15283
16465
|
}
|
|
15284
16466
|
async function readMemoryStore(profileName, target, limits) {
|
|
15285
16467
|
const filePath = memoryFilePath(profileName, target);
|
|
15286
16468
|
const entries = await readMemoryEntries(filePath);
|
|
15287
|
-
const fileStat = await
|
|
15288
|
-
if (
|
|
16469
|
+
const fileStat = await stat13(filePath).catch((error) => {
|
|
16470
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15289
16471
|
return null;
|
|
15290
16472
|
}
|
|
15291
16473
|
throw error;
|
|
@@ -15313,8 +16495,8 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
15313
16495
|
};
|
|
15314
16496
|
}
|
|
15315
16497
|
async function readMemoryEntries(filePath) {
|
|
15316
|
-
const raw = await
|
|
15317
|
-
if (
|
|
16498
|
+
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
16499
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15318
16500
|
return "";
|
|
15319
16501
|
}
|
|
15320
16502
|
throw error;
|
|
@@ -15334,9 +16516,9 @@ async function mutateMemoryEntries(profileName, target, mutate) {
|
|
|
15334
16516
|
async function writeMemoryEntries(profileName, target, entries) {
|
|
15335
16517
|
assertWithinLimit(target, entries, await readMemoryLimits(profileName));
|
|
15336
16518
|
const filePath = memoryFilePath(profileName, target);
|
|
15337
|
-
const dir =
|
|
16519
|
+
const dir = path19.dirname(filePath);
|
|
15338
16520
|
await mkdir12(dir, { recursive: true, mode: 448 });
|
|
15339
|
-
const tempPath =
|
|
16521
|
+
const tempPath = path19.join(
|
|
15340
16522
|
dir,
|
|
15341
16523
|
`.mem_${process.pid}_${Date.now()}_${target}.tmp`
|
|
15342
16524
|
);
|
|
@@ -15347,7 +16529,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
15347
16529
|
await rename6(tempPath, filePath);
|
|
15348
16530
|
}
|
|
15349
16531
|
function memoryFilePath(profileName, target) {
|
|
15350
|
-
return
|
|
16532
|
+
return path19.join(
|
|
15351
16533
|
resolveMemoryDir(profileName),
|
|
15352
16534
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
15353
16535
|
);
|
|
@@ -15407,7 +16589,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
15407
16589
|
configurable: true,
|
|
15408
16590
|
configured: true,
|
|
15409
16591
|
configurationIssue: null,
|
|
15410
|
-
providerConfigPath:
|
|
16592
|
+
providerConfigPath: path19.join(
|
|
15411
16593
|
resolveHermesProfileDir(profileName),
|
|
15412
16594
|
"<provider>.json"
|
|
15413
16595
|
),
|
|
@@ -15485,7 +16667,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15485
16667
|
const config2 = await readJsonObject(
|
|
15486
16668
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
15487
16669
|
);
|
|
15488
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
16670
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString15(config2.apiKey)) || isConfiguredEnvValue(readString15(config2.api_key)) || isConfiguredEnvValue(readString15(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
15489
16671
|
configured: false,
|
|
15490
16672
|
issue: "Honcho \u9700\u8981\u5148\u914D\u7F6E HONCHO_API_KEY\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
15491
16673
|
};
|
|
@@ -15494,7 +16676,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15494
16676
|
const config2 = await readJsonObject(
|
|
15495
16677
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
15496
16678
|
);
|
|
15497
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
16679
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString15(config2.api_key)) ? { configured: true, issue: null } : {
|
|
15498
16680
|
configured: false,
|
|
15499
16681
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u673A Hermes .env \u914D\u7F6E MEM0_API_KEY\u3002"
|
|
15500
16682
|
};
|
|
@@ -15536,7 +16718,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15536
16718
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15537
16719
|
);
|
|
15538
16720
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
15539
|
-
const apiKey =
|
|
16721
|
+
const apiKey = readString15(config.apiKey) ?? readString15(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
15540
16722
|
if (mode === "cloud") {
|
|
15541
16723
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
15542
16724
|
configured: false,
|
|
@@ -15544,15 +16726,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15544
16726
|
};
|
|
15545
16727
|
}
|
|
15546
16728
|
if (mode === "local_external") {
|
|
15547
|
-
const apiUrl =
|
|
16729
|
+
const apiUrl = readString15(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
15548
16730
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
15549
16731
|
configured: false,
|
|
15550
16732
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
15551
16733
|
};
|
|
15552
16734
|
}
|
|
15553
16735
|
if (mode === "local_embedded") {
|
|
15554
|
-
const llmProvider =
|
|
15555
|
-
const llmModel =
|
|
16736
|
+
const llmProvider = readString15(config.llm_provider) ?? "openai";
|
|
16737
|
+
const llmModel = readString15(config.llm_model);
|
|
15556
16738
|
if (!llmModel) {
|
|
15557
16739
|
return {
|
|
15558
16740
|
configured: false,
|
|
@@ -15560,7 +16742,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15560
16742
|
};
|
|
15561
16743
|
}
|
|
15562
16744
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
15563
|
-
|
|
16745
|
+
readString15(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
15564
16746
|
)) {
|
|
15565
16747
|
return {
|
|
15566
16748
|
configured: false,
|
|
@@ -15568,7 +16750,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15568
16750
|
};
|
|
15569
16751
|
}
|
|
15570
16752
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
15571
|
-
|
|
16753
|
+
readString15(config.llmApiKey) ?? readString15(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
15572
16754
|
)) {
|
|
15573
16755
|
return {
|
|
15574
16756
|
configured: false,
|
|
@@ -15708,8 +16890,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15708
16890
|
const config = await readJsonObject(
|
|
15709
16891
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15710
16892
|
);
|
|
15711
|
-
const banks =
|
|
15712
|
-
const hermesBank =
|
|
16893
|
+
const banks = toRecord14(config.banks);
|
|
16894
|
+
const hermesBank = toRecord14(banks.hermes);
|
|
15713
16895
|
const mode = normalizeHindsightMode(config.mode);
|
|
15714
16896
|
return [
|
|
15715
16897
|
selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
|
|
@@ -15761,7 +16943,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15761
16943
|
stringSetting(
|
|
15762
16944
|
"dbPath",
|
|
15763
16945
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
15764
|
-
config.db_path ??
|
|
16946
|
+
config.db_path ?? path19.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
15765
16947
|
),
|
|
15766
16948
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
15767
16949
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -15784,7 +16966,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15784
16966
|
stringSetting(
|
|
15785
16967
|
"workingDirectory",
|
|
15786
16968
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
15787
|
-
|
|
16969
|
+
path19.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
15788
16970
|
false
|
|
15789
16971
|
)
|
|
15790
16972
|
];
|
|
@@ -15793,16 +16975,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15793
16975
|
}
|
|
15794
16976
|
function memoryProviderConfigPath(profileName, provider) {
|
|
15795
16977
|
if (provider === "honcho") {
|
|
15796
|
-
return
|
|
16978
|
+
return path19.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
15797
16979
|
}
|
|
15798
16980
|
if (provider === "mem0") {
|
|
15799
|
-
return
|
|
16981
|
+
return path19.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
15800
16982
|
}
|
|
15801
16983
|
if (provider === "supermemory") {
|
|
15802
|
-
return
|
|
16984
|
+
return path19.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
15803
16985
|
}
|
|
15804
16986
|
if (provider === "hindsight") {
|
|
15805
|
-
return
|
|
16987
|
+
return path19.join(
|
|
15806
16988
|
resolveHermesProfileDir(profileName),
|
|
15807
16989
|
"hindsight",
|
|
15808
16990
|
"config.json"
|
|
@@ -15811,21 +16993,21 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
15811
16993
|
return null;
|
|
15812
16994
|
}
|
|
15813
16995
|
function customProviderConfigPath(profileName, provider) {
|
|
15814
|
-
return
|
|
16996
|
+
return path19.join(
|
|
15815
16997
|
resolveHermesProfileDir(profileName),
|
|
15816
16998
|
`${normalizeCustomProviderId(provider)}.json`
|
|
15817
16999
|
);
|
|
15818
17000
|
}
|
|
15819
17001
|
function customProviderRegistryPath(profileName) {
|
|
15820
|
-
return
|
|
17002
|
+
return path19.join(
|
|
15821
17003
|
resolveHermesProfileDir(profileName),
|
|
15822
17004
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
15823
17005
|
);
|
|
15824
17006
|
}
|
|
15825
17007
|
async function readCustomProviderRegistry(profileName) {
|
|
15826
|
-
const raw = await
|
|
17008
|
+
const raw = await readFile14(customProviderRegistryPath(profileName), "utf8").catch(
|
|
15827
17009
|
(error) => {
|
|
15828
|
-
if (
|
|
17010
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15829
17011
|
return "";
|
|
15830
17012
|
}
|
|
15831
17013
|
throw error;
|
|
@@ -15836,18 +17018,18 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
15836
17018
|
}
|
|
15837
17019
|
try {
|
|
15838
17020
|
const parsed = JSON.parse(raw);
|
|
15839
|
-
const providers = Array.isArray(parsed) ? parsed : Array.isArray(
|
|
17021
|
+
const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord14(parsed).providers) ? toRecord14(parsed).providers : [];
|
|
15840
17022
|
return providers.map((item) => {
|
|
15841
17023
|
if (typeof item === "string") {
|
|
15842
17024
|
const id2 = normalizeCustomProviderId(item);
|
|
15843
17025
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
15844
17026
|
}
|
|
15845
|
-
const record =
|
|
15846
|
-
const id = normalizeCustomProviderId(
|
|
17027
|
+
const record = toRecord14(item);
|
|
17028
|
+
const id = normalizeCustomProviderId(readString15(record.id) ?? "");
|
|
15847
17029
|
return {
|
|
15848
17030
|
id,
|
|
15849
|
-
label:
|
|
15850
|
-
description:
|
|
17031
|
+
label: readString15(record.label) ?? id,
|
|
17032
|
+
description: readString15(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15851
17033
|
};
|
|
15852
17034
|
}).filter((item) => item.id);
|
|
15853
17035
|
} catch {
|
|
@@ -15862,7 +17044,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
15862
17044
|
{ id: providerId, label: providerId, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" }
|
|
15863
17045
|
].sort((left, right) => left.id.localeCompare(right.id));
|
|
15864
17046
|
const registryPath2 = customProviderRegistryPath(profileName);
|
|
15865
|
-
await mkdir12(
|
|
17047
|
+
await mkdir12(path19.dirname(registryPath2), { recursive: true, mode: 448 });
|
|
15866
17048
|
await writeFile5(
|
|
15867
17049
|
registryPath2,
|
|
15868
17050
|
`${JSON.stringify({ providers }, null, 2)}
|
|
@@ -15871,10 +17053,10 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
15871
17053
|
);
|
|
15872
17054
|
}
|
|
15873
17055
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
15874
|
-
const pluginsDir =
|
|
15875
|
-
const entries = await
|
|
17056
|
+
const pluginsDir = path19.join(resolveHermesProfileDir(profileName), "plugins");
|
|
17057
|
+
const entries = await readdir9(pluginsDir, { withFileTypes: true }).catch(
|
|
15876
17058
|
(error) => {
|
|
15877
|
-
if (
|
|
17059
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15878
17060
|
return [];
|
|
15879
17061
|
}
|
|
15880
17062
|
throw error;
|
|
@@ -15891,21 +17073,21 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
15891
17073
|
} catch {
|
|
15892
17074
|
continue;
|
|
15893
17075
|
}
|
|
15894
|
-
const providerDir =
|
|
17076
|
+
const providerDir = path19.join(pluginsDir, entry.name);
|
|
15895
17077
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
15896
17078
|
continue;
|
|
15897
17079
|
}
|
|
15898
17080
|
const meta = await readPluginMetadata(providerDir);
|
|
15899
17081
|
descriptors.push({
|
|
15900
17082
|
id: providerId,
|
|
15901
|
-
label:
|
|
15902
|
-
description:
|
|
17083
|
+
label: readString15(meta.name) ?? providerId,
|
|
17084
|
+
description: readString15(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15903
17085
|
});
|
|
15904
17086
|
}
|
|
15905
17087
|
return descriptors;
|
|
15906
17088
|
}
|
|
15907
17089
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
15908
|
-
const providerDir =
|
|
17090
|
+
const providerDir = path19.join(
|
|
15909
17091
|
resolveHermesProfileDir(profileName),
|
|
15910
17092
|
"plugins",
|
|
15911
17093
|
normalizeCustomProviderId(provider)
|
|
@@ -15913,9 +17095,9 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
15913
17095
|
return isMemoryProviderPluginDir(providerDir);
|
|
15914
17096
|
}
|
|
15915
17097
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
15916
|
-
const source = await
|
|
17098
|
+
const source = await readFile14(path19.join(providerDir, "__init__.py"), "utf8").catch(
|
|
15917
17099
|
(error) => {
|
|
15918
|
-
if (
|
|
17100
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15919
17101
|
return "";
|
|
15920
17102
|
}
|
|
15921
17103
|
throw error;
|
|
@@ -15925,22 +17107,22 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
15925
17107
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
15926
17108
|
}
|
|
15927
17109
|
async function readPluginMetadata(providerDir) {
|
|
15928
|
-
const raw = await
|
|
17110
|
+
const raw = await readFile14(path19.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
15929
17111
|
(error) => {
|
|
15930
|
-
if (
|
|
17112
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15931
17113
|
return "";
|
|
15932
17114
|
}
|
|
15933
17115
|
throw error;
|
|
15934
17116
|
}
|
|
15935
17117
|
);
|
|
15936
|
-
return raw ?
|
|
17118
|
+
return raw ? toRecord14(YAML4.parse(raw)) : {};
|
|
15937
17119
|
}
|
|
15938
17120
|
async function resolveByteRoverCli() {
|
|
15939
17121
|
const candidates = [
|
|
15940
|
-
...(process.env.PATH ?? "").split(
|
|
15941
|
-
|
|
17122
|
+
...(process.env.PATH ?? "").split(path19.delimiter).filter(Boolean).map((dir) => path19.join(dir, "brv")),
|
|
17123
|
+
path19.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
15942
17124
|
"/usr/local/bin/brv",
|
|
15943
|
-
|
|
17125
|
+
path19.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
15944
17126
|
].filter(Boolean);
|
|
15945
17127
|
for (const candidate of candidates) {
|
|
15946
17128
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -15951,32 +17133,32 @@ async function resolveByteRoverCli() {
|
|
|
15951
17133
|
return null;
|
|
15952
17134
|
}
|
|
15953
17135
|
async function readHolographicProviderConfig(profileName) {
|
|
15954
|
-
const raw = await
|
|
17136
|
+
const raw = await readFile14(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
15955
17137
|
(error) => {
|
|
15956
|
-
if (
|
|
17138
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15957
17139
|
return "";
|
|
15958
17140
|
}
|
|
15959
17141
|
throw error;
|
|
15960
17142
|
}
|
|
15961
17143
|
);
|
|
15962
|
-
const config = raw ?
|
|
15963
|
-
const plugins =
|
|
15964
|
-
return
|
|
17144
|
+
const config = raw ? toRecord14(YAML4.parse(raw)) : {};
|
|
17145
|
+
const plugins = toRecord14(config.plugins);
|
|
17146
|
+
return toRecord14(plugins["hermes-memory-store"]);
|
|
15965
17147
|
}
|
|
15966
17148
|
async function patchHolographicProviderConfig(profileName, patch) {
|
|
15967
17149
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15968
|
-
const existingRaw = await
|
|
17150
|
+
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
15969
17151
|
(error) => {
|
|
15970
|
-
if (
|
|
17152
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
15971
17153
|
return null;
|
|
15972
17154
|
}
|
|
15973
17155
|
throw error;
|
|
15974
17156
|
}
|
|
15975
17157
|
);
|
|
15976
17158
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15977
|
-
const config =
|
|
15978
|
-
const plugins =
|
|
15979
|
-
const memoryStore =
|
|
17159
|
+
const config = toRecord14(document.toJSON());
|
|
17160
|
+
const plugins = toRecord14(config.plugins);
|
|
17161
|
+
const memoryStore = toRecord14(plugins["hermes-memory-store"]);
|
|
15980
17162
|
for (const [key, value] of Object.entries(patch)) {
|
|
15981
17163
|
if (value !== void 0) {
|
|
15982
17164
|
memoryStore[key] = value;
|
|
@@ -15984,7 +17166,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
|
|
|
15984
17166
|
}
|
|
15985
17167
|
plugins["hermes-memory-store"] = memoryStore;
|
|
15986
17168
|
config.plugins = plugins;
|
|
15987
|
-
await mkdir12(
|
|
17169
|
+
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
15988
17170
|
if (existingRaw) {
|
|
15989
17171
|
await copyFile3(configPath, `${configPath}.bak.${Date.now()}`);
|
|
15990
17172
|
}
|
|
@@ -16003,9 +17185,9 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
16003
17185
|
if (entries.length === 0) {
|
|
16004
17186
|
return;
|
|
16005
17187
|
}
|
|
16006
|
-
const envPath =
|
|
16007
|
-
const existingRaw = await
|
|
16008
|
-
if (
|
|
17188
|
+
const envPath = path19.join(resolveHermesProfileDir(profileName), ".env");
|
|
17189
|
+
const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
|
|
17190
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16009
17191
|
return "";
|
|
16010
17192
|
}
|
|
16011
17193
|
throw error;
|
|
@@ -16035,7 +17217,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
16035
17217
|
}
|
|
16036
17218
|
}
|
|
16037
17219
|
const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
|
|
16038
|
-
await mkdir12(
|
|
17220
|
+
await mkdir12(path19.dirname(envPath), { recursive: true, mode: 448 });
|
|
16039
17221
|
if (existingRaw) {
|
|
16040
17222
|
await copyFile3(envPath, `${envPath}.bak.${Date.now()}`);
|
|
16041
17223
|
}
|
|
@@ -16054,29 +17236,29 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
16054
17236
|
].includes(key);
|
|
16055
17237
|
}
|
|
16056
17238
|
function normalizeHindsightMode(value) {
|
|
16057
|
-
const mode =
|
|
17239
|
+
const mode = readString15(value) ?? "cloud";
|
|
16058
17240
|
return mode === "local" ? "local_embedded" : mode;
|
|
16059
17241
|
}
|
|
16060
17242
|
async function readActiveMemoryProvider(profileName) {
|
|
16061
|
-
const raw = await
|
|
17243
|
+
const raw = await readFile14(
|
|
16062
17244
|
resolveHermesConfigPath(profileName),
|
|
16063
17245
|
"utf8"
|
|
16064
17246
|
).catch((error) => {
|
|
16065
|
-
if (
|
|
17247
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16066
17248
|
return "";
|
|
16067
17249
|
}
|
|
16068
17250
|
throw error;
|
|
16069
17251
|
});
|
|
16070
|
-
const config = raw ?
|
|
16071
|
-
const memory =
|
|
16072
|
-
const provider =
|
|
17252
|
+
const config = raw ? toRecord14(YAML4.parse(raw)) : {};
|
|
17253
|
+
const memory = toRecord14(config.memory);
|
|
17254
|
+
const provider = readString15(memory.provider);
|
|
16073
17255
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
16074
17256
|
return null;
|
|
16075
17257
|
}
|
|
16076
17258
|
return provider;
|
|
16077
17259
|
}
|
|
16078
17260
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
16079
|
-
const configPath =
|
|
17261
|
+
const configPath = path19.join(
|
|
16080
17262
|
resolveHermesProfileDir(profileName),
|
|
16081
17263
|
relativePath
|
|
16082
17264
|
);
|
|
@@ -16087,7 +17269,7 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
16087
17269
|
next[key] = value;
|
|
16088
17270
|
}
|
|
16089
17271
|
}
|
|
16090
|
-
await mkdir12(
|
|
17272
|
+
await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
|
|
16091
17273
|
await writeFile5(configPath, `${JSON.stringify(next, null, 2)}
|
|
16092
17274
|
`, {
|
|
16093
17275
|
encoding: "utf8",
|
|
@@ -16095,18 +17277,18 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
16095
17277
|
});
|
|
16096
17278
|
}
|
|
16097
17279
|
async function readJsonObject(filePath) {
|
|
16098
|
-
const raw = await
|
|
16099
|
-
if (
|
|
17280
|
+
const raw = await readFile14(filePath, "utf8").catch((error) => {
|
|
17281
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16100
17282
|
return "{}";
|
|
16101
17283
|
}
|
|
16102
17284
|
throw error;
|
|
16103
17285
|
});
|
|
16104
17286
|
try {
|
|
16105
|
-
return
|
|
17287
|
+
return toRecord14(JSON.parse(raw || "{}"));
|
|
16106
17288
|
} catch {
|
|
16107
17289
|
throw new HermesMemoryError(
|
|
16108
17290
|
"memory_provider_config_invalid",
|
|
16109
|
-
`${
|
|
17291
|
+
`${path19.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
16110
17292
|
);
|
|
16111
17293
|
}
|
|
16112
17294
|
}
|
|
@@ -16114,7 +17296,7 @@ function booleanSetting(key, label, value) {
|
|
|
16114
17296
|
return {
|
|
16115
17297
|
key,
|
|
16116
17298
|
label,
|
|
16117
|
-
value:
|
|
17299
|
+
value: readBoolean3(value) ?? false,
|
|
16118
17300
|
editable: true,
|
|
16119
17301
|
kind: "boolean"
|
|
16120
17302
|
};
|
|
@@ -16127,7 +17309,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
16127
17309
|
return {
|
|
16128
17310
|
key,
|
|
16129
17311
|
label,
|
|
16130
|
-
value:
|
|
17312
|
+
value: readString15(value) ?? "",
|
|
16131
17313
|
editable,
|
|
16132
17314
|
kind: "string"
|
|
16133
17315
|
};
|
|
@@ -16142,21 +17324,21 @@ function textSetting(key, label, value, editable = true) {
|
|
|
16142
17324
|
};
|
|
16143
17325
|
}
|
|
16144
17326
|
function selectSetting(key, label, value, options, editable = true) {
|
|
16145
|
-
const stringValue =
|
|
17327
|
+
const stringValue = readString15(value) ?? options[0] ?? null;
|
|
16146
17328
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
16147
17329
|
}
|
|
16148
17330
|
async function readMemoryLimits(profileName) {
|
|
16149
|
-
const raw = await
|
|
17331
|
+
const raw = await readFile14(
|
|
16150
17332
|
resolveHermesConfigPath(profileName),
|
|
16151
17333
|
"utf8"
|
|
16152
17334
|
).catch((error) => {
|
|
16153
|
-
if (
|
|
17335
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16154
17336
|
return "";
|
|
16155
17337
|
}
|
|
16156
17338
|
throw error;
|
|
16157
17339
|
});
|
|
16158
|
-
const config = raw ?
|
|
16159
|
-
const memory =
|
|
17340
|
+
const config = raw ? toRecord14(YAML4.parse(raw)) : {};
|
|
17341
|
+
const memory = toRecord14(config.memory);
|
|
16160
17342
|
return {
|
|
16161
17343
|
memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
|
|
16162
17344
|
user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
|
|
@@ -16212,17 +17394,17 @@ function hashString(value) {
|
|
|
16212
17394
|
}
|
|
16213
17395
|
return hash.toString(16);
|
|
16214
17396
|
}
|
|
16215
|
-
function
|
|
17397
|
+
function toRecord14(value) {
|
|
16216
17398
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
16217
17399
|
}
|
|
16218
|
-
function
|
|
17400
|
+
function readString15(value) {
|
|
16219
17401
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16220
17402
|
}
|
|
16221
17403
|
function readPositiveInteger3(value) {
|
|
16222
17404
|
const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
|
|
16223
17405
|
return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
|
|
16224
17406
|
}
|
|
16225
|
-
function
|
|
17407
|
+
function readBoolean3(value) {
|
|
16226
17408
|
if (typeof value === "boolean") {
|
|
16227
17409
|
return value;
|
|
16228
17410
|
}
|
|
@@ -16243,7 +17425,7 @@ function formatEnvValue3(value) {
|
|
|
16243
17425
|
function escapeRegExp3(value) {
|
|
16244
17426
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16245
17427
|
}
|
|
16246
|
-
function
|
|
17428
|
+
function isNodeError15(error, code) {
|
|
16247
17429
|
return error instanceof Error && "code" in error && error.code === code;
|
|
16248
17430
|
}
|
|
16249
17431
|
|
|
@@ -16357,7 +17539,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
16357
17539
|
);
|
|
16358
17540
|
}
|
|
16359
17541
|
function readMemoryTarget(body) {
|
|
16360
|
-
const raw =
|
|
17542
|
+
const raw = readString14(body, "target");
|
|
16361
17543
|
if (raw === "memory" || raw === "user") {
|
|
16362
17544
|
return raw;
|
|
16363
17545
|
}
|
|
@@ -16368,7 +17550,7 @@ function readMemoryTarget(body) {
|
|
|
16368
17550
|
);
|
|
16369
17551
|
}
|
|
16370
17552
|
function readMemoryResetTarget(body) {
|
|
16371
|
-
const raw =
|
|
17553
|
+
const raw = readString14(body, "target") ?? "all";
|
|
16372
17554
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
16373
17555
|
return raw;
|
|
16374
17556
|
}
|
|
@@ -16379,7 +17561,7 @@ function readMemoryResetTarget(body) {
|
|
|
16379
17561
|
);
|
|
16380
17562
|
}
|
|
16381
17563
|
function readRequiredMemoryContent(body) {
|
|
16382
|
-
const content =
|
|
17564
|
+
const content = readString14(body, "content") ?? readString14(body, "text");
|
|
16383
17565
|
if (!content) {
|
|
16384
17566
|
throw new LinkHttpError(
|
|
16385
17567
|
400,
|
|
@@ -16390,7 +17572,7 @@ function readRequiredMemoryContent(body) {
|
|
|
16390
17572
|
return content;
|
|
16391
17573
|
}
|
|
16392
17574
|
function readRequiredMemoryMatch(body) {
|
|
16393
|
-
const oldText =
|
|
17575
|
+
const oldText = readString14(body, "old_text") ?? readString14(body, "oldText") ?? readString14(body, "match");
|
|
16394
17576
|
if (!oldText) {
|
|
16395
17577
|
throw new LinkHttpError(
|
|
16396
17578
|
400,
|
|
@@ -16401,7 +17583,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
16401
17583
|
return oldText;
|
|
16402
17584
|
}
|
|
16403
17585
|
function readRequiredMemoryProvider(body) {
|
|
16404
|
-
const provider =
|
|
17586
|
+
const provider = readString14(body, "provider") ?? readString14(body, "provider_id") ?? readString14(body, "providerId");
|
|
16405
17587
|
if (!provider) {
|
|
16406
17588
|
throw new LinkHttpError(
|
|
16407
17589
|
400,
|
|
@@ -16413,7 +17595,7 @@ function readRequiredMemoryProvider(body) {
|
|
|
16413
17595
|
}
|
|
16414
17596
|
function readMemorySettingsPatch(body) {
|
|
16415
17597
|
const input = {};
|
|
16416
|
-
const mode =
|
|
17598
|
+
const mode = readString14(body, "mode");
|
|
16417
17599
|
if (mode) {
|
|
16418
17600
|
input.mode = mode;
|
|
16419
17601
|
}
|
|
@@ -16425,7 +17607,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16425
17607
|
if (bankId !== void 0) {
|
|
16426
17608
|
input.bankId = bankId;
|
|
16427
17609
|
}
|
|
16428
|
-
const llmProvider =
|
|
17610
|
+
const llmProvider = readString14(body, "llm_provider") ?? readString14(body, "llmProvider");
|
|
16429
17611
|
if (llmProvider) {
|
|
16430
17612
|
input.llmProvider = llmProvider;
|
|
16431
17613
|
}
|
|
@@ -16441,23 +17623,23 @@ function readMemorySettingsPatch(body) {
|
|
|
16441
17623
|
if (containerTag !== void 0) {
|
|
16442
17624
|
input.containerTag = containerTag;
|
|
16443
17625
|
}
|
|
16444
|
-
const autoRecall =
|
|
17626
|
+
const autoRecall = readBoolean2(body.auto_recall ?? body.autoRecall);
|
|
16445
17627
|
if (autoRecall !== void 0) {
|
|
16446
17628
|
input.autoRecall = autoRecall;
|
|
16447
17629
|
}
|
|
16448
|
-
const autoCapture =
|
|
17630
|
+
const autoCapture = readBoolean2(body.auto_capture ?? body.autoCapture);
|
|
16449
17631
|
if (autoCapture !== void 0) {
|
|
16450
17632
|
input.autoCapture = autoCapture;
|
|
16451
17633
|
}
|
|
16452
|
-
const autoRetain =
|
|
17634
|
+
const autoRetain = readBoolean2(body.auto_retain ?? body.autoRetain);
|
|
16453
17635
|
if (autoRetain !== void 0) {
|
|
16454
17636
|
input.autoRetain = autoRetain;
|
|
16455
17637
|
}
|
|
16456
|
-
const memoryMode =
|
|
17638
|
+
const memoryMode = readString14(body, "memory_mode") ?? readString14(body, "memoryMode");
|
|
16457
17639
|
if (memoryMode) {
|
|
16458
17640
|
input.memoryMode = memoryMode;
|
|
16459
17641
|
}
|
|
16460
|
-
const recallBudget =
|
|
17642
|
+
const recallBudget = readString14(body, "recall_budget") ?? readString14(body, "recallBudget");
|
|
16461
17643
|
if (recallBudget) {
|
|
16462
17644
|
input.recallBudget = recallBudget;
|
|
16463
17645
|
}
|
|
@@ -16473,11 +17655,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16473
17655
|
if (profileFrequency !== void 0) {
|
|
16474
17656
|
input.profileFrequency = profileFrequency;
|
|
16475
17657
|
}
|
|
16476
|
-
const captureMode =
|
|
17658
|
+
const captureMode = readString14(body, "capture_mode") ?? readString14(body, "captureMode");
|
|
16477
17659
|
if (captureMode) {
|
|
16478
17660
|
input.captureMode = captureMode;
|
|
16479
17661
|
}
|
|
16480
|
-
const searchMode =
|
|
17662
|
+
const searchMode = readString14(body, "search_mode") ?? readString14(body, "searchMode");
|
|
16481
17663
|
if (searchMode) {
|
|
16482
17664
|
input.searchMode = searchMode;
|
|
16483
17665
|
}
|
|
@@ -16511,19 +17693,19 @@ function readMemorySettingsPatch(body) {
|
|
|
16511
17693
|
if (aiPeer !== void 0) {
|
|
16512
17694
|
input.aiPeer = aiPeer;
|
|
16513
17695
|
}
|
|
16514
|
-
const recallMode =
|
|
17696
|
+
const recallMode = readString14(body, "recall_mode") ?? readString14(body, "recallMode");
|
|
16515
17697
|
if (recallMode) {
|
|
16516
17698
|
input.recallMode = recallMode;
|
|
16517
17699
|
}
|
|
16518
|
-
const writeFrequency =
|
|
17700
|
+
const writeFrequency = readString14(body, "write_frequency") ?? readString14(body, "writeFrequency");
|
|
16519
17701
|
if (writeFrequency) {
|
|
16520
17702
|
input.writeFrequency = writeFrequency;
|
|
16521
17703
|
}
|
|
16522
|
-
const saveMessages =
|
|
17704
|
+
const saveMessages = readBoolean2(body.save_messages ?? body.saveMessages);
|
|
16523
17705
|
if (saveMessages !== void 0) {
|
|
16524
17706
|
input.saveMessages = saveMessages;
|
|
16525
17707
|
}
|
|
16526
|
-
const sessionStrategy =
|
|
17708
|
+
const sessionStrategy = readString14(body, "session_strategy") ?? readString14(body, "sessionStrategy");
|
|
16527
17709
|
if (sessionStrategy) {
|
|
16528
17710
|
input.sessionStrategy = sessionStrategy;
|
|
16529
17711
|
}
|
|
@@ -16559,7 +17741,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16559
17741
|
if (agentId !== void 0) {
|
|
16560
17742
|
input.agentId = agentId;
|
|
16561
17743
|
}
|
|
16562
|
-
const rerank =
|
|
17744
|
+
const rerank = readBoolean2(body.rerank);
|
|
16563
17745
|
if (rerank !== void 0) {
|
|
16564
17746
|
input.rerank = rerank;
|
|
16565
17747
|
}
|
|
@@ -16583,7 +17765,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16583
17765
|
if (dbPath !== void 0) {
|
|
16584
17766
|
input.dbPath = dbPath;
|
|
16585
17767
|
}
|
|
16586
|
-
const autoExtract =
|
|
17768
|
+
const autoExtract = readBoolean2(body.auto_extract ?? body.autoExtract);
|
|
16587
17769
|
if (autoExtract !== void 0) {
|
|
16588
17770
|
input.autoExtract = autoExtract;
|
|
16589
17771
|
}
|
|
@@ -16654,12 +17836,12 @@ function toMemoryHttpError(error) {
|
|
|
16654
17836
|
import {
|
|
16655
17837
|
copyFile as copyFile4,
|
|
16656
17838
|
mkdir as mkdir13,
|
|
16657
|
-
readFile as
|
|
16658
|
-
readdir as
|
|
17839
|
+
readFile as readFile15,
|
|
17840
|
+
readdir as readdir10,
|
|
16659
17841
|
rename as rename7,
|
|
16660
17842
|
writeFile as writeFile6
|
|
16661
17843
|
} from "fs/promises";
|
|
16662
|
-
import
|
|
17844
|
+
import path20 from "path";
|
|
16663
17845
|
import YAML5 from "yaml";
|
|
16664
17846
|
var HermesSkillNotFoundError = class extends Error {
|
|
16665
17847
|
constructor(skillName) {
|
|
@@ -16673,7 +17855,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
16673
17855
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
16674
17856
|
const profile = await readExistingProfile(profileName, paths);
|
|
16675
17857
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
16676
|
-
const skillsRoot =
|
|
17858
|
+
const skillsRoot = path20.join(profileDir, "skills");
|
|
16677
17859
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
16678
17860
|
findSkillFiles(skillsRoot),
|
|
16679
17861
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -16750,9 +17932,9 @@ async function findSkillFiles(root) {
|
|
|
16750
17932
|
return results.sort((left, right) => left.localeCompare(right));
|
|
16751
17933
|
}
|
|
16752
17934
|
async function collectSkillFiles(directory, results) {
|
|
16753
|
-
const entries = await
|
|
17935
|
+
const entries = await readdir10(directory, { withFileTypes: true }).catch(
|
|
16754
17936
|
(error) => {
|
|
16755
|
-
if (
|
|
17937
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16756
17938
|
return [];
|
|
16757
17939
|
}
|
|
16758
17940
|
throw error;
|
|
@@ -16764,7 +17946,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
16764
17946
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
16765
17947
|
continue;
|
|
16766
17948
|
}
|
|
16767
|
-
const entryPath =
|
|
17949
|
+
const entryPath = path20.join(directory, entry.name);
|
|
16768
17950
|
if (entry.isDirectory()) {
|
|
16769
17951
|
await collectSkillFiles(entryPath, results);
|
|
16770
17952
|
continue;
|
|
@@ -16775,9 +17957,9 @@ async function collectSkillFiles(directory, results) {
|
|
|
16775
17957
|
}
|
|
16776
17958
|
}
|
|
16777
17959
|
async function readSkillMetadata(input) {
|
|
16778
|
-
const raw = await
|
|
17960
|
+
const raw = await readFile15(input.skillFile, "utf8").catch(
|
|
16779
17961
|
(error) => {
|
|
16780
|
-
if (
|
|
17962
|
+
if (isNodeError16(error, "ENOENT") || isNodeError16(error, "EACCES")) {
|
|
16781
17963
|
return null;
|
|
16782
17964
|
}
|
|
16783
17965
|
throw error;
|
|
@@ -16786,16 +17968,16 @@ async function readSkillMetadata(input) {
|
|
|
16786
17968
|
if (raw === null) {
|
|
16787
17969
|
return null;
|
|
16788
17970
|
}
|
|
16789
|
-
const skillDir =
|
|
17971
|
+
const skillDir = path20.dirname(input.skillFile);
|
|
16790
17972
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
16791
17973
|
const name = normalizeSkillName(
|
|
16792
|
-
|
|
17974
|
+
readString16(frontmatter.name) ?? path20.basename(skillDir)
|
|
16793
17975
|
);
|
|
16794
17976
|
if (!name) {
|
|
16795
17977
|
return null;
|
|
16796
17978
|
}
|
|
16797
17979
|
const description = normalizeDescription(
|
|
16798
|
-
|
|
17980
|
+
readString16(frontmatter.description) ?? firstBodyDescription(body)
|
|
16799
17981
|
);
|
|
16800
17982
|
const provenance = input.provenance.get(name) ?? {
|
|
16801
17983
|
source: "local",
|
|
@@ -16808,7 +17990,7 @@ async function readSkillMetadata(input) {
|
|
|
16808
17990
|
enabled: !input.disabled.has(name),
|
|
16809
17991
|
source: provenance.source,
|
|
16810
17992
|
trust: provenance.trust,
|
|
16811
|
-
relativePath:
|
|
17993
|
+
relativePath: path20.relative(input.skillsRoot, skillDir)
|
|
16812
17994
|
};
|
|
16813
17995
|
}
|
|
16814
17996
|
function parseSkillDocument(raw) {
|
|
@@ -16821,7 +18003,7 @@ function parseSkillDocument(raw) {
|
|
|
16821
18003
|
}
|
|
16822
18004
|
try {
|
|
16823
18005
|
return {
|
|
16824
|
-
frontmatter:
|
|
18006
|
+
frontmatter: toRecord15(YAML5.parse(match[1] ?? "")),
|
|
16825
18007
|
body: content.slice(match[0].length)
|
|
16826
18008
|
};
|
|
16827
18009
|
} catch {
|
|
@@ -16829,8 +18011,8 @@ function parseSkillDocument(raw) {
|
|
|
16829
18011
|
}
|
|
16830
18012
|
}
|
|
16831
18013
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
16832
|
-
const relative =
|
|
16833
|
-
const parts = relative.split(
|
|
18014
|
+
const relative = path20.relative(skillsRoot, skillFile);
|
|
18015
|
+
const parts = relative.split(path20.sep).filter(Boolean);
|
|
16834
18016
|
return parts.length >= 3 ? parts[0] : null;
|
|
16835
18017
|
}
|
|
16836
18018
|
function firstBodyDescription(body) {
|
|
@@ -16853,8 +18035,8 @@ function normalizeDescription(value) {
|
|
|
16853
18035
|
return `${description.slice(0, MAX_DESCRIPTION_LENGTH - 3)}...`;
|
|
16854
18036
|
}
|
|
16855
18037
|
async function readDisabledSkillNames(configPath) {
|
|
16856
|
-
const raw = await
|
|
16857
|
-
if (
|
|
18038
|
+
const raw = await readFile15(configPath, "utf8").catch((error) => {
|
|
18039
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16858
18040
|
return "";
|
|
16859
18041
|
}
|
|
16860
18042
|
throw error;
|
|
@@ -16862,8 +18044,8 @@ async function readDisabledSkillNames(configPath) {
|
|
|
16862
18044
|
if (!raw.trim()) {
|
|
16863
18045
|
return /* @__PURE__ */ new Set();
|
|
16864
18046
|
}
|
|
16865
|
-
const config =
|
|
16866
|
-
const skills =
|
|
18047
|
+
const config = toRecord15(YAML5.parse(raw));
|
|
18048
|
+
const skills = toRecord15(config.skills);
|
|
16867
18049
|
return new Set(readStringList3(skills.disabled));
|
|
16868
18050
|
}
|
|
16869
18051
|
async function readSkillProvenance(root) {
|
|
@@ -16877,9 +18059,9 @@ async function readSkillProvenance(root) {
|
|
|
16877
18059
|
return provenance;
|
|
16878
18060
|
}
|
|
16879
18061
|
async function readBundledSkillNames(root) {
|
|
16880
|
-
const raw = await
|
|
18062
|
+
const raw = await readFile15(path20.join(root, ".bundled_manifest"), "utf8").catch(
|
|
16881
18063
|
(error) => {
|
|
16882
|
-
if (
|
|
18064
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16883
18065
|
return "";
|
|
16884
18066
|
}
|
|
16885
18067
|
throw error;
|
|
@@ -16900,9 +18082,9 @@ async function readBundledSkillNames(root) {
|
|
|
16900
18082
|
return names;
|
|
16901
18083
|
}
|
|
16902
18084
|
async function readHubInstalledSkills(root) {
|
|
16903
|
-
const raw = await
|
|
18085
|
+
const raw = await readFile15(path20.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
16904
18086
|
(error) => {
|
|
16905
|
-
if (
|
|
18087
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16906
18088
|
return "";
|
|
16907
18089
|
}
|
|
16908
18090
|
throw error;
|
|
@@ -16913,17 +18095,17 @@ async function readHubInstalledSkills(root) {
|
|
|
16913
18095
|
}
|
|
16914
18096
|
let lock;
|
|
16915
18097
|
try {
|
|
16916
|
-
lock =
|
|
18098
|
+
lock = toRecord15(JSON.parse(raw));
|
|
16917
18099
|
} catch {
|
|
16918
18100
|
return /* @__PURE__ */ new Map();
|
|
16919
18101
|
}
|
|
16920
|
-
const installed =
|
|
18102
|
+
const installed = toRecord15(lock.installed);
|
|
16921
18103
|
const result = /* @__PURE__ */ new Map();
|
|
16922
18104
|
for (const [name, rawEntry] of Object.entries(installed)) {
|
|
16923
|
-
const entry =
|
|
18105
|
+
const entry = toRecord15(rawEntry);
|
|
16924
18106
|
result.set(normalizeSkillName(name), {
|
|
16925
|
-
source:
|
|
16926
|
-
trust:
|
|
18107
|
+
source: readString16(entry.source) ?? "hub",
|
|
18108
|
+
trust: readString16(entry.trust_level) ?? null
|
|
16927
18109
|
});
|
|
16928
18110
|
}
|
|
16929
18111
|
return result;
|
|
@@ -16972,9 +18154,9 @@ function compareCategoryNames(left, right) {
|
|
|
16972
18154
|
return left.localeCompare(right);
|
|
16973
18155
|
}
|
|
16974
18156
|
async function readHermesConfigDocument2(configPath) {
|
|
16975
|
-
const existingRaw = await
|
|
18157
|
+
const existingRaw = await readFile15(configPath, "utf8").catch(
|
|
16976
18158
|
(error) => {
|
|
16977
|
-
if (
|
|
18159
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
16978
18160
|
return null;
|
|
16979
18161
|
}
|
|
16980
18162
|
throw error;
|
|
@@ -16983,13 +18165,13 @@ async function readHermesConfigDocument2(configPath) {
|
|
|
16983
18165
|
const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
|
|
16984
18166
|
return {
|
|
16985
18167
|
document,
|
|
16986
|
-
config:
|
|
18168
|
+
config: toRecord15(document.toJSON()),
|
|
16987
18169
|
existingRaw
|
|
16988
18170
|
};
|
|
16989
18171
|
}
|
|
16990
18172
|
async function writeHermesConfigDocument2(input) {
|
|
16991
18173
|
const backupPath = input.existingRaw ? `${input.configPath}.bak.${Date.now()}` : null;
|
|
16992
|
-
await mkdir13(
|
|
18174
|
+
await mkdir13(path20.dirname(input.configPath), { recursive: true, mode: 448 });
|
|
16993
18175
|
if (backupPath) {
|
|
16994
18176
|
await copyFile4(input.configPath, backupPath);
|
|
16995
18177
|
}
|
|
@@ -17005,21 +18187,21 @@ function readStringList3(value) {
|
|
|
17005
18187
|
}
|
|
17006
18188
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
17007
18189
|
}
|
|
17008
|
-
function
|
|
18190
|
+
function readString16(value) {
|
|
17009
18191
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17010
18192
|
}
|
|
17011
|
-
function
|
|
18193
|
+
function toRecord15(value) {
|
|
17012
18194
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
17013
18195
|
}
|
|
17014
18196
|
function ensureRecord3(target, key) {
|
|
17015
|
-
const current =
|
|
18197
|
+
const current = toRecord15(target[key]);
|
|
17016
18198
|
if (current === target[key]) {
|
|
17017
18199
|
return current;
|
|
17018
18200
|
}
|
|
17019
18201
|
target[key] = current;
|
|
17020
18202
|
return current;
|
|
17021
18203
|
}
|
|
17022
|
-
function
|
|
18204
|
+
function isNodeError16(error, code) {
|
|
17023
18205
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
17024
18206
|
}
|
|
17025
18207
|
|
|
@@ -17034,7 +18216,7 @@ function registerProfileSkillRoutes(router, options) {
|
|
|
17034
18216
|
router.patch("/api/v1/profiles/:name/skills/:skillName", async (ctx) => {
|
|
17035
18217
|
await authenticateRequest(ctx, paths);
|
|
17036
18218
|
const body = await readJsonBody(ctx.req);
|
|
17037
|
-
const enabled =
|
|
18219
|
+
const enabled = readBoolean2(body.enabled);
|
|
17038
18220
|
if (enabled === void 0) {
|
|
17039
18221
|
throw new LinkHttpError(
|
|
17040
18222
|
400,
|
|
@@ -17322,7 +18504,7 @@ function registerRunRoutes(router, options) {
|
|
|
17322
18504
|
router.post("/api/v1/runs", async (ctx) => {
|
|
17323
18505
|
await authenticateRequest(ctx, paths);
|
|
17324
18506
|
const body = await readJsonBody(ctx.req);
|
|
17325
|
-
const input =
|
|
18507
|
+
const input = readString14(body, "input");
|
|
17326
18508
|
if (!input) {
|
|
17327
18509
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
17328
18510
|
}
|
|
@@ -17330,11 +18512,11 @@ function registerRunRoutes(router, options) {
|
|
|
17330
18512
|
ctx.body = await createHermesRun(
|
|
17331
18513
|
{
|
|
17332
18514
|
input,
|
|
17333
|
-
instructions:
|
|
18515
|
+
instructions: readString14(body, "instructions") ?? void 0,
|
|
17334
18516
|
conversation_history: readConversationHistory(
|
|
17335
18517
|
body.conversation_history ?? body.conversationHistory
|
|
17336
18518
|
),
|
|
17337
|
-
session_id:
|
|
18519
|
+
session_id: readString14(body, "session_id") ?? readString14(body, "sessionId") ?? void 0
|
|
17338
18520
|
},
|
|
17339
18521
|
{ logger, profileName: readOptionalProfileName(body) }
|
|
17340
18522
|
);
|
|
@@ -17505,8 +18687,8 @@ function readModelList(payload) {
|
|
|
17505
18687
|
// src/hermes/updates.ts
|
|
17506
18688
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
17507
18689
|
import { spawn as spawn3 } from "child_process";
|
|
17508
|
-
import { mkdir as mkdir14, readFile as
|
|
17509
|
-
import
|
|
18690
|
+
import { mkdir as mkdir14, readFile as readFile16, rm as rm8 } from "fs/promises";
|
|
18691
|
+
import path21 from "path";
|
|
17510
18692
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
17511
18693
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
17512
18694
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -17727,24 +18909,24 @@ async function readRemoteRelease(options, now) {
|
|
|
17727
18909
|
}
|
|
17728
18910
|
}
|
|
17729
18911
|
function normalizeServerReleaseSnapshot(payload) {
|
|
17730
|
-
const snapshot =
|
|
18912
|
+
const snapshot = toRecord16(payload);
|
|
17731
18913
|
const remote = toNullableRecord(snapshot.remote);
|
|
17732
18914
|
return {
|
|
17733
18915
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
17734
|
-
cacheState:
|
|
17735
|
-
issue:
|
|
18916
|
+
cacheState: readString17(snapshot, "cache_state") ?? readString17(snapshot, "cacheState"),
|
|
18917
|
+
issue: readString17(snapshot, "issue")
|
|
17736
18918
|
};
|
|
17737
18919
|
}
|
|
17738
18920
|
function normalizeServerRelease(payload) {
|
|
17739
|
-
const tag =
|
|
17740
|
-
const name =
|
|
18921
|
+
const tag = readString17(payload, "tag");
|
|
18922
|
+
const name = readString17(payload, "name");
|
|
17741
18923
|
return {
|
|
17742
|
-
version:
|
|
18924
|
+
version: readString17(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
17743
18925
|
tag,
|
|
17744
18926
|
name,
|
|
17745
|
-
releaseUrl:
|
|
17746
|
-
publishedAt:
|
|
17747
|
-
fetchedAt:
|
|
18927
|
+
releaseUrl: readString17(payload, "releaseUrl") ?? readString17(payload, "release_url"),
|
|
18928
|
+
publishedAt: readString17(payload, "publishedAt") ?? readString17(payload, "published_at"),
|
|
18929
|
+
fetchedAt: readString17(payload, "fetchedAt") ?? readString17(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
17748
18930
|
};
|
|
17749
18931
|
}
|
|
17750
18932
|
async function readReleaseCache(paths) {
|
|
@@ -17763,7 +18945,7 @@ async function writeUpdateState(paths, state) {
|
|
|
17763
18945
|
await writeJsonFile(updateStatePath(paths), state);
|
|
17764
18946
|
}
|
|
17765
18947
|
async function readUpdateLogLines(paths) {
|
|
17766
|
-
const raw = await
|
|
18948
|
+
const raw = await readFile16(updateLogPath(paths), "utf8").catch(() => "");
|
|
17767
18949
|
if (!raw.trim()) {
|
|
17768
18950
|
return [];
|
|
17769
18951
|
}
|
|
@@ -17772,13 +18954,13 @@ async function readUpdateLogLines(paths) {
|
|
|
17772
18954
|
);
|
|
17773
18955
|
}
|
|
17774
18956
|
function releaseCachePath(paths) {
|
|
17775
|
-
return
|
|
18957
|
+
return path21.join(paths.indexesDir, "hermes-release-check.json");
|
|
17776
18958
|
}
|
|
17777
18959
|
function updateStatePath(paths) {
|
|
17778
|
-
return
|
|
18960
|
+
return path21.join(paths.runDir, "hermes-update-state.json");
|
|
17779
18961
|
}
|
|
17780
18962
|
function updateLogPath(paths) {
|
|
17781
|
-
return
|
|
18963
|
+
return path21.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
17782
18964
|
}
|
|
17783
18965
|
async function clearUpdateLogFiles(paths) {
|
|
17784
18966
|
const primary = updateLogPath(paths);
|
|
@@ -17816,7 +18998,7 @@ function compareSemver2(left, right) {
|
|
|
17816
18998
|
}
|
|
17817
18999
|
return 0;
|
|
17818
19000
|
}
|
|
17819
|
-
function
|
|
19001
|
+
function toRecord16(value) {
|
|
17820
19002
|
return typeof value === "object" && value !== null ? value : {};
|
|
17821
19003
|
}
|
|
17822
19004
|
function toNullableRecord(value) {
|
|
@@ -17862,7 +19044,7 @@ function isRecentRunningState2(state) {
|
|
|
17862
19044
|
const startedAt = Date.parse(state.started_at);
|
|
17863
19045
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
17864
19046
|
}
|
|
17865
|
-
function
|
|
19047
|
+
function readString17(payload, key) {
|
|
17866
19048
|
const value = payload[key];
|
|
17867
19049
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17868
19050
|
}
|
|
@@ -17870,13 +19052,13 @@ function readString16(payload, key) {
|
|
|
17870
19052
|
// src/link/updates.ts
|
|
17871
19053
|
import { spawn as spawn5 } from "child_process";
|
|
17872
19054
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
17873
|
-
import { mkdir as mkdir17, readFile as
|
|
17874
|
-
import
|
|
19055
|
+
import { mkdir as mkdir17, readFile as readFile18, rm as rm11 } from "fs/promises";
|
|
19056
|
+
import path23 from "path";
|
|
17875
19057
|
|
|
17876
19058
|
// src/daemon/process.ts
|
|
17877
19059
|
import { spawn as spawn4 } from "child_process";
|
|
17878
|
-
import { mkdir as mkdir16, readFile as
|
|
17879
|
-
import
|
|
19060
|
+
import { mkdir as mkdir16, readFile as readFile17, rm as rm10 } from "fs/promises";
|
|
19061
|
+
import path22 from "path";
|
|
17880
19062
|
|
|
17881
19063
|
// src/daemon/service.ts
|
|
17882
19064
|
import { createServer } from "http";
|
|
@@ -18054,7 +19236,7 @@ async function handleFrame(socket, raw, localPort, abortControllers) {
|
|
|
18054
19236
|
// src/runtime/system-info.ts
|
|
18055
19237
|
import { execFileSync } from "child_process";
|
|
18056
19238
|
import { readFileSync } from "fs";
|
|
18057
|
-
import
|
|
19239
|
+
import os6 from "os";
|
|
18058
19240
|
function readLinkSystemInfo() {
|
|
18059
19241
|
const platform = process.platform;
|
|
18060
19242
|
const hostname = readHostname(platform);
|
|
@@ -18093,7 +19275,7 @@ function readHostname(platform) {
|
|
|
18093
19275
|
return computerName;
|
|
18094
19276
|
}
|
|
18095
19277
|
}
|
|
18096
|
-
return normalizeText(
|
|
19278
|
+
return normalizeText(os6.hostname());
|
|
18097
19279
|
}
|
|
18098
19280
|
function readOsLabel(platform) {
|
|
18099
19281
|
if (platform === "darwin") {
|
|
@@ -18101,12 +19283,12 @@ function readOsLabel(platform) {
|
|
|
18101
19283
|
return version ? `macOS ${version}` : "macOS";
|
|
18102
19284
|
}
|
|
18103
19285
|
if (platform === "linux") {
|
|
18104
|
-
return readLinuxOsRelease() ?? `Linux ${
|
|
19286
|
+
return readLinuxOsRelease() ?? `Linux ${os6.release()}`;
|
|
18105
19287
|
}
|
|
18106
19288
|
if (platform === "win32") {
|
|
18107
|
-
return `Windows ${
|
|
19289
|
+
return `Windows ${os6.release()}`;
|
|
18108
19290
|
}
|
|
18109
|
-
return `${
|
|
19291
|
+
return `${os6.type()} ${os6.release()}`.trim();
|
|
18110
19292
|
}
|
|
18111
19293
|
function readLinuxOsRelease() {
|
|
18112
19294
|
for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
|
|
@@ -18153,11 +19335,11 @@ function truncateText(value, maxLength) {
|
|
|
18153
19335
|
}
|
|
18154
19336
|
|
|
18155
19337
|
// src/topology/network.ts
|
|
18156
|
-
import
|
|
19338
|
+
import os8 from "os";
|
|
18157
19339
|
|
|
18158
19340
|
// src/topology/environment.ts
|
|
18159
19341
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
18160
|
-
import
|
|
19342
|
+
import os7 from "os";
|
|
18161
19343
|
function detectRuntimeEnvironment(env = process.env) {
|
|
18162
19344
|
if (isWsl(env)) {
|
|
18163
19345
|
return {
|
|
@@ -18186,7 +19368,7 @@ function isWsl(env) {
|
|
|
18186
19368
|
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
|
|
18187
19369
|
return true;
|
|
18188
19370
|
}
|
|
18189
|
-
const release =
|
|
19371
|
+
const release = os7.release().toLowerCase();
|
|
18190
19372
|
return release.includes("microsoft") || release.includes("wsl");
|
|
18191
19373
|
}
|
|
18192
19374
|
function isContainer(env) {
|
|
@@ -18231,7 +19413,7 @@ async function discoverRouteCandidates(options) {
|
|
|
18231
19413
|
};
|
|
18232
19414
|
}
|
|
18233
19415
|
function discoverLanIps() {
|
|
18234
|
-
return discoverLanIpsFromInterfaces(
|
|
19416
|
+
return discoverLanIpsFromInterfaces(os8.networkInterfaces());
|
|
18235
19417
|
}
|
|
18236
19418
|
function discoverLanIpsFromInterfaces(interfaces) {
|
|
18237
19419
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -18724,6 +19906,33 @@ function startCronDeliveryScheduler(options) {
|
|
|
18724
19906
|
}
|
|
18725
19907
|
};
|
|
18726
19908
|
}
|
|
19909
|
+
function startHermesSessionSyncScheduler(options) {
|
|
19910
|
+
let running = false;
|
|
19911
|
+
const syncSessions = async () => {
|
|
19912
|
+
if (running) {
|
|
19913
|
+
return;
|
|
19914
|
+
}
|
|
19915
|
+
running = true;
|
|
19916
|
+
try {
|
|
19917
|
+
await options.conversations.syncHermesSessions();
|
|
19918
|
+
} catch (error) {
|
|
19919
|
+
void options.logger.warn("hermes_session_sync_failed", {
|
|
19920
|
+
error: error instanceof Error ? error.message : String(error)
|
|
19921
|
+
});
|
|
19922
|
+
} finally {
|
|
19923
|
+
running = false;
|
|
19924
|
+
}
|
|
19925
|
+
};
|
|
19926
|
+
const timer = setInterval(() => {
|
|
19927
|
+
void syncSessions();
|
|
19928
|
+
}, options.intervalMs ?? 10 * 60 * 1e3);
|
|
19929
|
+
timer.unref?.();
|
|
19930
|
+
return {
|
|
19931
|
+
close() {
|
|
19932
|
+
clearInterval(timer);
|
|
19933
|
+
}
|
|
19934
|
+
};
|
|
19935
|
+
}
|
|
18727
19936
|
|
|
18728
19937
|
// src/daemon/service.ts
|
|
18729
19938
|
async function startLinkService(options = {}) {
|
|
@@ -18744,11 +19953,21 @@ async function startLinkService(options = {}) {
|
|
|
18744
19953
|
}
|
|
18745
19954
|
const conversations = new ConversationService(paths, logger);
|
|
18746
19955
|
await conversations.rebuildStatisticsIndex();
|
|
19956
|
+
const triggerHermesSessionSync = () => {
|
|
19957
|
+
void conversations.syncHermesSessions().catch((error) => {
|
|
19958
|
+
void logger.warn("hermes_session_sync_failed", {
|
|
19959
|
+
error: error instanceof Error ? error.message : String(error)
|
|
19960
|
+
});
|
|
19961
|
+
});
|
|
19962
|
+
};
|
|
18747
19963
|
const app = await createApp({
|
|
18748
19964
|
paths,
|
|
18749
19965
|
logger,
|
|
18750
19966
|
conversations,
|
|
18751
|
-
onPairingClaimed:
|
|
19967
|
+
onPairingClaimed: async () => {
|
|
19968
|
+
triggerHermesSessionSync();
|
|
19969
|
+
await options.onPairingClaimed?.();
|
|
19970
|
+
}
|
|
18752
19971
|
});
|
|
18753
19972
|
const server = createServer(app.callback());
|
|
18754
19973
|
try {
|
|
@@ -18768,11 +19987,16 @@ async function startLinkService(options = {}) {
|
|
|
18768
19987
|
port: config.port,
|
|
18769
19988
|
link_id: identity?.link_id ?? null
|
|
18770
19989
|
});
|
|
19990
|
+
triggerHermesSessionSync();
|
|
18771
19991
|
const scheduler = startCronDeliveryScheduler({
|
|
18772
19992
|
paths,
|
|
18773
19993
|
conversations,
|
|
18774
19994
|
logger
|
|
18775
19995
|
});
|
|
19996
|
+
const hermesSessionSyncScheduler = startHermesSessionSyncScheduler({
|
|
19997
|
+
conversations,
|
|
19998
|
+
logger
|
|
19999
|
+
});
|
|
18776
20000
|
let relay = null;
|
|
18777
20001
|
if (identity?.link_id) {
|
|
18778
20002
|
relay = connectRelayControl({
|
|
@@ -18805,6 +20029,7 @@ async function startLinkService(options = {}) {
|
|
|
18805
20029
|
return {
|
|
18806
20030
|
async close() {
|
|
18807
20031
|
scheduler.close();
|
|
20032
|
+
hermesSessionSyncScheduler.close();
|
|
18808
20033
|
lanIpMonitor.close();
|
|
18809
20034
|
relay?.close();
|
|
18810
20035
|
await closeServer(server);
|
|
@@ -18917,7 +20142,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
18917
20142
|
await mkdir16(paths.logsDir, { recursive: true, mode: 448 });
|
|
18918
20143
|
const log = createRotatingTextLogWriter({
|
|
18919
20144
|
paths,
|
|
18920
|
-
fileName:
|
|
20145
|
+
fileName: path22.basename(daemonLogFile(paths))
|
|
18921
20146
|
});
|
|
18922
20147
|
const scriptPath = currentCliScriptPath();
|
|
18923
20148
|
const child = spawn4(process.execPath, [scriptPath, "daemon", "--foreground"], {
|
|
@@ -19046,7 +20271,7 @@ function currentCliScriptPath() {
|
|
|
19046
20271
|
return process.argv[1];
|
|
19047
20272
|
}
|
|
19048
20273
|
async function readPid(filePath) {
|
|
19049
|
-
const raw = await
|
|
20274
|
+
const raw = await readFile17(filePath, "utf8").catch(() => null);
|
|
19050
20275
|
if (!raw) {
|
|
19051
20276
|
return null;
|
|
19052
20277
|
}
|
|
@@ -19372,21 +20597,21 @@ async function readRemoteLinkPolicy(options) {
|
|
|
19372
20597
|
}
|
|
19373
20598
|
}
|
|
19374
20599
|
function normalizeServerSnapshot(payload) {
|
|
19375
|
-
const snapshot =
|
|
20600
|
+
const snapshot = toRecord17(payload);
|
|
19376
20601
|
const policy = toNullableRecord2(snapshot.policy);
|
|
19377
20602
|
if (!policy) {
|
|
19378
20603
|
return {
|
|
19379
20604
|
remote: null,
|
|
19380
|
-
issue:
|
|
20605
|
+
issue: readString18(snapshot, "issue")
|
|
19381
20606
|
};
|
|
19382
20607
|
}
|
|
19383
20608
|
const release = toNullableRecord2(snapshot.release);
|
|
19384
|
-
const currentVersion =
|
|
19385
|
-
const minSafeVersion =
|
|
20609
|
+
const currentVersion = readString18(policy, "current_version") ?? readString18(policy, "currentVersion");
|
|
20610
|
+
const minSafeVersion = readString18(policy, "min_safe_version") ?? readString18(policy, "minSafeVersion");
|
|
19386
20611
|
if (!currentVersion) {
|
|
19387
20612
|
return {
|
|
19388
20613
|
remote: null,
|
|
19389
|
-
issue:
|
|
20614
|
+
issue: readString18(snapshot, "issue")
|
|
19390
20615
|
};
|
|
19391
20616
|
}
|
|
19392
20617
|
return {
|
|
@@ -19394,10 +20619,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
19394
20619
|
current_version: currentVersion,
|
|
19395
20620
|
min_safe_version: minSafeVersion,
|
|
19396
20621
|
target_version: currentVersion,
|
|
19397
|
-
release_url: release ?
|
|
19398
|
-
published_at: release ?
|
|
20622
|
+
release_url: release ? readString18(release, "release_url") ?? readString18(release, "releaseUrl") : null,
|
|
20623
|
+
published_at: release ? readString18(release, "published_at") ?? readString18(release, "publishedAt") : null
|
|
19399
20624
|
},
|
|
19400
|
-
issue:
|
|
20625
|
+
issue: readString18(snapshot, "issue")
|
|
19401
20626
|
};
|
|
19402
20627
|
}
|
|
19403
20628
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
|
|
@@ -19447,7 +20672,7 @@ async function writeUpdateState2(paths, state) {
|
|
|
19447
20672
|
await writeJsonFile(updateStatePath2(paths), state);
|
|
19448
20673
|
}
|
|
19449
20674
|
async function readUpdateLogLines2(paths) {
|
|
19450
|
-
const raw = await
|
|
20675
|
+
const raw = await readFile18(updateLogPath2(paths), "utf8").catch(() => "");
|
|
19451
20676
|
if (!raw.trim()) {
|
|
19452
20677
|
return [];
|
|
19453
20678
|
}
|
|
@@ -19456,10 +20681,10 @@ async function readUpdateLogLines2(paths) {
|
|
|
19456
20681
|
);
|
|
19457
20682
|
}
|
|
19458
20683
|
function updateStatePath2(paths) {
|
|
19459
|
-
return
|
|
20684
|
+
return path23.join(paths.runDir, "link-update-state.json");
|
|
19460
20685
|
}
|
|
19461
20686
|
function updateLogPath2(paths) {
|
|
19462
|
-
return
|
|
20687
|
+
return path23.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
19463
20688
|
}
|
|
19464
20689
|
async function clearUpdateLogFiles2(paths) {
|
|
19465
20690
|
const primary = updateLogPath2(paths);
|
|
@@ -19511,19 +20736,19 @@ function isProcessAlive4(pid) {
|
|
|
19511
20736
|
return false;
|
|
19512
20737
|
}
|
|
19513
20738
|
}
|
|
19514
|
-
function
|
|
20739
|
+
function toRecord17(value) {
|
|
19515
20740
|
return typeof value === "object" && value !== null ? value : {};
|
|
19516
20741
|
}
|
|
19517
20742
|
function toNullableRecord2(value) {
|
|
19518
20743
|
return typeof value === "object" && value !== null ? value : null;
|
|
19519
20744
|
}
|
|
19520
|
-
function
|
|
20745
|
+
function readString18(payload, key) {
|
|
19521
20746
|
const value = payload[key];
|
|
19522
20747
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19523
20748
|
}
|
|
19524
20749
|
|
|
19525
20750
|
// src/pairing/pairing.ts
|
|
19526
|
-
import
|
|
20751
|
+
import path24 from "path";
|
|
19527
20752
|
import { rm as rm12 } from "fs/promises";
|
|
19528
20753
|
|
|
19529
20754
|
// src/relay/bootstrap.ts
|
|
@@ -19799,8 +21024,8 @@ async function loadRequiredIdentity2(paths) {
|
|
|
19799
21024
|
}
|
|
19800
21025
|
return identity;
|
|
19801
21026
|
}
|
|
19802
|
-
async function postServerJson(serverBaseUrl,
|
|
19803
|
-
const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
21027
|
+
async function postServerJson(serverBaseUrl, path25, body) {
|
|
21028
|
+
const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
19804
21029
|
method: "POST",
|
|
19805
21030
|
headers: {
|
|
19806
21031
|
accept: "application/json",
|
|
@@ -19840,8 +21065,8 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
19840
21065
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
19841
21066
|
};
|
|
19842
21067
|
}
|
|
19843
|
-
async function patchServerJson(serverBaseUrl,
|
|
19844
|
-
const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
21068
|
+
async function patchServerJson(serverBaseUrl, path25, token, body) {
|
|
21069
|
+
const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
19845
21070
|
method: "PATCH",
|
|
19846
21071
|
headers: {
|
|
19847
21072
|
accept: "application/json",
|
|
@@ -19872,10 +21097,10 @@ function readErrorMessage5(payload) {
|
|
|
19872
21097
|
return typeof message === "string" ? message : null;
|
|
19873
21098
|
}
|
|
19874
21099
|
function pairingClaimPath(sessionId, paths) {
|
|
19875
|
-
return
|
|
21100
|
+
return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
19876
21101
|
}
|
|
19877
21102
|
function pairingSessionPath(sessionId, paths) {
|
|
19878
|
-
return
|
|
21103
|
+
return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
19879
21104
|
}
|
|
19880
21105
|
function qrPreferredUrls(routes) {
|
|
19881
21106
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -19946,8 +21171,8 @@ function registerSystemRoutes(router, options) {
|
|
|
19946
21171
|
});
|
|
19947
21172
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
19948
21173
|
const body = await readJsonBody(ctx.req);
|
|
19949
|
-
const sessionId =
|
|
19950
|
-
const claimToken =
|
|
21174
|
+
const sessionId = readString14(body, "session_id") ?? readString14(body, "sessionId");
|
|
21175
|
+
const claimToken = readString14(body, "claim_token") ?? readString14(body, "claimToken");
|
|
19951
21176
|
if (!sessionId || !claimToken) {
|
|
19952
21177
|
throw new LinkHttpError(
|
|
19953
21178
|
400,
|
|
@@ -19958,10 +21183,10 @@ function registerSystemRoutes(router, options) {
|
|
|
19958
21183
|
const claimed = await claimPairing({
|
|
19959
21184
|
sessionId,
|
|
19960
21185
|
claimToken,
|
|
19961
|
-
deviceLabel:
|
|
19962
|
-
devicePlatform:
|
|
19963
|
-
deviceModel:
|
|
19964
|
-
appInstanceId:
|
|
21186
|
+
deviceLabel: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
|
|
21187
|
+
devicePlatform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
|
|
21188
|
+
deviceModel: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
|
|
21189
|
+
appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
|
|
19965
21190
|
paths
|
|
19966
21191
|
});
|
|
19967
21192
|
ctx.body = claimed;
|
|
@@ -20036,9 +21261,9 @@ function registerSystemRoutes(router, options) {
|
|
|
20036
21261
|
const body = await readJsonBody(ctx.req);
|
|
20037
21262
|
const session = await createDeviceSession(
|
|
20038
21263
|
{
|
|
20039
|
-
label:
|
|
20040
|
-
platform:
|
|
20041
|
-
model:
|
|
21264
|
+
label: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
|
|
21265
|
+
platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
|
|
21266
|
+
model: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
|
|
20042
21267
|
appInstanceId: auth.appInstanceId
|
|
20043
21268
|
},
|
|
20044
21269
|
paths
|
|
@@ -20067,7 +21292,7 @@ function registerSystemRoutes(router, options) {
|
|
|
20067
21292
|
});
|
|
20068
21293
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
20069
21294
|
const body = await readJsonBody(ctx.req);
|
|
20070
|
-
const refreshToken =
|
|
21295
|
+
const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
|
|
20071
21296
|
if (!refreshToken) {
|
|
20072
21297
|
throw new LinkHttpError(
|
|
20073
21298
|
400,
|
|
@@ -20078,10 +21303,10 @@ function registerSystemRoutes(router, options) {
|
|
|
20078
21303
|
const session = await refreshDeviceSession(
|
|
20079
21304
|
refreshToken,
|
|
20080
21305
|
{
|
|
20081
|
-
appInstanceId:
|
|
20082
|
-
label:
|
|
20083
|
-
platform:
|
|
20084
|
-
model:
|
|
21306
|
+
appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
|
|
21307
|
+
label: readString14(body, "device_label") ?? readString14(body, "deviceLabel"),
|
|
21308
|
+
platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform"),
|
|
21309
|
+
model: readString14(body, "device_model") ?? readString14(body, "deviceModel")
|
|
20085
21310
|
},
|
|
20086
21311
|
paths
|
|
20087
21312
|
);
|
|
@@ -20100,7 +21325,7 @@ function registerSystemRoutes(router, options) {
|
|
|
20100
21325
|
});
|
|
20101
21326
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
20102
21327
|
const body = await readJsonBody(ctx.req);
|
|
20103
|
-
const refreshToken =
|
|
21328
|
+
const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
|
|
20104
21329
|
if (refreshToken) {
|
|
20105
21330
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
20106
21331
|
}
|
|
@@ -20325,7 +21550,7 @@ function registerSystemRoutes(router, options) {
|
|
|
20325
21550
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
20326
21551
|
const auth = await authenticateRequest(ctx, paths);
|
|
20327
21552
|
const body = await readJsonBody(ctx.req);
|
|
20328
|
-
const label =
|
|
21553
|
+
const label = readString14(body, "label") ?? readString14(body, "device_label");
|
|
20329
21554
|
if (!label) {
|
|
20330
21555
|
throw new LinkHttpError(
|
|
20331
21556
|
400,
|
|
@@ -20365,11 +21590,11 @@ async function readActiveCronJobCount(profiles, logger) {
|
|
|
20365
21590
|
}, 0);
|
|
20366
21591
|
}
|
|
20367
21592
|
function isActiveCronJob(job) {
|
|
20368
|
-
const enabled =
|
|
21593
|
+
const enabled = readBoolean2(job.enabled) ?? true;
|
|
20369
21594
|
if (!enabled) {
|
|
20370
21595
|
return false;
|
|
20371
21596
|
}
|
|
20372
|
-
const state =
|
|
21597
|
+
const state = readString14(job, "state")?.toLowerCase();
|
|
20373
21598
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
20374
21599
|
}
|
|
20375
21600
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -20463,7 +21688,7 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
20463
21688
|
ctx.body = await startLinkUpdate({
|
|
20464
21689
|
paths,
|
|
20465
21690
|
logger,
|
|
20466
|
-
targetVersion:
|
|
21691
|
+
targetVersion: readString14(body, "target_version") ?? readString14(body, "targetVersion")
|
|
20467
21692
|
});
|
|
20468
21693
|
});
|
|
20469
21694
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -20497,7 +21722,7 @@ import QRCode from "qrcode";
|
|
|
20497
21722
|
function registerPairingRoutes(router, options) {
|
|
20498
21723
|
const { paths } = options;
|
|
20499
21724
|
router.get("/pair", async (ctx) => {
|
|
20500
|
-
const sessionId =
|
|
21725
|
+
const sessionId = readString14(ctx.query, "session_id");
|
|
20501
21726
|
if (!sessionId) {
|
|
20502
21727
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20503
21728
|
}
|
|
@@ -20522,7 +21747,7 @@ function registerPairingRoutes(router, options) {
|
|
|
20522
21747
|
ctx.body = page;
|
|
20523
21748
|
});
|
|
20524
21749
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
20525
|
-
const sessionId =
|
|
21750
|
+
const sessionId = readString14(ctx.query, "session_id");
|
|
20526
21751
|
if (!sessionId) {
|
|
20527
21752
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20528
21753
|
}
|
|
@@ -20926,7 +22151,7 @@ function registerInternalRoutes(router, options) {
|
|
|
20926
22151
|
router.post("/internal/deliver", async (ctx) => {
|
|
20927
22152
|
assertLoopbackRequest(ctx.req);
|
|
20928
22153
|
const body = await readJsonBody(ctx.req);
|
|
20929
|
-
const stagingDir =
|
|
22154
|
+
const stagingDir = readString14(body, "staging_dir") ?? readString14(body, "stagingDir");
|
|
20930
22155
|
if (!stagingDir) {
|
|
20931
22156
|
throw new LinkHttpError(
|
|
20932
22157
|
400,
|