@hermespilot/link 0.6.7 → 0.6.8
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.
|
@@ -1359,6 +1359,7 @@ function isNodeError(error, code) {
|
|
|
1359
1359
|
}
|
|
1360
1360
|
|
|
1361
1361
|
// src/storage/atomic-json.ts
|
|
1362
|
+
var jsonUpdateQueues = /* @__PURE__ */ new Map();
|
|
1362
1363
|
async function readJsonFile(filePath) {
|
|
1363
1364
|
try {
|
|
1364
1365
|
const raw = await readFile(filePath, "utf8");
|
|
@@ -1375,6 +1376,24 @@ async function writeJsonFile(filePath, value, mode = 384) {
|
|
|
1375
1376
|
`;
|
|
1376
1377
|
await atomicWriteFilePreservingMetadata(filePath, payload, { mode });
|
|
1377
1378
|
}
|
|
1379
|
+
async function updateJsonFile(filePath, update, mode = 384) {
|
|
1380
|
+
const previous = jsonUpdateQueues.get(filePath) ?? Promise.resolve();
|
|
1381
|
+
let next;
|
|
1382
|
+
const operation = previous.catch(() => void 0).then(async () => {
|
|
1383
|
+
const current = await readJsonFile(filePath);
|
|
1384
|
+
next = await update(current);
|
|
1385
|
+
await writeJsonFile(filePath, next, mode);
|
|
1386
|
+
});
|
|
1387
|
+
const queued = operation.catch(() => void 0);
|
|
1388
|
+
jsonUpdateQueues.set(filePath, queued);
|
|
1389
|
+
void queued.finally(() => {
|
|
1390
|
+
if (jsonUpdateQueues.get(filePath) === queued) {
|
|
1391
|
+
jsonUpdateQueues.delete(filePath);
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
await operation;
|
|
1395
|
+
return next;
|
|
1396
|
+
}
|
|
1378
1397
|
function isNodeError2(error, code) {
|
|
1379
1398
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1380
1399
|
}
|
|
@@ -5485,7 +5504,7 @@ import os2 from "os";
|
|
|
5485
5504
|
import path5 from "path";
|
|
5486
5505
|
|
|
5487
5506
|
// src/constants.ts
|
|
5488
|
-
var LINK_VERSION = "0.6.
|
|
5507
|
+
var LINK_VERSION = "0.6.8";
|
|
5489
5508
|
var LINK_COMMAND = "hermeslink";
|
|
5490
5509
|
var LINK_DEFAULT_PORT = 52379;
|
|
5491
5510
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -9123,6 +9142,7 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
9123
9142
|
clearPlans;
|
|
9124
9143
|
archivePlans;
|
|
9125
9144
|
async prepareClearAllConversationPlan(targetStatus = "active") {
|
|
9145
|
+
assertArchivedClearPlanTarget(targetStatus);
|
|
9126
9146
|
const targets = [];
|
|
9127
9147
|
for (const conversationId of await this.deps.store.listConversationIds()) {
|
|
9128
9148
|
const manifest = await this.deps.store.readManifest(conversationId).catch(() => null);
|
|
@@ -9145,6 +9165,7 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
9145
9165
|
}
|
|
9146
9166
|
async executeClearAllConversationPlan(planId) {
|
|
9147
9167
|
let plan = await this.clearPlans.read(planId);
|
|
9168
|
+
assertArchivedClearPlanTarget(plan.target_status ?? "active");
|
|
9148
9169
|
if (plan.status === "completed") {
|
|
9149
9170
|
return plan;
|
|
9150
9171
|
}
|
|
@@ -9207,6 +9228,7 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
9207
9228
|
}
|
|
9208
9229
|
async startClearAllConversationPlan(planId) {
|
|
9209
9230
|
const plan = await this.clearPlans.read(planId);
|
|
9231
|
+
assertArchivedClearPlanTarget(plan.target_status ?? "active");
|
|
9210
9232
|
if (plan.status === "completed" || plan.status === "executing") {
|
|
9211
9233
|
return plan;
|
|
9212
9234
|
}
|
|
@@ -9688,6 +9710,16 @@ var ConversationMaintenanceCoordinator = class {
|
|
|
9688
9710
|
return plan;
|
|
9689
9711
|
}
|
|
9690
9712
|
};
|
|
9713
|
+
function assertArchivedClearPlanTarget(targetStatus) {
|
|
9714
|
+
if (targetStatus === "archived") {
|
|
9715
|
+
return;
|
|
9716
|
+
}
|
|
9717
|
+
throw new LinkHttpError(
|
|
9718
|
+
409,
|
|
9719
|
+
"active_conversation_clear_plan_disabled",
|
|
9720
|
+
"Bulk deletion of active conversations is disabled. Archive active conversations first, or delete explicitly selected conversations."
|
|
9721
|
+
);
|
|
9722
|
+
}
|
|
9691
9723
|
function isVoiceAttachmentInput(attachment) {
|
|
9692
9724
|
return attachment.kind === "voice" || attachment.type === "voice" || attachment.is_voice_note === true || attachment.isVoiceNote === true;
|
|
9693
9725
|
}
|
|
@@ -19979,7 +20011,7 @@ function isExpectedClientDisconnectError(error) {
|
|
|
19979
20011
|
|
|
19980
20012
|
// src/http/routes/conversations.ts
|
|
19981
20013
|
function registerConversationRoutes(router, options) {
|
|
19982
|
-
const { paths, conversations } = options;
|
|
20014
|
+
const { paths, logger, conversations } = options;
|
|
19983
20015
|
router.get("/api/v1/conversations", async (ctx) => {
|
|
19984
20016
|
await authenticateRequest(ctx, paths);
|
|
19985
20017
|
ctx.set("cache-control", "no-store");
|
|
@@ -20185,12 +20217,20 @@ function registerConversationRoutes(router, options) {
|
|
|
20185
20217
|
ctx.body = { ok: true };
|
|
20186
20218
|
});
|
|
20187
20219
|
router.post("/api/v1/conversations/clear-plans", async (ctx) => {
|
|
20188
|
-
await authenticateRequest(ctx, paths);
|
|
20220
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
20189
20221
|
const body = await readJsonBody(ctx.req);
|
|
20190
20222
|
const targetStatus = readConversationClearPlanTargetStatus(body);
|
|
20191
20223
|
const plan = await conversations.prepareClearAllConversationPlan(
|
|
20192
20224
|
targetStatus
|
|
20193
20225
|
);
|
|
20226
|
+
void logger.warn(
|
|
20227
|
+
"conversation_clear_plan_prepared",
|
|
20228
|
+
conversationMutationAuditFields(ctx, auth, {
|
|
20229
|
+
plan_id: plan.id,
|
|
20230
|
+
target_status: plan.target_status,
|
|
20231
|
+
total_count: plan.total_count
|
|
20232
|
+
})
|
|
20233
|
+
);
|
|
20194
20234
|
ctx.status = 201;
|
|
20195
20235
|
ctx.body = {
|
|
20196
20236
|
ok: true,
|
|
@@ -20208,10 +20248,19 @@ function registerConversationRoutes(router, options) {
|
|
|
20208
20248
|
router.post(
|
|
20209
20249
|
"/api/v1/conversations/clear-plans/:planId/execute",
|
|
20210
20250
|
async (ctx) => {
|
|
20211
|
-
await authenticateRequest(ctx, paths);
|
|
20251
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
20212
20252
|
const plan = await conversations.startClearAllConversationPlan(
|
|
20213
20253
|
ctx.params.planId
|
|
20214
20254
|
);
|
|
20255
|
+
void logger.warn(
|
|
20256
|
+
"conversation_clear_plan_execute_requested",
|
|
20257
|
+
conversationMutationAuditFields(ctx, auth, {
|
|
20258
|
+
plan_id: plan.id,
|
|
20259
|
+
target_status: plan.target_status,
|
|
20260
|
+
total_count: plan.total_count,
|
|
20261
|
+
status: plan.status
|
|
20262
|
+
})
|
|
20263
|
+
);
|
|
20215
20264
|
ctx.status = plan.status === "completed" ? 200 : 202;
|
|
20216
20265
|
ctx.body = {
|
|
20217
20266
|
ok: true,
|
|
@@ -20260,7 +20309,7 @@ function registerConversationRoutes(router, options) {
|
|
|
20260
20309
|
}
|
|
20261
20310
|
);
|
|
20262
20311
|
router.delete("/api/v1/conversations", async (ctx) => {
|
|
20263
|
-
await authenticateRequest(ctx, paths);
|
|
20312
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
20264
20313
|
const body = await readJsonBody(ctx.req);
|
|
20265
20314
|
const conversationIds = readStringArray(
|
|
20266
20315
|
body,
|
|
@@ -20275,6 +20324,14 @@ function registerConversationRoutes(router, options) {
|
|
|
20275
20324
|
);
|
|
20276
20325
|
}
|
|
20277
20326
|
const deleted = await conversations.deleteConversations(conversationIds);
|
|
20327
|
+
void logger.warn(
|
|
20328
|
+
"conversation_bulk_delete_requested",
|
|
20329
|
+
conversationMutationAuditFields(ctx, auth, {
|
|
20330
|
+
requested_count: conversationIds.length,
|
|
20331
|
+
deleted_count: deleted.deleted_count,
|
|
20332
|
+
failed_count: deleted.failed_count
|
|
20333
|
+
})
|
|
20334
|
+
);
|
|
20278
20335
|
const ok = deleted.failed_count === 0;
|
|
20279
20336
|
ctx.status = ok ? 200 : 409;
|
|
20280
20337
|
ctx.body = {
|
|
@@ -20354,10 +20411,21 @@ function registerConversationRoutes(router, options) {
|
|
|
20354
20411
|
}
|
|
20355
20412
|
);
|
|
20356
20413
|
router.delete("/api/v1/conversations/:conversationId", async (ctx) => {
|
|
20357
|
-
await authenticateRequest(ctx, paths);
|
|
20414
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
20415
|
+
const result = await conversations.deleteConversation(
|
|
20416
|
+
ctx.params.conversationId
|
|
20417
|
+
);
|
|
20418
|
+
void logger.warn(
|
|
20419
|
+
"conversation_delete_requested",
|
|
20420
|
+
conversationMutationAuditFields(ctx, auth, {
|
|
20421
|
+
conversation_id: result.conversation_id,
|
|
20422
|
+
hermes_deleted: result.hermes_deleted,
|
|
20423
|
+
hermes_session_count: result.hermes_session_ids?.length ?? 0
|
|
20424
|
+
})
|
|
20425
|
+
);
|
|
20358
20426
|
ctx.body = {
|
|
20359
20427
|
ok: true,
|
|
20360
|
-
...
|
|
20428
|
+
...result,
|
|
20361
20429
|
blob_gc_completed: true
|
|
20362
20430
|
};
|
|
20363
20431
|
});
|
|
@@ -20420,6 +20488,21 @@ function readConversationClearPlanTargetStatus(body) {
|
|
|
20420
20488
|
"Conversation clear plan target status is invalid"
|
|
20421
20489
|
);
|
|
20422
20490
|
}
|
|
20491
|
+
function conversationMutationAuditFields(ctx, auth, fields) {
|
|
20492
|
+
return {
|
|
20493
|
+
method: ctx.method,
|
|
20494
|
+
path: ctx.path,
|
|
20495
|
+
auth_kind: auth.kind,
|
|
20496
|
+
device_id: auth.device?.id ?? null,
|
|
20497
|
+
device_label: auth.device?.label ?? null,
|
|
20498
|
+
device_platform: auth.device?.platform ?? null,
|
|
20499
|
+
device_model: auth.device?.model ?? null,
|
|
20500
|
+
account_id: auth.accountId ?? null,
|
|
20501
|
+
app_instance_id: auth.appInstanceId ?? null,
|
|
20502
|
+
user_agent: ctx.get("user-agent") || null,
|
|
20503
|
+
...fields
|
|
20504
|
+
};
|
|
20505
|
+
}
|
|
20423
20506
|
function readNonNegativeIntegerHeader(value) {
|
|
20424
20507
|
const raw = Array.isArray(value) ? value[0] : value;
|
|
20425
20508
|
if (!raw) {
|
|
@@ -26263,12 +26346,10 @@ function computeRelayBackoffMs(attempt, options = {}) {
|
|
|
26263
26346
|
return exponential + Math.floor(exponential * ratio);
|
|
26264
26347
|
}
|
|
26265
26348
|
async function updateRelayReconnectState(paths, update) {
|
|
26266
|
-
|
|
26267
|
-
const next = {
|
|
26349
|
+
await updateJsonFile(paths.stateFile, (state) => ({
|
|
26268
26350
|
...state,
|
|
26269
|
-
relayReconnect: update(normalizeRelayReconnectState(state
|
|
26270
|
-
};
|
|
26271
|
-
await writeJsonFile(paths.stateFile, next);
|
|
26351
|
+
relayReconnect: update(normalizeRelayReconnectState(state?.relayReconnect))
|
|
26352
|
+
}));
|
|
26272
26353
|
}
|
|
26273
26354
|
async function readLinkState(paths) {
|
|
26274
26355
|
const state = await readJsonFile(paths.stateFile);
|
|
@@ -26375,6 +26456,9 @@ function readInteger4(value) {
|
|
|
26375
26456
|
}
|
|
26376
26457
|
|
|
26377
26458
|
// src/relay/control-client.ts
|
|
26459
|
+
var DEFAULT_RELAY_HANDSHAKE_TIMEOUT_MS = 2e4;
|
|
26460
|
+
var DEFAULT_RELAY_PING_INTERVAL_MS = 3 * 6e4;
|
|
26461
|
+
var DEFAULT_RELAY_PONG_TIMEOUT_MS = 3e4;
|
|
26378
26462
|
function connectRelayControl(options) {
|
|
26379
26463
|
const wsUrl = new URL(`${options.relayBaseUrl.replace(/\/+$/u, "")}/api/v1/relay/link/connect`);
|
|
26380
26464
|
wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
@@ -26383,10 +26467,15 @@ function connectRelayControl(options) {
|
|
|
26383
26467
|
const maxReconnectAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
26384
26468
|
const backoffBaseMs = options.backoffBaseMs ?? DEFAULT_RELAY_RECONNECT_BASE_MS;
|
|
26385
26469
|
const backoffMaxMs = options.backoffMaxMs ?? DEFAULT_RELAY_RECONNECT_MAX_MS;
|
|
26470
|
+
const handshakeTimeoutMs = positiveInteger2(options.handshakeTimeoutMs, DEFAULT_RELAY_HANDSHAKE_TIMEOUT_MS);
|
|
26471
|
+
const pingIntervalMs = positiveInteger2(options.pingIntervalMs, DEFAULT_RELAY_PING_INTERVAL_MS);
|
|
26472
|
+
const pongTimeoutMs = positiveInteger2(options.pongTimeoutMs, DEFAULT_RELAY_PONG_TIMEOUT_MS);
|
|
26386
26473
|
let reconnectAttempts = 0;
|
|
26387
26474
|
let closedByUser = false;
|
|
26388
26475
|
let socket = null;
|
|
26389
26476
|
let retryTimer = null;
|
|
26477
|
+
let pingTimer = null;
|
|
26478
|
+
let pongTimer = null;
|
|
26390
26479
|
let abortControllers = /* @__PURE__ */ new Map();
|
|
26391
26480
|
let fatalRelayRejection = null;
|
|
26392
26481
|
let relayRetryAfterMs = null;
|
|
@@ -26414,15 +26503,19 @@ function connectRelayControl(options) {
|
|
|
26414
26503
|
});
|
|
26415
26504
|
};
|
|
26416
26505
|
const connect = () => {
|
|
26506
|
+
clearRetryTimer();
|
|
26507
|
+
clearHeartbeatTimers();
|
|
26417
26508
|
options.onStatus?.({ state: "connecting", attempt: reconnectAttempts });
|
|
26418
26509
|
fatalRelayRejection = null;
|
|
26419
26510
|
relayRetryAfterMs = null;
|
|
26420
26511
|
let closeHandled = false;
|
|
26512
|
+
let localCloseReason;
|
|
26421
26513
|
const handleConnectionClosed = (reason) => {
|
|
26422
26514
|
if (closeHandled) {
|
|
26423
26515
|
return;
|
|
26424
26516
|
}
|
|
26425
26517
|
closeHandled = true;
|
|
26518
|
+
clearHeartbeatTimers();
|
|
26426
26519
|
abortAll(abortControllers);
|
|
26427
26520
|
abortControllers = /* @__PURE__ */ new Map();
|
|
26428
26521
|
if (fatalRelayRejection) {
|
|
@@ -26449,30 +26542,48 @@ function connectRelayControl(options) {
|
|
|
26449
26542
|
scheduleTimer(backoffMaxMs, "retrying", `Relay reconnect scheduling failed: ${message}`);
|
|
26450
26543
|
});
|
|
26451
26544
|
};
|
|
26452
|
-
|
|
26545
|
+
const currentSocket = new WebSocket(wsUrl, {
|
|
26546
|
+
handshakeTimeout: handshakeTimeoutMs,
|
|
26453
26547
|
headers: {
|
|
26454
26548
|
"x-hermes-link-version": LINK_VERSION
|
|
26455
26549
|
}
|
|
26456
26550
|
});
|
|
26457
|
-
socket
|
|
26551
|
+
socket = currentSocket;
|
|
26552
|
+
currentSocket.on("open", () => {
|
|
26553
|
+
if (socket !== currentSocket) {
|
|
26554
|
+
return;
|
|
26555
|
+
}
|
|
26458
26556
|
reconnectAttempts = 0;
|
|
26459
26557
|
void clearRelayReconnectState(paths).catch(() => void 0);
|
|
26460
26558
|
options.onStatus?.({ state: "connected", attempt: reconnectAttempts });
|
|
26461
|
-
|
|
26462
|
-
|
|
26559
|
+
startHeartbeat(currentSocket, (message) => {
|
|
26560
|
+
localCloseReason = message;
|
|
26561
|
+
options.onStatus?.({ state: "disconnected", attempt: reconnectAttempts, message });
|
|
26562
|
+
currentSocket.terminate();
|
|
26563
|
+
});
|
|
26564
|
+
if (latestNetworkRoutes) {
|
|
26463
26565
|
sendNetworkRoutes(currentSocket, options.linkId, latestNetworkRoutes);
|
|
26464
26566
|
}
|
|
26465
26567
|
});
|
|
26466
|
-
|
|
26467
|
-
if (
|
|
26568
|
+
currentSocket.on("pong", () => {
|
|
26569
|
+
if (socket !== currentSocket) {
|
|
26468
26570
|
return;
|
|
26469
26571
|
}
|
|
26470
|
-
|
|
26572
|
+
clearPongTimer();
|
|
26573
|
+
});
|
|
26574
|
+
currentSocket.on("message", (raw) => {
|
|
26575
|
+
if (socket !== currentSocket || typeof raw !== "string" && !Buffer.isBuffer(raw)) {
|
|
26576
|
+
return;
|
|
26577
|
+
}
|
|
26578
|
+
void handleFrame(currentSocket, String(raw), options.localPort, abortControllers, streamBatchPolicy).catch((error) => {
|
|
26471
26579
|
const message = error instanceof Error ? error.message : "Relay request failed";
|
|
26472
|
-
|
|
26580
|
+
currentSocket.send(JSON.stringify({ type: "http.error", id: "unknown", status: 502, message }));
|
|
26473
26581
|
});
|
|
26474
26582
|
});
|
|
26475
|
-
|
|
26583
|
+
currentSocket.on("unexpected-response", (request, response) => {
|
|
26584
|
+
if (socket !== currentSocket) {
|
|
26585
|
+
return;
|
|
26586
|
+
}
|
|
26476
26587
|
const statusCode = response.statusCode ?? 0;
|
|
26477
26588
|
fatalRelayRejection = resolveFatalRelayRejectionFromStatus(statusCode);
|
|
26478
26589
|
relayRetryAfterMs = readRetryAfterMs(response);
|
|
@@ -26486,7 +26597,10 @@ function connectRelayControl(options) {
|
|
|
26486
26597
|
handleConnectionClosed(message);
|
|
26487
26598
|
request.destroy();
|
|
26488
26599
|
});
|
|
26489
|
-
|
|
26600
|
+
currentSocket.on("error", (error) => {
|
|
26601
|
+
if (socket !== currentSocket) {
|
|
26602
|
+
return;
|
|
26603
|
+
}
|
|
26490
26604
|
const message = error instanceof Error ? error.message : "Relay websocket error";
|
|
26491
26605
|
fatalRelayRejection = resolveFatalRelayRejection(message);
|
|
26492
26606
|
options.onStatus?.({
|
|
@@ -26495,8 +26609,12 @@ function connectRelayControl(options) {
|
|
|
26495
26609
|
message: fatalRelayRejection ?? message
|
|
26496
26610
|
});
|
|
26497
26611
|
});
|
|
26498
|
-
|
|
26499
|
-
|
|
26612
|
+
currentSocket.on("close", (code, reason) => {
|
|
26613
|
+
if (socket !== currentSocket) {
|
|
26614
|
+
return;
|
|
26615
|
+
}
|
|
26616
|
+
socket = null;
|
|
26617
|
+
handleConnectionClosed(localCloseReason ?? formatCloseReason(code, reason));
|
|
26500
26618
|
});
|
|
26501
26619
|
};
|
|
26502
26620
|
startConnect();
|
|
@@ -26526,10 +26644,59 @@ function connectRelayControl(options) {
|
|
|
26526
26644
|
return await readRelayCooldownDelayMs(paths).catch(() => 0);
|
|
26527
26645
|
}
|
|
26528
26646
|
function scheduleTimer(delay4, state, message) {
|
|
26647
|
+
clearRetryTimer();
|
|
26529
26648
|
options.onStatus?.({ state, attempt: reconnectAttempts, message });
|
|
26530
26649
|
retryTimer = setTimeout(connect, delay4);
|
|
26531
26650
|
retryTimer.unref?.();
|
|
26532
26651
|
}
|
|
26652
|
+
function clearRetryTimer() {
|
|
26653
|
+
if (retryTimer == null) {
|
|
26654
|
+
return;
|
|
26655
|
+
}
|
|
26656
|
+
clearTimeout(retryTimer);
|
|
26657
|
+
retryTimer = null;
|
|
26658
|
+
}
|
|
26659
|
+
function startHeartbeat(currentSocket, onTimeout) {
|
|
26660
|
+
clearHeartbeatTimers();
|
|
26661
|
+
pingTimer = setInterval(() => {
|
|
26662
|
+
if (socket !== currentSocket || currentSocket.readyState !== WebSocket.OPEN) {
|
|
26663
|
+
clearHeartbeatTimers();
|
|
26664
|
+
return;
|
|
26665
|
+
}
|
|
26666
|
+
if (pongTimer != null) {
|
|
26667
|
+
return;
|
|
26668
|
+
}
|
|
26669
|
+
try {
|
|
26670
|
+
currentSocket.ping();
|
|
26671
|
+
} catch {
|
|
26672
|
+
onTimeout("Relay websocket ping failed");
|
|
26673
|
+
return;
|
|
26674
|
+
}
|
|
26675
|
+
pongTimer = setTimeout(() => {
|
|
26676
|
+
if (socket !== currentSocket || currentSocket.readyState !== WebSocket.OPEN) {
|
|
26677
|
+
clearPongTimer();
|
|
26678
|
+
return;
|
|
26679
|
+
}
|
|
26680
|
+
onTimeout(`Relay websocket pong timed out after ${pongTimeoutMs}ms`);
|
|
26681
|
+
}, pongTimeoutMs);
|
|
26682
|
+
pongTimer.unref?.();
|
|
26683
|
+
}, pingIntervalMs);
|
|
26684
|
+
pingTimer.unref?.();
|
|
26685
|
+
}
|
|
26686
|
+
function clearHeartbeatTimers() {
|
|
26687
|
+
if (pingTimer != null) {
|
|
26688
|
+
clearInterval(pingTimer);
|
|
26689
|
+
pingTimer = null;
|
|
26690
|
+
}
|
|
26691
|
+
clearPongTimer();
|
|
26692
|
+
}
|
|
26693
|
+
function clearPongTimer() {
|
|
26694
|
+
if (pongTimer == null) {
|
|
26695
|
+
return;
|
|
26696
|
+
}
|
|
26697
|
+
clearTimeout(pongTimer);
|
|
26698
|
+
pongTimer = null;
|
|
26699
|
+
}
|
|
26533
26700
|
return {
|
|
26534
26701
|
publishNetworkRoutes(routes) {
|
|
26535
26702
|
latestNetworkRoutes = routes;
|
|
@@ -26543,10 +26710,8 @@ function connectRelayControl(options) {
|
|
|
26543
26710
|
},
|
|
26544
26711
|
close() {
|
|
26545
26712
|
closedByUser = true;
|
|
26546
|
-
|
|
26547
|
-
|
|
26548
|
-
retryTimer = null;
|
|
26549
|
-
}
|
|
26713
|
+
clearRetryTimer();
|
|
26714
|
+
clearHeartbeatTimers();
|
|
26550
26715
|
abortAll(abortControllers);
|
|
26551
26716
|
socket?.terminate();
|
|
26552
26717
|
}
|
|
@@ -26598,6 +26763,16 @@ function readRetryAfterMs(response) {
|
|
|
26598
26763
|
}
|
|
26599
26764
|
return Math.max(0, dateMs - Date.now());
|
|
26600
26765
|
}
|
|
26766
|
+
function formatCloseReason(code, reason) {
|
|
26767
|
+
const text = reason.toString("utf8").trim();
|
|
26768
|
+
if (code === 1e3 && !text) {
|
|
26769
|
+
return void 0;
|
|
26770
|
+
}
|
|
26771
|
+
return text ? `Relay websocket closed (${code}): ${text}` : `Relay websocket closed (${code})`;
|
|
26772
|
+
}
|
|
26773
|
+
function positiveInteger2(value, fallback) {
|
|
26774
|
+
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
|
|
26775
|
+
}
|
|
26601
26776
|
async function handleFrame(socket, raw, localPort, abortControllers, streamBatchPolicy) {
|
|
26602
26777
|
const frame = JSON.parse(raw);
|
|
26603
26778
|
if (frame.type === "relay.config.update") {
|
|
@@ -26723,8 +26898,7 @@ async function readRelayStatusSnapshot(paths) {
|
|
|
26723
26898
|
return normalizeRelayStatusSnapshot(state.relayStatus);
|
|
26724
26899
|
}
|
|
26725
26900
|
async function writeRelayStatusSnapshot(paths, status, now = /* @__PURE__ */ new Date()) {
|
|
26726
|
-
|
|
26727
|
-
await writeJsonFile(paths.stateFile, {
|
|
26901
|
+
await updateLinkState(paths, (current) => ({
|
|
26728
26902
|
...current,
|
|
26729
26903
|
relayStatus: {
|
|
26730
26904
|
state: status.state,
|
|
@@ -26732,7 +26906,10 @@ async function writeRelayStatusSnapshot(paths, status, now = /* @__PURE__ */ new
|
|
|
26732
26906
|
message: normalizeMessage(status.message),
|
|
26733
26907
|
updatedAt: now.toISOString()
|
|
26734
26908
|
}
|
|
26735
|
-
});
|
|
26909
|
+
}));
|
|
26910
|
+
}
|
|
26911
|
+
async function updateLinkState(paths, update) {
|
|
26912
|
+
await updateJsonFile(paths.stateFile, (state) => update(state && typeof state === "object" ? state : {}));
|
|
26736
26913
|
}
|
|
26737
26914
|
async function readLinkState2(paths) {
|
|
26738
26915
|
const state = await readJsonFile(paths.stateFile);
|
|
@@ -27151,12 +27328,10 @@ async function mergeLastReportedPublicRoutes(paths, snapshotInput) {
|
|
|
27151
27328
|
};
|
|
27152
27329
|
}
|
|
27153
27330
|
async function updateNetworkReportState(paths, update) {
|
|
27154
|
-
|
|
27155
|
-
const next = {
|
|
27331
|
+
await updateJsonFile(paths.stateFile, (state) => ({
|
|
27156
27332
|
...state,
|
|
27157
|
-
networkReport: update(normalizeNetworkReportState(state
|
|
27158
|
-
};
|
|
27159
|
-
await writeJsonFile(paths.stateFile, next);
|
|
27333
|
+
networkReport: update(normalizeNetworkReportState(state?.networkReport))
|
|
27334
|
+
}));
|
|
27160
27335
|
}
|
|
27161
27336
|
async function readLinkState3(paths) {
|
|
27162
27337
|
const state = await readJsonFile(paths.stateFile);
|
|
@@ -30817,7 +30992,7 @@ async function createApp(options = {}) {
|
|
|
30817
30992
|
conversations,
|
|
30818
30993
|
syncCronDeliveries
|
|
30819
30994
|
});
|
|
30820
|
-
registerConversationRoutes(router, { paths, conversations });
|
|
30995
|
+
registerConversationRoutes(router, { paths, logger, conversations });
|
|
30821
30996
|
registerRunRoutes(router, { paths, logger, conversations });
|
|
30822
30997
|
registerProfileRoutes(router, { paths, logger, conversations });
|
|
30823
30998
|
app.use(router.routes());
|
package/dist/cli/index.js
CHANGED
package/dist/http/app.js
CHANGED