@clawchatsai/connector 0.0.86 → 0.0.87
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/LICENSE +661 -0
- package/README.md +67 -13
- package/dist/index.js +0 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +9 -5
- package/server/bootstrap/identity.js +47 -0
- package/server/bootstrap/native.js +9 -0
- package/server/config.js +62 -0
- package/server/controllers/files.js +64 -0
- package/server/controllers/filesystem.js +139 -0
- package/server/controllers/memory.js +86 -0
- package/server/controllers/messages.js +128 -0
- package/server/controllers/threads.js +113 -0
- package/server/controllers/transcribe.js +51 -0
- package/server/controllers/workspaces.js +102 -0
- package/server/debug.js +56 -0
- package/server/gateway-cleanup.js +47 -0
- package/server/gateway.js +331 -0
- package/server/index.js +422 -0
- package/server/providers/memory.js +144 -0
- package/server/util/context.js +49 -0
- package/server/util/helpers.js +111 -0
- package/server/util/http.js +57 -0
- package/server/util/multipart.js +46 -0
- package/server.js +155 -222
- package/dist/migrate.d.ts +0 -16
- package/dist/migrate.js +0 -114
package/server.js
CHANGED
|
@@ -3,73 +3,45 @@
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// server/index.js
|
|
7
7
|
import http from "node:http";
|
|
8
8
|
import fs13 from "node:fs";
|
|
9
|
-
import
|
|
9
|
+
import path14 from "node:path";
|
|
10
10
|
import os5 from "node:os";
|
|
11
|
-
import { fileURLToPath as
|
|
11
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12
12
|
import { WebSocket as WS2, WebSocketServer } from "ws";
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
import {
|
|
16
|
-
import { createRequire } from "node:module";
|
|
14
|
+
// server/bootstrap/native.js
|
|
15
|
+
import { DatabaseSync as Database } from "node:sqlite";
|
|
17
16
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
|
-
import { fileURLToPath } from "node:url";
|
|
19
|
-
import path from "node:path";
|
|
20
|
-
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
var _require = createRequire(import.meta.url);
|
|
22
17
|
var requestDbStore = new AsyncLocalStorage();
|
|
23
|
-
function loadDatabase() {
|
|
24
|
-
const isAbiMismatch = /* @__PURE__ */ __name((e) => e.message && (e.message.includes("did not self-register") || e.message.includes("NODE_MODULE_VERSION") || e.message.includes("was compiled against a different Node.js version")), "isAbiMismatch");
|
|
25
|
-
try {
|
|
26
|
-
return _require("better-sqlite3");
|
|
27
|
-
} catch (e) {
|
|
28
|
-
if (!isAbiMismatch(e)) throw e;
|
|
29
|
-
console.error("[ClawChats] better-sqlite3 binary is incompatible with your Node.js version. Attempting auto-rebuild...");
|
|
30
|
-
try {
|
|
31
|
-
execSync("npm rebuild better-sqlite3", { cwd: __dirname, stdio: "inherit" });
|
|
32
|
-
const db = _require("better-sqlite3");
|
|
33
|
-
console.log("[ClawChats] Auto-rebuild succeeded \u2014 continuing startup.");
|
|
34
|
-
return db;
|
|
35
|
-
} catch (rebuildErr) {
|
|
36
|
-
console.error("[ClawChats] Auto-rebuild failed. Build tools may be missing.");
|
|
37
|
-
console.error(`[ClawChats] cd ${__dirname} && npm rebuild better-sqlite3`);
|
|
38
|
-
console.error("[ClawChats] Linux: sudo apt install build-essential python3");
|
|
39
|
-
console.error("[ClawChats] macOS: xcode-select --install");
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
__name(loadDatabase, "loadDatabase");
|
|
45
|
-
var Database = loadDatabase();
|
|
46
18
|
|
|
47
|
-
//
|
|
19
|
+
// server/config.js
|
|
48
20
|
import fs from "node:fs";
|
|
49
|
-
import
|
|
21
|
+
import path from "node:path";
|
|
50
22
|
import os from "node:os";
|
|
51
|
-
import {
|
|
52
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
53
24
|
var HOME = os.homedir();
|
|
54
25
|
var MAX_PREAMBLE_CHARS = 5e4;
|
|
55
|
-
var __filename =
|
|
56
|
-
var
|
|
26
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
27
|
+
var __dirname = path.dirname(__filename);
|
|
57
28
|
function parseConfigField(field) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
29
|
+
const candidates = [path.join(__dirname, "config.js"), path.join(__dirname, "..", "config.js")];
|
|
30
|
+
for (const configPath of candidates) {
|
|
31
|
+
try {
|
|
32
|
+
const configText = fs.readFileSync(configPath, "utf8");
|
|
33
|
+
const match = configText.match(new RegExp(`${field}:\\s*['"]([^'"]+)['"]`));
|
|
34
|
+
if (match) return match[1];
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
64
37
|
}
|
|
38
|
+
return null;
|
|
65
39
|
}
|
|
66
40
|
__name(parseConfigField, "parseConfigField");
|
|
67
|
-
var
|
|
68
|
-
if (!_authToken) console.error("WARNING: No auth token configured. Set CLAWCHATS_AUTH_TOKEN or create config.js");
|
|
69
|
-
var AUTH_TOKEN = _authToken;
|
|
41
|
+
var AUTH_TOKEN = process.env.CLAWCHATS_AUTH_TOKEN || parseConfigField("authToken") || "";
|
|
70
42
|
function discoverGatewayWsUrl() {
|
|
71
43
|
if (process.env.GATEWAY_WS_URL) return process.env.GATEWAY_WS_URL;
|
|
72
|
-
for (const cfgPath of [
|
|
44
|
+
for (const cfgPath of [path.join(HOME, ".openclaw", "openclaw.json"), "/etc/openclaw/openclaw.json"]) {
|
|
73
45
|
try {
|
|
74
46
|
const raw = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
|
|
75
47
|
const port = raw.gateway?.port || raw.port;
|
|
@@ -82,58 +54,30 @@ function discoverGatewayWsUrl() {
|
|
|
82
54
|
}
|
|
83
55
|
__name(discoverGatewayWsUrl, "discoverGatewayWsUrl");
|
|
84
56
|
var GATEWAY_WS_URL = discoverGatewayWsUrl();
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const out = execSync2("openclaw status --json", { encoding: "utf8", timeout: 5e3 });
|
|
88
|
-
const status = JSON.parse(out);
|
|
89
|
-
if (status.sessions?.paths?.[0]) return path2.dirname(status.sessions.paths[0]);
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
__name(discoverViaCliSync, "discoverViaCliSync");
|
|
95
|
-
var OPENCLAW_SESSIONS_DIR = (() => {
|
|
96
|
-
if (process.env.OPENCLAW_SESSIONS_DIR) {
|
|
97
|
-
console.log(`Sessions dir: ${process.env.OPENCLAW_SESSIONS_DIR} (env)`);
|
|
98
|
-
return process.env.OPENCLAW_SESSIONS_DIR;
|
|
99
|
-
}
|
|
100
|
-
const cfgDir = parseConfigField("sessionsDir");
|
|
101
|
-
if (cfgDir) {
|
|
102
|
-
console.log(`Sessions dir: ${cfgDir} (config)`);
|
|
103
|
-
return cfgDir;
|
|
104
|
-
}
|
|
105
|
-
const cliDir = discoverViaCliSync();
|
|
106
|
-
if (cliDir) {
|
|
107
|
-
console.log(`Sessions dir: ${cliDir} (cli)`);
|
|
108
|
-
return cliDir;
|
|
109
|
-
}
|
|
110
|
-
const fallback = path2.join(HOME, ".openclaw", "agents", "main", "sessions");
|
|
111
|
-
console.log(`Sessions dir: ${fallback} (fallback)`);
|
|
112
|
-
return fallback;
|
|
113
|
-
})();
|
|
57
|
+
var OPENCLAW_SESSIONS_DIR = process.env.OPENCLAW_SESSIONS_DIR || parseConfigField("sessionsDir") || path.join(HOME, ".openclaw", "agents", "main", "sessions");
|
|
114
58
|
function getSessionsDirForAgent(agentId) {
|
|
115
59
|
if (!agentId || agentId === "main") return OPENCLAW_SESSIONS_DIR;
|
|
116
|
-
return
|
|
60
|
+
return path.join(HOME, ".openclaw", "agents", agentId, "sessions");
|
|
117
61
|
}
|
|
118
62
|
__name(getSessionsDirForAgent, "getSessionsDirForAgent");
|
|
119
63
|
function validateAgent(agentId) {
|
|
120
64
|
if (!agentId) return "main";
|
|
121
65
|
if (!/^[a-zA-Z0-9_-]+$/.test(agentId)) throw new Error("Invalid agent ID");
|
|
122
|
-
const agentDir =
|
|
66
|
+
const agentDir = path.join(HOME, ".openclaw", "agents", agentId);
|
|
123
67
|
if (!fs.existsSync(agentDir)) throw new Error(`Agent not found: ${agentId}`);
|
|
124
68
|
return agentId;
|
|
125
69
|
}
|
|
126
70
|
__name(validateAgent, "validateAgent");
|
|
127
71
|
|
|
128
|
-
//
|
|
72
|
+
// server/debug.js
|
|
129
73
|
import fs2 from "node:fs";
|
|
130
|
-
import
|
|
74
|
+
import path2 from "node:path";
|
|
131
75
|
var DebugLogger = class {
|
|
132
76
|
static {
|
|
133
77
|
__name(this, "DebugLogger");
|
|
134
78
|
}
|
|
135
79
|
constructor(baseDir) {
|
|
136
|
-
this.baseDir =
|
|
80
|
+
this.baseDir = path2.join(baseDir, "..", "debug");
|
|
137
81
|
this.active = false;
|
|
138
82
|
this.sessionId = null;
|
|
139
83
|
this.wsStream = null;
|
|
@@ -144,7 +88,7 @@ var DebugLogger = class {
|
|
|
144
88
|
this.sessionId = ts.replace(/[:.]/g, "-");
|
|
145
89
|
this.originatingClient = originatingClient;
|
|
146
90
|
fs2.mkdirSync(this.baseDir, { recursive: true });
|
|
147
|
-
this.wsStream = fs2.createWriteStream(
|
|
91
|
+
this.wsStream = fs2.createWriteStream(path2.join(this.baseDir, `session-${this.sessionId}-ws.log`), { flags: "a" });
|
|
148
92
|
this.active = true;
|
|
149
93
|
console.log(`Debug recording started: ${this.sessionId}`);
|
|
150
94
|
return { sessionId: this.sessionId };
|
|
@@ -171,15 +115,15 @@ var DebugLogger = class {
|
|
|
171
115
|
${err.stack || ""}
|
|
172
116
|
`;
|
|
173
117
|
if (logContent) {
|
|
174
|
-
fs2.writeFileSync(
|
|
118
|
+
fs2.writeFileSync(path2.join(this.baseDir, `session-${id}-client.log`), logContent);
|
|
175
119
|
files.push(`session-${id}-client.log`);
|
|
176
120
|
}
|
|
177
121
|
if (payload.state) {
|
|
178
|
-
fs2.writeFileSync(
|
|
122
|
+
fs2.writeFileSync(path2.join(this.baseDir, `session-${id}-state.json`), JSON.stringify(payload.state, null, 2));
|
|
179
123
|
files.push(`session-${id}-state.json`);
|
|
180
124
|
}
|
|
181
125
|
if (payload.screenshot) {
|
|
182
|
-
fs2.writeFileSync(
|
|
126
|
+
fs2.writeFileSync(path2.join(this.baseDir, `session-${id}-screenshot.jpg`), Buffer.from(payload.screenshot, "base64"));
|
|
183
127
|
files.push(`session-${id}-screenshot.jpg`);
|
|
184
128
|
}
|
|
185
129
|
const savedId = id;
|
|
@@ -205,14 +149,14 @@ ${err.stack || ""}
|
|
|
205
149
|
}
|
|
206
150
|
};
|
|
207
151
|
|
|
208
|
-
//
|
|
209
|
-
import
|
|
152
|
+
// server/gateway.js
|
|
153
|
+
import path4 from "node:path";
|
|
210
154
|
import { WebSocket as WS } from "ws";
|
|
211
155
|
|
|
212
|
-
//
|
|
156
|
+
// server/bootstrap/identity.js
|
|
213
157
|
import crypto from "node:crypto";
|
|
214
158
|
import fs3 from "node:fs";
|
|
215
|
-
import
|
|
159
|
+
import path3 from "node:path";
|
|
216
160
|
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
217
161
|
function derivePublicKeyRaw(publicKeyPem) {
|
|
218
162
|
const spki = crypto.createPublicKey(publicKeyPem).export({ type: "spki", format: "der" });
|
|
@@ -242,7 +186,7 @@ function loadOrCreateDeviceIdentity(identityPath) {
|
|
|
242
186
|
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
|
|
243
187
|
const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
244
188
|
const identity = { version: 1, deviceId: fingerprintPublicKey(publicKeyPem), publicKeyPem, privateKeyPem, createdAtMs: Date.now() };
|
|
245
|
-
fs3.mkdirSync(
|
|
189
|
+
fs3.mkdirSync(path3.dirname(identityPath), { recursive: true });
|
|
246
190
|
fs3.writeFileSync(identityPath, JSON.stringify(identity, null, 2) + "\n", { mode: 384 });
|
|
247
191
|
return identity;
|
|
248
192
|
}
|
|
@@ -257,7 +201,7 @@ function buildDeviceAuth(identity, { clientId, clientMode, role, scopes, token,
|
|
|
257
201
|
}
|
|
258
202
|
__name(buildDeviceAuth, "buildDeviceAuth");
|
|
259
203
|
|
|
260
|
-
//
|
|
204
|
+
// server/util/helpers.js
|
|
261
205
|
function syncThreadUnreadCount(db, threadId) {
|
|
262
206
|
const count = db.prepare("SELECT COUNT(*) as c FROM unread_messages WHERE thread_id = ?").get(threadId).c;
|
|
263
207
|
db.prepare("UPDATE threads SET unread_count = ? WHERE id = ?").run(count, threadId);
|
|
@@ -378,7 +322,7 @@ function writeActivityToDb(getDbFn, broadcastFn, runId, log) {
|
|
|
378
322
|
}
|
|
379
323
|
__name(writeActivityToDb, "writeActivityToDb");
|
|
380
324
|
|
|
381
|
-
//
|
|
325
|
+
// server/gateway.js
|
|
382
326
|
var GatewayClient = class {
|
|
383
327
|
static {
|
|
384
328
|
__name(this, "GatewayClient");
|
|
@@ -440,7 +384,7 @@ var GatewayClient = class {
|
|
|
440
384
|
return;
|
|
441
385
|
}
|
|
442
386
|
if (msg.type === "event" && msg.event === "connect.challenge") {
|
|
443
|
-
const identity = loadOrCreateDeviceIdentity(
|
|
387
|
+
const identity = loadOrCreateDeviceIdentity(path4.join(this.dataDir, "device-identity.json"));
|
|
444
388
|
const device = buildDeviceAuth(identity, { clientId: "gateway-client", clientMode: "backend", role: "operator", scopes: ["operator.read", "operator.write", "operator.admin"], token: this.authToken, nonce: msg.payload?.nonce || "" });
|
|
445
389
|
this.ws.send(JSON.stringify({ type: "req", id: "gw-connect-1", method: "connect", params: { minProtocol: 3, maxProtocol: 3, client: { id: "gateway-client", version: "0.1.0", platform: "node", mode: "backend" }, role: "operator", scopes: ["operator.read", "operator.write", "operator.admin"], device, auth: { token: this.authToken }, caps: ["tool-events"] } }));
|
|
446
390
|
return;
|
|
@@ -773,14 +717,14 @@ Title:`, deliver: false, idempotencyKey: reqId } }));
|
|
|
773
717
|
}
|
|
774
718
|
};
|
|
775
719
|
|
|
776
|
-
//
|
|
720
|
+
// server/providers/memory.js
|
|
777
721
|
import fs4 from "node:fs";
|
|
778
|
-
import
|
|
722
|
+
import path5 from "node:path";
|
|
779
723
|
import os2 from "node:os";
|
|
780
724
|
function discoverMemoryConfig() {
|
|
781
725
|
const defaults = { provider: "qdrant", host: "localhost", port: 6333, collection: null };
|
|
782
726
|
let oc = null;
|
|
783
|
-
for (const cfgPath of [
|
|
727
|
+
for (const cfgPath of [path5.join(os2.homedir(), ".openclaw", "openclaw.json"), "/etc/openclaw/openclaw.json"]) {
|
|
784
728
|
try {
|
|
785
729
|
oc = JSON.parse(fs4.readFileSync(cfgPath, "utf8"));
|
|
786
730
|
break;
|
|
@@ -815,7 +759,7 @@ function discoverMemoryConfig() {
|
|
|
815
759
|
} catch {
|
|
816
760
|
}
|
|
817
761
|
}
|
|
818
|
-
if (!cfg.workspaceDir) cfg.workspaceDir =
|
|
762
|
+
if (!cfg.workspaceDir) cfg.workspaceDir = path5.join(os2.homedir(), ".openclaw", "workspace");
|
|
819
763
|
return cfg;
|
|
820
764
|
}
|
|
821
765
|
__name(discoverMemoryConfig, "discoverMemoryConfig");
|
|
@@ -951,11 +895,11 @@ function createMemoryProvider(config) {
|
|
|
951
895
|
}
|
|
952
896
|
__name(createMemoryProvider, "createMemoryProvider");
|
|
953
897
|
|
|
954
|
-
//
|
|
898
|
+
// server/controllers/workspaces.js
|
|
955
899
|
import fs6 from "node:fs";
|
|
956
|
-
import
|
|
900
|
+
import path7 from "node:path";
|
|
957
901
|
|
|
958
|
-
//
|
|
902
|
+
// server/util/http.js
|
|
959
903
|
import crypto2 from "node:crypto";
|
|
960
904
|
function parseBody(req) {
|
|
961
905
|
return new Promise((resolve, reject) => {
|
|
@@ -1017,20 +961,20 @@ function matchRoute(method, url, pattern) {
|
|
|
1017
961
|
}
|
|
1018
962
|
__name(matchRoute, "matchRoute");
|
|
1019
963
|
|
|
1020
|
-
//
|
|
964
|
+
// server/gateway-cleanup.js
|
|
1021
965
|
import fs5 from "node:fs";
|
|
1022
|
-
import
|
|
966
|
+
import path6 from "node:path";
|
|
1023
967
|
function cleanGatewaySession(sessionKey) {
|
|
1024
968
|
try {
|
|
1025
969
|
const agentMatch = (sessionKey || "").match(/^agent:([^:]+):/);
|
|
1026
970
|
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
1027
|
-
const sessionsPath =
|
|
971
|
+
const sessionsPath = path6.join(sessionsDir, "sessions.json");
|
|
1028
972
|
const store = JSON.parse(fs5.readFileSync(sessionsPath, "utf8"));
|
|
1029
973
|
const entry = store[sessionKey];
|
|
1030
974
|
if (!entry) return null;
|
|
1031
975
|
if (entry.sessionId) {
|
|
1032
976
|
try {
|
|
1033
|
-
fs5.unlinkSync(
|
|
977
|
+
fs5.unlinkSync(path6.join(sessionsDir, `${entry.sessionId}.jsonl`));
|
|
1034
978
|
} catch {
|
|
1035
979
|
}
|
|
1036
980
|
}
|
|
@@ -1048,14 +992,14 @@ function cleanGatewaySessionsByPrefix(prefix) {
|
|
|
1048
992
|
try {
|
|
1049
993
|
const agentMatch = (prefix || "").match(/^agent:([^:]+):/);
|
|
1050
994
|
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
1051
|
-
const sessionsPath =
|
|
995
|
+
const sessionsPath = path6.join(sessionsDir, "sessions.json");
|
|
1052
996
|
const store = JSON.parse(fs5.readFileSync(sessionsPath, "utf8"));
|
|
1053
997
|
let cleaned = 0;
|
|
1054
998
|
for (const key of Object.keys(store)) {
|
|
1055
999
|
if (!key.startsWith(prefix)) continue;
|
|
1056
1000
|
if (store[key]?.sessionId) {
|
|
1057
1001
|
try {
|
|
1058
|
-
fs5.unlinkSync(
|
|
1002
|
+
fs5.unlinkSync(path6.join(sessionsDir, `${store[key].sessionId}.jsonl`));
|
|
1059
1003
|
} catch {
|
|
1060
1004
|
}
|
|
1061
1005
|
}
|
|
@@ -1071,7 +1015,7 @@ function cleanGatewaySessionsByPrefix(prefix) {
|
|
|
1071
1015
|
}
|
|
1072
1016
|
__name(cleanGatewaySessionsByPrefix, "cleanGatewaySessionsByPrefix");
|
|
1073
1017
|
|
|
1074
|
-
//
|
|
1018
|
+
// server/controllers/workspaces.js
|
|
1075
1019
|
var WorkspaceController = class {
|
|
1076
1020
|
static {
|
|
1077
1021
|
__name(this, "WorkspaceController");
|
|
@@ -1148,7 +1092,7 @@ var WorkspaceController = class {
|
|
|
1148
1092
|
if (!ws.workspaces[params.name]) return sendError(res, 404, "Workspace not found");
|
|
1149
1093
|
if (Object.keys(ws.workspaces).length <= 1) return sendError(res, 400, "Cannot delete the only workspace");
|
|
1150
1094
|
this.closeDb(params.name);
|
|
1151
|
-
const dbPath =
|
|
1095
|
+
const dbPath = path7.join(this.dataDir, `${params.name}.db`);
|
|
1152
1096
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
1153
1097
|
try {
|
|
1154
1098
|
fs6.unlinkSync(dbPath + suffix);
|
|
@@ -1183,9 +1127,9 @@ var WorkspaceController = class {
|
|
|
1183
1127
|
}
|
|
1184
1128
|
};
|
|
1185
1129
|
|
|
1186
|
-
//
|
|
1130
|
+
// server/controllers/threads.js
|
|
1187
1131
|
import fs7 from "node:fs";
|
|
1188
|
-
import
|
|
1132
|
+
import path8 from "node:path";
|
|
1189
1133
|
var ThreadController = class {
|
|
1190
1134
|
static {
|
|
1191
1135
|
__name(this, "ThreadController");
|
|
@@ -1295,28 +1239,28 @@ var ThreadController = class {
|
|
|
1295
1239
|
let sessionIdToDelete = thread.last_session_id;
|
|
1296
1240
|
if (!sessionIdToDelete) {
|
|
1297
1241
|
try {
|
|
1298
|
-
sessionIdToDelete = JSON.parse(fs7.readFileSync(
|
|
1242
|
+
sessionIdToDelete = JSON.parse(fs7.readFileSync(path8.join(sessionsDir, "sessions.json"), "utf8"))[thread.session_key]?.sessionId;
|
|
1299
1243
|
} catch {
|
|
1300
1244
|
}
|
|
1301
1245
|
}
|
|
1302
1246
|
cleanGatewaySession(thread.session_key);
|
|
1303
1247
|
if (sessionIdToDelete) {
|
|
1304
1248
|
try {
|
|
1305
|
-
fs7.unlinkSync(
|
|
1249
|
+
fs7.unlinkSync(path8.join(sessionsDir, `${sessionIdToDelete}.jsonl`));
|
|
1306
1250
|
} catch {
|
|
1307
1251
|
}
|
|
1308
1252
|
}
|
|
1309
1253
|
try {
|
|
1310
|
-
fs7.rmSync(
|
|
1254
|
+
fs7.rmSync(path8.join(this.uploadsDir, params.id), { recursive: true });
|
|
1311
1255
|
} catch {
|
|
1312
1256
|
}
|
|
1313
1257
|
send(res, 200, { ok: true });
|
|
1314
1258
|
}
|
|
1315
1259
|
};
|
|
1316
1260
|
|
|
1317
|
-
//
|
|
1261
|
+
// server/util/context.js
|
|
1318
1262
|
import fs8 from "node:fs";
|
|
1319
|
-
import
|
|
1263
|
+
import path9 from "node:path";
|
|
1320
1264
|
function buildContextPreamble(db, threadId, lastSessionId, sessionKey) {
|
|
1321
1265
|
let summary = null;
|
|
1322
1266
|
let method = "raw";
|
|
@@ -1324,7 +1268,7 @@ function buildContextPreamble(db, threadId, lastSessionId, sessionKey) {
|
|
|
1324
1268
|
const agentMatch = (sessionKey || "").match(/^agent:([^:]+):/);
|
|
1325
1269
|
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
1326
1270
|
try {
|
|
1327
|
-
const lines = fs8.readFileSync(
|
|
1271
|
+
const lines = fs8.readFileSync(path9.join(sessionsDir, `${lastSessionId}.jsonl`), "utf8").split("\n").filter(Boolean);
|
|
1328
1272
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1329
1273
|
try {
|
|
1330
1274
|
const entry = JSON.parse(lines[i]);
|
|
@@ -1369,7 +1313,7 @@ function buildContextPreamble(db, threadId, lastSessionId, sessionKey) {
|
|
|
1369
1313
|
}
|
|
1370
1314
|
__name(buildContextPreamble, "buildContextPreamble");
|
|
1371
1315
|
|
|
1372
|
-
//
|
|
1316
|
+
// server/controllers/messages.js
|
|
1373
1317
|
var MessageController = class {
|
|
1374
1318
|
static {
|
|
1375
1319
|
__name(this, "MessageController");
|
|
@@ -1498,7 +1442,8 @@ var MessageController = class {
|
|
|
1498
1442
|
let threadsImported = 0, messagesImported = 0;
|
|
1499
1443
|
const insertThread = db.prepare("INSERT OR IGNORE INTO threads (id, session_key, title, pinned, pin_order, model, last_session_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1500
1444
|
const insertMsg = db.prepare("INSERT OR IGNORE INTO messages (id, thread_id, role, content, status, metadata, seq, timestamp, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1501
|
-
db.
|
|
1445
|
+
db.exec("BEGIN");
|
|
1446
|
+
try {
|
|
1502
1447
|
for (const t of body.threads) {
|
|
1503
1448
|
if (!t.id) continue;
|
|
1504
1449
|
const sessionKey = t.session_key || `agent:main:${ws.active}:chat:${t.id}`;
|
|
@@ -1509,16 +1454,20 @@ var MessageController = class {
|
|
|
1509
1454
|
if (insertMsg.run(m.id, t.id, m.role, m.content || "", m.status || "sent", meta, m.seq || null, m.timestamp || Date.now(), m.created_at || Date.now()).changes > 0) messagesImported++;
|
|
1510
1455
|
}
|
|
1511
1456
|
}
|
|
1512
|
-
|
|
1457
|
+
db.exec("COMMIT");
|
|
1458
|
+
} catch (e) {
|
|
1459
|
+
db.exec("ROLLBACK");
|
|
1460
|
+
throw e;
|
|
1461
|
+
}
|
|
1513
1462
|
send(res, 200, { ok: true, threadsImported, messagesImported });
|
|
1514
1463
|
}
|
|
1515
1464
|
};
|
|
1516
1465
|
|
|
1517
|
-
//
|
|
1466
|
+
// server/controllers/files.js
|
|
1518
1467
|
import fs9 from "node:fs";
|
|
1519
|
-
import
|
|
1468
|
+
import path10 from "node:path";
|
|
1520
1469
|
|
|
1521
|
-
//
|
|
1470
|
+
// server/util/multipart.js
|
|
1522
1471
|
function parseMultipart(req) {
|
|
1523
1472
|
return new Promise((resolve, reject) => {
|
|
1524
1473
|
const contentType = req.headers["content-type"] || "";
|
|
@@ -1566,7 +1515,7 @@ function parseMultipart(req) {
|
|
|
1566
1515
|
}
|
|
1567
1516
|
__name(parseMultipart, "parseMultipart");
|
|
1568
1517
|
|
|
1569
|
-
//
|
|
1518
|
+
// server/controllers/files.js
|
|
1570
1519
|
var FileController = class {
|
|
1571
1520
|
static {
|
|
1572
1521
|
__name(this, "FileController");
|
|
@@ -1580,35 +1529,35 @@ var FileController = class {
|
|
|
1580
1529
|
async upload(req, res, params) {
|
|
1581
1530
|
if (!this.getActiveDb().prepare("SELECT id FROM threads WHERE id = ?").get(params.id)) return sendError(res, 404, "Thread not found");
|
|
1582
1531
|
const files = await parseMultipart(req);
|
|
1583
|
-
const dir =
|
|
1532
|
+
const dir = path10.join(this.uploadsDir, params.id);
|
|
1584
1533
|
fs9.mkdirSync(dir, { recursive: true });
|
|
1585
1534
|
const savedFiles = [];
|
|
1586
1535
|
for (const file of files) {
|
|
1587
1536
|
const fileId = uuid();
|
|
1588
|
-
const ext =
|
|
1589
|
-
fs9.writeFileSync(
|
|
1537
|
+
const ext = path10.extname(file.filename) || "";
|
|
1538
|
+
fs9.writeFileSync(path10.join(dir, fileId + ext), file.data);
|
|
1590
1539
|
savedFiles.push({ id: fileId, filename: file.filename, path: `/api/uploads/${params.id}/${fileId}${ext}`, mimeType: file.mimeType, size: file.data.length });
|
|
1591
1540
|
}
|
|
1592
1541
|
send(res, 200, { files: savedFiles });
|
|
1593
1542
|
}
|
|
1594
1543
|
serveUpload(req, res, params) {
|
|
1595
|
-
const base =
|
|
1544
|
+
const base = path10.join(this.uploadsDir, params.threadId, params.fileId);
|
|
1596
1545
|
let resolved = base;
|
|
1597
1546
|
if (!fs9.existsSync(resolved)) {
|
|
1598
1547
|
try {
|
|
1599
|
-
const match = fs9.readdirSync(
|
|
1600
|
-
if (match) resolved =
|
|
1548
|
+
const match = fs9.readdirSync(path10.join(this.uploadsDir, params.threadId)).find((e) => e.startsWith(params.fileId.replace(/\.[^.]+$/, "")));
|
|
1549
|
+
if (match) resolved = path10.join(this.uploadsDir, params.threadId, match);
|
|
1601
1550
|
} catch {
|
|
1602
1551
|
}
|
|
1603
1552
|
}
|
|
1604
1553
|
if (!fs9.existsSync(resolved)) return sendError(res, 404, "File not found");
|
|
1605
1554
|
const MIME = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".pdf": "application/pdf", ".txt": "text/plain", ".json": "application/json" };
|
|
1606
1555
|
const stat = fs9.statSync(resolved);
|
|
1607
|
-
res.writeHead(200, { "Content-Type": MIME[
|
|
1556
|
+
res.writeHead(200, { "Content-Type": MIME[path10.extname(resolved).toLowerCase()] || "application/octet-stream", "Content-Length": stat.size, "Cache-Control": "public, max-age=86400", "Access-Control-Allow-Origin": "*" });
|
|
1608
1557
|
fs9.createReadStream(resolved).pipe(res);
|
|
1609
1558
|
}
|
|
1610
1559
|
_intelligencePath(threadId) {
|
|
1611
|
-
return
|
|
1560
|
+
return path10.join(this.intelligenceDir, this.getWorkspaces().active, `${threadId}.json`);
|
|
1612
1561
|
}
|
|
1613
1562
|
getIntelligence(req, res, params) {
|
|
1614
1563
|
const filePath = this._intelligencePath(params.id);
|
|
@@ -1622,16 +1571,16 @@ var FileController = class {
|
|
|
1622
1571
|
async saveIntelligence(req, res, params) {
|
|
1623
1572
|
const body = await parseBody(req);
|
|
1624
1573
|
const filePath = this._intelligencePath(params.id);
|
|
1625
|
-
fs9.mkdirSync(
|
|
1574
|
+
fs9.mkdirSync(path10.dirname(filePath), { recursive: true });
|
|
1626
1575
|
const data = { versions: body.versions || [], currentVersion: body.currentVersion ?? -1, updatedAt: Date.now() };
|
|
1627
1576
|
fs9.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
1628
1577
|
send(res, 200, data);
|
|
1629
1578
|
}
|
|
1630
1579
|
};
|
|
1631
1580
|
|
|
1632
|
-
//
|
|
1581
|
+
// server/controllers/memory.js
|
|
1633
1582
|
import fs10 from "node:fs";
|
|
1634
|
-
import
|
|
1583
|
+
import path11 from "node:path";
|
|
1635
1584
|
var MemoryController = class {
|
|
1636
1585
|
static {
|
|
1637
1586
|
__name(this, "MemoryController");
|
|
@@ -1675,7 +1624,7 @@ var MemoryController = class {
|
|
|
1675
1624
|
return;
|
|
1676
1625
|
}
|
|
1677
1626
|
for (const entry of entries) {
|
|
1678
|
-
const fullPath =
|
|
1627
|
+
const fullPath = path11.join(dir, entry);
|
|
1679
1628
|
const stat = (() => {
|
|
1680
1629
|
try {
|
|
1681
1630
|
return fs10.statSync(fullPath);
|
|
@@ -1736,16 +1685,16 @@ ${body}` : body, createdAt: dateMatch ? `${dateMatch[1]}T00:00:00Z` : stat.mtime
|
|
|
1736
1685
|
}
|
|
1737
1686
|
};
|
|
1738
1687
|
|
|
1739
|
-
//
|
|
1688
|
+
// server/controllers/filesystem.js
|
|
1740
1689
|
import fs11 from "node:fs";
|
|
1741
|
-
import
|
|
1690
|
+
import path12 from "node:path";
|
|
1742
1691
|
import os3 from "node:os";
|
|
1743
1692
|
var HOME2 = os3.homedir();
|
|
1744
1693
|
var ALLOWED_FILE_DIRS = [HOME2, "/tmp"];
|
|
1745
1694
|
function handleServeFile(req, res, query, memoryConfig) {
|
|
1746
1695
|
const filePath = query.path;
|
|
1747
1696
|
if (!filePath) return sendError(res, 400, "Missing path parameter");
|
|
1748
|
-
const resolved = filePath.startsWith("./") || filePath.startsWith("../") ?
|
|
1697
|
+
const resolved = filePath.startsWith("./") || filePath.startsWith("../") ? path12.resolve(memoryConfig.workspaceDir, filePath) : path12.resolve(filePath);
|
|
1749
1698
|
if (!ALLOWED_FILE_DIRS.some((dir) => resolved.startsWith(dir + "/") || resolved === dir)) return sendError(res, 403, "Access denied: path not in allowed directories");
|
|
1750
1699
|
if (!fs11.existsSync(resolved) || !fs11.statSync(resolved).isFile()) return sendError(res, 404, "File not found");
|
|
1751
1700
|
const MIME = {
|
|
@@ -1781,7 +1730,7 @@ function handleServeFile(req, res, query, memoryConfig) {
|
|
|
1781
1730
|
".webm": "video/webm"
|
|
1782
1731
|
};
|
|
1783
1732
|
const stat = fs11.statSync(resolved);
|
|
1784
|
-
res.writeHead(200, { "Content-Type": MIME[
|
|
1733
|
+
res.writeHead(200, { "Content-Type": MIME[path12.extname(resolved).toLowerCase()] || "application/octet-stream", "Content-Length": stat.size, "Cache-Control": "public, max-age=86400", "Access-Control-Allow-Origin": "*" });
|
|
1785
1734
|
fs11.createReadStream(resolved).pipe(res);
|
|
1786
1735
|
}
|
|
1787
1736
|
__name(handleServeFile, "handleServeFile");
|
|
@@ -1789,17 +1738,17 @@ function handleWorkspaceList(req, res, query) {
|
|
|
1789
1738
|
const reqPath = query.path || "~/.openclaw/workspace";
|
|
1790
1739
|
const depth = parseInt(query.depth || "2", 10);
|
|
1791
1740
|
const showHidden = query.hidden === "1" || query.hidden === "true";
|
|
1792
|
-
const resolved =
|
|
1741
|
+
const resolved = path12.resolve(reqPath.replace(/^~/, HOME2));
|
|
1793
1742
|
if (!resolved.startsWith(HOME2)) return sendError(res, 403, "Access denied");
|
|
1794
1743
|
if (!fs11.existsSync(resolved)) return sendError(res, 404, "Path not found");
|
|
1795
|
-
const files = [{ path: resolved + "/", type: "dir", name:
|
|
1744
|
+
const files = [{ path: resolved + "/", type: "dir", name: path12.basename(resolved), size: 0 }];
|
|
1796
1745
|
const walk = /* @__PURE__ */ __name((dir, d) => {
|
|
1797
1746
|
if (d > depth) return;
|
|
1798
1747
|
try {
|
|
1799
1748
|
for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
1800
1749
|
if (entry.name.startsWith(".") && entry.name !== ".openclaw" && !showHidden) continue;
|
|
1801
1750
|
if (entry.name === "node_modules") continue;
|
|
1802
|
-
const fullPath =
|
|
1751
|
+
const fullPath = path12.join(dir, entry.name);
|
|
1803
1752
|
const isDir = entry.isDirectory();
|
|
1804
1753
|
files.push({ path: fullPath + (isDir ? "/" : ""), type: isDir ? "dir" : "file", name: entry.name, size: isDir ? 0 : (() => {
|
|
1805
1754
|
try {
|
|
@@ -1820,11 +1769,11 @@ __name(handleWorkspaceList, "handleWorkspaceList");
|
|
|
1820
1769
|
function handleWorkspaceFileRead(req, res, query) {
|
|
1821
1770
|
const filePath = query.path;
|
|
1822
1771
|
if (!filePath) return sendError(res, 400, "Missing path parameter");
|
|
1823
|
-
const resolved =
|
|
1772
|
+
const resolved = path12.resolve(filePath.replace(/^~/, HOME2));
|
|
1824
1773
|
if (!resolved.startsWith(HOME2)) return sendError(res, 403, "Access denied");
|
|
1825
1774
|
if (!fs11.existsSync(resolved) || !fs11.statSync(resolved).isFile()) return sendError(res, 404, "File not found");
|
|
1826
1775
|
const stat = fs11.statSync(resolved);
|
|
1827
|
-
const ext =
|
|
1776
|
+
const ext = path12.extname(resolved).toLowerCase().slice(1);
|
|
1828
1777
|
const binaryMime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", svg: "image/svg+xml", bmp: "image/bmp", ico: "image/x-icon", pdf: "application/pdf", mp3: "audio/mpeg", mp4: "video/mp4", wav: "audio/wav", ogg: "audio/ogg", webm: "video/webm" };
|
|
1829
1778
|
const mime = binaryMime[ext];
|
|
1830
1779
|
if (mime) {
|
|
@@ -1841,11 +1790,11 @@ __name(handleWorkspaceFileRead, "handleWorkspaceFileRead");
|
|
|
1841
1790
|
async function handleWorkspaceFileWrite(req, res, query) {
|
|
1842
1791
|
const filePath = query.path;
|
|
1843
1792
|
if (!filePath) return sendError(res, 400, "Missing path parameter");
|
|
1844
|
-
const resolved =
|
|
1793
|
+
const resolved = path12.resolve(filePath.replace(/^~/, HOME2));
|
|
1845
1794
|
if (!resolved.startsWith(HOME2)) return sendError(res, 403, "Can only write to workspace directory");
|
|
1846
1795
|
const chunks = [];
|
|
1847
1796
|
for await (const chunk of req) chunks.push(chunk);
|
|
1848
|
-
const dir =
|
|
1797
|
+
const dir = path12.dirname(resolved);
|
|
1849
1798
|
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
1850
1799
|
fs11.writeFileSync(resolved, Buffer.concat(chunks).toString("utf8"), "utf8");
|
|
1851
1800
|
send(res, 200, { ok: true });
|
|
@@ -1854,7 +1803,7 @@ __name(handleWorkspaceFileWrite, "handleWorkspaceFileWrite");
|
|
|
1854
1803
|
function handleWorkspaceFileDelete(req, res, query) {
|
|
1855
1804
|
const filePath = query.path;
|
|
1856
1805
|
if (!filePath) return sendError(res, 400, "Missing path parameter");
|
|
1857
|
-
const resolved =
|
|
1806
|
+
const resolved = path12.resolve(filePath.replace(/^~/, HOME2));
|
|
1858
1807
|
if (!resolved.startsWith(HOME2)) return sendError(res, 403, "Access denied");
|
|
1859
1808
|
if (!fs11.existsSync(resolved)) return sendError(res, 404, "Path not found");
|
|
1860
1809
|
try {
|
|
@@ -1874,52 +1823,36 @@ __name(handleWorkspaceFileDelete, "handleWorkspaceFileDelete");
|
|
|
1874
1823
|
async function handleWorkspaceUpload(req, res, query) {
|
|
1875
1824
|
const targetDir = query.path;
|
|
1876
1825
|
if (!targetDir) return sendError(res, 400, "Missing path parameter");
|
|
1877
|
-
const resolved =
|
|
1826
|
+
const resolved = path12.resolve(targetDir.replace(/^~/, HOME2));
|
|
1878
1827
|
if (!resolved.startsWith(HOME2)) return sendError(res, 403, "Access denied");
|
|
1879
1828
|
if (!fs11.existsSync(resolved) || !fs11.statSync(resolved).isDirectory()) return sendError(res, 404, "Target directory not found");
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
const boundaryBuf = Buffer.from("--" + boundary);
|
|
1829
|
+
if (!(req.headers["content-type"] || "").includes("multipart/form-data")) return sendError(res, 400, "Expected multipart/form-data");
|
|
1830
|
+
let files;
|
|
1831
|
+
try {
|
|
1832
|
+
files = await parseMultipart(req);
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
return sendError(res, 400, "Invalid multipart data: " + err.message);
|
|
1835
|
+
}
|
|
1888
1836
|
const uploaded = [];
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
if (filenameMatch && fileContent.length > 0) {
|
|
1901
|
-
const filename = path13.basename(filenameMatch[1]);
|
|
1902
|
-
let finalPath = path13.join(resolved, filename);
|
|
1903
|
-
let counter = 1;
|
|
1904
|
-
while (fs11.existsSync(finalPath)) {
|
|
1905
|
-
const ext = path13.extname(filename);
|
|
1906
|
-
finalPath = path13.join(resolved, `${path13.basename(filename, ext)} (${counter})${ext}`);
|
|
1907
|
-
counter++;
|
|
1908
|
-
}
|
|
1909
|
-
fs11.writeFileSync(finalPath, fileContent);
|
|
1910
|
-
uploaded.push({ name: path13.basename(finalPath), size: fileContent.length });
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
start = idx + boundaryBuf.length + 2;
|
|
1837
|
+
for (const { filename, data } of files) {
|
|
1838
|
+
if (!filename || !data.length) continue;
|
|
1839
|
+
const safeName = path12.basename(filename);
|
|
1840
|
+
let finalPath = path12.join(resolved, safeName);
|
|
1841
|
+
let counter = 1;
|
|
1842
|
+
while (fs11.existsSync(finalPath)) {
|
|
1843
|
+
const ext = path12.extname(safeName);
|
|
1844
|
+
finalPath = path12.join(resolved, `${path12.basename(safeName, ext)} (${counter++})${ext}`);
|
|
1845
|
+
}
|
|
1846
|
+
fs11.writeFileSync(finalPath, data);
|
|
1847
|
+
uploaded.push({ name: path12.basename(finalPath), size: data.length });
|
|
1915
1848
|
}
|
|
1916
1849
|
send(res, 200, { ok: true, uploaded });
|
|
1917
1850
|
}
|
|
1918
1851
|
__name(handleWorkspaceUpload, "handleWorkspaceUpload");
|
|
1919
1852
|
|
|
1920
|
-
//
|
|
1853
|
+
// server/controllers/transcribe.js
|
|
1921
1854
|
import fs12 from "node:fs";
|
|
1922
|
-
import
|
|
1855
|
+
import path13 from "node:path";
|
|
1923
1856
|
import os4 from "node:os";
|
|
1924
1857
|
async function handleTranscribe(req, res) {
|
|
1925
1858
|
try {
|
|
@@ -1930,7 +1863,7 @@ async function handleTranscribe(req, res) {
|
|
|
1930
1863
|
if (audioBuffer.length > 25 * 1024 * 1024) return send(res, 400, { error: "Audio too large (max 25MB)" });
|
|
1931
1864
|
let apiKey;
|
|
1932
1865
|
try {
|
|
1933
|
-
const ocConfig = JSON.parse(fs12.readFileSync(
|
|
1866
|
+
const ocConfig = JSON.parse(fs12.readFileSync(path13.join(os4.homedir(), ".openclaw", "openclaw.json"), "utf8"));
|
|
1934
1867
|
apiKey = ocConfig?.skills?.entries?.["openai-whisper-api"]?.apiKey;
|
|
1935
1868
|
} catch {
|
|
1936
1869
|
}
|
|
@@ -1978,17 +1911,17 @@ json\r
|
|
|
1978
1911
|
}
|
|
1979
1912
|
__name(handleTranscribe, "handleTranscribe");
|
|
1980
1913
|
|
|
1981
|
-
//
|
|
1914
|
+
// server/index.js
|
|
1982
1915
|
var HOME3 = os5.homedir();
|
|
1983
1916
|
var PORT = parseInt(process.env.PORT || "3001", 10);
|
|
1984
|
-
var
|
|
1985
|
-
var PLUGIN_DIR =
|
|
1917
|
+
var __dirname2 = path14.dirname(fileURLToPath2(import.meta.url));
|
|
1918
|
+
var PLUGIN_DIR = path14.resolve(__dirname2, "..");
|
|
1986
1919
|
function createApp(config = {}) {
|
|
1987
|
-
const DATA_DIR = config.dataDir ||
|
|
1988
|
-
const UPLOADS_DIR = config.uploadsDir ||
|
|
1989
|
-
const WORKSPACES_FILE =
|
|
1990
|
-
const SETTINGS_FILE =
|
|
1991
|
-
const INTELLIGENCE_DIR =
|
|
1920
|
+
const DATA_DIR = config.dataDir || path14.join(PLUGIN_DIR, "data");
|
|
1921
|
+
const UPLOADS_DIR = config.uploadsDir || path14.join(PLUGIN_DIR, "uploads");
|
|
1922
|
+
const WORKSPACES_FILE = path14.join(DATA_DIR, "workspaces.json");
|
|
1923
|
+
const SETTINGS_FILE = path14.join(DATA_DIR, "settings.json");
|
|
1924
|
+
const INTELLIGENCE_DIR = path14.join(DATA_DIR, "intelligence");
|
|
1992
1925
|
const authToken = config.authToken !== void 0 ? config.authToken : AUTH_TOKEN;
|
|
1993
1926
|
const gatewayToken = config.gatewayToken !== void 0 ? config.gatewayToken : authToken;
|
|
1994
1927
|
const gatewayUrl = config.gatewayUrl || GATEWAY_WS_URL;
|
|
@@ -1997,10 +1930,10 @@ function createApp(config = {}) {
|
|
|
1997
1930
|
const dbCache = /* @__PURE__ */ new Map();
|
|
1998
1931
|
function getDb(workspaceName) {
|
|
1999
1932
|
if (dbCache.has(workspaceName)) return dbCache.get(workspaceName);
|
|
2000
|
-
const db = new Database(
|
|
2001
|
-
db.
|
|
2002
|
-
db.
|
|
2003
|
-
|
|
1933
|
+
const db = new Database(path14.join(DATA_DIR, `${workspaceName}.db`));
|
|
1934
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
1935
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
1936
|
+
migrate(db);
|
|
2004
1937
|
dbCache.set(workspaceName, db);
|
|
2005
1938
|
return db;
|
|
2006
1939
|
}
|
|
@@ -2027,8 +1960,8 @@ function createApp(config = {}) {
|
|
|
2027
1960
|
const globalDbCache = {
|
|
2028
1961
|
get() {
|
|
2029
1962
|
if (_globalDb) return _globalDb;
|
|
2030
|
-
_globalDb = new Database(
|
|
2031
|
-
_globalDb.
|
|
1963
|
+
_globalDb = new Database(path14.join(DATA_DIR, "global.db"));
|
|
1964
|
+
_globalDb.exec("PRAGMA journal_mode = WAL");
|
|
2032
1965
|
_globalDb.exec(`CREATE TABLE IF NOT EXISTS custom_emojis (name TEXT NOT NULL, pack TEXT NOT NULL DEFAULT 'slackmojis', url TEXT NOT NULL, mime_type TEXT, created_at INTEGER DEFAULT (strftime('%s','now')), PRIMARY KEY (name, pack))`);
|
|
2033
1966
|
return _globalDb;
|
|
2034
1967
|
},
|
|
@@ -2062,7 +1995,7 @@ function createApp(config = {}) {
|
|
|
2062
1995
|
const memoryConfig = discoverMemoryConfig();
|
|
2063
1996
|
const memoryProvider = createMemoryProvider(memoryConfig);
|
|
2064
1997
|
memoryProvider.init().catch((err) => console.error("[createApp] Memory provider init error:", err.message));
|
|
2065
|
-
const MEMORY_FILES_DIR =
|
|
1998
|
+
const MEMORY_FILES_DIR = path14.join(memoryConfig.workspaceDir, "memory");
|
|
2066
1999
|
const gatewayClient = new GatewayClient({ getDb, getWorkspaces, dataDir: DATA_DIR, debugLogger, gatewayWsUrl: gatewayUrl, authToken: gatewayToken, mediaStash });
|
|
2067
2000
|
const broadcast = /* @__PURE__ */ __name((msg) => gatewayClient.broadcastToBrowsers(msg), "broadcast");
|
|
2068
2001
|
const workspaces = new WorkspaceController({ getDb, closeDb, getWorkspaces, setWorkspaces, dataDir: DATA_DIR, broadcast });
|
|
@@ -2080,7 +2013,7 @@ function createApp(config = {}) {
|
|
|
2080
2013
|
__name(handleGetSettings, "handleGetSettings");
|
|
2081
2014
|
async function handleSaveSettings(req, res) {
|
|
2082
2015
|
const body = await parseBody(req);
|
|
2083
|
-
fs13.mkdirSync(
|
|
2016
|
+
fs13.mkdirSync(path14.dirname(SETTINGS_FILE), { recursive: true });
|
|
2084
2017
|
fs13.writeFileSync(SETTINGS_FILE, JSON.stringify(body, null, 2));
|
|
2085
2018
|
send(res, 200, { ok: true });
|
|
2086
2019
|
}
|
|
@@ -2124,10 +2057,10 @@ function createApp(config = {}) {
|
|
|
2124
2057
|
const fileName = STATIC[urlPath];
|
|
2125
2058
|
const isAllowed = fileName || urlPath.startsWith("/icons/") || urlPath.startsWith("/lib/") || urlPath.startsWith("/frontend/") || urlPath.startsWith("/emoji/") || urlPath === "/config.js";
|
|
2126
2059
|
if (isAllowed) {
|
|
2127
|
-
const staticPath =
|
|
2060
|
+
const staticPath = path14.join(PLUGIN_DIR, fileName || urlPath.slice(1));
|
|
2128
2061
|
if (fs13.existsSync(staticPath) && fs13.statSync(staticPath).isFile()) {
|
|
2129
2062
|
const MIME = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".ico": "image/x-icon", ".png": "image/png", ".svg": "image/svg+xml", ".gif": "image/gif", ".webp": "image/webp" };
|
|
2130
|
-
const ext =
|
|
2063
|
+
const ext = path14.extname(staticPath).toLowerCase();
|
|
2131
2064
|
const stat = fs13.statSync(staticPath);
|
|
2132
2065
|
res.writeHead(200, { "Content-Type": MIME[ext] || "application/octet-stream", "Content-Length": stat.size, "Cache-Control": ext === ".html" ? "no-cache" : "public, max-age=3600" });
|
|
2133
2066
|
return fs13.createReadStream(staticPath).pipe(res);
|
|
@@ -2214,7 +2147,7 @@ function createApp(config = {}) {
|
|
|
2214
2147
|
if (method === "GET" && urlPath === "/api/health") return send(res, 200, { ok: true, workspace: getWorkspaces().active, uptime: process.uptime() });
|
|
2215
2148
|
if (method === "GET" && urlPath === "/api/agents") {
|
|
2216
2149
|
try {
|
|
2217
|
-
send(res, 200, { agents: fs13.readdirSync(
|
|
2150
|
+
send(res, 200, { agents: fs13.readdirSync(path14.join(HOME3, ".openclaw", "agents"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name) });
|
|
2218
2151
|
} catch {
|
|
2219
2152
|
send(res, 200, { agents: ["main"] });
|
|
2220
2153
|
}
|
|
@@ -2303,7 +2236,7 @@ function createApp(config = {}) {
|
|
|
2303
2236
|
if (msg.type === "req" && msg.method === "chat.send" && msg.params?.attachments?.length > 0) {
|
|
2304
2237
|
const parsed = parseSessionKey(msg.params.sessionKey || "");
|
|
2305
2238
|
const threadId = parsed?.threadId || "misc";
|
|
2306
|
-
const uploadDir =
|
|
2239
|
+
const uploadDir = path14.join(UPLOADS_DIR, threadId);
|
|
2307
2240
|
fs13.mkdirSync(uploadDir, { recursive: true });
|
|
2308
2241
|
const extMap = { jpeg: "jpg", jpg: "jpg", png: "png", gif: "gif", webp: "webp", pdf: "pdf", "svg+xml": "svg", mp3: "mp3", mp4: "mp4", wav: "wav", webm: "webm" };
|
|
2309
2242
|
const savedPaths = [];
|
|
@@ -2311,7 +2244,7 @@ function createApp(config = {}) {
|
|
|
2311
2244
|
if (!att.content || !att.mimeType) continue;
|
|
2312
2245
|
try {
|
|
2313
2246
|
const rawExt = att.mimeType.split("/")[1]?.split(";")[0] || "bin";
|
|
2314
|
-
const filePath =
|
|
2247
|
+
const filePath = path14.join(uploadDir, `${Date.now()}_${Math.random().toString(36).slice(2, 6)}.${extMap[rawExt] || rawExt}`);
|
|
2315
2248
|
fs13.writeFileSync(filePath, Buffer.from(att.content, "base64"));
|
|
2316
2249
|
savedPaths.push(filePath);
|
|
2317
2250
|
} catch (err) {
|
|
@@ -2353,7 +2286,7 @@ ${savedPaths.map((p) => `- ${p}`).join("\n")}]`;
|
|
|
2353
2286
|
};
|
|
2354
2287
|
}
|
|
2355
2288
|
__name(createApp, "createApp");
|
|
2356
|
-
function
|
|
2289
|
+
function migrate(db) {
|
|
2357
2290
|
db.exec(`
|
|
2358
2291
|
CREATE TABLE IF NOT EXISTS threads (
|
|
2359
2292
|
id TEXT PRIMARY KEY, session_key TEXT UNIQUE NOT NULL, title TEXT DEFAULT 'New chat',
|
|
@@ -2378,25 +2311,25 @@ function _migrate(db) {
|
|
|
2378
2311
|
}
|
|
2379
2312
|
db.exec(`CREATE TABLE IF NOT EXISTS unread_messages (thread_id TEXT NOT NULL, message_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY (thread_id, message_id), FOREIGN KEY (thread_id) REFERENCES threads(id) ON DELETE CASCADE)`);
|
|
2380
2313
|
db.exec("CREATE INDEX IF NOT EXISTS idx_unread_thread ON unread_messages(thread_id)");
|
|
2381
|
-
|
|
2314
|
+
ensureFts(db);
|
|
2382
2315
|
}
|
|
2383
|
-
__name(
|
|
2384
|
-
function
|
|
2316
|
+
__name(migrate, "migrate");
|
|
2317
|
+
function createFts(db) {
|
|
2385
2318
|
db.exec(`CREATE VIRTUAL TABLE messages_fts USING fts5(content, content=messages, content_rowid=rowid, tokenize='porter unicode61 tokenchars x27')`);
|
|
2386
2319
|
db.exec(`CREATE TRIGGER messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, new.content); END`);
|
|
2387
2320
|
db.exec(`CREATE TRIGGER messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES('delete', old.rowid, old.content); END`);
|
|
2388
2321
|
db.exec(`CREATE TRIGGER messages_au AFTER UPDATE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES('delete', old.rowid, old.content); INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, new.content); END`);
|
|
2389
2322
|
}
|
|
2390
|
-
__name(
|
|
2391
|
-
function
|
|
2323
|
+
__name(createFts, "createFts");
|
|
2324
|
+
function dropFts(db) {
|
|
2392
2325
|
db.exec("DROP TABLE IF EXISTS messages_fts; DROP TRIGGER IF EXISTS messages_ai; DROP TRIGGER IF EXISTS messages_ad; DROP TRIGGER IF EXISTS messages_au;");
|
|
2393
2326
|
}
|
|
2394
|
-
__name(
|
|
2395
|
-
function
|
|
2327
|
+
__name(dropFts, "dropFts");
|
|
2328
|
+
function ensureFts(db) {
|
|
2396
2329
|
const hasFts = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
|
|
2397
2330
|
if (!hasFts) {
|
|
2398
2331
|
try {
|
|
2399
|
-
|
|
2332
|
+
createFts(db);
|
|
2400
2333
|
db.prepare("INSERT INTO messages_fts(messages_fts) VALUES('rebuild')").run();
|
|
2401
2334
|
} catch (e) {
|
|
2402
2335
|
console.error("[DB] messages_fts creation failed:", e.message);
|
|
@@ -2407,13 +2340,13 @@ function _ensureFts(db) {
|
|
|
2407
2340
|
if (schema && !schema.sql.includes("tokenchars")) {
|
|
2408
2341
|
console.log("[DB] Upgrading messages_fts tokenizer...");
|
|
2409
2342
|
try {
|
|
2410
|
-
|
|
2411
|
-
|
|
2343
|
+
dropFts(db);
|
|
2344
|
+
createFts(db);
|
|
2412
2345
|
db.prepare("INSERT INTO messages_fts(messages_fts) VALUES('rebuild')").run();
|
|
2413
2346
|
console.log("[DB] Upgrade complete");
|
|
2414
2347
|
} catch (e) {
|
|
2415
2348
|
console.error("[DB] Upgrade failed:", e.message);
|
|
2416
|
-
|
|
2349
|
+
dropFts(db);
|
|
2417
2350
|
}
|
|
2418
2351
|
} else {
|
|
2419
2352
|
try {
|
|
@@ -2423,12 +2356,12 @@ function _ensureFts(db) {
|
|
|
2423
2356
|
db.prepare("INSERT INTO messages_fts(messages_fts) VALUES('rebuild')").run();
|
|
2424
2357
|
} catch (e) {
|
|
2425
2358
|
console.error("[DB] FTS rebuild failed:", e.message);
|
|
2426
|
-
|
|
2359
|
+
dropFts(db);
|
|
2427
2360
|
}
|
|
2428
2361
|
}
|
|
2429
2362
|
}
|
|
2430
2363
|
}
|
|
2431
|
-
__name(
|
|
2364
|
+
__name(ensureFts, "ensureFts");
|
|
2432
2365
|
var isDirectRun = import.meta.url === `file://${process.argv[1]}`;
|
|
2433
2366
|
if (isDirectRun) {
|
|
2434
2367
|
const app = createApp();
|