@hermespilot/link 0.3.1 → 0.3.3
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-PSHYSZAP.js → chunk-IJTQ6YVR.js} +336 -62
- package/dist/cli/index.js +1 -1
- package/dist/http/app.d.ts +2 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -353,7 +353,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
|
|
|
353
353
|
FROM run_usage_facts
|
|
354
354
|
${where.sql}
|
|
355
355
|
GROUP BY COALESCE(NULLIF(model, ''), 'unknown'), provider
|
|
356
|
-
HAVING total_tokens > 0
|
|
356
|
+
HAVING SUM(total_tokens) > 0
|
|
357
357
|
ORDER BY total_tokens DESC, run_count DESC, model ASC
|
|
358
358
|
LIMIT 12
|
|
359
359
|
`).all(...where.params);
|
|
@@ -379,7 +379,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
|
|
|
379
379
|
NULLIF(profile_uid, ''),
|
|
380
380
|
'unknown'
|
|
381
381
|
)
|
|
382
|
-
HAVING total_tokens > 0
|
|
382
|
+
HAVING SUM(total_tokens) > 0
|
|
383
383
|
ORDER BY total_tokens DESC, run_count DESC, profile ASC
|
|
384
384
|
LIMIT 12
|
|
385
385
|
`).all(...where.params);
|
|
@@ -3724,7 +3724,7 @@ import os2 from "os";
|
|
|
3724
3724
|
import path5 from "path";
|
|
3725
3725
|
|
|
3726
3726
|
// src/constants.ts
|
|
3727
|
-
var LINK_VERSION = "0.3.
|
|
3727
|
+
var LINK_VERSION = "0.3.3";
|
|
3728
3728
|
var LINK_COMMAND = "hermeslink";
|
|
3729
3729
|
var LINK_DEFAULT_PORT = 52379;
|
|
3730
3730
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -4091,7 +4091,8 @@ import { execFile } from "child_process";
|
|
|
4091
4091
|
import { promisify } from "util";
|
|
4092
4092
|
var execFileAsync = promisify(execFile);
|
|
4093
4093
|
async function deleteHermesSession(sessionId, profileName = "default") {
|
|
4094
|
-
|
|
4094
|
+
const normalizedSessionId = sessionId.trim();
|
|
4095
|
+
if (!normalizedSessionId) {
|
|
4095
4096
|
throw new LinkHttpError(
|
|
4096
4097
|
400,
|
|
4097
4098
|
"hermes_session_id_required",
|
|
@@ -4099,15 +4100,31 @@ async function deleteHermesSession(sessionId, profileName = "default") {
|
|
|
4099
4100
|
);
|
|
4100
4101
|
}
|
|
4101
4102
|
try {
|
|
4102
|
-
await execFileAsync(
|
|
4103
|
+
const output = await execFileAsync(
|
|
4103
4104
|
resolveHermesBin(),
|
|
4104
|
-
[
|
|
4105
|
+
[
|
|
4106
|
+
...profileArgs(profileName),
|
|
4107
|
+
"sessions",
|
|
4108
|
+
"delete",
|
|
4109
|
+
normalizedSessionId,
|
|
4110
|
+
"--yes"
|
|
4111
|
+
],
|
|
4105
4112
|
{
|
|
4106
4113
|
timeout: 1e4,
|
|
4107
4114
|
windowsHide: true
|
|
4108
4115
|
}
|
|
4109
4116
|
);
|
|
4117
|
+
return {
|
|
4118
|
+
session_id: normalizedSessionId,
|
|
4119
|
+
status: readSessionDeleteStatus(output.stdout, output.stderr)
|
|
4120
|
+
};
|
|
4110
4121
|
} catch (error) {
|
|
4122
|
+
if (isSessionNotFoundOutput(readExecErrorOutput(error))) {
|
|
4123
|
+
return {
|
|
4124
|
+
session_id: normalizedSessionId,
|
|
4125
|
+
status: "not_found"
|
|
4126
|
+
};
|
|
4127
|
+
}
|
|
4111
4128
|
throw new LinkHttpError(
|
|
4112
4129
|
502,
|
|
4113
4130
|
"hermes_session_delete_failed",
|
|
@@ -4158,6 +4175,29 @@ async function renameHermesSession(sessionId, title, profileName = "default") {
|
|
|
4158
4175
|
function resolveHermesBin() {
|
|
4159
4176
|
return process.env.HERMES_BIN?.trim() || "hermes";
|
|
4160
4177
|
}
|
|
4178
|
+
function readSessionDeleteStatus(stdout, stderr) {
|
|
4179
|
+
const output = `${stdout.toString()}
|
|
4180
|
+
${stderr.toString()}`;
|
|
4181
|
+
if (isSessionNotFoundOutput(output)) {
|
|
4182
|
+
return "not_found";
|
|
4183
|
+
}
|
|
4184
|
+
if (/deleted session\b/i.test(output)) {
|
|
4185
|
+
return "deleted";
|
|
4186
|
+
}
|
|
4187
|
+
return "unknown";
|
|
4188
|
+
}
|
|
4189
|
+
function isSessionNotFoundOutput(output) {
|
|
4190
|
+
return /\bsession\b[\s\S]*\bnot found\b/i.test(output);
|
|
4191
|
+
}
|
|
4192
|
+
function readExecErrorOutput(error) {
|
|
4193
|
+
if (typeof error !== "object" || error === null) {
|
|
4194
|
+
return "";
|
|
4195
|
+
}
|
|
4196
|
+
const stdout = "stdout" in error && error.stdout != null ? String(error.stdout) : "";
|
|
4197
|
+
const stderr = "stderr" in error && error.stderr != null ? String(error.stderr) : "";
|
|
4198
|
+
return `${stdout}
|
|
4199
|
+
${stderr}`;
|
|
4200
|
+
}
|
|
4161
4201
|
function profileArgs(profileName) {
|
|
4162
4202
|
const normalized = profileName.trim() || "default";
|
|
4163
4203
|
return normalized === "default" ? [] : ["-p", normalized];
|
|
@@ -6327,6 +6367,51 @@ function safePathSegment(value, fallback) {
|
|
|
6327
6367
|
return safe.length > 0 ? safe.slice(0, 120) : fallback;
|
|
6328
6368
|
}
|
|
6329
6369
|
|
|
6370
|
+
// src/conversations/conversation-session-ids.ts
|
|
6371
|
+
function normalizeHermesSessionIds(values) {
|
|
6372
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6373
|
+
for (const value of values) {
|
|
6374
|
+
const sessionId = value?.trim();
|
|
6375
|
+
if (!sessionId || seen.has(sessionId)) {
|
|
6376
|
+
continue;
|
|
6377
|
+
}
|
|
6378
|
+
seen.add(sessionId);
|
|
6379
|
+
}
|
|
6380
|
+
return [...seen];
|
|
6381
|
+
}
|
|
6382
|
+
function addHermesSessionIdToManifest(manifest, sessionId) {
|
|
6383
|
+
const normalizedSessionId = sessionId.trim();
|
|
6384
|
+
if (!normalizedSessionId) {
|
|
6385
|
+
return manifest;
|
|
6386
|
+
}
|
|
6387
|
+
const hermesSessionIds = normalizeHermesSessionIds([
|
|
6388
|
+
...manifest.hermes_session_ids ?? [],
|
|
6389
|
+
manifest.hermes_session_id,
|
|
6390
|
+
normalizedSessionId
|
|
6391
|
+
]);
|
|
6392
|
+
if (manifest.hermes_session_id === normalizedSessionId && arraysEqual(manifest.hermes_session_ids ?? [], hermesSessionIds)) {
|
|
6393
|
+
return manifest;
|
|
6394
|
+
}
|
|
6395
|
+
return {
|
|
6396
|
+
...manifest,
|
|
6397
|
+
hermes_session_id: normalizedSessionId,
|
|
6398
|
+
hermes_session_ids: hermesSessionIds
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
6401
|
+
function collectHermesSessionIds(manifest, snapshot) {
|
|
6402
|
+
return normalizeHermesSessionIds([
|
|
6403
|
+
manifest.hermes_session_id,
|
|
6404
|
+
...manifest.hermes_session_ids ?? [],
|
|
6405
|
+
...snapshot.runs.map((run) => run.hermes_session_id)
|
|
6406
|
+
]);
|
|
6407
|
+
}
|
|
6408
|
+
function arraysEqual(left, right) {
|
|
6409
|
+
if (left.length !== right.length) {
|
|
6410
|
+
return false;
|
|
6411
|
+
}
|
|
6412
|
+
return left.every((item, index) => item === right[index]);
|
|
6413
|
+
}
|
|
6414
|
+
|
|
6330
6415
|
// src/conversations/conversation-maintenance.ts
|
|
6331
6416
|
var MAX_UPLOADED_BLOB_BYTES = 50 * 1024 * 1024;
|
|
6332
6417
|
var ConversationMaintenanceCoordinator = class {
|
|
@@ -6447,10 +6532,18 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
6447
6532
|
...collectBlobIds(snapshot),
|
|
6448
6533
|
...await this.listConversationBlobIds(conversationId)
|
|
6449
6534
|
]);
|
|
6450
|
-
|
|
6451
|
-
|
|
6535
|
+
const hermesSessionIds = collectHermesSessionIds(manifest, snapshot);
|
|
6536
|
+
const hermesDeleteResults = await this.deleteHermesSessions(
|
|
6537
|
+
hermesSessionIds,
|
|
6452
6538
|
manifest.profile_name_snapshot ?? manifest.profile ?? "default"
|
|
6453
6539
|
);
|
|
6540
|
+
if (snapshot.runs.length > 0 && hermesDeleteResults.every((result) => result.status === "not_found")) {
|
|
6541
|
+
throw new LinkHttpError(
|
|
6542
|
+
502,
|
|
6543
|
+
"hermes_session_delete_not_confirmed",
|
|
6544
|
+
"Hermes session deletion was not confirmed"
|
|
6545
|
+
);
|
|
6546
|
+
}
|
|
6454
6547
|
const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6455
6548
|
const stats = buildConversationStats(
|
|
6456
6549
|
{
|
|
@@ -6464,6 +6557,7 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
6464
6557
|
const next = {
|
|
6465
6558
|
...manifest,
|
|
6466
6559
|
status: "deleted_soft",
|
|
6560
|
+
hermes_session_ids: hermesSessionIds,
|
|
6467
6561
|
updated_at: deletedAt,
|
|
6468
6562
|
deleted_at: deletedAt,
|
|
6469
6563
|
stats
|
|
@@ -6473,7 +6567,9 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
6473
6567
|
type: "conversation.deleted",
|
|
6474
6568
|
payload: {
|
|
6475
6569
|
deleted_at: deletedAt,
|
|
6476
|
-
hermes_session_id: manifest.hermes_session_id
|
|
6570
|
+
hermes_session_id: manifest.hermes_session_id,
|
|
6571
|
+
hermes_session_ids: hermesSessionIds,
|
|
6572
|
+
hermes_delete_results: hermesDeleteResults
|
|
6477
6573
|
}
|
|
6478
6574
|
});
|
|
6479
6575
|
await this.deps.store.writeSnapshot(conversationId, emptySnapshot2());
|
|
@@ -6491,9 +6587,17 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
6491
6587
|
return {
|
|
6492
6588
|
conversation_id: conversationId,
|
|
6493
6589
|
hermes_deleted: true,
|
|
6590
|
+
hermes_session_ids: hermesSessionIds,
|
|
6494
6591
|
deleted_at: deletedAt
|
|
6495
6592
|
};
|
|
6496
6593
|
}
|
|
6594
|
+
async deleteHermesSessions(sessionIds, profileName) {
|
|
6595
|
+
const results = [];
|
|
6596
|
+
for (const sessionId of sessionIds) {
|
|
6597
|
+
results.push(await deleteHermesSession(sessionId, profileName));
|
|
6598
|
+
}
|
|
6599
|
+
return results;
|
|
6600
|
+
}
|
|
6497
6601
|
async pruneConversationBlobReferences(conversationId, blobIds) {
|
|
6498
6602
|
for (const blobId of blobIds) {
|
|
6499
6603
|
try {
|
|
@@ -7640,8 +7744,12 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
7640
7744
|
const resetAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7641
7745
|
const previousSessionId = input.manifest.hermes_session_id;
|
|
7642
7746
|
const nextSessionId = freshHermesSessionId(input.manifest.id);
|
|
7747
|
+
const nextManifest = addHermesSessionIdToManifest(
|
|
7748
|
+
input.manifest,
|
|
7749
|
+
nextSessionId
|
|
7750
|
+
);
|
|
7643
7751
|
await this.deps.store.writeManifest({
|
|
7644
|
-
...
|
|
7752
|
+
...nextManifest,
|
|
7645
7753
|
hermes_session_id: nextSessionId,
|
|
7646
7754
|
command_state: {
|
|
7647
7755
|
...input.manifest.command_state,
|
|
@@ -9787,9 +9895,11 @@ var ConversationRunLifecycle = class {
|
|
|
9787
9895
|
this.deps.paths,
|
|
9788
9896
|
run.profile
|
|
9789
9897
|
).catch(() => void 0) ?? run.hermes_session_id;
|
|
9790
|
-
await this.
|
|
9791
|
-
|
|
9792
|
-
|
|
9898
|
+
await this.rememberRunHermesSessionId(
|
|
9899
|
+
conversationId,
|
|
9900
|
+
runId,
|
|
9901
|
+
hermesSessionId
|
|
9902
|
+
);
|
|
9793
9903
|
const conversationHistory = await buildConversationHistory({
|
|
9794
9904
|
paths: this.deps.paths,
|
|
9795
9905
|
profileName: run.profile,
|
|
@@ -9859,9 +9969,11 @@ var ConversationRunLifecycle = class {
|
|
|
9859
9969
|
);
|
|
9860
9970
|
const responseSessionId = response.headers.get("x-hermes-session-id")?.trim();
|
|
9861
9971
|
if (responseSessionId) {
|
|
9862
|
-
await this.
|
|
9863
|
-
|
|
9864
|
-
|
|
9972
|
+
await this.rememberRunHermesSessionId(
|
|
9973
|
+
conversationId,
|
|
9974
|
+
runId,
|
|
9975
|
+
responseSessionId
|
|
9976
|
+
);
|
|
9865
9977
|
}
|
|
9866
9978
|
for await (const rawEvent of parseSseResponse(response)) {
|
|
9867
9979
|
if (controller.signal.aborted) {
|
|
@@ -10047,6 +10159,29 @@ ${attachmentLines.join("\n")}`
|
|
|
10047
10159
|
Object.assign(run, patch);
|
|
10048
10160
|
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10049
10161
|
}
|
|
10162
|
+
async rememberRunHermesSessionId(conversationId, runId, sessionId) {
|
|
10163
|
+
const normalizedSessionId = sessionId.trim();
|
|
10164
|
+
if (!normalizedSessionId) {
|
|
10165
|
+
return;
|
|
10166
|
+
}
|
|
10167
|
+
return this.deps.withConversationLock(conversationId, async () => {
|
|
10168
|
+
const snapshot = await this.deps.readSnapshot(conversationId);
|
|
10169
|
+
const run = snapshot.runs.find((item) => item.id === runId);
|
|
10170
|
+
if (!run) {
|
|
10171
|
+
return;
|
|
10172
|
+
}
|
|
10173
|
+
run.hermes_session_id = normalizedSessionId;
|
|
10174
|
+
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10175
|
+
const manifest = await this.deps.readActiveManifest(conversationId);
|
|
10176
|
+
const nextManifest = addHermesSessionIdToManifest(
|
|
10177
|
+
manifest,
|
|
10178
|
+
normalizedSessionId
|
|
10179
|
+
);
|
|
10180
|
+
if (nextManifest !== manifest) {
|
|
10181
|
+
await this.deps.writeManifest(nextManifest);
|
|
10182
|
+
}
|
|
10183
|
+
});
|
|
10184
|
+
}
|
|
10050
10185
|
async runHasAssistantOutput(conversationId, runId) {
|
|
10051
10186
|
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
10052
10187
|
const run = snapshot?.runs.find((item) => item.id === runId);
|
|
@@ -10984,6 +11119,7 @@ var ConversationService = class {
|
|
|
10984
11119
|
statsOverride
|
|
10985
11120
|
),
|
|
10986
11121
|
readActiveManifest: (conversationId) => this.store.readActiveManifest(conversationId),
|
|
11122
|
+
writeManifest: (manifest) => this.store.writeManifest(manifest),
|
|
10987
11123
|
isConversationActive: (conversationId) => this.store.isConversationActive(conversationId),
|
|
10988
11124
|
writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input),
|
|
10989
11125
|
syncCronDeliveries: () => this.syncCronDeliveries(),
|
|
@@ -11089,6 +11225,7 @@ var ConversationService = class {
|
|
|
11089
11225
|
title_source: isDefaultConversationTitle(title) ? "default" : "hermes",
|
|
11090
11226
|
status: "active",
|
|
11091
11227
|
hermes_session_id: `hp_${id}`,
|
|
11228
|
+
hermes_session_ids: [`hp_${id}`],
|
|
11092
11229
|
profile_uid: profile.profileUid,
|
|
11093
11230
|
profile_name_snapshot: profile.profileName,
|
|
11094
11231
|
profile: profile.profileName,
|
|
@@ -12714,7 +12851,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12714
12851
|
ctx.set("cache-control", "private, max-age=86400");
|
|
12715
12852
|
ctx.set(
|
|
12716
12853
|
"content-disposition",
|
|
12717
|
-
|
|
12854
|
+
contentDispositionInline(blob.filename)
|
|
12718
12855
|
);
|
|
12719
12856
|
ctx.body = blob.bytes;
|
|
12720
12857
|
}
|
|
@@ -12733,6 +12870,34 @@ function registerConversationRoutes(router, options) {
|
|
|
12733
12870
|
}
|
|
12734
12871
|
);
|
|
12735
12872
|
}
|
|
12873
|
+
function contentDispositionInline(filename) {
|
|
12874
|
+
const fallback = asciiFilenameFallback(filename);
|
|
12875
|
+
return `inline; filename="${fallback}"; filename*=UTF-8''${encodeRfc5987Value(filename)}`;
|
|
12876
|
+
}
|
|
12877
|
+
function asciiFilenameFallback(filename) {
|
|
12878
|
+
const basename = filename.trim().split(/[\\/]/u).pop()?.trim() ?? "";
|
|
12879
|
+
const extension = safeAsciiExtension(basename);
|
|
12880
|
+
const stem = extension ? basename.slice(0, -extension.length) : basename;
|
|
12881
|
+
const asciiStem = stem.replace(/[^\x20-\x7E]/gu, "_").replace(/["\\]/gu, "_").replace(/[^A-Za-z0-9._ -]/gu, "_").replace(/_+/gu, "_").trim();
|
|
12882
|
+
if (/[A-Za-z0-9]/u.test(asciiStem)) {
|
|
12883
|
+
return `${asciiStem.slice(0, 120)}${extension}`;
|
|
12884
|
+
}
|
|
12885
|
+
return `attachment${extension}`;
|
|
12886
|
+
}
|
|
12887
|
+
function safeAsciiExtension(filename) {
|
|
12888
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
12889
|
+
if (dotIndex < 0 || dotIndex === filename.length - 1) {
|
|
12890
|
+
return "";
|
|
12891
|
+
}
|
|
12892
|
+
const extension = filename.slice(dotIndex).toLowerCase();
|
|
12893
|
+
return /^\.[a-z0-9]{1,12}$/u.test(extension) ? extension : "";
|
|
12894
|
+
}
|
|
12895
|
+
function encodeRfc5987Value(value) {
|
|
12896
|
+
return encodeURIComponent(value).replace(
|
|
12897
|
+
/['()*]/gu,
|
|
12898
|
+
(char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
|
|
12899
|
+
);
|
|
12900
|
+
}
|
|
12736
12901
|
function isConversationNotificationEvent(event) {
|
|
12737
12902
|
const type = event.type.toLowerCase();
|
|
12738
12903
|
return type === "conversation.created" || type === "conversation.updated" || type === "conversation.deleted" || type === "message.created" || type === "message.completed" || type === "message.failed" || type === "run.completed" || type === "run.failed" || type === "run.cancelled" || type === "run.canceled" || type === "approval.requested" || readPayloadBool(event.payload, "requires_action") || readPayloadBool(event.payload, "requires_user_action") || readPayloadBool(event.payload, "requires_approval");
|
|
@@ -14513,8 +14678,8 @@ import {
|
|
|
14513
14678
|
import path18 from "path";
|
|
14514
14679
|
import YAML4 from "yaml";
|
|
14515
14680
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
14516
|
-
var
|
|
14517
|
-
var
|
|
14681
|
+
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
14682
|
+
var DEFAULT_USER_LIMIT = 1375;
|
|
14518
14683
|
var CUSTOM_PROVIDER_CARD_ID = "__custom__";
|
|
14519
14684
|
var CUSTOM_PROVIDER_REGISTRY_FILE = "memory-providers.json";
|
|
14520
14685
|
var HINDSIGHT_DEFAULT_API_URL = "https://api.hindsight.vectorize.io";
|
|
@@ -14597,9 +14762,10 @@ var HermesMemoryError = class extends Error {
|
|
|
14597
14762
|
};
|
|
14598
14763
|
async function readHermesProfileMemory(profileName = "default") {
|
|
14599
14764
|
const memoryDir = resolveMemoryDir(profileName);
|
|
14765
|
+
const limits = await readMemoryLimits(profileName);
|
|
14600
14766
|
const [memoryStore, userStore, settings] = await Promise.all([
|
|
14601
|
-
readMemoryStore(profileName, "memory"),
|
|
14602
|
-
readMemoryStore(profileName, "user"),
|
|
14767
|
+
readMemoryStore(profileName, "memory", limits),
|
|
14768
|
+
readMemoryStore(profileName, "user", limits),
|
|
14603
14769
|
readMemorySettings(profileName)
|
|
14604
14770
|
]);
|
|
14605
14771
|
return {
|
|
@@ -14617,9 +14783,7 @@ async function addHermesMemoryEntry(profileName, target, content) {
|
|
|
14617
14783
|
if (entries.includes(normalized)) {
|
|
14618
14784
|
return entries;
|
|
14619
14785
|
}
|
|
14620
|
-
|
|
14621
|
-
assertWithinLimit(target, next);
|
|
14622
|
-
return next;
|
|
14786
|
+
return [...entries, normalized];
|
|
14623
14787
|
});
|
|
14624
14788
|
return readHermesProfileMemory(profileName);
|
|
14625
14789
|
}
|
|
@@ -14630,7 +14794,6 @@ async function replaceHermesMemoryEntry(profileName, target, oldText, content) {
|
|
|
14630
14794
|
const index = findSingleMatch(entries, needle);
|
|
14631
14795
|
const next = [...entries];
|
|
14632
14796
|
next[index] = normalized;
|
|
14633
|
-
assertWithinLimit(target, next);
|
|
14634
14797
|
return next;
|
|
14635
14798
|
});
|
|
14636
14799
|
return readHermesProfileMemory(profileName);
|
|
@@ -14903,7 +15066,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
14903
15066
|
function resolveMemoryDir(profileName) {
|
|
14904
15067
|
return path18.join(resolveHermesProfileDir(profileName), "memories");
|
|
14905
15068
|
}
|
|
14906
|
-
async function readMemoryStore(profileName, target) {
|
|
15069
|
+
async function readMemoryStore(profileName, target, limits) {
|
|
14907
15070
|
const filePath = memoryFilePath(profileName, target);
|
|
14908
15071
|
const entries = await readMemoryEntries(filePath);
|
|
14909
15072
|
const fileStat = await stat12(filePath).catch((error) => {
|
|
@@ -14913,7 +15076,7 @@ async function readMemoryStore(profileName, target) {
|
|
|
14913
15076
|
throw error;
|
|
14914
15077
|
});
|
|
14915
15078
|
const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
|
|
14916
|
-
const limit = target
|
|
15079
|
+
const limit = memoryLimitForTarget(limits, target);
|
|
14917
15080
|
return {
|
|
14918
15081
|
target,
|
|
14919
15082
|
label: target === "user" ? "\u5173\u4E8E\u7528\u6237" : "Agent \u7B14\u8BB0",
|
|
@@ -14954,7 +15117,7 @@ async function mutateMemoryEntries(profileName, target, mutate) {
|
|
|
14954
15117
|
await writeMemoryEntries(profileName, target, mutate([...new Set(current)]));
|
|
14955
15118
|
}
|
|
14956
15119
|
async function writeMemoryEntries(profileName, target, entries) {
|
|
14957
|
-
assertWithinLimit(target, entries);
|
|
15120
|
+
assertWithinLimit(target, entries, await readMemoryLimits(profileName));
|
|
14958
15121
|
const filePath = memoryFilePath(profileName, target);
|
|
14959
15122
|
const dir = path18.dirname(filePath);
|
|
14960
15123
|
await mkdir12(dir, { recursive: true, mode: 448 });
|
|
@@ -15767,8 +15930,28 @@ function selectSetting(key, label, value, options, editable = true) {
|
|
|
15767
15930
|
const stringValue = readString13(value) ?? options[0] ?? null;
|
|
15768
15931
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
15769
15932
|
}
|
|
15770
|
-
function
|
|
15771
|
-
const
|
|
15933
|
+
async function readMemoryLimits(profileName) {
|
|
15934
|
+
const raw = await readFile13(
|
|
15935
|
+
resolveHermesConfigPath(profileName),
|
|
15936
|
+
"utf8"
|
|
15937
|
+
).catch((error) => {
|
|
15938
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
15939
|
+
return "";
|
|
15940
|
+
}
|
|
15941
|
+
throw error;
|
|
15942
|
+
});
|
|
15943
|
+
const config = raw ? toRecord12(YAML4.parse(raw)) : {};
|
|
15944
|
+
const memory = toRecord12(config.memory);
|
|
15945
|
+
return {
|
|
15946
|
+
memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
|
|
15947
|
+
user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
|
|
15948
|
+
};
|
|
15949
|
+
}
|
|
15950
|
+
function memoryLimitForTarget(limits, target) {
|
|
15951
|
+
return target === "user" ? limits.user : limits.memory;
|
|
15952
|
+
}
|
|
15953
|
+
function assertWithinLimit(target, entries, limits) {
|
|
15954
|
+
const limit = memoryLimitForTarget(limits, target);
|
|
15772
15955
|
const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
|
|
15773
15956
|
if (chars > limit) {
|
|
15774
15957
|
throw new HermesMemoryError(
|
|
@@ -15820,6 +16003,10 @@ function toRecord12(value) {
|
|
|
15820
16003
|
function readString13(value) {
|
|
15821
16004
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
15822
16005
|
}
|
|
16006
|
+
function readPositiveInteger3(value) {
|
|
16007
|
+
const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
|
|
16008
|
+
return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
|
|
16009
|
+
}
|
|
15823
16010
|
function readBoolean2(value) {
|
|
15824
16011
|
if (typeof value === "boolean") {
|
|
15825
16012
|
return value;
|
|
@@ -17495,6 +17682,7 @@ function connectRelayControl(options) {
|
|
|
17495
17682
|
let retryTimer = null;
|
|
17496
17683
|
let abortControllers = /* @__PURE__ */ new Map();
|
|
17497
17684
|
let fatalRelayRejection = null;
|
|
17685
|
+
let latestNetworkRoutes = null;
|
|
17498
17686
|
const connect = () => {
|
|
17499
17687
|
options.onStatus?.({ state: "connecting", attempt: reconnectAttempts });
|
|
17500
17688
|
fatalRelayRejection = null;
|
|
@@ -17506,6 +17694,10 @@ function connectRelayControl(options) {
|
|
|
17506
17694
|
socket.on("open", () => {
|
|
17507
17695
|
reconnectAttempts = 0;
|
|
17508
17696
|
options.onStatus?.({ state: "connected", attempt: reconnectAttempts });
|
|
17697
|
+
const currentSocket = socket;
|
|
17698
|
+
if (currentSocket && latestNetworkRoutes) {
|
|
17699
|
+
sendNetworkRoutes(currentSocket, options.linkId, latestNetworkRoutes);
|
|
17700
|
+
}
|
|
17509
17701
|
});
|
|
17510
17702
|
socket.on("message", (raw) => {
|
|
17511
17703
|
if (!socket || typeof raw !== "string" && !Buffer.isBuffer(raw)) {
|
|
@@ -17553,6 +17745,12 @@ function connectRelayControl(options) {
|
|
|
17553
17745
|
};
|
|
17554
17746
|
connect();
|
|
17555
17747
|
return {
|
|
17748
|
+
publishNetworkRoutes(routes) {
|
|
17749
|
+
latestNetworkRoutes = routes;
|
|
17750
|
+
if (socket?.readyState === WebSocket.OPEN) {
|
|
17751
|
+
sendNetworkRoutes(socket, options.linkId, routes);
|
|
17752
|
+
}
|
|
17753
|
+
},
|
|
17556
17754
|
close() {
|
|
17557
17755
|
closedByUser = true;
|
|
17558
17756
|
if (retryTimer) {
|
|
@@ -17564,6 +17762,19 @@ function connectRelayControl(options) {
|
|
|
17564
17762
|
}
|
|
17565
17763
|
};
|
|
17566
17764
|
}
|
|
17765
|
+
function sendNetworkRoutes(socket, linkId, routes) {
|
|
17766
|
+
socket.send(JSON.stringify({
|
|
17767
|
+
type: "network.routes",
|
|
17768
|
+
id: `routes_${Date.now().toString(36)}`,
|
|
17769
|
+
payload: {
|
|
17770
|
+
link_id: linkId,
|
|
17771
|
+
lan_ips: routes.lanIps,
|
|
17772
|
+
public_ipv4s: routes.publicIpv4s,
|
|
17773
|
+
public_ipv6s: routes.publicIpv6s,
|
|
17774
|
+
observed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
17775
|
+
}
|
|
17776
|
+
}));
|
|
17777
|
+
}
|
|
17567
17778
|
function resolveFatalRelayRejection(message) {
|
|
17568
17779
|
if (!/Unexpected server response:\s*(400|401|403|426)\b/u.test(message)) {
|
|
17569
17780
|
return null;
|
|
@@ -17787,7 +17998,7 @@ async function discoverRouteCandidates(options) {
|
|
|
17787
17998
|
const environment = detectRuntimeEnvironment();
|
|
17788
17999
|
const configuredLanHost = normalizeLanHost(options.configuredLanHost);
|
|
17789
18000
|
const lanIps = configuredLanHost ? [configuredLanHost] : environment.lanAutoDiscoveryUsable ? discoverLanIps() : [];
|
|
17790
|
-
const publicIps = options.relayBootstrapToken ? await observePublicRoute(options).catch(() => ({ publicIpv4s: [], publicIpv6s: [] })) : { publicIpv4s: [], publicIpv6s: [] };
|
|
18001
|
+
const publicIps = options.relayBootstrapToken || options.observePublicRoute ? await observePublicRoute(options).catch(() => ({ publicIpv4s: [], publicIpv6s: [] })) : { publicIpv4s: [], publicIpv6s: [] };
|
|
17791
18002
|
const publicIpv4s = unique(publicIps.publicIpv4s.filter(isUsablePublicIpv4)).slice(0, MAX_PUBLIC_IPV4S);
|
|
17792
18003
|
const publicIpv6s = unique(publicIps.publicIpv6s.filter(isUsablePublicIpv6)).slice(0, MAX_PUBLIC_IPV6S);
|
|
17793
18004
|
const preferredUrls = [
|
|
@@ -17830,8 +18041,8 @@ async function observePublicRoute(options) {
|
|
|
17830
18041
|
const response = await fetcher(`${options.relayBaseUrl.replace(/\/+$/u, "")}/api/v1/relay/public-route/observe`, {
|
|
17831
18042
|
method: "POST",
|
|
17832
18043
|
headers: {
|
|
17833
|
-
|
|
17834
|
-
|
|
18044
|
+
"content-type": "application/json",
|
|
18045
|
+
...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
|
|
17835
18046
|
},
|
|
17836
18047
|
body: JSON.stringify({
|
|
17837
18048
|
install_id: options.installId,
|
|
@@ -17943,25 +18154,32 @@ function unique(values) {
|
|
|
17943
18154
|
|
|
17944
18155
|
// src/link/network-report-state.ts
|
|
17945
18156
|
var DEFAULT_AUTO_DAILY_LIMIT = 20;
|
|
17946
|
-
async function markNetworkStatusReported(paths,
|
|
18157
|
+
async function markNetworkStatusReported(paths, snapshotInput, reportedAt = /* @__PURE__ */ new Date()) {
|
|
18158
|
+
const snapshot = normalizeNetworkSnapshot(snapshotInput);
|
|
17947
18159
|
await updateNetworkReportState(paths, (current) => ({
|
|
17948
18160
|
...current,
|
|
17949
|
-
lastReportedLanIps:
|
|
18161
|
+
lastReportedLanIps: snapshot.lanIps,
|
|
18162
|
+
lastReportedPublicIpv4s: snapshot.publicIpv4s,
|
|
18163
|
+
lastReportedPublicIpv6s: snapshot.publicIpv6s,
|
|
17950
18164
|
lastReportedAt: reportedAt.toISOString(),
|
|
17951
|
-
lastAutoAttempt: current.lastAutoAttempt ? { ...current.lastAutoAttempt, success: true } : null
|
|
18165
|
+
lastAutoAttempt: current.lastAutoAttempt ? { ...current.lastAutoAttempt, ...snapshot, success: true } : null
|
|
17952
18166
|
}));
|
|
17953
18167
|
}
|
|
17954
|
-
async function reserveAutomaticNetworkReport(paths,
|
|
17955
|
-
const snapshot =
|
|
18168
|
+
async function reserveAutomaticNetworkReport(paths, snapshotInput, options = {}) {
|
|
18169
|
+
const snapshot = normalizeNetworkSnapshot(snapshotInput);
|
|
17956
18170
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
17957
18171
|
const dailyLimit = Math.max(0, Math.floor(options.dailyLimit ?? DEFAULT_AUTO_DAILY_LIMIT));
|
|
17958
18172
|
let reservation = { allowed: false, reason: "unchanged" };
|
|
17959
18173
|
await updateNetworkReportState(paths, (current) => {
|
|
17960
|
-
if (
|
|
17961
|
-
|
|
17962
|
-
|
|
18174
|
+
if (sameNetworkSnapshot(readReportedSnapshot(current), snapshot)) {
|
|
18175
|
+
const lastReportedAt = current.lastReportedAt ? Date.parse(current.lastReportedAt) : Number.NaN;
|
|
18176
|
+
const unchangedMinIntervalMs = Math.max(0, Math.floor(options.unchangedMinIntervalMs ?? 0));
|
|
18177
|
+
if (!options.force || Number.isFinite(lastReportedAt) && now.getTime() - lastReportedAt < unchangedMinIntervalMs) {
|
|
18178
|
+
reservation = { allowed: false, reason: "unchanged" };
|
|
18179
|
+
return current;
|
|
18180
|
+
}
|
|
17963
18181
|
}
|
|
17964
|
-
if (current.lastAutoAttempt && !current.lastAutoAttempt.success &&
|
|
18182
|
+
if (current.lastAutoAttempt && !current.lastAutoAttempt.success && sameNetworkSnapshot(readAttemptSnapshot(current.lastAutoAttempt), snapshot)) {
|
|
17965
18183
|
reservation = { allowed: false, reason: "failed_snapshot_not_retried" };
|
|
17966
18184
|
return current;
|
|
17967
18185
|
}
|
|
@@ -17977,7 +18195,7 @@ async function reserveAutomaticNetworkReport(paths, lanIps, options = {}) {
|
|
|
17977
18195
|
autoQuotaDay: quotaDay,
|
|
17978
18196
|
autoReportsToday: reportsToday + 1,
|
|
17979
18197
|
lastAutoAttempt: {
|
|
17980
|
-
|
|
18198
|
+
...snapshot,
|
|
17981
18199
|
attemptedAt: now.toISOString(),
|
|
17982
18200
|
success: false
|
|
17983
18201
|
}
|
|
@@ -18001,6 +18219,8 @@ function normalizeNetworkReportState(value) {
|
|
|
18001
18219
|
const record = value && typeof value === "object" ? value : {};
|
|
18002
18220
|
return {
|
|
18003
18221
|
lastReportedLanIps: normalizeLanIps(record.lastReportedLanIps),
|
|
18222
|
+
lastReportedPublicIpv4s: normalizeLanIps(record.lastReportedPublicIpv4s),
|
|
18223
|
+
lastReportedPublicIpv6s: normalizeLanIps(record.lastReportedPublicIpv6s),
|
|
18004
18224
|
lastReportedAt: typeof record.lastReportedAt === "string" ? record.lastReportedAt : null,
|
|
18005
18225
|
autoQuotaDay: typeof record.autoQuotaDay === "string" ? record.autoQuotaDay : null,
|
|
18006
18226
|
autoReportsToday: Number.isFinite(record.autoReportsToday) ? Math.max(0, Math.floor(record.autoReportsToday ?? 0)) : 0,
|
|
@@ -18017,10 +18237,41 @@ function normalizeAttempt(value) {
|
|
|
18017
18237
|
}
|
|
18018
18238
|
return {
|
|
18019
18239
|
lanIps: normalizeLanIps(record.lanIps),
|
|
18240
|
+
publicIpv4s: normalizeLanIps(record.publicIpv4s),
|
|
18241
|
+
publicIpv6s: normalizeLanIps(record.publicIpv6s),
|
|
18020
18242
|
attemptedAt: record.attemptedAt,
|
|
18021
18243
|
success: record.success === true
|
|
18022
18244
|
};
|
|
18023
18245
|
}
|
|
18246
|
+
function normalizeNetworkSnapshot(value) {
|
|
18247
|
+
if (Array.isArray(value)) {
|
|
18248
|
+
return {
|
|
18249
|
+
lanIps: normalizeLanIps(value),
|
|
18250
|
+
publicIpv4s: [],
|
|
18251
|
+
publicIpv6s: []
|
|
18252
|
+
};
|
|
18253
|
+
}
|
|
18254
|
+
const record = value && typeof value === "object" ? value : {};
|
|
18255
|
+
return {
|
|
18256
|
+
lanIps: normalizeLanIps(record.lanIps),
|
|
18257
|
+
publicIpv4s: normalizeLanIps(record.publicIpv4s),
|
|
18258
|
+
publicIpv6s: normalizeLanIps(record.publicIpv6s)
|
|
18259
|
+
};
|
|
18260
|
+
}
|
|
18261
|
+
function readReportedSnapshot(state) {
|
|
18262
|
+
return {
|
|
18263
|
+
lanIps: state.lastReportedLanIps,
|
|
18264
|
+
publicIpv4s: state.lastReportedPublicIpv4s,
|
|
18265
|
+
publicIpv6s: state.lastReportedPublicIpv6s
|
|
18266
|
+
};
|
|
18267
|
+
}
|
|
18268
|
+
function readAttemptSnapshot(attempt) {
|
|
18269
|
+
return {
|
|
18270
|
+
lanIps: attempt.lanIps,
|
|
18271
|
+
publicIpv4s: attempt.publicIpv4s,
|
|
18272
|
+
publicIpv6s: attempt.publicIpv6s
|
|
18273
|
+
};
|
|
18274
|
+
}
|
|
18024
18275
|
function normalizeLanIps(value) {
|
|
18025
18276
|
if (!Array.isArray(value)) {
|
|
18026
18277
|
return [];
|
|
@@ -18031,7 +18282,10 @@ function normalizeLanIps(value) {
|
|
|
18031
18282
|
)
|
|
18032
18283
|
];
|
|
18033
18284
|
}
|
|
18034
|
-
function
|
|
18285
|
+
function sameNetworkSnapshot(left, right) {
|
|
18286
|
+
return sameStringList(left.lanIps, right.lanIps) && sameStringList(left.publicIpv4s, right.publicIpv4s) && sameStringList(left.publicIpv6s, right.publicIpv6s);
|
|
18287
|
+
}
|
|
18288
|
+
function sameStringList(left, right) {
|
|
18035
18289
|
if (left.length !== right.length) {
|
|
18036
18290
|
return false;
|
|
18037
18291
|
}
|
|
@@ -18048,12 +18302,13 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
18048
18302
|
if (!identity?.link_id) {
|
|
18049
18303
|
return null;
|
|
18050
18304
|
}
|
|
18051
|
-
const routes = await discoverRouteCandidates({
|
|
18305
|
+
const routes = options.routes ?? await discoverRouteCandidates({
|
|
18052
18306
|
port: config.port,
|
|
18053
18307
|
relayBaseUrl: config.relayBaseUrl,
|
|
18054
18308
|
linkId: identity.link_id,
|
|
18055
18309
|
installId: identity.install_id,
|
|
18056
18310
|
publicKeyPem: identity.public_key_pem,
|
|
18311
|
+
observePublicRoute: true,
|
|
18057
18312
|
configuredLanHost: config.lanHost,
|
|
18058
18313
|
fetchImpl: options.fetchImpl
|
|
18059
18314
|
});
|
|
@@ -18093,7 +18348,7 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
18093
18348
|
const message = readErrorMessage3(body) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
|
|
18094
18349
|
throw new LinkHttpError(response.status, "server_request_failed", message);
|
|
18095
18350
|
}
|
|
18096
|
-
await markNetworkStatusReported(paths, routes
|
|
18351
|
+
await markNetworkStatusReported(paths, routes);
|
|
18097
18352
|
return body;
|
|
18098
18353
|
}
|
|
18099
18354
|
function canonicalJson(value) {
|
|
@@ -18128,16 +18383,17 @@ function readErrorMessage3(payload) {
|
|
|
18128
18383
|
// src/daemon/lan-ip-monitor.ts
|
|
18129
18384
|
var DEFAULT_INTERVAL_MS = 5 * 6e4;
|
|
18130
18385
|
var DEFAULT_DAILY_REPORT_LIMIT = 20;
|
|
18386
|
+
var DEFAULT_STARTUP_REPORT_MIN_INTERVAL_MS = 15 * 6e4;
|
|
18131
18387
|
function startLanIpMonitor(options) {
|
|
18132
18388
|
let running = false;
|
|
18133
18389
|
let closed = false;
|
|
18134
|
-
const check = async () => {
|
|
18390
|
+
const check = async (context = {}) => {
|
|
18135
18391
|
if (running || closed) {
|
|
18136
18392
|
return;
|
|
18137
18393
|
}
|
|
18138
18394
|
running = true;
|
|
18139
18395
|
try {
|
|
18140
|
-
await checkLanIpChange(options);
|
|
18396
|
+
await checkLanIpChange(options, context);
|
|
18141
18397
|
} catch (error) {
|
|
18142
18398
|
void options.logger.warn("lan_ip_monitor_failed", {
|
|
18143
18399
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -18146,7 +18402,7 @@ function startLanIpMonitor(options) {
|
|
|
18146
18402
|
running = false;
|
|
18147
18403
|
}
|
|
18148
18404
|
};
|
|
18149
|
-
void check();
|
|
18405
|
+
void check({ forceReport: true, publishToRelay: true });
|
|
18150
18406
|
const timer = setInterval(() => {
|
|
18151
18407
|
void check();
|
|
18152
18408
|
}, options.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
@@ -18158,7 +18414,7 @@ function startLanIpMonitor(options) {
|
|
|
18158
18414
|
}
|
|
18159
18415
|
};
|
|
18160
18416
|
}
|
|
18161
|
-
async function checkLanIpChange(options) {
|
|
18417
|
+
async function checkLanIpChange(options, context = {}) {
|
|
18162
18418
|
const [identity, config] = await Promise.all([
|
|
18163
18419
|
loadIdentity(options.paths),
|
|
18164
18420
|
loadConfig(options.paths)
|
|
@@ -18172,14 +18428,25 @@ async function checkLanIpChange(options) {
|
|
|
18172
18428
|
linkId: identity.link_id,
|
|
18173
18429
|
installId: identity.install_id,
|
|
18174
18430
|
publicKeyPem: identity.public_key_pem,
|
|
18431
|
+
observePublicRoute: true,
|
|
18175
18432
|
configuredLanHost: config.lanHost,
|
|
18176
18433
|
fetchImpl: options.fetchImpl
|
|
18177
18434
|
});
|
|
18178
|
-
|
|
18179
|
-
|
|
18435
|
+
if (context.publishToRelay) {
|
|
18436
|
+
options.onNetworkRoutes?.(routes);
|
|
18437
|
+
}
|
|
18438
|
+
const reservation = await reserveAutomaticNetworkReport(options.paths, routes, {
|
|
18439
|
+
dailyLimit: options.dailyReportLimit ?? DEFAULT_DAILY_REPORT_LIMIT,
|
|
18440
|
+
force: context.forceReport === true,
|
|
18441
|
+
unchangedMinIntervalMs: options.startupReportMinIntervalMs ?? DEFAULT_STARTUP_REPORT_MIN_INTERVAL_MS
|
|
18180
18442
|
});
|
|
18181
18443
|
if (!reservation.allowed) {
|
|
18182
|
-
const logFields = {
|
|
18444
|
+
const logFields = {
|
|
18445
|
+
lan_ips: routes.lanIps,
|
|
18446
|
+
public_ipv4s: routes.publicIpv4s,
|
|
18447
|
+
public_ipv6s: routes.publicIpv6s,
|
|
18448
|
+
reason: reservation.reason
|
|
18449
|
+
};
|
|
18183
18450
|
if (reservation.reason === "daily_limit_reached") {
|
|
18184
18451
|
void options.logger.warn("lan_ip_report_skipped", logFields);
|
|
18185
18452
|
} else {
|
|
@@ -18190,12 +18457,16 @@ async function checkLanIpChange(options) {
|
|
|
18190
18457
|
try {
|
|
18191
18458
|
const result = await reportLinkStatusToServer({
|
|
18192
18459
|
paths: options.paths,
|
|
18193
|
-
fetchImpl: options.fetchImpl
|
|
18460
|
+
fetchImpl: options.fetchImpl,
|
|
18461
|
+
routes
|
|
18194
18462
|
});
|
|
18195
18463
|
if (result) {
|
|
18464
|
+
options.onNetworkRoutes?.(routes);
|
|
18196
18465
|
void options.logger.info("lan_ip_change_reported", {
|
|
18197
18466
|
link_id: result.linkId,
|
|
18198
|
-
lan_ips: routes.lanIps
|
|
18467
|
+
lan_ips: routes.lanIps,
|
|
18468
|
+
public_ipv4s: routes.publicIpv4s,
|
|
18469
|
+
public_ipv6s: routes.publicIpv6s
|
|
18199
18470
|
});
|
|
18200
18471
|
}
|
|
18201
18472
|
} catch (error) {
|
|
@@ -18287,13 +18558,6 @@ async function startLinkService(options = {}) {
|
|
|
18287
18558
|
conversations,
|
|
18288
18559
|
logger
|
|
18289
18560
|
});
|
|
18290
|
-
const lanIpMonitor = startLanIpMonitor({
|
|
18291
|
-
paths,
|
|
18292
|
-
logger,
|
|
18293
|
-
intervalMs: options.lanIpMonitorIntervalMs,
|
|
18294
|
-
dailyReportLimit: options.lanIpMonitorDailyReportLimit,
|
|
18295
|
-
fetchImpl: options.lanIpMonitorFetchImpl
|
|
18296
|
-
});
|
|
18297
18561
|
let relay = null;
|
|
18298
18562
|
if (identity?.link_id) {
|
|
18299
18563
|
relay = connectRelayControl({
|
|
@@ -18310,6 +18574,16 @@ async function startLinkService(options = {}) {
|
|
|
18310
18574
|
} else {
|
|
18311
18575
|
void logger.info("relay_skipped", { reason: "link_not_paired" });
|
|
18312
18576
|
}
|
|
18577
|
+
const lanIpMonitor = startLanIpMonitor({
|
|
18578
|
+
paths,
|
|
18579
|
+
logger,
|
|
18580
|
+
intervalMs: options.lanIpMonitorIntervalMs,
|
|
18581
|
+
dailyReportLimit: options.lanIpMonitorDailyReportLimit,
|
|
18582
|
+
fetchImpl: options.lanIpMonitorFetchImpl,
|
|
18583
|
+
onNetworkRoutes: (routes) => {
|
|
18584
|
+
relay?.publishNetworkRoutes(routes);
|
|
18585
|
+
}
|
|
18586
|
+
});
|
|
18313
18587
|
if (options.writePidFile) {
|
|
18314
18588
|
await writePidFile(paths);
|
|
18315
18589
|
}
|
package/dist/cli/index.js
CHANGED
package/dist/http/app.d.ts
CHANGED
|
@@ -237,12 +237,14 @@ interface ConversationEvent {
|
|
|
237
237
|
interface DeleteConversationResult {
|
|
238
238
|
conversation_id: string;
|
|
239
239
|
hermes_deleted: boolean;
|
|
240
|
+
hermes_session_ids?: string[];
|
|
240
241
|
deleted_at: string;
|
|
241
242
|
}
|
|
242
243
|
interface BulkDeleteConversationResult {
|
|
243
244
|
conversation_id: string;
|
|
244
245
|
status: 'deleted' | 'failed';
|
|
245
246
|
hermes_deleted?: boolean;
|
|
247
|
+
hermes_session_ids?: string[];
|
|
246
248
|
deleted_at?: string;
|
|
247
249
|
error?: {
|
|
248
250
|
code: string;
|
package/dist/http/app.js
CHANGED