@agentvault/agentvault 0.14.3 → 0.14.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/channel.d.ts +12 -0
- package/dist/channel.d.ts.map +1 -1
- package/dist/cli.js +354 -24
- package/dist/cli.js.map +4 -4
- package/dist/index.js +309 -12
- package/dist/index.js.map +4 -4
- package/dist/workspace-handlers.d.ts +62 -0
- package/dist/workspace-handlers.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x2) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x2, {
|
|
4
|
+
get: (a2, b2) => (typeof require !== "undefined" ? require : a2)[b2]
|
|
5
|
+
}) : x2)(function(x2) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x2 + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -45022,9 +45028,53 @@ var init_file_crypto = __esm({
|
|
|
45022
45028
|
});
|
|
45023
45029
|
|
|
45024
45030
|
// ../crypto/dist/did.js
|
|
45031
|
+
function canonicalize(obj) {
|
|
45032
|
+
const json = jsonCanonical(obj) ?? "";
|
|
45033
|
+
return libsodium_wrappers_default.from_string(json);
|
|
45034
|
+
}
|
|
45035
|
+
function jsonCanonical(value) {
|
|
45036
|
+
if (value === void 0)
|
|
45037
|
+
return void 0;
|
|
45038
|
+
if (value === null)
|
|
45039
|
+
return "null";
|
|
45040
|
+
if (typeof value === "boolean" || typeof value === "number")
|
|
45041
|
+
return JSON.stringify(value);
|
|
45042
|
+
if (typeof value === "string")
|
|
45043
|
+
return JSON.stringify(value);
|
|
45044
|
+
if (Array.isArray(value)) {
|
|
45045
|
+
return "[" + value.map(jsonCanonical).join(",") + "]";
|
|
45046
|
+
}
|
|
45047
|
+
if (typeof value === "object") {
|
|
45048
|
+
const keys = Object.keys(value).sort();
|
|
45049
|
+
const entries = keys.map((k2) => {
|
|
45050
|
+
const v2 = jsonCanonical(value[k2]);
|
|
45051
|
+
if (v2 === void 0)
|
|
45052
|
+
return void 0;
|
|
45053
|
+
return JSON.stringify(k2) + ":" + v2;
|
|
45054
|
+
}).filter((entry) => entry !== void 0);
|
|
45055
|
+
return "{" + entries.join(",") + "}";
|
|
45056
|
+
}
|
|
45057
|
+
return JSON.stringify(value);
|
|
45058
|
+
}
|
|
45059
|
+
async function verifyDocumentSignature(document, signature, publicKey) {
|
|
45060
|
+
await libsodium_wrappers_default.ready;
|
|
45061
|
+
const canonical = canonicalize(document);
|
|
45062
|
+
const domainPrefix = libsodium_wrappers_default.from_string(DOMAIN_DID_DOCUMENT);
|
|
45063
|
+
const message = new Uint8Array(domainPrefix.length + canonical.length);
|
|
45064
|
+
message.set(domainPrefix);
|
|
45065
|
+
message.set(canonical, domainPrefix.length);
|
|
45066
|
+
try {
|
|
45067
|
+
return libsodium_wrappers_default.crypto_sign_verify_detached(signature, message, publicKey);
|
|
45068
|
+
} catch {
|
|
45069
|
+
return false;
|
|
45070
|
+
}
|
|
45071
|
+
}
|
|
45072
|
+
var DOMAIN_DID_DOCUMENT;
|
|
45025
45073
|
var init_did = __esm({
|
|
45026
45074
|
async "../crypto/dist/did.js"() {
|
|
45027
45075
|
"use strict";
|
|
45076
|
+
await init_libsodium_wrappers();
|
|
45077
|
+
DOMAIN_DID_DOCUMENT = "DID-DOCUMENT:";
|
|
45028
45078
|
}
|
|
45029
45079
|
});
|
|
45030
45080
|
|
|
@@ -45979,13 +46029,152 @@ var init_http_handlers = __esm({
|
|
|
45979
46029
|
}
|
|
45980
46030
|
});
|
|
45981
46031
|
|
|
46032
|
+
// src/workspace-handlers.ts
|
|
46033
|
+
var workspace_handlers_exports = {};
|
|
46034
|
+
__export(workspace_handlers_exports, {
|
|
46035
|
+
handleWorkspaceList: () => handleWorkspaceList,
|
|
46036
|
+
handleWorkspaceRead: () => handleWorkspaceRead,
|
|
46037
|
+
handleWorkspaceUpload: () => handleWorkspaceUpload,
|
|
46038
|
+
validateWorkspaceFilename: () => validateWorkspaceFilename
|
|
46039
|
+
});
|
|
46040
|
+
import { readdir, readFile as readFile2, writeFile as writeFile2, rename as rename2, stat, unlink } from "node:fs/promises";
|
|
46041
|
+
import { join as join2 } from "node:path";
|
|
46042
|
+
import { randomUUID } from "node:crypto";
|
|
46043
|
+
function validateWorkspaceFilename(filename) {
|
|
46044
|
+
if (!filename || typeof filename !== "string") {
|
|
46045
|
+
return "Filename is required";
|
|
46046
|
+
}
|
|
46047
|
+
if (filename.includes("\0")) {
|
|
46048
|
+
return "Filename contains null bytes";
|
|
46049
|
+
}
|
|
46050
|
+
if (filename.includes("..")) {
|
|
46051
|
+
return "Path traversal not allowed";
|
|
46052
|
+
}
|
|
46053
|
+
if (filename.includes("/") || filename.includes("\\")) {
|
|
46054
|
+
return "Path separators not allowed in filename";
|
|
46055
|
+
}
|
|
46056
|
+
if (!filename.endsWith(".md")) {
|
|
46057
|
+
return "Only .md files are allowed";
|
|
46058
|
+
}
|
|
46059
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {
|
|
46060
|
+
return "Filename may only contain letters, numbers, hyphens, underscores, and dots";
|
|
46061
|
+
}
|
|
46062
|
+
return null;
|
|
46063
|
+
}
|
|
46064
|
+
async function handleWorkspaceUpload(data, workspaceDir) {
|
|
46065
|
+
const filenameError = validateWorkspaceFilename(data.filename);
|
|
46066
|
+
if (filenameError) {
|
|
46067
|
+
return { status: "error", error: filenameError };
|
|
46068
|
+
}
|
|
46069
|
+
if (!data.content || typeof data.content !== "string") {
|
|
46070
|
+
return { status: "error", error: "File content is required" };
|
|
46071
|
+
}
|
|
46072
|
+
const contentBytes = Buffer.byteLength(data.content, "utf-8");
|
|
46073
|
+
if (contentBytes > MAX_FILE_SIZE) {
|
|
46074
|
+
return {
|
|
46075
|
+
status: "error",
|
|
46076
|
+
error: `File exceeds 100KB limit (${Math.round(contentBytes / 1024)}KB)`
|
|
46077
|
+
};
|
|
46078
|
+
}
|
|
46079
|
+
let verified = false;
|
|
46080
|
+
try {
|
|
46081
|
+
const signatureBytes = base64ToBytes(data.signature);
|
|
46082
|
+
const publicKeyBytes = base64ToBytes(data.signer_public_key);
|
|
46083
|
+
const signedPayload = {
|
|
46084
|
+
filename: data.filename,
|
|
46085
|
+
content: data.content,
|
|
46086
|
+
timestamp: data.timestamp
|
|
46087
|
+
};
|
|
46088
|
+
verified = await verifyDocumentSignature(signedPayload, signatureBytes, publicKeyBytes);
|
|
46089
|
+
} catch (err) {
|
|
46090
|
+
return { status: "error", error: "Signature verification failed: invalid format" };
|
|
46091
|
+
}
|
|
46092
|
+
if (!verified) {
|
|
46093
|
+
return { status: "error", error: "Invalid signature \u2014 file may have been tampered with" };
|
|
46094
|
+
}
|
|
46095
|
+
const targetPath = join2(workspaceDir, data.filename);
|
|
46096
|
+
const tempPath = join2(workspaceDir, `.tmp_${randomUUID()}_${data.filename}`);
|
|
46097
|
+
try {
|
|
46098
|
+
await writeFile2(tempPath, data.content, "utf-8");
|
|
46099
|
+
await rename2(tempPath, targetPath);
|
|
46100
|
+
} catch (err) {
|
|
46101
|
+
try {
|
|
46102
|
+
await unlink(tempPath);
|
|
46103
|
+
} catch {
|
|
46104
|
+
}
|
|
46105
|
+
return { status: "error", error: `Failed to write file: ${err.message}` };
|
|
46106
|
+
}
|
|
46107
|
+
console.log(`[Workspace] Wrote ${data.filename} (${contentBytes} bytes, sig verified) \u2192 ${targetPath}`);
|
|
46108
|
+
return {
|
|
46109
|
+
status: "success",
|
|
46110
|
+
written_path: targetPath,
|
|
46111
|
+
verified_signature: true
|
|
46112
|
+
};
|
|
46113
|
+
}
|
|
46114
|
+
async function handleWorkspaceList(workspaceDir) {
|
|
46115
|
+
try {
|
|
46116
|
+
const entries = await readdir(workspaceDir);
|
|
46117
|
+
const files = [];
|
|
46118
|
+
for (const entry of entries) {
|
|
46119
|
+
if (!entry.endsWith(".md")) continue;
|
|
46120
|
+
try {
|
|
46121
|
+
const filePath = join2(workspaceDir, entry);
|
|
46122
|
+
const fileStat = await stat(filePath);
|
|
46123
|
+
if (!fileStat.isFile()) continue;
|
|
46124
|
+
files.push({
|
|
46125
|
+
filename: entry,
|
|
46126
|
+
size: fileStat.size,
|
|
46127
|
+
modified: fileStat.mtime.toISOString()
|
|
46128
|
+
});
|
|
46129
|
+
} catch {
|
|
46130
|
+
}
|
|
46131
|
+
}
|
|
46132
|
+
files.sort((a2, b2) => a2.filename.localeCompare(b2.filename));
|
|
46133
|
+
return { files };
|
|
46134
|
+
} catch (err) {
|
|
46135
|
+
if (err.code === "ENOENT") {
|
|
46136
|
+
return { files: [] };
|
|
46137
|
+
}
|
|
46138
|
+
throw err;
|
|
46139
|
+
}
|
|
46140
|
+
}
|
|
46141
|
+
async function handleWorkspaceRead(data, workspaceDir) {
|
|
46142
|
+
const filenameError = validateWorkspaceFilename(data.filename);
|
|
46143
|
+
if (filenameError) {
|
|
46144
|
+
return { error: filenameError };
|
|
46145
|
+
}
|
|
46146
|
+
const filePath = join2(workspaceDir, data.filename);
|
|
46147
|
+
try {
|
|
46148
|
+
const content = await readFile2(filePath, "utf-8");
|
|
46149
|
+
const fileStat = await stat(filePath);
|
|
46150
|
+
return {
|
|
46151
|
+
filename: data.filename,
|
|
46152
|
+
content,
|
|
46153
|
+
size: fileStat.size
|
|
46154
|
+
};
|
|
46155
|
+
} catch (err) {
|
|
46156
|
+
if (err.code === "ENOENT") {
|
|
46157
|
+
return { error: `File not found: ${data.filename}` };
|
|
46158
|
+
}
|
|
46159
|
+
return { error: `Failed to read file: ${err.message}` };
|
|
46160
|
+
}
|
|
46161
|
+
}
|
|
46162
|
+
var MAX_FILE_SIZE;
|
|
46163
|
+
var init_workspace_handlers = __esm({
|
|
46164
|
+
async "src/workspace-handlers.ts"() {
|
|
46165
|
+
"use strict";
|
|
46166
|
+
await init_dist();
|
|
46167
|
+
MAX_FILE_SIZE = 100 * 1024;
|
|
46168
|
+
}
|
|
46169
|
+
});
|
|
46170
|
+
|
|
45982
46171
|
// src/channel.ts
|
|
45983
46172
|
import { EventEmitter } from "node:events";
|
|
45984
46173
|
import { createServer } from "node:http";
|
|
45985
|
-
import { randomUUID } from "node:crypto";
|
|
45986
|
-
import { writeFile as
|
|
45987
|
-
import { join as
|
|
45988
|
-
import { readFile as
|
|
46174
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
46175
|
+
import { writeFile as writeFile3, mkdir as mkdir2 } from "node:fs/promises";
|
|
46176
|
+
import { join as join3 } from "node:path";
|
|
46177
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
45989
46178
|
import WebSocket from "ws";
|
|
45990
46179
|
function migratePersistedState(raw) {
|
|
45991
46180
|
if (raw.sessions && raw.primaryConversationId) {
|
|
@@ -46038,6 +46227,8 @@ var init_channel = __esm({
|
|
|
46038
46227
|
_pollTimer = null;
|
|
46039
46228
|
_reconnectAttempt = 0;
|
|
46040
46229
|
_reconnectTimer = null;
|
|
46230
|
+
_rapidDisconnects = 0;
|
|
46231
|
+
_lastWsOpenTime = 0;
|
|
46041
46232
|
_pingTimer = null;
|
|
46042
46233
|
_lastServerMessage = 0;
|
|
46043
46234
|
_pendingAcks = [];
|
|
@@ -46227,7 +46418,7 @@ var init_channel = __esm({
|
|
|
46227
46418
|
}
|
|
46228
46419
|
}
|
|
46229
46420
|
}
|
|
46230
|
-
const messageGroupId =
|
|
46421
|
+
const messageGroupId = randomUUID2();
|
|
46231
46422
|
let sentCount = 0;
|
|
46232
46423
|
for (const [convId, session] of this._sessions) {
|
|
46233
46424
|
if (!session.activated) continue;
|
|
@@ -46322,7 +46513,7 @@ var init_channel = __esm({
|
|
|
46322
46513
|
* as a high-priority message. Returns the generated decision_id.
|
|
46323
46514
|
*/
|
|
46324
46515
|
async sendDecisionRequest(request) {
|
|
46325
|
-
const decision_id = `dec_${
|
|
46516
|
+
const decision_id = `dec_${randomUUID2().replace(/-/g, "").slice(0, 16)}`;
|
|
46326
46517
|
const payload = JSON.stringify({
|
|
46327
46518
|
type: "message",
|
|
46328
46519
|
text: `\u{1F4CB} ${request.title}`,
|
|
@@ -46627,7 +46818,7 @@ var init_channel = __esm({
|
|
|
46627
46818
|
description: artifact.description,
|
|
46628
46819
|
attachment: attachMeta
|
|
46629
46820
|
});
|
|
46630
|
-
const messageGroupId =
|
|
46821
|
+
const messageGroupId = randomUUID2();
|
|
46631
46822
|
for (const [convId, session] of this._sessions) {
|
|
46632
46823
|
if (!session.activated) continue;
|
|
46633
46824
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
@@ -47264,6 +47455,7 @@ var init_channel = __esm({
|
|
|
47264
47455
|
ws.on("open", async () => {
|
|
47265
47456
|
try {
|
|
47266
47457
|
this._reconnectAttempt = 0;
|
|
47458
|
+
this._lastWsOpenTime = Date.now();
|
|
47267
47459
|
this._startPing(ws);
|
|
47268
47460
|
this._startWakeDetector();
|
|
47269
47461
|
this._startPendingPoll();
|
|
@@ -47789,6 +47981,41 @@ var init_channel = __esm({
|
|
|
47789
47981
|
await this._persistState();
|
|
47790
47982
|
return;
|
|
47791
47983
|
}
|
|
47984
|
+
if (messageType === "workspace_file_upload") {
|
|
47985
|
+
try {
|
|
47986
|
+
const parsed = JSON.parse(plaintext);
|
|
47987
|
+
const workspaceDir = this._resolveWorkspaceDir();
|
|
47988
|
+
const { handleWorkspaceUpload: handleWorkspaceUpload2 } = await init_workspace_handlers().then(() => workspace_handlers_exports);
|
|
47989
|
+
const result = await handleWorkspaceUpload2(parsed, workspaceDir);
|
|
47990
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_ack", filename: parsed.filename, ...result });
|
|
47991
|
+
} catch (err) {
|
|
47992
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_ack", status: "error", error: err.message });
|
|
47993
|
+
}
|
|
47994
|
+
return;
|
|
47995
|
+
}
|
|
47996
|
+
if (messageType === "workspace_file_list") {
|
|
47997
|
+
try {
|
|
47998
|
+
const workspaceDir = this._resolveWorkspaceDir();
|
|
47999
|
+
const { handleWorkspaceList: handleWorkspaceList2 } = await init_workspace_handlers().then(() => workspace_handlers_exports);
|
|
48000
|
+
const result = await handleWorkspaceList2(workspaceDir);
|
|
48001
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_list_response", ...result });
|
|
48002
|
+
} catch (err) {
|
|
48003
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_list_response", files: [], error: err.message });
|
|
48004
|
+
}
|
|
48005
|
+
return;
|
|
48006
|
+
}
|
|
48007
|
+
if (messageType === "workspace_file_read") {
|
|
48008
|
+
try {
|
|
48009
|
+
const parsed = JSON.parse(plaintext);
|
|
48010
|
+
const workspaceDir = this._resolveWorkspaceDir();
|
|
48011
|
+
const { handleWorkspaceRead: handleWorkspaceRead2 } = await init_workspace_handlers().then(() => workspace_handlers_exports);
|
|
48012
|
+
const result = await handleWorkspaceRead2(parsed, workspaceDir);
|
|
48013
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_read_response", ...result });
|
|
48014
|
+
} catch (err) {
|
|
48015
|
+
await this._sendStructuredReply(convId, { type: "workspace_file_read_response", error: err.message });
|
|
48016
|
+
}
|
|
48017
|
+
return;
|
|
48018
|
+
}
|
|
47792
48019
|
if (this._scanEngine) {
|
|
47793
48020
|
const scanResult = this._scanEngine.scanInbound(messageText);
|
|
47794
48021
|
if (scanResult.status === "blocked") {
|
|
@@ -47869,7 +48096,7 @@ ${messageText}`;
|
|
|
47869
48096
|
* and save the plaintext file to disk.
|
|
47870
48097
|
*/
|
|
47871
48098
|
async _downloadAndDecryptAttachment(info) {
|
|
47872
|
-
const attachDir =
|
|
48099
|
+
const attachDir = join3(this.config.dataDir, "attachments");
|
|
47873
48100
|
await mkdir2(attachDir, { recursive: true });
|
|
47874
48101
|
const url = `${this.config.apiUrl}${info.blobUrl}`;
|
|
47875
48102
|
const res = await fetch(url, {
|
|
@@ -47887,8 +48114,8 @@ ${messageText}`;
|
|
|
47887
48114
|
const fileKey = base64ToBytes(info.fileKey);
|
|
47888
48115
|
const fileNonce = base64ToBytes(info.fileNonce);
|
|
47889
48116
|
const decrypted = decryptFile(encryptedData, fileKey, fileNonce);
|
|
47890
|
-
const filePath =
|
|
47891
|
-
await
|
|
48117
|
+
const filePath = join3(attachDir, info.filename);
|
|
48118
|
+
await writeFile3(filePath, decrypted);
|
|
47892
48119
|
console.log(`[SecureChannel] Attachment saved: ${filePath} (${decrypted.length} bytes)`);
|
|
47893
48120
|
return { filePath, decrypted };
|
|
47894
48121
|
}
|
|
@@ -47897,7 +48124,7 @@ ${messageText}`;
|
|
|
47897
48124
|
* for inclusion in the message envelope.
|
|
47898
48125
|
*/
|
|
47899
48126
|
async _uploadAttachment(filePath, conversationId) {
|
|
47900
|
-
const data = await
|
|
48127
|
+
const data = await readFile3(filePath);
|
|
47901
48128
|
const plainData = new Uint8Array(data);
|
|
47902
48129
|
const result = encryptFile(plainData);
|
|
47903
48130
|
const { Blob: NodeBlob, FormData: NodeFormData } = await import("node:buffer").then(
|
|
@@ -47949,7 +48176,7 @@ ${messageText}`;
|
|
|
47949
48176
|
attachment: attachMeta
|
|
47950
48177
|
});
|
|
47951
48178
|
this._appendHistory("agent", plaintext, topicId);
|
|
47952
|
-
const messageGroupId =
|
|
48179
|
+
const messageGroupId = randomUUID2();
|
|
47953
48180
|
for (const [convId, session] of this._sessions) {
|
|
47954
48181
|
if (!session.activated) continue;
|
|
47955
48182
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
@@ -47999,6 +48226,56 @@ ${messageText}`;
|
|
|
47999
48226
|
);
|
|
48000
48227
|
}
|
|
48001
48228
|
}
|
|
48229
|
+
/**
|
|
48230
|
+
* Resolve the agent's workspace directory.
|
|
48231
|
+
* Looks for OpenClaw workspace config, falls back to default path.
|
|
48232
|
+
*/
|
|
48233
|
+
_resolveWorkspaceDir() {
|
|
48234
|
+
const homedir = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
48235
|
+
try {
|
|
48236
|
+
const configPath = join3(homedir, ".openclaw", "openclaw.json");
|
|
48237
|
+
const raw = __require("node:fs").readFileSync(configPath, "utf-8");
|
|
48238
|
+
const config = JSON.parse(raw);
|
|
48239
|
+
const agents = config?.agents?.list;
|
|
48240
|
+
if (Array.isArray(agents)) {
|
|
48241
|
+
for (const agent of agents) {
|
|
48242
|
+
if (agent.workspace && typeof agent.workspace === "string") {
|
|
48243
|
+
return agent.workspace;
|
|
48244
|
+
}
|
|
48245
|
+
}
|
|
48246
|
+
}
|
|
48247
|
+
} catch {
|
|
48248
|
+
}
|
|
48249
|
+
if (this.config.dataDir) {
|
|
48250
|
+
return join3(this.config.dataDir, "..", "workspace");
|
|
48251
|
+
}
|
|
48252
|
+
return join3(homedir, ".openclaw", "workspace");
|
|
48253
|
+
}
|
|
48254
|
+
/**
|
|
48255
|
+
* Send a structured JSON reply to a specific conversation.
|
|
48256
|
+
* Encrypts the payload via the conversation's ratchet and sends via WebSocket.
|
|
48257
|
+
*/
|
|
48258
|
+
async _sendStructuredReply(convId, payload) {
|
|
48259
|
+
const session = this._sessions.get(convId);
|
|
48260
|
+
if (!session || !this._ws) {
|
|
48261
|
+
console.warn(`[SecureChannel] Cannot send structured reply \u2014 no session for ${convId.slice(0, 8)}...`);
|
|
48262
|
+
return;
|
|
48263
|
+
}
|
|
48264
|
+
const plaintext = JSON.stringify(payload);
|
|
48265
|
+
const encrypted = session.ratchet.encrypt(plaintext);
|
|
48266
|
+
const transport = encryptedMessageToTransport(encrypted);
|
|
48267
|
+
this._ws.send(
|
|
48268
|
+
JSON.stringify({
|
|
48269
|
+
event: "message",
|
|
48270
|
+
data: {
|
|
48271
|
+
conversation_id: convId,
|
|
48272
|
+
header_blob: transport.header_blob,
|
|
48273
|
+
ciphertext: transport.ciphertext
|
|
48274
|
+
}
|
|
48275
|
+
})
|
|
48276
|
+
);
|
|
48277
|
+
await this._persistState();
|
|
48278
|
+
}
|
|
48002
48279
|
/**
|
|
48003
48280
|
* Send stored message history to a newly-activated session.
|
|
48004
48281
|
* Batches all history into a single encrypted message.
|
|
@@ -48559,6 +48836,26 @@ ${messageText}`;
|
|
|
48559
48836
|
_scheduleReconnect() {
|
|
48560
48837
|
if (this._stopped) return;
|
|
48561
48838
|
if (this._reconnectTimer) return;
|
|
48839
|
+
const sinceOpen = Date.now() - this._lastWsOpenTime;
|
|
48840
|
+
if (this._lastWsOpenTime > 0 && sinceOpen < 1e4) {
|
|
48841
|
+
this._rapidDisconnects++;
|
|
48842
|
+
} else {
|
|
48843
|
+
this._rapidDisconnects = 0;
|
|
48844
|
+
}
|
|
48845
|
+
if (this._rapidDisconnects >= 5) {
|
|
48846
|
+
console.error(
|
|
48847
|
+
`[SecureChannel] Detected rapid connect/disconnect loop (${this._rapidDisconnects} times in <10s each).`
|
|
48848
|
+
);
|
|
48849
|
+
console.error(
|
|
48850
|
+
`[SecureChannel] This usually means another process is already connected as this device.`
|
|
48851
|
+
);
|
|
48852
|
+
console.error(
|
|
48853
|
+
`[SecureChannel] Stopping reconnection. Check for duplicate gateway processes or stale CLI sessions.`
|
|
48854
|
+
);
|
|
48855
|
+
this._setState("error");
|
|
48856
|
+
this.emit("error", new Error("Reconnect loop detected \u2014 another process may be connected as this device"));
|
|
48857
|
+
return;
|
|
48858
|
+
}
|
|
48562
48859
|
const delay = Math.min(
|
|
48563
48860
|
RECONNECT_BASE_MS * Math.pow(2, this._reconnectAttempt),
|
|
48564
48861
|
RECONNECT_MAX_MS
|