@poprobertdaniel/openclaw-memory 0.1.0
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 +21 -0
- package/README.md +410 -0
- package/dist/chunk-CRPEAZ44.cjs +1881 -0
- package/dist/chunk-CRPEAZ44.cjs.map +1 -0
- package/dist/chunk-JNWCMHOB.js +309 -0
- package/dist/chunk-JNWCMHOB.js.map +1 -0
- package/dist/chunk-JSQBXYDM.js +1881 -0
- package/dist/chunk-JSQBXYDM.js.map +1 -0
- package/dist/chunk-NHFPLDZK.js +236 -0
- package/dist/chunk-NHFPLDZK.js.map +1 -0
- package/dist/chunk-NMUPGLJW.cjs +752 -0
- package/dist/chunk-NMUPGLJW.cjs.map +1 -0
- package/dist/chunk-RFLG2CCR.js +752 -0
- package/dist/chunk-RFLG2CCR.js.map +1 -0
- package/dist/chunk-VXULEX3A.cjs +236 -0
- package/dist/chunk-VXULEX3A.cjs.map +1 -0
- package/dist/chunk-ZY2C2CJQ.cjs +309 -0
- package/dist/chunk-ZY2C2CJQ.cjs.map +1 -0
- package/dist/cli/index.cjs +764 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +764 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.cjs +48 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +790 -0
- package/dist/index.d.ts +790 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-service-6WDMF6KX.cjs +9 -0
- package/dist/memory-service-6WDMF6KX.cjs.map +1 -0
- package/dist/memory-service-GKEG6J2D.js +9 -0
- package/dist/memory-service-GKEG6J2D.js.map +1 -0
- package/dist/server-BTbRv-yX.d.cts +1199 -0
- package/dist/server-BTbRv-yX.d.ts +1199 -0
- package/dist/server.cjs +9 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +2 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +9 -0
- package/dist/server.js.map +1 -0
- package/docker/full.yml +45 -0
- package/docker/standard.yml +26 -0
- package/package.json +109 -0
- package/skill/SKILL.md +139 -0
- package/templates/.env.example +42 -0
- package/templates/docker-compose.full.yml +45 -0
- package/templates/docker-compose.standard.yml +26 -0
- package/templates/openclaw-memory.config.ts +49 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/config/index.ts
|
|
2
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
3
|
+
var _os = require('os'); var _os2 = _interopRequireDefault(_os);
|
|
4
|
+
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
|
|
5
|
+
|
|
6
|
+
// src/core/errors.ts
|
|
7
|
+
var MemoryError = class extends Error {
|
|
8
|
+
constructor(message, code) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = "MemoryError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var ConfigError = class extends MemoryError {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message, "CONFIG_ERROR");
|
|
17
|
+
this.name = "ConfigError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var ValidationError = class extends MemoryError {
|
|
21
|
+
constructor(message, details) {
|
|
22
|
+
super(message, "VALIDATION_ERROR");
|
|
23
|
+
this.details = details;
|
|
24
|
+
this.name = "ValidationError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var NotFoundError = class extends MemoryError {
|
|
28
|
+
constructor(resource, id) {
|
|
29
|
+
super(`${resource} not found: ${id}`, "NOT_FOUND");
|
|
30
|
+
this.name = "NotFoundError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var AuthError = class extends MemoryError {
|
|
34
|
+
constructor(message = "Unauthorized") {
|
|
35
|
+
super(message, "AUTH_ERROR");
|
|
36
|
+
this.name = "AuthError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/config/defaults.ts
|
|
41
|
+
var DEFAULTS = {
|
|
42
|
+
port: 7777,
|
|
43
|
+
host: "0.0.0.0",
|
|
44
|
+
sqlite: {
|
|
45
|
+
path: "~/.openclaw-memory/memory.sqlite"
|
|
46
|
+
},
|
|
47
|
+
qdrant: {
|
|
48
|
+
collection: "openclaw_memories"
|
|
49
|
+
},
|
|
50
|
+
age: {
|
|
51
|
+
port: 5432,
|
|
52
|
+
graph: "agent_memory"
|
|
53
|
+
},
|
|
54
|
+
embedding: {
|
|
55
|
+
model: "text-embedding-3-small",
|
|
56
|
+
dimensions: 1536
|
|
57
|
+
},
|
|
58
|
+
extraction: {
|
|
59
|
+
model: "gpt-4o-mini",
|
|
60
|
+
enabled: true
|
|
61
|
+
},
|
|
62
|
+
auth: {
|
|
63
|
+
enabled: true
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/config/index.ts
|
|
68
|
+
function expandHome(p) {
|
|
69
|
+
if (p.startsWith("~/") || p === "~") {
|
|
70
|
+
return _path2.default.join(_os2.default.homedir(), p.slice(1));
|
|
71
|
+
}
|
|
72
|
+
return p;
|
|
73
|
+
}
|
|
74
|
+
function getDataDir() {
|
|
75
|
+
return process.env.OPENCLAW_MEMORY_DATA_DIR || _path2.default.join(_os2.default.homedir(), ".openclaw-memory");
|
|
76
|
+
}
|
|
77
|
+
function getDefaultSqlitePath() {
|
|
78
|
+
return _path2.default.join(getDataDir(), "memory.sqlite");
|
|
79
|
+
}
|
|
80
|
+
function getPidFilePath() {
|
|
81
|
+
return _path2.default.join(getDataDir(), "server.pid");
|
|
82
|
+
}
|
|
83
|
+
function getConfigSearchPaths(cwd) {
|
|
84
|
+
return [
|
|
85
|
+
_path2.default.join(cwd, "openclaw-memory.config.ts"),
|
|
86
|
+
_path2.default.join(cwd, "openclaw-memory.config.js"),
|
|
87
|
+
_path2.default.join(cwd, "openclaw-memory.config.json"),
|
|
88
|
+
_path2.default.join(getDataDir(), "config.ts"),
|
|
89
|
+
_path2.default.join(getDataDir(), "config.json")
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
async function loadConfigFile(configPath) {
|
|
93
|
+
const searchPaths = configPath ? [configPath] : getConfigSearchPaths(process.cwd());
|
|
94
|
+
for (const p of searchPaths) {
|
|
95
|
+
if (_fs2.default.existsSync(p)) {
|
|
96
|
+
if (p.endsWith(".json")) {
|
|
97
|
+
const content = _fs2.default.readFileSync(p, "utf-8");
|
|
98
|
+
return JSON.parse(content);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const mod = await Promise.resolve().then(() => _interopRequireWildcard(require(p)));
|
|
102
|
+
return mod.default || mod;
|
|
103
|
+
} catch (e) {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function loadFromEnv() {
|
|
110
|
+
const config = {};
|
|
111
|
+
try {
|
|
112
|
+
const dotenvPath = _path2.default.join(process.cwd(), ".env");
|
|
113
|
+
if (_fs2.default.existsSync(dotenvPath)) {
|
|
114
|
+
const envContent = _fs2.default.readFileSync(dotenvPath, "utf-8");
|
|
115
|
+
for (const line of envContent.split("\n")) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
118
|
+
const eqIdx = trimmed.indexOf("=");
|
|
119
|
+
if (eqIdx === -1) continue;
|
|
120
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
121
|
+
const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
122
|
+
if (!process.env[key]) {
|
|
123
|
+
process.env[key] = value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (e2) {
|
|
128
|
+
}
|
|
129
|
+
const env = process.env;
|
|
130
|
+
const tier = env.OPENCLAW_MEMORY_TIER || env.MEMORY_TIER;
|
|
131
|
+
if (tier && ["lite", "standard", "full"].includes(tier)) {
|
|
132
|
+
config.tier = tier;
|
|
133
|
+
}
|
|
134
|
+
const port = env.OPENCLAW_MEMORY_PORT || env.PORT;
|
|
135
|
+
if (port) config.port = parseInt(port, 10);
|
|
136
|
+
const host = env.OPENCLAW_MEMORY_HOST;
|
|
137
|
+
if (host) config.host = host;
|
|
138
|
+
const authToken = env.OPENCLAW_MEMORY_AUTH__TOKEN || env.AUTH_TOKEN || env.MEMORY_TOKEN;
|
|
139
|
+
if (authToken) {
|
|
140
|
+
config.auth = { token: authToken, enabled: true };
|
|
141
|
+
}
|
|
142
|
+
const sqlitePath = env.OPENCLAW_MEMORY_SQLITE__PATH || env.SQLITE_PATH;
|
|
143
|
+
if (sqlitePath) {
|
|
144
|
+
config.sqlite = { path: sqlitePath };
|
|
145
|
+
}
|
|
146
|
+
const qdrantUrl = env.OPENCLAW_MEMORY_QDRANT__URL || env.QDRANT_URL;
|
|
147
|
+
if (qdrantUrl) {
|
|
148
|
+
config.qdrant = {
|
|
149
|
+
url: qdrantUrl,
|
|
150
|
+
collection: env.OPENCLAW_MEMORY_QDRANT__COLLECTION || env.QDRANT_COLLECTION || DEFAULTS.qdrant.collection,
|
|
151
|
+
apiKey: env.OPENCLAW_MEMORY_QDRANT__API_KEY
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const ageHost = env.OPENCLAW_MEMORY_AGE__HOST || env.PGHOST;
|
|
155
|
+
if (ageHost) {
|
|
156
|
+
config.age = {
|
|
157
|
+
host: ageHost,
|
|
158
|
+
port: parseInt(env.OPENCLAW_MEMORY_AGE__PORT || env.PGPORT || String(DEFAULTS.age.port), 10),
|
|
159
|
+
user: env.OPENCLAW_MEMORY_AGE__USER || env.PGUSER || "",
|
|
160
|
+
password: env.OPENCLAW_MEMORY_AGE__PASSWORD || env.PGPASSWORD || "",
|
|
161
|
+
database: env.OPENCLAW_MEMORY_AGE__DATABASE || env.PGDATABASE || "",
|
|
162
|
+
graph: env.OPENCLAW_MEMORY_AGE__GRAPH || env.AGE_GRAPH || DEFAULTS.age.graph
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const embeddingApiKey = env.OPENCLAW_MEMORY_EMBEDDING__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;
|
|
166
|
+
if (embeddingApiKey) {
|
|
167
|
+
config.embedding = {
|
|
168
|
+
apiKey: embeddingApiKey,
|
|
169
|
+
baseUrl: env.OPENCLAW_MEMORY_EMBEDDING__BASE_URL || env.EMBEDDING_BASE_URL,
|
|
170
|
+
model: env.OPENCLAW_MEMORY_EMBEDDING__MODEL || env.EMBEDDING_MODEL || DEFAULTS.embedding.model,
|
|
171
|
+
dimensions: parseInt(env.OPENCLAW_MEMORY_EMBEDDING__DIMENSIONS || String(DEFAULTS.embedding.dimensions), 10)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const extractionApiKey = env.OPENCLAW_MEMORY_EXTRACTION__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;
|
|
175
|
+
if (extractionApiKey) {
|
|
176
|
+
config.extraction = {
|
|
177
|
+
apiKey: extractionApiKey,
|
|
178
|
+
baseUrl: env.OPENCLAW_MEMORY_EXTRACTION__BASE_URL || env.EXTRACTION_BASE_URL,
|
|
179
|
+
model: env.OPENCLAW_MEMORY_EXTRACTION__MODEL || env.EXTRACTION_MODEL || DEFAULTS.extraction.model,
|
|
180
|
+
enabled: env.OPENCLAW_MEMORY_EXTRACTION__ENABLED !== "false"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return config;
|
|
184
|
+
}
|
|
185
|
+
function inferTier(config) {
|
|
186
|
+
if (config.tier) return config.tier;
|
|
187
|
+
if (config.qdrant && config.age) return "full";
|
|
188
|
+
if (config.qdrant) return "standard";
|
|
189
|
+
return "lite";
|
|
190
|
+
}
|
|
191
|
+
async function loadConfig(configPath) {
|
|
192
|
+
const fileConfig = await loadConfigFile(configPath);
|
|
193
|
+
const envConfig = loadFromEnv();
|
|
194
|
+
const merged = {
|
|
195
|
+
...fileConfig,
|
|
196
|
+
...envConfig,
|
|
197
|
+
// Deep merge nested objects
|
|
198
|
+
auth: { ..._optionalChain([fileConfig, 'optionalAccess', _ => _.auth]), ...envConfig.auth },
|
|
199
|
+
sqlite: { ..._optionalChain([fileConfig, 'optionalAccess', _2 => _2.sqlite]), ...envConfig.sqlite }
|
|
200
|
+
};
|
|
201
|
+
if (envConfig.qdrant || _optionalChain([fileConfig, 'optionalAccess', _3 => _3.qdrant])) {
|
|
202
|
+
merged.qdrant = { ..._optionalChain([fileConfig, 'optionalAccess', _4 => _4.qdrant]), ...envConfig.qdrant };
|
|
203
|
+
}
|
|
204
|
+
if (envConfig.age || _optionalChain([fileConfig, 'optionalAccess', _5 => _5.age])) {
|
|
205
|
+
merged.age = { ..._optionalChain([fileConfig, 'optionalAccess', _6 => _6.age]), ...envConfig.age };
|
|
206
|
+
}
|
|
207
|
+
if (envConfig.embedding || _optionalChain([fileConfig, 'optionalAccess', _7 => _7.embedding])) {
|
|
208
|
+
merged.embedding = { ..._optionalChain([fileConfig, 'optionalAccess', _8 => _8.embedding]), ...envConfig.embedding };
|
|
209
|
+
}
|
|
210
|
+
if (envConfig.extraction || _optionalChain([fileConfig, 'optionalAccess', _9 => _9.extraction])) {
|
|
211
|
+
merged.extraction = { ..._optionalChain([fileConfig, 'optionalAccess', _10 => _10.extraction]), ...envConfig.extraction };
|
|
212
|
+
}
|
|
213
|
+
const tier = inferTier(merged);
|
|
214
|
+
const sqlitePath = expandHome(_optionalChain([merged, 'access', _11 => _11.sqlite, 'optionalAccess', _12 => _12.path]) || DEFAULTS.sqlite.path);
|
|
215
|
+
let qdrant = null;
|
|
216
|
+
if (tier !== "lite" && _optionalChain([merged, 'access', _13 => _13.qdrant, 'optionalAccess', _14 => _14.url])) {
|
|
217
|
+
qdrant = {
|
|
218
|
+
url: merged.qdrant.url,
|
|
219
|
+
collection: merged.qdrant.collection || DEFAULTS.qdrant.collection,
|
|
220
|
+
apiKey: merged.qdrant.apiKey
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
let age = null;
|
|
224
|
+
if (tier === "full" && _optionalChain([merged, 'access', _15 => _15.age, 'optionalAccess', _16 => _16.host])) {
|
|
225
|
+
if (!merged.age.user || !merged.age.password || !merged.age.database) {
|
|
226
|
+
throw new ConfigError("Full tier requires age.user, age.password, and age.database");
|
|
227
|
+
}
|
|
228
|
+
age = {
|
|
229
|
+
host: merged.age.host,
|
|
230
|
+
port: merged.age.port || DEFAULTS.age.port,
|
|
231
|
+
user: merged.age.user,
|
|
232
|
+
password: merged.age.password,
|
|
233
|
+
database: merged.age.database,
|
|
234
|
+
graph: merged.age.graph || DEFAULTS.age.graph
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
let embedding = null;
|
|
238
|
+
if (tier !== "lite" && _optionalChain([merged, 'access', _17 => _17.embedding, 'optionalAccess', _18 => _18.apiKey])) {
|
|
239
|
+
embedding = {
|
|
240
|
+
apiKey: merged.embedding.apiKey,
|
|
241
|
+
baseUrl: merged.embedding.baseUrl,
|
|
242
|
+
model: merged.embedding.model || DEFAULTS.embedding.model,
|
|
243
|
+
dimensions: merged.embedding.dimensions || DEFAULTS.embedding.dimensions
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
let extraction = null;
|
|
247
|
+
if (tier !== "lite" && _optionalChain([merged, 'access', _19 => _19.extraction, 'optionalAccess', _20 => _20.apiKey])) {
|
|
248
|
+
extraction = {
|
|
249
|
+
apiKey: merged.extraction.apiKey,
|
|
250
|
+
baseUrl: merged.extraction.baseUrl,
|
|
251
|
+
model: merged.extraction.model || DEFAULTS.extraction.model,
|
|
252
|
+
enabled: _nullishCoalesce(merged.extraction.enabled, () => ( DEFAULTS.extraction.enabled))
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
tier,
|
|
257
|
+
port: merged.port || DEFAULTS.port,
|
|
258
|
+
host: merged.host || DEFAULTS.host,
|
|
259
|
+
auth: {
|
|
260
|
+
token: _optionalChain([merged, 'access', _21 => _21.auth, 'optionalAccess', _22 => _22.token]),
|
|
261
|
+
enabled: _nullishCoalesce(_optionalChain([merged, 'access', _23 => _23.auth, 'optionalAccess', _24 => _24.enabled]), () => ( DEFAULTS.auth.enabled))
|
|
262
|
+
},
|
|
263
|
+
sqlite: { path: sqlitePath },
|
|
264
|
+
qdrant,
|
|
265
|
+
age,
|
|
266
|
+
embedding,
|
|
267
|
+
extraction,
|
|
268
|
+
agents: merged.agents || _optionalChain([fileConfig, 'optionalAccess', _25 => _25.agents]) || []
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function defineConfig(config) {
|
|
272
|
+
return config;
|
|
273
|
+
}
|
|
274
|
+
function configSummary(config) {
|
|
275
|
+
const lines = [
|
|
276
|
+
`Tier: ${config.tier}`,
|
|
277
|
+
`Port: ${config.port}`,
|
|
278
|
+
`Host: ${config.host}`,
|
|
279
|
+
`SQLite: ${config.sqlite.path}`,
|
|
280
|
+
`Auth: ${config.auth.enabled ? "enabled" : "disabled"}`
|
|
281
|
+
];
|
|
282
|
+
if (config.qdrant) {
|
|
283
|
+
lines.push(`Qdrant: ${config.qdrant.url} (collection: ${config.qdrant.collection})`);
|
|
284
|
+
}
|
|
285
|
+
if (config.age) {
|
|
286
|
+
lines.push(`AGE: ${config.age.host}:${config.age.port}/${config.age.database}`);
|
|
287
|
+
}
|
|
288
|
+
if (config.embedding) {
|
|
289
|
+
lines.push(`Embedding: ${config.embedding.model}${config.embedding.baseUrl ? ` via ${config.embedding.baseUrl}` : ""}`);
|
|
290
|
+
}
|
|
291
|
+
if (config.extraction) {
|
|
292
|
+
lines.push(`Extraction: ${config.extraction.model}${config.extraction.enabled ? "" : " (disabled)"}`);
|
|
293
|
+
}
|
|
294
|
+
return lines.join("\n");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
exports.MemoryError = MemoryError; exports.ValidationError = ValidationError; exports.NotFoundError = NotFoundError; exports.AuthError = AuthError; exports.getDataDir = getDataDir; exports.getDefaultSqlitePath = getDefaultSqlitePath; exports.getPidFilePath = getPidFilePath; exports.loadConfig = loadConfig; exports.defineConfig = defineConfig; exports.configSummary = configSummary;
|
|
309
|
+
//# sourceMappingURL=chunk-ZY2C2CJQ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/robertpop/work/personal/openclaw-memory/dist/chunk-ZY2C2CJQ.cjs","../src/config/index.ts","../src/core/errors.ts","../src/config/defaults.ts"],"names":[],"mappings":"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;AACT,EAAA;AACI,EAAA;AACK,IAAA;AACT,EAAA;AAGM,EAAA;AAGA,EAAA;AAGF,EAAA;AACA,EAAA;AACF,IAAA;AACO,MAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACM,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAIgB;AACP,EAAA;AACT;AAIgB;AACR,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AAEO,EAAA;AACT;ADZW;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/robertpop/work/personal/openclaw-memory/dist/chunk-ZY2C2CJQ.cjs","sourcesContent":[null,"import path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\nimport type { Tier } from \"../core/types.js\";\nimport { ConfigError } from \"../core/errors.js\";\nimport { DEFAULTS } from \"./defaults.js\";\nimport type { Config, ResolvedConfig, QdrantConfig, AgeConfig, EmbeddingConfig, ExtractionConfig } from \"./schema.js\";\n\nexport type { Config, ResolvedConfig } from \"./schema.js\";\n\n// ── Helper: expand ~ to home dir ────────────────────────────────────────\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\") || p === \"~\") {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\n// ── Data Directory ──────────────────────────────────────────────────────\n\nexport function getDataDir(): string {\n return process.env.OPENCLAW_MEMORY_DATA_DIR || path.join(os.homedir(), \".openclaw-memory\");\n}\n\nexport function getDefaultSqlitePath(): string {\n return path.join(getDataDir(), \"memory.sqlite\");\n}\n\nexport function getPidFilePath(): string {\n return path.join(getDataDir(), \"server.pid\");\n}\n\n// ── Config File Discovery ───────────────────────────────────────────────\n\nfunction getConfigSearchPaths(cwd: string): string[] {\n return [\n path.join(cwd, \"openclaw-memory.config.ts\"),\n path.join(cwd, \"openclaw-memory.config.js\"),\n path.join(cwd, \"openclaw-memory.config.json\"),\n path.join(getDataDir(), \"config.ts\"),\n path.join(getDataDir(), \"config.json\"),\n ];\n}\n\nasync function loadConfigFile(configPath?: string): Promise<Config | null> {\n const searchPaths = configPath ? [configPath] : getConfigSearchPaths(process.cwd());\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) {\n if (p.endsWith(\".json\")) {\n const content = fs.readFileSync(p, \"utf-8\");\n return JSON.parse(content) as Config;\n }\n // For .ts and .js files, try dynamic import\n try {\n const mod = await import(p);\n return (mod.default || mod) as Config;\n } catch {\n // Can't import .ts outside of Bun — skip\n }\n }\n }\n\n return null;\n}\n\n// ── Environment Variable Loading ────────────────────────────────────────\n\nfunction loadFromEnv(): Partial<Config> {\n const config: Partial<Config> = {};\n\n // Load .env file if dotenv is available\n try {\n const dotenvPath = path.join(process.cwd(), \".env\");\n if (fs.existsSync(dotenvPath)) {\n const envContent = fs.readFileSync(dotenvPath, \"utf-8\");\n for (const line of envContent.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n const value = trimmed.slice(eqIdx + 1).trim().replace(/^[\"']|[\"']$/g, \"\");\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n }\n } catch {\n // No .env file — that's fine\n }\n\n const env = process.env;\n\n // Tier\n const tier = env.OPENCLAW_MEMORY_TIER || env.MEMORY_TIER;\n if (tier && [\"lite\", \"standard\", \"full\"].includes(tier)) {\n config.tier = tier as Tier;\n }\n\n // Port/Host\n const port = env.OPENCLAW_MEMORY_PORT || env.PORT;\n if (port) config.port = parseInt(port, 10);\n\n const host = env.OPENCLAW_MEMORY_HOST;\n if (host) config.host = host;\n\n // Auth\n const authToken = env.OPENCLAW_MEMORY_AUTH__TOKEN || env.AUTH_TOKEN || env.MEMORY_TOKEN;\n if (authToken) {\n config.auth = { token: authToken, enabled: true };\n }\n\n // SQLite\n const sqlitePath = env.OPENCLAW_MEMORY_SQLITE__PATH || env.SQLITE_PATH;\n if (sqlitePath) {\n config.sqlite = { path: sqlitePath };\n }\n\n // Qdrant\n const qdrantUrl = env.OPENCLAW_MEMORY_QDRANT__URL || env.QDRANT_URL;\n if (qdrantUrl) {\n config.qdrant = {\n url: qdrantUrl,\n collection: env.OPENCLAW_MEMORY_QDRANT__COLLECTION || env.QDRANT_COLLECTION || DEFAULTS.qdrant.collection,\n apiKey: env.OPENCLAW_MEMORY_QDRANT__API_KEY,\n };\n }\n\n // AGE\n const ageHost = env.OPENCLAW_MEMORY_AGE__HOST || env.PGHOST;\n if (ageHost) {\n config.age = {\n host: ageHost,\n port: parseInt(env.OPENCLAW_MEMORY_AGE__PORT || env.PGPORT || String(DEFAULTS.age.port), 10),\n user: env.OPENCLAW_MEMORY_AGE__USER || env.PGUSER || \"\",\n password: env.OPENCLAW_MEMORY_AGE__PASSWORD || env.PGPASSWORD || \"\",\n database: env.OPENCLAW_MEMORY_AGE__DATABASE || env.PGDATABASE || \"\",\n graph: env.OPENCLAW_MEMORY_AGE__GRAPH || env.AGE_GRAPH || DEFAULTS.age.graph,\n };\n }\n\n // Embedding\n const embeddingApiKey = env.OPENCLAW_MEMORY_EMBEDDING__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (embeddingApiKey) {\n config.embedding = {\n apiKey: embeddingApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EMBEDDING__BASE_URL || env.EMBEDDING_BASE_URL,\n model: env.OPENCLAW_MEMORY_EMBEDDING__MODEL || env.EMBEDDING_MODEL || DEFAULTS.embedding.model,\n dimensions: parseInt(env.OPENCLAW_MEMORY_EMBEDDING__DIMENSIONS || String(DEFAULTS.embedding.dimensions), 10),\n };\n }\n\n // Extraction\n const extractionApiKey = env.OPENCLAW_MEMORY_EXTRACTION__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (extractionApiKey) {\n config.extraction = {\n apiKey: extractionApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EXTRACTION__BASE_URL || env.EXTRACTION_BASE_URL,\n model: env.OPENCLAW_MEMORY_EXTRACTION__MODEL || env.EXTRACTION_MODEL || DEFAULTS.extraction.model,\n enabled: env.OPENCLAW_MEMORY_EXTRACTION__ENABLED !== \"false\",\n };\n }\n\n return config;\n}\n\n// ── Tier Inference ──────────────────────────────────────────────────────\n\nfunction inferTier(config: Partial<Config>): Tier {\n if (config.tier) return config.tier;\n if (config.qdrant && config.age) return \"full\";\n if (config.qdrant) return \"standard\";\n return \"lite\";\n}\n\n// ── Config Resolution ───────────────────────────────────────────────────\n\nexport async function loadConfig(configPath?: string): Promise<ResolvedConfig> {\n // 1. Load from file\n const fileConfig = await loadConfigFile(configPath);\n\n // 2. Load from env\n const envConfig = loadFromEnv();\n\n // 3. Merge: file < env (env wins)\n const merged: Partial<Config> = {\n ...fileConfig,\n ...envConfig,\n // Deep merge nested objects\n auth: { ...fileConfig?.auth, ...envConfig.auth },\n sqlite: { ...fileConfig?.sqlite, ...envConfig.sqlite },\n };\n\n if (envConfig.qdrant || fileConfig?.qdrant) {\n merged.qdrant = { ...fileConfig?.qdrant, ...envConfig.qdrant } as Config[\"qdrant\"];\n }\n if (envConfig.age || fileConfig?.age) {\n merged.age = { ...fileConfig?.age, ...envConfig.age } as Config[\"age\"];\n }\n if (envConfig.embedding || fileConfig?.embedding) {\n merged.embedding = { ...fileConfig?.embedding, ...envConfig.embedding } as Config[\"embedding\"];\n }\n if (envConfig.extraction || fileConfig?.extraction) {\n merged.extraction = { ...fileConfig?.extraction, ...envConfig.extraction } as Config[\"extraction\"];\n }\n\n // 4. Infer tier\n const tier = inferTier(merged);\n\n // 5. Resolve defaults\n const sqlitePath = expandHome(merged.sqlite?.path || DEFAULTS.sqlite.path);\n\n // 6. Resolve optional layers based on tier\n let qdrant: QdrantConfig | null = null;\n if (tier !== \"lite\" && merged.qdrant?.url) {\n qdrant = {\n url: merged.qdrant.url,\n collection: merged.qdrant.collection || DEFAULTS.qdrant.collection,\n apiKey: merged.qdrant.apiKey,\n };\n }\n\n let age: AgeConfig | null = null;\n if (tier === \"full\" && merged.age?.host) {\n if (!merged.age.user || !merged.age.password || !merged.age.database) {\n throw new ConfigError(\"Full tier requires age.user, age.password, and age.database\");\n }\n age = {\n host: merged.age.host,\n port: merged.age.port || DEFAULTS.age.port,\n user: merged.age.user,\n password: merged.age.password,\n database: merged.age.database,\n graph: merged.age.graph || DEFAULTS.age.graph,\n };\n }\n\n let embedding: EmbeddingConfig | null = null;\n if (tier !== \"lite\" && merged.embedding?.apiKey) {\n embedding = {\n apiKey: merged.embedding.apiKey,\n baseUrl: merged.embedding.baseUrl,\n model: merged.embedding.model || DEFAULTS.embedding.model,\n dimensions: merged.embedding.dimensions || DEFAULTS.embedding.dimensions,\n };\n }\n\n let extraction: ExtractionConfig | null = null;\n if (tier !== \"lite\" && merged.extraction?.apiKey) {\n extraction = {\n apiKey: merged.extraction.apiKey,\n baseUrl: merged.extraction.baseUrl,\n model: merged.extraction.model || DEFAULTS.extraction.model,\n enabled: merged.extraction.enabled ?? DEFAULTS.extraction.enabled,\n };\n }\n\n return {\n tier,\n port: merged.port || DEFAULTS.port,\n host: merged.host || DEFAULTS.host,\n auth: {\n token: merged.auth?.token,\n enabled: merged.auth?.enabled ?? DEFAULTS.auth.enabled,\n },\n sqlite: { path: sqlitePath },\n qdrant,\n age,\n embedding,\n extraction,\n agents: merged.agents || fileConfig?.agents || [],\n };\n}\n\n// ── defineConfig helper for config files ────────────────────────────────\n\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n// ── Config summary (redacted) for logging ───────────────────────────────\n\nexport function configSummary(config: ResolvedConfig): string {\n const lines: string[] = [\n `Tier: ${config.tier}`,\n `Port: ${config.port}`,\n `Host: ${config.host}`,\n `SQLite: ${config.sqlite.path}`,\n `Auth: ${config.auth.enabled ? \"enabled\" : \"disabled\"}`,\n ];\n\n if (config.qdrant) {\n lines.push(`Qdrant: ${config.qdrant.url} (collection: ${config.qdrant.collection})`);\n }\n if (config.age) {\n lines.push(`AGE: ${config.age.host}:${config.age.port}/${config.age.database}`);\n }\n if (config.embedding) {\n lines.push(`Embedding: ${config.embedding.model}${config.embedding.baseUrl ? ` via ${config.embedding.baseUrl}` : \"\"}`);\n }\n if (config.extraction) {\n lines.push(`Extraction: ${config.extraction.model}${config.extraction.enabled ? \"\" : \" (disabled)\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","// ── Custom Error Classes ────────────────────────────────────────────────\n\nexport class MemoryError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = \"MemoryError\";\n }\n}\n\nexport class ConfigError extends MemoryError {\n constructor(message: string) {\n super(message, \"CONFIG_ERROR\");\n this.name = \"ConfigError\";\n }\n}\n\nexport class StorageError extends MemoryError {\n constructor(message: string, public readonly layer: \"sqlite\" | \"qdrant\" | \"age\") {\n super(message, \"STORAGE_ERROR\");\n this.name = \"StorageError\";\n }\n}\n\nexport class ValidationError extends MemoryError {\n constructor(message: string, public readonly details?: unknown) {\n super(message, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends MemoryError {\n constructor(resource: string, id: string) {\n super(`${resource} not found: ${id}`, \"NOT_FOUND\");\n this.name = \"NotFoundError\";\n }\n}\n\nexport class AuthError extends MemoryError {\n constructor(message: string = \"Unauthorized\") {\n super(message, \"AUTH_ERROR\");\n this.name = \"AuthError\";\n }\n}\n","// ── Default Configuration Values ────────────────────────────────────────\n\nexport const DEFAULTS = {\n port: 7777,\n host: \"0.0.0.0\",\n sqlite: {\n path: \"~/.openclaw-memory/memory.sqlite\",\n },\n qdrant: {\n collection: \"openclaw_memories\",\n },\n age: {\n port: 5432,\n graph: \"agent_memory\",\n },\n embedding: {\n model: \"text-embedding-3-small\",\n dimensions: 1536,\n },\n extraction: {\n model: \"gpt-4o-mini\",\n enabled: true,\n },\n auth: {\n enabled: true,\n },\n} as const;\n"]}
|