@cortexmemory/cli 0.1.0 → 0.22.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/README.md +8 -0
- package/dist/commands/conversations.d.ts +1 -1
- package/dist/commands/conversations.d.ts.map +1 -1
- package/dist/commands/conversations.js +58 -28
- package/dist/commands/conversations.js.map +1 -1
- package/dist/commands/convex.d.ts +1 -1
- package/dist/commands/convex.d.ts.map +1 -1
- package/dist/commands/convex.js +237 -64
- package/dist/commands/convex.js.map +1 -1
- package/dist/commands/db.d.ts +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +511 -113
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts +8 -8
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +734 -513
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/facts.d.ts +1 -1
- package/dist/commands/facts.d.ts.map +1 -1
- package/dist/commands/facts.js +79 -49
- package/dist/commands/facts.js.map +1 -1
- package/dist/commands/init.d.ts +28 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +895 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/memory.d.ts +1 -1
- package/dist/commands/memory.d.ts.map +1 -1
- package/dist/commands/memory.js +84 -48
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/setup.d.ts +4 -5
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +613 -265
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/spaces.d.ts +1 -1
- package/dist/commands/spaces.d.ts.map +1 -1
- package/dist/commands/spaces.js +100 -43
- package/dist/commands/spaces.js.map +1 -1
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +314 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/users.d.ts +1 -1
- package/dist/commands/users.d.ts.map +1 -1
- package/dist/commands/users.js +152 -45
- package/dist/commands/users.js.map +1 -1
- package/dist/index.js +61 -19
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/__tests__/client.test.d.ts +5 -0
- package/dist/utils/__tests__/client.test.d.ts.map +1 -0
- package/dist/utils/__tests__/client.test.js +88 -0
- package/dist/utils/__tests__/client.test.js.map +1 -0
- package/dist/utils/__tests__/env-file.test.d.ts +7 -0
- package/dist/utils/__tests__/env-file.test.d.ts.map +1 -0
- package/dist/utils/__tests__/env-file.test.js +196 -0
- package/dist/utils/__tests__/env-file.test.js.map +1 -0
- package/dist/utils/__tests__/shell.test.d.ts +7 -0
- package/dist/utils/__tests__/shell.test.d.ts.map +1 -0
- package/dist/utils/__tests__/shell.test.js +89 -0
- package/dist/utils/__tests__/shell.test.js.map +1 -0
- package/dist/utils/client.d.ts +1 -0
- package/dist/utils/client.d.ts.map +1 -1
- package/dist/utils/client.js +7 -1
- package/dist/utils/client.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -39
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/deployment-selector.d.ts +50 -0
- package/dist/utils/deployment-selector.d.ts.map +1 -0
- package/dist/utils/deployment-selector.js +129 -0
- package/dist/utils/deployment-selector.js.map +1 -0
- package/dist/utils/env-file.d.ts +48 -0
- package/dist/utils/env-file.d.ts.map +1 -0
- package/dist/utils/env-file.js +152 -0
- package/dist/utils/env-file.js.map +1 -0
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +4 -0
- package/dist/utils/formatting.js.map +1 -1
- package/dist/utils/init/convex-setup.d.ts +30 -0
- package/dist/utils/init/convex-setup.d.ts.map +1 -0
- package/dist/utils/init/convex-setup.js +225 -0
- package/dist/utils/init/convex-setup.js.map +1 -0
- package/dist/utils/init/env-generator.d.ts +32 -0
- package/dist/utils/init/env-generator.d.ts.map +1 -0
- package/dist/utils/init/env-generator.js +210 -0
- package/dist/utils/init/env-generator.js.map +1 -0
- package/dist/utils/init/file-operations.d.ts +22 -0
- package/dist/utils/init/file-operations.d.ts.map +1 -0
- package/dist/utils/init/file-operations.js +211 -0
- package/dist/utils/init/file-operations.js.map +1 -0
- package/dist/utils/init/graph-setup.d.ts +35 -0
- package/dist/utils/init/graph-setup.d.ts.map +1 -0
- package/dist/utils/init/graph-setup.js +413 -0
- package/dist/utils/init/graph-setup.js.map +1 -0
- package/dist/utils/init/index.d.ts +11 -0
- package/dist/utils/init/index.d.ts.map +1 -0
- package/dist/utils/init/index.js +11 -0
- package/dist/utils/init/index.js.map +1 -0
- package/dist/utils/init/types.d.ts +73 -0
- package/dist/utils/init/types.d.ts.map +1 -0
- package/dist/utils/init/types.js +5 -0
- package/dist/utils/init/types.js.map +1 -0
- package/dist/utils/shell.d.ts +60 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +188 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +30 -19
- package/templates/basic/README.md +105 -0
- package/templates/basic/dev-runner.mjs +215 -0
- package/templates/basic/package-lock.json +1263 -0
- package/templates/basic/package.json +22 -0
- package/templates/basic/src/index.ts +85 -0
- package/templates/basic/tsconfig.json +17 -0
package/dist/commands/db.js
CHANGED
|
@@ -9,80 +9,155 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import ora from "ora";
|
|
11
11
|
import { withClient } from "../utils/client.js";
|
|
12
|
-
import { resolveConfig } from "../utils/config.js";
|
|
12
|
+
import { resolveConfig, loadConfig } from "../utils/config.js";
|
|
13
|
+
import { selectDeployment } from "../utils/deployment-selector.js";
|
|
13
14
|
import { formatOutput, printSuccess, printError, printWarning, printSection, formatTimestamp, formatBytes, } from "../utils/formatting.js";
|
|
14
|
-
import { validateFilePath, requireConfirmation
|
|
15
|
+
import { validateFilePath, requireConfirmation } from "../utils/validation.js";
|
|
15
16
|
import { writeFile, readFile } from "fs/promises";
|
|
16
17
|
import pc from "picocolors";
|
|
18
|
+
import prompts from "prompts";
|
|
19
|
+
const MAX_LIMIT = 1000;
|
|
17
20
|
/**
|
|
18
21
|
* Register database commands
|
|
19
22
|
*/
|
|
20
|
-
export function registerDbCommands(program,
|
|
23
|
+
export function registerDbCommands(program, _config) {
|
|
21
24
|
const db = program.command("db").description("Database-wide operations");
|
|
22
25
|
// db stats
|
|
23
26
|
db.command("stats")
|
|
24
27
|
.description("Show database statistics")
|
|
28
|
+
.option("-d, --deployment <name>", "Target deployment")
|
|
25
29
|
.option("-f, --format <format>", "Output format: table, json")
|
|
26
30
|
.action(async (options) => {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
31
|
+
const currentConfig = await loadConfig();
|
|
32
|
+
const selection = await selectDeployment(currentConfig, options, "view stats");
|
|
33
|
+
if (!selection)
|
|
34
|
+
return;
|
|
35
|
+
const { name: targetName, deployment } = selection;
|
|
36
|
+
const targetUrl = deployment.url;
|
|
37
|
+
const resolved = resolveConfig(currentConfig, { deployment: targetName });
|
|
29
38
|
const format = (options.format ?? resolved.format);
|
|
30
|
-
const spinner = ora("Loading database statistics...").start();
|
|
31
39
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
const spinner = ora(`Loading statistics for ${targetName}...`).start();
|
|
41
|
+
await withClient(currentConfig, { deployment: targetName }, async (client) => {
|
|
42
|
+
// Get deployment info
|
|
43
|
+
const info = {
|
|
44
|
+
url: targetUrl,
|
|
45
|
+
isLocal: targetUrl.includes("127.0.0.1") ||
|
|
46
|
+
targetUrl.includes("localhost"),
|
|
47
|
+
};
|
|
48
|
+
const rawClient = client.getClient();
|
|
49
|
+
// Get comprehensive counts from all tables using admin function
|
|
50
|
+
spinner.text = "Counting all tables...";
|
|
51
|
+
let tableCounts = {};
|
|
52
|
+
try {
|
|
53
|
+
tableCounts = await rawClient.query("admin:getAllCounts", {});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Fall back to individual counts if admin function not available
|
|
57
|
+
tableCounts = {
|
|
58
|
+
agents: 0,
|
|
59
|
+
contexts: 0,
|
|
60
|
+
conversations: 0,
|
|
61
|
+
facts: 0,
|
|
62
|
+
governanceEnforcement: 0,
|
|
63
|
+
governancePolicies: 0,
|
|
64
|
+
graphSyncQueue: 0,
|
|
65
|
+
immutable: 0,
|
|
66
|
+
memories: 0,
|
|
67
|
+
memorySpaces: 0,
|
|
68
|
+
mutable: 0,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Get user count from SDK (users may be managed separately)
|
|
72
|
+
let usersCount = 0;
|
|
73
|
+
try {
|
|
74
|
+
usersCount = await client.users.count();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Users API may not be available
|
|
78
|
+
}
|
|
79
|
+
// Count messages in conversations
|
|
80
|
+
spinner.text = "Counting messages...";
|
|
81
|
+
let totalMessages = 0;
|
|
82
|
+
try {
|
|
83
|
+
const convosResult = await client.conversations.list({
|
|
84
|
+
limit: MAX_LIMIT,
|
|
85
|
+
});
|
|
86
|
+
for (const convo of convosResult.conversations) {
|
|
87
|
+
totalMessages += convo.messageCount ?? 0;
|
|
52
88
|
}
|
|
53
89
|
}
|
|
90
|
+
catch {
|
|
91
|
+
// Skip if not available
|
|
92
|
+
}
|
|
54
93
|
spinner.stop();
|
|
55
94
|
const stats = {
|
|
56
|
-
memorySpaces:
|
|
57
|
-
conversations:
|
|
58
|
-
memories:
|
|
59
|
-
facts:
|
|
95
|
+
memorySpaces: tableCounts.memorySpaces ?? 0,
|
|
96
|
+
conversations: tableCounts.conversations ?? 0,
|
|
97
|
+
memories: tableCounts.memories ?? 0,
|
|
98
|
+
facts: tableCounts.facts ?? 0,
|
|
60
99
|
users: usersCount,
|
|
61
|
-
immutableRecords: 0,
|
|
62
|
-
mutableRecords: 0,
|
|
63
|
-
contexts: 0,
|
|
100
|
+
immutableRecords: tableCounts.immutable ?? 0,
|
|
101
|
+
mutableRecords: tableCounts.mutable ?? 0,
|
|
102
|
+
contexts: tableCounts.contexts ?? 0,
|
|
64
103
|
};
|
|
65
104
|
if (format === "json") {
|
|
66
|
-
console.log(formatOutput(
|
|
105
|
+
console.log(formatOutput({
|
|
106
|
+
...stats,
|
|
107
|
+
agents: tableCounts.agents ?? 0,
|
|
108
|
+
messages: totalMessages,
|
|
109
|
+
governancePolicies: tableCounts.governancePolicies ?? 0,
|
|
110
|
+
governanceEnforcement: tableCounts.governanceEnforcement ?? 0,
|
|
111
|
+
graphSyncQueue: tableCounts.graphSyncQueue ?? 0,
|
|
112
|
+
deployment: {
|
|
113
|
+
name: targetName,
|
|
114
|
+
url: info.url,
|
|
115
|
+
isLocal: info.isLocal,
|
|
116
|
+
},
|
|
117
|
+
}, "json"));
|
|
67
118
|
}
|
|
68
119
|
else {
|
|
69
120
|
console.log();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log(
|
|
121
|
+
console.log(pc.bold(`📊 Database Statistics: ${pc.cyan(targetName)}`));
|
|
122
|
+
console.log(pc.dim("─".repeat(45)));
|
|
123
|
+
console.log();
|
|
124
|
+
// Core entities
|
|
125
|
+
console.log(pc.bold(" Core Entities"));
|
|
126
|
+
console.log(` Memory Spaces: ${pc.yellow(String(stats.memorySpaces))}`);
|
|
127
|
+
console.log(` Users: ${pc.yellow(String(stats.users))}`);
|
|
128
|
+
console.log(` Agents: ${pc.yellow(String(tableCounts.agents ?? 0))}`);
|
|
129
|
+
console.log();
|
|
130
|
+
// Memory data
|
|
131
|
+
console.log(pc.bold(" Memory Data"));
|
|
132
|
+
console.log(` Memories: ${pc.yellow(String(stats.memories))}`);
|
|
133
|
+
console.log(` Facts: ${pc.yellow(String(stats.facts))}`);
|
|
134
|
+
console.log(` Contexts: ${pc.yellow(String(stats.contexts))}`);
|
|
135
|
+
console.log();
|
|
136
|
+
// Conversation data
|
|
137
|
+
console.log(pc.bold(" Conversations"));
|
|
138
|
+
console.log(` Conversations: ${pc.yellow(String(stats.conversations))}`);
|
|
139
|
+
console.log(` Messages: ${pc.yellow(String(totalMessages))}`);
|
|
140
|
+
console.log();
|
|
141
|
+
// Shared stores
|
|
142
|
+
console.log(pc.bold(" Shared Stores"));
|
|
143
|
+
console.log(` Immutable: ${pc.yellow(String(stats.immutableRecords))}`);
|
|
144
|
+
console.log(` Mutable: ${pc.yellow(String(stats.mutableRecords))}`);
|
|
145
|
+
console.log();
|
|
146
|
+
// System tables
|
|
147
|
+
console.log(pc.bold(" System Tables"));
|
|
148
|
+
console.log(` Gov. Policies: ${pc.yellow(String(tableCounts.governancePolicies ?? 0))}`);
|
|
149
|
+
console.log(` Gov. Logs: ${pc.yellow(String(tableCounts.governanceEnforcement ?? 0))}`);
|
|
150
|
+
console.log(` Graph Sync Queue: ${pc.yellow(String(tableCounts.graphSyncQueue ?? 0))}`);
|
|
151
|
+
console.log();
|
|
152
|
+
// Deployment info
|
|
153
|
+
console.log(pc.bold(" Deployment"));
|
|
154
|
+
console.log(` URL: ${pc.dim(info.url)}`);
|
|
155
|
+
console.log(` Mode: ${info.isLocal ? pc.green("Local") : pc.blue("Cloud")}`);
|
|
156
|
+
console.log();
|
|
81
157
|
}
|
|
82
158
|
});
|
|
83
159
|
}
|
|
84
160
|
catch (error) {
|
|
85
|
-
spinner.stop();
|
|
86
161
|
printError(error instanceof Error ? error.message : "Failed to load statistics");
|
|
87
162
|
process.exit(1);
|
|
88
163
|
}
|
|
@@ -90,67 +165,371 @@ export function registerDbCommands(program, config) {
|
|
|
90
165
|
// db clear
|
|
91
166
|
db.command("clear")
|
|
92
167
|
.description("Clear entire database (DANGEROUS!)")
|
|
93
|
-
.option("--
|
|
168
|
+
.option("-d, --deployment <name>", "Target deployment")
|
|
169
|
+
.option("-y, --yes", "Skip confirmation prompt", false)
|
|
94
170
|
.action(async (options) => {
|
|
95
|
-
const
|
|
171
|
+
const currentConfig = await loadConfig();
|
|
172
|
+
console.log();
|
|
173
|
+
console.log(pc.red(pc.bold("⚠️ DANGER: Clear Database")));
|
|
174
|
+
const selection = await selectDeployment(currentConfig, options, "clear");
|
|
175
|
+
if (!selection)
|
|
176
|
+
return;
|
|
177
|
+
const { name: targetName, deployment } = selection;
|
|
178
|
+
const targetUrl = deployment.url;
|
|
96
179
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
111
|
-
console.log();
|
|
112
|
-
console.log(pc.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
180
|
+
console.log();
|
|
181
|
+
console.log("This will permanently delete:");
|
|
182
|
+
console.log(" • All memory spaces and memories");
|
|
183
|
+
console.log(" • All conversations and messages");
|
|
184
|
+
console.log(" • All facts and user profiles");
|
|
185
|
+
// Check if graph sync is enabled (same logic as Cortex.create())
|
|
186
|
+
const neo4jUri = process.env.NEO4J_URI;
|
|
187
|
+
const memgraphUri = process.env.MEMGRAPH_URI;
|
|
188
|
+
const graphSyncEnabled = process.env.CORTEX_GRAPH_SYNC === "true" ||
|
|
189
|
+
!!(neo4jUri || memgraphUri);
|
|
190
|
+
// Debug: Show env var detection
|
|
191
|
+
if (process.env.DEBUG || program.opts().debug) {
|
|
192
|
+
console.log(pc.dim(` [DEBUG] CORTEX_GRAPH_SYNC=${process.env.CORTEX_GRAPH_SYNC}`));
|
|
193
|
+
console.log(pc.dim(` [DEBUG] NEO4J_URI=${neo4jUri ? "set" : "unset"}`));
|
|
194
|
+
console.log(pc.dim(` [DEBUG] MEMGRAPH_URI=${memgraphUri ? "set" : "unset"}`));
|
|
195
|
+
console.log(pc.dim(` [DEBUG] graphSyncEnabled=${graphSyncEnabled}`));
|
|
196
|
+
}
|
|
197
|
+
if (graphSyncEnabled) {
|
|
198
|
+
const dbType = neo4jUri ? "Neo4j" : "Memgraph";
|
|
199
|
+
console.log(` • All graph database nodes and relationships (${dbType})`);
|
|
200
|
+
}
|
|
201
|
+
console.log();
|
|
202
|
+
// Simple y/N confirmation
|
|
203
|
+
if (!options.yes) {
|
|
204
|
+
const confirmResponse = await prompts({
|
|
205
|
+
type: "confirm",
|
|
206
|
+
name: "confirmed",
|
|
207
|
+
message: `Clear ALL data from ${pc.red(targetName)}?`,
|
|
208
|
+
initial: false,
|
|
209
|
+
});
|
|
210
|
+
if (!confirmResponse.confirmed) {
|
|
116
211
|
printWarning("Operation cancelled");
|
|
117
212
|
return;
|
|
118
213
|
}
|
|
119
214
|
}
|
|
120
|
-
const spinner = ora(
|
|
121
|
-
await withClient(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
215
|
+
const spinner = ora(`Clearing ${targetName}...`).start();
|
|
216
|
+
await withClient(currentConfig, { deployment: targetName }, async (client) => {
|
|
217
|
+
const deleted = {
|
|
218
|
+
agents: 0,
|
|
219
|
+
contexts: 0,
|
|
220
|
+
conversations: 0,
|
|
221
|
+
messages: 0,
|
|
222
|
+
facts: 0,
|
|
223
|
+
memories: 0,
|
|
224
|
+
memorySpaces: 0,
|
|
225
|
+
immutable: 0,
|
|
226
|
+
mutable: 0,
|
|
227
|
+
users: 0,
|
|
228
|
+
governancePolicies: 0,
|
|
229
|
+
governanceEnforcement: 0,
|
|
230
|
+
graphSyncQueue: 0,
|
|
231
|
+
};
|
|
232
|
+
// Get raw Convex client for direct table access via admin functions
|
|
233
|
+
const rawClient = client.getClient();
|
|
234
|
+
// Helper to clear a table using the admin:clearTable mutation
|
|
235
|
+
const clearTableDirect = async (tableName, counter) => {
|
|
236
|
+
let hasMore = true;
|
|
237
|
+
while (hasMore) {
|
|
238
|
+
spinner.text = `Clearing ${tableName}... (${deleted[counter]} deleted)`;
|
|
239
|
+
try {
|
|
240
|
+
const result = await rawClient.mutation("admin:clearTable", { table: tableName, limit: MAX_LIMIT });
|
|
241
|
+
deleted[counter] += result.deleted;
|
|
242
|
+
hasMore = result.hasMore;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
hasMore = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
// 1. Clear agents (using SDK for proper unregister)
|
|
250
|
+
let hasMoreAgents = true;
|
|
251
|
+
while (hasMoreAgents) {
|
|
252
|
+
spinner.text = `Clearing agents... (${deleted.agents} deleted)`;
|
|
253
|
+
try {
|
|
254
|
+
const agents = await client.agents.list({ limit: MAX_LIMIT });
|
|
255
|
+
if (agents.length === 0) {
|
|
256
|
+
hasMoreAgents = false;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
for (const agent of agents) {
|
|
260
|
+
try {
|
|
261
|
+
await client.agents.unregister(agent.id, { cascade: false });
|
|
262
|
+
deleted.agents++;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Continue on error
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (agents.length < MAX_LIMIT) {
|
|
269
|
+
hasMoreAgents = false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// Fall back to direct table clear if SDK fails
|
|
274
|
+
await clearTableDirect("agents", "agents");
|
|
275
|
+
hasMoreAgents = false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// 2. Clear contexts (using SDK for cascade)
|
|
279
|
+
let hasMoreContexts = true;
|
|
280
|
+
while (hasMoreContexts) {
|
|
281
|
+
spinner.text = `Clearing contexts... (${deleted.contexts} deleted)`;
|
|
126
282
|
try {
|
|
127
|
-
await client.
|
|
128
|
-
|
|
283
|
+
const contexts = await client.contexts.list({ limit: MAX_LIMIT });
|
|
284
|
+
if (contexts.length === 0) {
|
|
285
|
+
hasMoreContexts = false;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
for (const ctx of contexts) {
|
|
289
|
+
try {
|
|
290
|
+
await client.contexts.delete(ctx.contextId, {
|
|
291
|
+
cascadeChildren: true,
|
|
292
|
+
});
|
|
293
|
+
deleted.contexts++;
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Continue on error
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (contexts.length < MAX_LIMIT) {
|
|
300
|
+
hasMoreContexts = false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
await clearTableDirect("contexts", "contexts");
|
|
305
|
+
hasMoreContexts = false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// 3. Clear conversations (count messages)
|
|
309
|
+
let hasMoreConvos = true;
|
|
310
|
+
while (hasMoreConvos) {
|
|
311
|
+
spinner.text = `Clearing conversations... (${deleted.conversations} deleted, ${deleted.messages} messages)`;
|
|
312
|
+
try {
|
|
313
|
+
const convosResult = await client.conversations.list({
|
|
314
|
+
limit: MAX_LIMIT,
|
|
315
|
+
});
|
|
316
|
+
const convos = convosResult.conversations;
|
|
317
|
+
if (convos.length === 0) {
|
|
318
|
+
hasMoreConvos = false;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
for (const convo of convos) {
|
|
322
|
+
try {
|
|
323
|
+
deleted.messages += convo.messageCount || 0;
|
|
324
|
+
await client.conversations.delete(convo.conversationId);
|
|
325
|
+
deleted.conversations++;
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// Continue on error
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (convos.length < MAX_LIMIT) {
|
|
332
|
+
hasMoreConvos = false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
await clearTableDirect("conversations", "conversations");
|
|
337
|
+
hasMoreConvos = false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// 4. Clear facts (direct table clear)
|
|
341
|
+
await clearTableDirect("facts", "facts");
|
|
342
|
+
// 5. Clear memories (direct table clear)
|
|
343
|
+
await clearTableDirect("memories", "memories");
|
|
344
|
+
// 6. Clear memory spaces (using raw client for invalid IDs)
|
|
345
|
+
let hasMoreSpaces = true;
|
|
346
|
+
while (hasMoreSpaces) {
|
|
347
|
+
spinner.text = `Clearing memorySpaces... (${deleted.memorySpaces} deleted)`;
|
|
348
|
+
try {
|
|
349
|
+
const spacesResult = await client.memorySpaces.list({
|
|
350
|
+
limit: MAX_LIMIT,
|
|
129
351
|
});
|
|
130
|
-
|
|
352
|
+
const spaces = spacesResult.spaces;
|
|
353
|
+
if (spaces.length === 0) {
|
|
354
|
+
hasMoreSpaces = false;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
for (const space of spaces) {
|
|
358
|
+
try {
|
|
359
|
+
await rawClient.mutation("memorySpaces:deleteSpace", { memorySpaceId: space.memorySpaceId, cascade: true });
|
|
360
|
+
deleted.memorySpaces++;
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
// Continue on error
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (spaces.length < MAX_LIMIT) {
|
|
367
|
+
hasMoreSpaces = false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
await clearTableDirect("memorySpaces", "memorySpaces");
|
|
372
|
+
hasMoreSpaces = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// 7. Clear immutable (using SDK)
|
|
376
|
+
let hasMoreImmutable = true;
|
|
377
|
+
while (hasMoreImmutable) {
|
|
378
|
+
spinner.text = `Clearing immutable... (${deleted.immutable} deleted)`;
|
|
379
|
+
try {
|
|
380
|
+
const records = await client.immutable.list({ limit: MAX_LIMIT });
|
|
381
|
+
if (records.length === 0) {
|
|
382
|
+
hasMoreImmutable = false;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
for (const record of records) {
|
|
386
|
+
try {
|
|
387
|
+
await client.immutable.purge(record.type, record.id);
|
|
388
|
+
deleted.immutable++;
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
// Continue on error
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (records.length < MAX_LIMIT) {
|
|
395
|
+
hasMoreImmutable = false;
|
|
396
|
+
}
|
|
131
397
|
}
|
|
132
398
|
catch {
|
|
133
|
-
|
|
399
|
+
await clearTableDirect("immutable", "immutable");
|
|
400
|
+
hasMoreImmutable = false;
|
|
134
401
|
}
|
|
135
402
|
}
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
403
|
+
// 8. Clear mutable (direct table clear)
|
|
404
|
+
await clearTableDirect("mutable", "mutable");
|
|
405
|
+
// 9. Clear users (using SDK for cascade - users table is virtual/SDK-managed)
|
|
406
|
+
let hasMoreUsers = true;
|
|
407
|
+
while (hasMoreUsers) {
|
|
408
|
+
spinner.text = `Clearing users... (${deleted.users} deleted)`;
|
|
140
409
|
try {
|
|
141
|
-
await client.users.
|
|
142
|
-
|
|
410
|
+
const usersResult = await client.users.list({ limit: MAX_LIMIT });
|
|
411
|
+
const users = usersResult.users;
|
|
412
|
+
if (users.length === 0) {
|
|
413
|
+
hasMoreUsers = false;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
for (const user of users) {
|
|
417
|
+
try {
|
|
418
|
+
await client.users.delete(user.id, { cascade: true });
|
|
419
|
+
deleted.users++;
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Continue on error
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (users.length < MAX_LIMIT) {
|
|
426
|
+
hasMoreUsers = false;
|
|
427
|
+
}
|
|
143
428
|
}
|
|
144
429
|
catch {
|
|
145
|
-
|
|
430
|
+
hasMoreUsers = false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// 10. Clear governance policies
|
|
434
|
+
await clearTableDirect("governancePolicies", "governancePolicies");
|
|
435
|
+
// 11. Clear governance enforcement logs
|
|
436
|
+
await clearTableDirect("governanceEnforcement", "governanceEnforcement");
|
|
437
|
+
// 12. Clear graph sync queue
|
|
438
|
+
await clearTableDirect("graphSyncQueue", "graphSyncQueue");
|
|
439
|
+
// 13. Clear graph database if graph sync is enabled
|
|
440
|
+
// Check both explicit flag and auto-detection (same logic as Cortex.create())
|
|
441
|
+
const neo4jUri = process.env.NEO4J_URI;
|
|
442
|
+
const memgraphUri = process.env.MEMGRAPH_URI;
|
|
443
|
+
const graphSyncEnabled = process.env.CORTEX_GRAPH_SYNC === "true" ||
|
|
444
|
+
!!(neo4jUri || memgraphUri);
|
|
445
|
+
if (graphSyncEnabled) {
|
|
446
|
+
spinner.text = "Clearing graph database...";
|
|
447
|
+
let graphCleared = false;
|
|
448
|
+
try {
|
|
449
|
+
if (neo4jUri || memgraphUri) {
|
|
450
|
+
// Dynamically import neo4j-driver only when needed
|
|
451
|
+
const neo4j = await import("neo4j-driver");
|
|
452
|
+
// Determine which database to connect to
|
|
453
|
+
const uri = neo4jUri || memgraphUri;
|
|
454
|
+
const username = neo4jUri
|
|
455
|
+
? process.env.NEO4J_USERNAME || "neo4j"
|
|
456
|
+
: process.env.MEMGRAPH_USERNAME || "memgraph";
|
|
457
|
+
const password = neo4jUri
|
|
458
|
+
? process.env.NEO4J_PASSWORD || ""
|
|
459
|
+
: process.env.MEMGRAPH_PASSWORD || "";
|
|
460
|
+
// Connect to graph database
|
|
461
|
+
const driver = neo4j.default.driver(uri, neo4j.default.auth.basic(username, password));
|
|
462
|
+
// Verify connectivity
|
|
463
|
+
await driver.verifyConnectivity();
|
|
464
|
+
// Create session and clear all data
|
|
465
|
+
const session = driver.session();
|
|
466
|
+
try {
|
|
467
|
+
// DETACH DELETE removes nodes and all their relationships
|
|
468
|
+
// Works for both Neo4j and Memgraph
|
|
469
|
+
await session.run("MATCH (n) DETACH DELETE n");
|
|
470
|
+
graphCleared = true;
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
await session.close();
|
|
474
|
+
}
|
|
475
|
+
await driver.close();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
// Log warning but don't fail the entire operation
|
|
480
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
481
|
+
spinner.warn(pc.yellow(`Graph database clear failed: ${errorMsg}`));
|
|
482
|
+
}
|
|
483
|
+
if (graphCleared) {
|
|
484
|
+
// Only show success if we actually cleared
|
|
485
|
+
deleted.graphSyncQueue = -1; // Use as flag to indicate graph was cleared
|
|
146
486
|
}
|
|
147
487
|
}
|
|
148
488
|
spinner.stop();
|
|
149
|
-
printSuccess(
|
|
489
|
+
printSuccess(`Database "${targetName}" cleared`);
|
|
490
|
+
console.log();
|
|
150
491
|
printSection("Deletion Summary", {
|
|
151
|
-
|
|
152
|
-
|
|
492
|
+
Database: targetName,
|
|
493
|
+
URL: targetUrl,
|
|
153
494
|
});
|
|
495
|
+
console.log();
|
|
496
|
+
// Show counts with categories
|
|
497
|
+
const coreEntities = {
|
|
498
|
+
Agents: deleted.agents,
|
|
499
|
+
Users: deleted.users,
|
|
500
|
+
"Memory Spaces": deleted.memorySpaces,
|
|
501
|
+
};
|
|
502
|
+
const memoryData = {
|
|
503
|
+
Memories: deleted.memories,
|
|
504
|
+
Facts: deleted.facts,
|
|
505
|
+
Contexts: deleted.contexts,
|
|
506
|
+
};
|
|
507
|
+
const conversationData = {
|
|
508
|
+
Conversations: deleted.conversations,
|
|
509
|
+
Messages: deleted.messages,
|
|
510
|
+
};
|
|
511
|
+
const sharedStores = {
|
|
512
|
+
Immutable: deleted.immutable,
|
|
513
|
+
Mutable: deleted.mutable,
|
|
514
|
+
};
|
|
515
|
+
const systemTables = {
|
|
516
|
+
"Governance Policies": deleted.governancePolicies,
|
|
517
|
+
"Governance Logs": deleted.governanceEnforcement,
|
|
518
|
+
"Graph Sync Queue": deleted.graphSyncQueue >= 0 ? deleted.graphSyncQueue : 0,
|
|
519
|
+
};
|
|
520
|
+
printSection("Core Entities", coreEntities);
|
|
521
|
+
printSection("Memory Data", memoryData);
|
|
522
|
+
printSection("Conversations", conversationData);
|
|
523
|
+
printSection("Shared Stores", sharedStores);
|
|
524
|
+
printSection("System Tables", systemTables);
|
|
525
|
+
// Show graph database status if it was cleared
|
|
526
|
+
if (deleted.graphSyncQueue === -1) {
|
|
527
|
+
const dbType = process.env.NEO4J_URI ? "Neo4j" : "Memgraph";
|
|
528
|
+
console.log();
|
|
529
|
+
printSection("Graph Database", {
|
|
530
|
+
[dbType]: pc.green("Cleared ✓"),
|
|
531
|
+
});
|
|
532
|
+
}
|
|
154
533
|
});
|
|
155
534
|
}
|
|
156
535
|
catch (error) {
|
|
@@ -161,39 +540,47 @@ export function registerDbCommands(program, config) {
|
|
|
161
540
|
// db backup
|
|
162
541
|
db.command("backup")
|
|
163
542
|
.description("Backup database to a file")
|
|
543
|
+
.option("-d, --deployment <name>", "Target deployment")
|
|
164
544
|
.option("-o, --output <file>", "Output file path", "cortex-backup.json")
|
|
165
545
|
.option("--include-all", "Include all data (may be large)", false)
|
|
166
546
|
.action(async (options) => {
|
|
167
|
-
const
|
|
168
|
-
const
|
|
547
|
+
const currentConfig = await loadConfig();
|
|
548
|
+
const selection = await selectDeployment(currentConfig, options, "backup");
|
|
549
|
+
if (!selection)
|
|
550
|
+
return;
|
|
551
|
+
const { name: targetName, deployment } = selection;
|
|
552
|
+
const targetUrl = deployment.url;
|
|
169
553
|
try {
|
|
170
554
|
validateFilePath(options.output);
|
|
171
|
-
|
|
555
|
+
const spinner = ora(`Creating backup of ${targetName}...`).start();
|
|
556
|
+
await withClient(currentConfig, { deployment: targetName }, async (client) => {
|
|
172
557
|
const backup = {
|
|
173
558
|
version: "1.0",
|
|
174
559
|
timestamp: Date.now(),
|
|
175
|
-
deployment:
|
|
560
|
+
deployment: targetUrl,
|
|
176
561
|
data: {},
|
|
177
562
|
};
|
|
178
|
-
// Backup memory spaces
|
|
563
|
+
// Backup memory spaces (paginate if needed)
|
|
179
564
|
spinner.text = "Backing up memory spaces...";
|
|
180
|
-
|
|
181
|
-
limit:
|
|
565
|
+
const spacesResult = await client.memorySpaces.list({
|
|
566
|
+
limit: MAX_LIMIT,
|
|
182
567
|
});
|
|
183
|
-
|
|
568
|
+
backup.data.memorySpaces = spacesResult.spaces;
|
|
569
|
+
// Backup users (paginate if needed)
|
|
184
570
|
spinner.text = "Backing up users...";
|
|
185
|
-
|
|
571
|
+
const usersResult = await client.users.list({ limit: MAX_LIMIT });
|
|
572
|
+
backup.data.users = usersResult.users;
|
|
186
573
|
if (options.includeAll) {
|
|
187
574
|
// Backup conversations
|
|
188
575
|
spinner.text = "Backing up conversations...";
|
|
189
576
|
const spaces = backup.data.memorySpaces;
|
|
190
577
|
backup.data.conversations = [];
|
|
191
578
|
for (const space of spaces) {
|
|
192
|
-
const
|
|
579
|
+
const convsResult = await client.conversations.list({
|
|
193
580
|
memorySpaceId: space.memorySpaceId,
|
|
194
|
-
limit:
|
|
581
|
+
limit: MAX_LIMIT,
|
|
195
582
|
});
|
|
196
|
-
backup.data.conversations.push(...
|
|
583
|
+
backup.data.conversations.push(...convsResult.conversations);
|
|
197
584
|
}
|
|
198
585
|
// Backup memories
|
|
199
586
|
spinner.text = "Backing up memories...";
|
|
@@ -201,7 +588,7 @@ export function registerDbCommands(program, config) {
|
|
|
201
588
|
for (const space of spaces) {
|
|
202
589
|
const memories = await client.memory.list({
|
|
203
590
|
memorySpaceId: space.memorySpaceId,
|
|
204
|
-
limit:
|
|
591
|
+
limit: MAX_LIMIT,
|
|
205
592
|
});
|
|
206
593
|
backup.data.memories.push(...memories);
|
|
207
594
|
}
|
|
@@ -211,7 +598,7 @@ export function registerDbCommands(program, config) {
|
|
|
211
598
|
for (const space of spaces) {
|
|
212
599
|
const facts = await client.facts.list({
|
|
213
600
|
memorySpaceId: space.memorySpaceId,
|
|
214
|
-
limit:
|
|
601
|
+
limit: MAX_LIMIT,
|
|
215
602
|
});
|
|
216
603
|
backup.data.facts.push(...facts);
|
|
217
604
|
}
|
|
@@ -241,7 +628,6 @@ export function registerDbCommands(program, config) {
|
|
|
241
628
|
});
|
|
242
629
|
}
|
|
243
630
|
catch (error) {
|
|
244
|
-
spinner.stop();
|
|
245
631
|
printError(error instanceof Error ? error.message : "Backup failed");
|
|
246
632
|
process.exit(1);
|
|
247
633
|
}
|
|
@@ -249,14 +635,14 @@ export function registerDbCommands(program, config) {
|
|
|
249
635
|
// db restore
|
|
250
636
|
db.command("restore")
|
|
251
637
|
.description("Restore database from a backup file")
|
|
638
|
+
.option("-d, --deployment <name>", "Target deployment")
|
|
252
639
|
.requiredOption("-i, --input <file>", "Backup file path")
|
|
253
640
|
.option("--dry-run", "Preview what would be restored", false)
|
|
254
641
|
.option("-y, --yes", "Skip confirmation", false)
|
|
255
642
|
.action(async (options) => {
|
|
256
|
-
const globalOpts = program.opts();
|
|
257
643
|
try {
|
|
258
644
|
validateFilePath(options.input);
|
|
259
|
-
// Read backup file
|
|
645
|
+
// Read backup file first to show info before selecting target
|
|
260
646
|
const content = await readFile(options.input, "utf-8");
|
|
261
647
|
const backup = JSON.parse(content);
|
|
262
648
|
// Validate backup format
|
|
@@ -279,15 +665,21 @@ export function registerDbCommands(program, config) {
|
|
|
279
665
|
printWarning("DRY RUN - No data will be restored");
|
|
280
666
|
return;
|
|
281
667
|
}
|
|
668
|
+
// Select target database
|
|
669
|
+
const currentConfig = await loadConfig();
|
|
670
|
+
const selection = await selectDeployment(currentConfig, options, "restore to");
|
|
671
|
+
if (!selection)
|
|
672
|
+
return;
|
|
673
|
+
const { name: targetName } = selection;
|
|
282
674
|
if (!options.yes) {
|
|
283
|
-
const confirmed = await requireConfirmation(
|
|
675
|
+
const confirmed = await requireConfirmation(`Restore this backup to ${targetName}? Existing data may be overwritten.`, currentConfig);
|
|
284
676
|
if (!confirmed) {
|
|
285
677
|
printWarning("Restore cancelled");
|
|
286
678
|
return;
|
|
287
679
|
}
|
|
288
680
|
}
|
|
289
|
-
const spinner = ora(
|
|
290
|
-
await withClient(
|
|
681
|
+
const spinner = ora(`Restoring backup to ${targetName}...`).start();
|
|
682
|
+
await withClient(currentConfig, { deployment: targetName }, async (client) => {
|
|
291
683
|
let restored = {
|
|
292
684
|
spaces: 0,
|
|
293
685
|
users: 0,
|
|
@@ -343,17 +735,24 @@ export function registerDbCommands(program, config) {
|
|
|
343
735
|
// db export
|
|
344
736
|
db.command("export")
|
|
345
737
|
.description("Export all data to JSON")
|
|
738
|
+
.option("-d, --deployment <name>", "Target deployment")
|
|
346
739
|
.option("-o, --output <file>", "Output file path", "cortex-export.json")
|
|
347
740
|
.action(async (options) => {
|
|
348
|
-
const
|
|
349
|
-
const
|
|
741
|
+
const currentConfig = await loadConfig();
|
|
742
|
+
const selection = await selectDeployment(currentConfig, options, "export");
|
|
743
|
+
if (!selection)
|
|
744
|
+
return;
|
|
745
|
+
const { name: targetName, deployment } = selection;
|
|
746
|
+
const targetUrl = deployment.url;
|
|
350
747
|
try {
|
|
351
748
|
validateFilePath(options.output);
|
|
352
|
-
|
|
749
|
+
const spinner = ora(`Exporting data from ${targetName}...`).start();
|
|
750
|
+
await withClient(currentConfig, { deployment: targetName }, async (client) => {
|
|
353
751
|
const exportData = {
|
|
354
752
|
exportedAt: Date.now(),
|
|
355
|
-
|
|
356
|
-
|
|
753
|
+
deployment: { name: targetName, url: targetUrl },
|
|
754
|
+
memorySpaces: await client.memorySpaces.list({ limit: MAX_LIMIT }),
|
|
755
|
+
users: await client.users.list({ limit: MAX_LIMIT }),
|
|
357
756
|
};
|
|
358
757
|
const content = JSON.stringify(exportData, null, 2);
|
|
359
758
|
await writeFile(options.output, content, "utf-8");
|
|
@@ -362,7 +761,6 @@ export function registerDbCommands(program, config) {
|
|
|
362
761
|
});
|
|
363
762
|
}
|
|
364
763
|
catch (error) {
|
|
365
|
-
spinner.stop();
|
|
366
764
|
printError(error instanceof Error ? error.message : "Export failed");
|
|
367
765
|
process.exit(1);
|
|
368
766
|
}
|