@lotargo/memory_plugin 1.5.3 → 1.6.1

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +406 -352
  3. package/mcp-server/admin/auth.js +13 -4
  4. package/mcp-server/admin/snapshot.js +24 -7
  5. package/mcp-server/boot.js +43 -0
  6. package/mcp-server/cli/direct_commands.js +334 -313
  7. package/mcp-server/cli/handlers/engine_actions.js +41 -0
  8. package/mcp-server/cli/handlers/storage_actions.js +58 -0
  9. package/mcp-server/cli/secret_input.js +44 -0
  10. package/mcp-server/cli/ui.js +564 -565
  11. package/mcp-server/cli.js +356 -324
  12. package/mcp-server/cli_boot.js +37 -0
  13. package/mcp-server/config/auth_store.js +74 -16
  14. package/mcp-server/config/config_manager.js +4 -0
  15. package/mcp-server/db/database.js +33 -14
  16. package/mcp-server/db/sync_queue.js +9 -19
  17. package/mcp-server/index.js +112 -42
  18. package/mcp-server/ingest/normalizer.js +116 -29
  19. package/mcp-server/ingest/pipeline.js +94 -6
  20. package/mcp-server/logger.js +49 -0
  21. package/mcp-server/memory.js +6 -9
  22. package/mcp-server/ml/gpu_monitor.js +169 -166
  23. package/mcp-server/ml/model_manager.js +17 -4
  24. package/mcp-server/preinstall.js +23 -2
  25. package/mcp-server/retrieval/retriever.js +35 -15
  26. package/mcp-server/security/path_guard.js +67 -0
  27. package/mcp-server/setup.js +10 -2
  28. package/mcp-server/storage/blob_store.js +15 -2
  29. package/mcp-server/tools/core/memory_core.js +393 -0
  30. package/mcp-server/tools/helpers.js +59 -39
  31. package/mcp-server/tools/memory_tools.js +123 -516
  32. package/mcp-server/tools/rag_tools.js +49 -1
  33. package/opencode-plugin/index.js +94 -397
  34. package/package.json +13 -4
  35. package/skills/using-memory/SKILL.md +7 -2
@@ -1,313 +1,334 @@
1
- import { basename } from "node:path";
2
- import {
3
- readMemory,
4
- writeMemory,
5
- storeFilePath,
6
- canonicalPath,
7
- listProjectStores,
8
- migrateStoreTitles,
9
- GLOBAL_KEY,
10
- projectKey,
11
- } from "../memory.js";
12
- import { factBody } from "../fact_format.js";
13
-
14
- export async function handleDirectCommands(cliArgs) {
15
- if (cliArgs[0] === "link") {
16
- const dirIdx = cliArgs.indexOf("--dir");
17
- const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
18
- const remIdx = cliArgs.indexOf("--remote");
19
- const remote = remIdx >= 0 && cliArgs[remIdx + 1] ? cliArgs[remIdx + 1] : null;
20
-
21
- try {
22
- const { getDatabase } = await import("../db/database.js");
23
- const { resolveProjectIdentity, upsertIdentity, registerAlias, normalizeRemoteUrl } = await import("../identity.js");
24
- const db = await getDatabase();
25
-
26
- const identity = await resolveProjectIdentity(dir);
27
- if (!identity && !remote) {
28
- console.error("Error: No Git repository detected and no remote URL specified.");
29
- process.exit(1);
30
- }
31
-
32
- let key = identity ? identity.key : `git:${normalizeRemoteUrl(remote)}`;
33
- let name = identity ? identity.name : basename(dir) || "unbound";
34
- let primaryRemote = remote ? normalizeRemoteUrl(remote) : (identity ? identity.primaryRemote : null);
35
-
36
- await upsertIdentity(db, { key, name, primaryRemote });
37
-
38
- const aliases = [];
39
- if (primaryRemote) {
40
- aliases.push({ alias: `remote:${primaryRemote}`, kind: "remote" });
41
- }
42
- aliases.push({ alias: `path:${canonicalPath(dir)}`, kind: "path" });
43
- aliases.push({ alias: `basename:${name}`, kind: "basename" });
44
-
45
- for (const a of aliases) {
46
- await registerAlias(db, { alias: a.alias, identityKey: key, kind: a.kind });
47
- }
48
-
49
- console.log(`\n [OK] Linked directory "${dir}" successfully to identity key: ${key}\n`);
50
- } catch (err) {
51
- console.error(`Error: ${err.message}`);
52
- process.exit(1);
53
- }
54
- return true;
55
- }
56
-
57
- if (cliArgs[0] === "unlink") {
58
- const dirIdx = cliArgs.indexOf("--dir");
59
- const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
60
- const purge = cliArgs.includes("--purge");
61
-
62
- try {
63
- const { getDatabase } = await import("../db/database.js");
64
- const { unregisterAlias, removeIdentity, resolveProjectIdentity } = await import("../identity.js");
65
- const db = await getDatabase();
66
-
67
- const alias = `path:${canonicalPath(dir)}`;
68
- await unregisterAlias(db, alias);
69
-
70
- if (purge) {
71
- const identity = await resolveProjectIdentity(dir);
72
- if (identity) {
73
- await removeIdentity(db, identity.key);
74
- }
75
- }
76
-
77
- console.log(`\n [OK] Unlinked directory "${dir}" successfully.\n`);
78
- } catch (err) {
79
- console.error(`Error: ${err.message}`);
80
- process.exit(1);
81
- }
82
- return true;
83
- }
84
-
85
- if (cliArgs[0] === "relink") {
86
- const dirIdx = cliArgs.indexOf("--dir");
87
- const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
88
- const remIdx = cliArgs.indexOf("--remote");
89
- const remote = remIdx >= 0 && cliArgs[remIdx + 1] ? cliArgs[remIdx + 1] : null;
90
-
91
- if (!remote) {
92
- console.error("Error: --remote parameter is required for relink.");
93
- process.exit(1);
94
- }
95
-
96
- try {
97
- const { getDatabase } = await import("../db/database.js");
98
- const { resolveProjectIdentity, upsertIdentity, removeIdentity, normalizeRemoteUrl } = await import("../identity.js");
99
- const db = await getDatabase();
100
-
101
- const sourceIdentity = await resolveProjectIdentity(dir);
102
- if (!sourceIdentity) {
103
- console.error("Error: Source project identity not detected.");
104
- process.exit(1);
105
- }
106
-
107
- const targetKey = `git:${normalizeRemoteUrl(remote)}`;
108
- const sourceKey = sourceIdentity.key;
109
-
110
- if (sourceKey === targetKey) {
111
- console.log("Source and target identities are already identical.");
112
- return true;
113
- }
114
-
115
- const sourceFacts = await readMemory(sourceKey);
116
- const targetFacts = await readMemory(targetKey);
117
- const seen = new Set(targetFacts.map((e) => factBody(e).toLowerCase().trim()));
118
-
119
- let mergedCount = 0;
120
- for (const f of sourceFacts) {
121
- const body = factBody(f).toLowerCase().trim();
122
- if (!seen.has(body)) {
123
- seen.add(body);
124
- targetFacts.push(f);
125
- mergedCount++;
126
- }
127
- }
128
-
129
- await writeMemory(targetKey, targetFacts);
130
- await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
131
- await upsertIdentity(db, { key: targetKey, name: sourceIdentity.name, primaryRemote: normalizeRemoteUrl(remote) });
132
- await removeIdentity(db, sourceKey);
133
-
134
- try {
135
- const sourceFp = storeFilePath(sourceKey);
136
- const { existsSync } = await import("node:fs");
137
- if (existsSync(sourceFp)) {
138
- const { unlink } = await import("fs/promises");
139
- await unlink(sourceFp);
140
- }
141
- } catch (e) {}
142
-
143
- console.log(`\n [OK] Relinked and merged ${mergedCount} facts successfully!\n`);
144
- } catch (err) {
145
- console.error(`Error: ${err.message}`);
146
- process.exit(1);
147
- }
148
- return true;
149
- }
150
-
151
- if (cliArgs[0] === "identity") {
152
- const dirIdx = cliArgs.indexOf("--dir");
153
- const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
154
-
155
- try {
156
- const { resolveProjectIdentity } = await import("../identity.js");
157
- const identity = await resolveProjectIdentity(dir);
158
- console.log(`\n PROJECT IDENTITY`);
159
- if (identity) {
160
- console.log(` - Key: ${identity.key}`);
161
- console.log(` - Name: ${identity.name}`);
162
- console.log(` - Primary Remote: ${identity.primaryRemote || "none"}`);
163
- console.log(` - Toplevel Directory: ${identity.toplevel}`);
164
- } else {
165
- console.log(" No Git repository detected.");
166
- }
167
- } catch (err) {
168
- console.error(`Error: ${err.message}`);
169
- process.exit(1);
170
- }
171
- return true;
172
- }
173
-
174
- if (cliArgs[0] === "migrate_titles") {
175
- const keyIdx = cliArgs.indexOf("--key");
176
- const key = keyIdx >= 0 && cliArgs[keyIdx + 1] ? cliArgs[keyIdx + 1] : null;
177
-
178
- try {
179
- const targets = [];
180
- if (key) {
181
- targets.push(key);
182
- } else {
183
- const gitKey = await projectKey(process.cwd(), null);
184
- if (gitKey) targets.push(gitKey);
185
- targets.push(GLOBAL_KEY);
186
- const stores = await listProjectStores();
187
- for (const s of stores) {
188
- if (!targets.includes(s.key)) targets.push(s.key);
189
- }
190
- }
191
-
192
- let total = 0;
193
- for (const k of targets) {
194
- const res = await migrateStoreTitles(k);
195
- if (res.ok) {
196
- total += res.changed;
197
- console.log(` [OK] ${k}: ${res.changed} fact(s) titled`);
198
- } else {
199
- console.log(` [SKIP] ${k}: ${res.reason}`);
200
- }
201
- }
202
- console.log(`\n Done. ${total} fact(s) updated across ${targets.length} store(s).\n`);
203
- } catch (err) {
204
- console.error(`Error: ${err.message}`);
205
- process.exit(1);
206
- }
207
- return true;
208
- }
209
-
210
- if (cliArgs.includes("--enable-prompt") || cliArgs.includes("enable-prompt")) {
211
- const { enableGlobalPrompt } = await import("../prompt_manager.js");
212
- const results = await enableGlobalPrompt();
213
- console.log("\n [OK] Global prompt enabled across client configurations:\n");
214
- results.forEach((r) => console.log(` - ${r.name}: ${r.filePath} (${r.status})`));
215
- console.log("");
216
- return true;
217
- }
218
-
219
- if (cliArgs.includes("--disable-prompt") || cliArgs.includes("disable-prompt")) {
220
- const { disableGlobalPrompt } = await import("../prompt_manager.js");
221
- const results = await disableGlobalPrompt();
222
- console.log("\n [OK] Global prompt disabled across client configurations:\n");
223
- results.forEach((r) => console.log(` - ${r.name}: ${r.filePath} (${r.status})`));
224
- console.log("");
225
- return true;
226
- }
227
-
228
- if (cliArgs.includes("login")) {
229
- console.log("\n [CLOUD] Starting Turso cloud authorization...");
230
- const loginIdx = cliArgs.indexOf("login");
231
- const loginArgs = cliArgs.slice(loginIdx + 1);
232
- const flagValue = (name) => {
233
- const i = loginArgs.indexOf(name);
234
- return i >= 0 && loginArgs[i + 1] ? loginArgs[i + 1] : null;
235
- };
236
- const { loginToCloud, loginWithApiToken, loginWithDatabaseToken, loginFromEnv } = await import("../admin/auth.js");
237
- try {
238
- let secrets;
239
- if (loginArgs.includes("--from-env")) {
240
- const res = await loginFromEnv({ persist: false });
241
- if (!res.ok) throw new Error(res.reason);
242
- secrets = res.secrets;
243
- } else if (loginArgs.includes("--db-url") && loginArgs.includes("--db-token")) {
244
- secrets = await loginWithDatabaseToken({
245
- dbUrl: flagValue("--db-url"),
246
- token: flagValue("--db-token"),
247
- username: flagValue("--username") || "",
248
- org: flagValue("--org") || "",
249
- db: flagValue("--database") || "",
250
- validate: !loginArgs.includes("--no-validate"),
251
- });
252
- } else if (loginArgs.includes("--token") || loginArgs.includes("--api-token") || loginArgs.includes("--api-key")) {
253
- const token = flagValue("--token") || flagValue("--api-token") || flagValue("--api-key");
254
- if (!token) throw new Error("Missing token value. Usage: memory_plugin login --api-token <TOKEN> [--org <ORG>] [--database <DB>]");
255
- secrets = await loginWithApiToken({
256
- token,
257
- org: flagValue("--org") || null,
258
- databaseName: flagValue("--database") || null,
259
- });
260
- } else {
261
- secrets = await loginToCloud();
262
- }
263
- console.log(`\n \x1b[32m[OK] Successfully signed in to the cloud! Connected to endpoint: ${secrets.dbUrl}\x1b[0m\n`);
264
- } catch (e) {
265
- console.error(`\n \x1b[31m[ERROR] Authorization failed: ${e.message}\x1b[0m\n`);
266
- process.exit(1);
267
- }
268
- return true;
269
- }
270
-
271
- if (cliArgs.includes("logout")) {
272
- const { logoutFromCloud, clearApiKey } = await import("../admin/auth.js");
273
- if (cliArgs.includes("--api-key")) {
274
- const res = clearApiKey();
275
- if (res.removed) {
276
- console.log(
277
- res.keptDbSession
278
- ? " \x1b[32m[OK] API token removed. The resolved database session is kept and stays authorized.\x1b[0m\n"
279
- : " \x1b[32m[OK] API token removed. Encrypted secrets purged.\x1b[0m\n"
280
- );
281
- } else {
282
- console.log(" [*] No stored API token to remove.\x1b[0m\n");
283
- }
284
- return true;
285
- }
286
- console.log("\n [CLOUD] Signing out of the cloud...");
287
- const deleted = logoutFromCloud();
288
- if (deleted) {
289
- console.log(" \x1b[32m[OK] You have been signed out. Encrypted secrets removed. Mode reverted to only-local.\x1b[0m\n");
290
- } else {
291
- console.log(" [*] Mode reverted to only-local. No session tokens were found.\x1b[0m\n");
292
- }
293
- return true;
294
- }
295
-
296
- if (cliArgs.includes("auth-status") || cliArgs.includes("auth_status") || cliArgs.includes("auth")) {
297
- const { getAuthStatus } = await import("../admin/auth.js");
298
- const st = getAuthStatus();
299
- console.log("\n [CLOUD] Authentication status:");
300
- console.log(` Source: ${st.source}`);
301
- console.log(` Authorized: ${st.authorized ? "YES" : "no"}`);
302
- console.log(` API Key: ${st.hasApiKey ? "SET" : "not set"}`);
303
- console.log(` Endpoint: ${st.dbUrl || "(none)"}`);
304
- console.log(` Username: ${st.username || "(unknown)"}`);
305
- console.log(` Organization: ${st.org || "(unknown)"}`);
306
- console.log(` Database: ${st.database || "(unknown)"}`);
307
- console.log(` Mode: ${st.mode}`);
308
- console.log("");
309
- return true;
310
- }
311
-
312
- return false;
313
- }
1
+ import { basename } from "node:path";
2
+ import {
3
+ readMemory,
4
+ writeMemory,
5
+ storeFilePath,
6
+ canonicalPath,
7
+ listProjectStores,
8
+ migrateStoreTitles,
9
+ GLOBAL_KEY,
10
+ projectKey,
11
+ } from "../memory.js";
12
+ import { factBody } from "../fact_format.js";
13
+
14
+ export async function handleDirectCommands(cliArgs) {
15
+ if (cliArgs[0] === "link") {
16
+ const dirIdx = cliArgs.indexOf("--dir");
17
+ const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
18
+ const remIdx = cliArgs.indexOf("--remote");
19
+ const remote = remIdx >= 0 && cliArgs[remIdx + 1] ? cliArgs[remIdx + 1] : null;
20
+
21
+ try {
22
+ const { getDatabase } = await import("../db/database.js");
23
+ const { resolveProjectIdentity, upsertIdentity, registerAlias, normalizeRemoteUrl } = await import("../identity.js");
24
+ const db = await getDatabase();
25
+
26
+ const identity = await resolveProjectIdentity(dir);
27
+ if (!identity && !remote) {
28
+ console.error("Error: No Git repository detected and no remote URL specified.");
29
+ process.exit(1);
30
+ }
31
+
32
+ let key = identity ? identity.key : `git:${normalizeRemoteUrl(remote)}`;
33
+ let name = identity ? identity.name : basename(dir) || "unbound";
34
+ let primaryRemote = remote ? normalizeRemoteUrl(remote) : (identity ? identity.primaryRemote : null);
35
+
36
+ await upsertIdentity(db, { key, name, primaryRemote });
37
+
38
+ const aliases = [];
39
+ if (primaryRemote) {
40
+ aliases.push({ alias: `remote:${primaryRemote}`, kind: "remote" });
41
+ }
42
+ aliases.push({ alias: `path:${canonicalPath(dir)}`, kind: "path" });
43
+ aliases.push({ alias: `basename:${name}`, kind: "basename" });
44
+
45
+ for (const a of aliases) {
46
+ await registerAlias(db, { alias: a.alias, identityKey: key, kind: a.kind });
47
+ }
48
+
49
+ console.log(`\n [OK] Linked directory "${dir}" successfully to identity key: ${key}\n`);
50
+ } catch (err) {
51
+ console.error(`Error: ${err.message}`);
52
+ process.exit(1);
53
+ }
54
+ return true;
55
+ }
56
+
57
+ if (cliArgs[0] === "unlink") {
58
+ const dirIdx = cliArgs.indexOf("--dir");
59
+ const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
60
+ const purge = cliArgs.includes("--purge");
61
+
62
+ try {
63
+ const { getDatabase } = await import("../db/database.js");
64
+ const { unregisterAlias, removeIdentity, resolveProjectIdentity } = await import("../identity.js");
65
+ const db = await getDatabase();
66
+
67
+ const alias = `path:${canonicalPath(dir)}`;
68
+ await unregisterAlias(db, alias);
69
+
70
+ if (purge) {
71
+ const identity = await resolveProjectIdentity(dir);
72
+ if (identity) {
73
+ await removeIdentity(db, identity.key);
74
+ }
75
+ }
76
+
77
+ console.log(`\n [OK] Unlinked directory "${dir}" successfully.\n`);
78
+ } catch (err) {
79
+ console.error(`Error: ${err.message}`);
80
+ process.exit(1);
81
+ }
82
+ return true;
83
+ }
84
+
85
+ if (cliArgs[0] === "relink") {
86
+ const dirIdx = cliArgs.indexOf("--dir");
87
+ const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
88
+ const remIdx = cliArgs.indexOf("--remote");
89
+ const remote = remIdx >= 0 && cliArgs[remIdx + 1] ? cliArgs[remIdx + 1] : null;
90
+
91
+ if (!remote) {
92
+ console.error("Error: --remote parameter is required for relink.");
93
+ process.exit(1);
94
+ }
95
+
96
+ try {
97
+ const { getDatabase } = await import("../db/database.js");
98
+ const { resolveProjectIdentity, upsertIdentity, removeIdentity, normalizeRemoteUrl } = await import("../identity.js");
99
+ const db = await getDatabase();
100
+
101
+ const sourceIdentity = await resolveProjectIdentity(dir);
102
+ if (!sourceIdentity) {
103
+ console.error("Error: Source project identity not detected.");
104
+ process.exit(1);
105
+ }
106
+
107
+ const targetKey = `git:${normalizeRemoteUrl(remote)}`;
108
+ const sourceKey = sourceIdentity.key;
109
+
110
+ if (sourceKey === targetKey) {
111
+ console.log("Source and target identities are already identical.");
112
+ return true;
113
+ }
114
+
115
+ const sourceFacts = await readMemory(sourceKey);
116
+ const targetFacts = await readMemory(targetKey);
117
+ const seen = new Set(targetFacts.map((e) => factBody(e).toLowerCase().trim()));
118
+
119
+ let mergedCount = 0;
120
+ for (const f of sourceFacts) {
121
+ const body = factBody(f).toLowerCase().trim();
122
+ if (!seen.has(body)) {
123
+ seen.add(body);
124
+ targetFacts.push(f);
125
+ mergedCount++;
126
+ }
127
+ }
128
+
129
+ await writeMemory(targetKey, targetFacts);
130
+ await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
131
+ await upsertIdentity(db, { key: targetKey, name: sourceIdentity.name, primaryRemote: normalizeRemoteUrl(remote) });
132
+ await removeIdentity(db, sourceKey);
133
+
134
+ try {
135
+ const sourceFp = storeFilePath(sourceKey);
136
+ const { existsSync } = await import("node:fs");
137
+ if (existsSync(sourceFp)) {
138
+ const { unlink } = await import("fs/promises");
139
+ await unlink(sourceFp);
140
+ }
141
+ } catch (e) {}
142
+
143
+ console.log(`\n [OK] Relinked and merged ${mergedCount} facts successfully!\n`);
144
+ } catch (err) {
145
+ console.error(`Error: ${err.message}`);
146
+ process.exit(1);
147
+ }
148
+ return true;
149
+ }
150
+
151
+ if (cliArgs[0] === "identity") {
152
+ const dirIdx = cliArgs.indexOf("--dir");
153
+ const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
154
+
155
+ try {
156
+ const { resolveProjectIdentity } = await import("../identity.js");
157
+ const identity = await resolveProjectIdentity(dir);
158
+ console.log(`\n PROJECT IDENTITY`);
159
+ if (identity) {
160
+ console.log(` - Key: ${identity.key}`);
161
+ console.log(` - Name: ${identity.name}`);
162
+ console.log(` - Primary Remote: ${identity.primaryRemote || "none"}`);
163
+ console.log(` - Toplevel Directory: ${identity.toplevel}`);
164
+ } else {
165
+ console.log(" No Git repository detected.");
166
+ }
167
+ } catch (err) {
168
+ console.error(`Error: ${err.message}`);
169
+ process.exit(1);
170
+ }
171
+ return true;
172
+ }
173
+
174
+ if (cliArgs[0] === "migrate_titles") {
175
+ const keyIdx = cliArgs.indexOf("--key");
176
+ const key = keyIdx >= 0 && cliArgs[keyIdx + 1] ? cliArgs[keyIdx + 1] : null;
177
+
178
+ try {
179
+ const targets = [];
180
+ if (key) {
181
+ targets.push(key);
182
+ } else {
183
+ const gitKey = await projectKey(process.cwd(), null);
184
+ if (gitKey) targets.push(gitKey);
185
+ targets.push(GLOBAL_KEY);
186
+ const stores = await listProjectStores();
187
+ for (const s of stores) {
188
+ if (!targets.includes(s.key)) targets.push(s.key);
189
+ }
190
+ }
191
+
192
+ let total = 0;
193
+ for (const k of targets) {
194
+ const res = await migrateStoreTitles(k);
195
+ if (res.ok) {
196
+ total += res.changed;
197
+ console.log(` [OK] ${k}: ${res.changed} fact(s) titled`);
198
+ } else {
199
+ console.log(` [SKIP] ${k}: ${res.reason}`);
200
+ }
201
+ }
202
+ console.log(`\n Done. ${total} fact(s) updated across ${targets.length} store(s).\n`);
203
+ } catch (err) {
204
+ console.error(`Error: ${err.message}`);
205
+ process.exit(1);
206
+ }
207
+ return true;
208
+ }
209
+
210
+ if (cliArgs.includes("--enable-prompt") || cliArgs.includes("enable-prompt")) {
211
+ const { enableGlobalPrompt } = await import("../prompt_manager.js");
212
+ const results = await enableGlobalPrompt();
213
+ console.log("\n [OK] Global prompt enabled across client configurations:\n");
214
+ results.forEach((r) => console.log(` - ${r.name}: ${r.filePath} (${r.status})`));
215
+ console.log("");
216
+ return true;
217
+ }
218
+
219
+ if (cliArgs.includes("--disable-prompt") || cliArgs.includes("disable-prompt")) {
220
+ const { disableGlobalPrompt } = await import("../prompt_manager.js");
221
+ const results = await disableGlobalPrompt();
222
+ console.log("\n [OK] Global prompt disabled across client configurations:\n");
223
+ results.forEach((r) => console.log(` - ${r.name}: ${r.filePath} (${r.status})`));
224
+ console.log("");
225
+ return true;
226
+ }
227
+
228
+ if (cliArgs.includes("login")) {
229
+ console.log("\n [CLOUD] Starting Turso cloud authorization...");
230
+ const loginIdx = cliArgs.indexOf("login");
231
+ const loginArgs = cliArgs.slice(loginIdx + 1);
232
+ const flagValue = (name) => {
233
+ const i = loginArgs.indexOf(name);
234
+ return i >= 0 && loginArgs[i + 1] ? loginArgs[i + 1] : null;
235
+ };
236
+ const { loginToCloud, loginWithApiToken, loginWithDatabaseToken, loginFromEnv } = await import("../admin/auth.js");
237
+ try {
238
+ let secrets;
239
+ if (loginArgs.includes("--from-env")) {
240
+ const res = await loginFromEnv({ persist: false });
241
+ if (!res.ok) throw new Error(res.reason);
242
+ secrets = res.secrets;
243
+ } else if (loginArgs.includes("--db-url") || loginArgs.includes("--db-token") || process.env.TURSO_DB_TOKEN) {
244
+ const { resolveSecret } = await import("./secret_input.js");
245
+ const dbToken = await resolveSecret({
246
+ argvValue: flagValue("--db-token"),
247
+ envKeys: ["TURSO_DB_TOKEN", "TURSO_TOKEN"],
248
+ promptLabel: "Turso database token",
249
+ });
250
+ if (!dbToken) throw new Error("Missing database token. Set TURSO_DB_TOKEN or provide it at the prompt.");
251
+ secrets = await loginWithDatabaseToken({
252
+ dbUrl: flagValue("--db-url") || process.env.TURSO_DB_URL || process.env.TURSO_URL,
253
+ token: dbToken,
254
+ username: flagValue("--username") || "",
255
+ org: flagValue("--org") || "",
256
+ db: flagValue("--database") || "",
257
+ validate: !loginArgs.includes("--no-validate"),
258
+ });
259
+ } else if (
260
+ loginArgs.includes("--token") ||
261
+ loginArgs.includes("--api-token") ||
262
+ loginArgs.includes("--api-key") ||
263
+ process.env.TURSO_API_TOKEN
264
+ ) {
265
+ const { resolveSecret } = await import("./secret_input.js");
266
+ const token = await resolveSecret({
267
+ argvValue: flagValue("--token") || flagValue("--api-token") || flagValue("--api-key"),
268
+ envKeys: ["TURSO_API_TOKEN"],
269
+ promptLabel: "Turso API token",
270
+ });
271
+ if (!token) {
272
+ throw new Error(
273
+ "Missing token value. Set TURSO_API_TOKEN, or run: memory-cli login --api-token (you will be prompted)"
274
+ );
275
+ }
276
+ secrets = await loginWithApiToken({
277
+ token,
278
+ org: flagValue("--org") || null,
279
+ databaseName: flagValue("--database") || null,
280
+ });
281
+ } else {
282
+ secrets = await loginToCloud();
283
+ }
284
+ console.log(`\n \x1b[32m[OK] Successfully signed in to the cloud! Connected to endpoint: ${secrets.dbUrl}\x1b[0m\n`);
285
+ } catch (e) {
286
+ console.error(`\n \x1b[31m[ERROR] Authorization failed: ${e.message}\x1b[0m\n`);
287
+ process.exit(1);
288
+ }
289
+ return true;
290
+ }
291
+
292
+ if (cliArgs.includes("logout")) {
293
+ const { logoutFromCloud, clearApiKey } = await import("../admin/auth.js");
294
+ if (cliArgs.includes("--api-key")) {
295
+ const res = clearApiKey();
296
+ if (res.removed) {
297
+ console.log(
298
+ res.keptDbSession
299
+ ? " \x1b[32m[OK] API token removed. The resolved database session is kept and stays authorized.\x1b[0m\n"
300
+ : " \x1b[32m[OK] API token removed. Encrypted secrets purged.\x1b[0m\n"
301
+ );
302
+ } else {
303
+ console.log(" [*] No stored API token to remove.\x1b[0m\n");
304
+ }
305
+ return true;
306
+ }
307
+ console.log("\n [CLOUD] Signing out of the cloud...");
308
+ const deleted = logoutFromCloud();
309
+ if (deleted) {
310
+ console.log(" \x1b[32m[OK] You have been signed out. Encrypted secrets removed. Mode reverted to only-local.\x1b[0m\n");
311
+ } else {
312
+ console.log(" [*] Mode reverted to only-local. No session tokens were found.\x1b[0m\n");
313
+ }
314
+ return true;
315
+ }
316
+
317
+ if (cliArgs.includes("auth-status") || cliArgs.includes("auth_status") || cliArgs.includes("auth")) {
318
+ const { getAuthStatus } = await import("../admin/auth.js");
319
+ const st = getAuthStatus();
320
+ console.log("\n [CLOUD] Authentication status:");
321
+ console.log(` Source: ${st.source}`);
322
+ console.log(` Authorized: ${st.authorized ? "YES" : "no"}`);
323
+ console.log(` API Key: ${st.hasApiKey ? "SET" : "not set"}`);
324
+ console.log(` Endpoint: ${st.dbUrl || "(none)"}`);
325
+ console.log(` Username: ${st.username || "(unknown)"}`);
326
+ console.log(` Organization: ${st.org || "(unknown)"}`);
327
+ console.log(` Database: ${st.database || "(unknown)"}`);
328
+ console.log(` Mode: ${st.mode}`);
329
+ console.log("");
330
+ return true;
331
+ }
332
+
333
+ return false;
334
+ }