@xqli02/mneme 0.1.5 → 0.1.7
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/bin/mneme.mjs +13 -1
- package/package.json +1 -1
- package/src/commands/compact.mjs +1 -1
- package/src/commands/init.mjs +12 -48
- package/src/commands/server.mjs +144 -0
- package/src/dolt.mjs +108 -0
package/bin/mneme.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* mneme CLI — Three-layer memory architecture for AI coding agents.
|
|
5
5
|
*
|
|
6
6
|
* Unified entry point that routes to:
|
|
7
|
-
* 1. mneme's own commands (init, doctor, status, compact, facts, propose, review, auto)
|
|
7
|
+
* 1. mneme's own commands (init, doctor, status, compact, facts, propose, review, auto, server)
|
|
8
8
|
* 2. opencode commands (run, web, serve, etc.) — default fallback
|
|
9
9
|
* 3. bd/beads commands (ready, list, create, close, etc.)
|
|
10
10
|
*
|
|
@@ -56,6 +56,7 @@ const MNEME_COMMANDS = new Set([
|
|
|
56
56
|
"propose",
|
|
57
57
|
"review",
|
|
58
58
|
"auto",
|
|
59
|
+
"server",
|
|
59
60
|
"version",
|
|
60
61
|
"--version",
|
|
61
62
|
"-v",
|
|
@@ -118,6 +119,11 @@ switch (command) {
|
|
|
118
119
|
await auto(args.slice(1));
|
|
119
120
|
break;
|
|
120
121
|
}
|
|
122
|
+
case "server": {
|
|
123
|
+
const { server } = await import("../src/commands/server.mjs");
|
|
124
|
+
await server(args.slice(1));
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
121
127
|
case "version":
|
|
122
128
|
case "--version":
|
|
123
129
|
case "-v":
|
|
@@ -146,6 +152,12 @@ Usage:
|
|
|
146
152
|
mneme status Show three-layer memory dashboard
|
|
147
153
|
mneme compact Pre-compaction persistence check
|
|
148
154
|
|
|
155
|
+
${bold("Dolt server:")}
|
|
156
|
+
mneme server start Start the dolt server
|
|
157
|
+
mneme server stop Stop the dolt server
|
|
158
|
+
mneme server status Show server status (port, PID, data-dir)
|
|
159
|
+
mneme server restart Restart the dolt server
|
|
160
|
+
|
|
149
161
|
${bold("Task management (beads):")}
|
|
150
162
|
mneme ready Show tasks with no blockers
|
|
151
163
|
mneme list [--status=STATUS] List tasks
|
package/package.json
CHANGED
package/src/commands/compact.mjs
CHANGED
package/src/commands/init.mjs
CHANGED
|
@@ -236,47 +236,23 @@ function installBd() {
|
|
|
236
236
|
|
|
237
237
|
// ── Dolt server + bd init ───────────────────────────────────────────────────
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
* Shared dolt data directory. All projects store their databases here,
|
|
241
|
-
* isolated by database name (e.g. beads_projectA, beads_projectB).
|
|
242
|
-
* One dolt server on port 3307 serves all projects on this machine.
|
|
243
|
-
*/
|
|
244
|
-
const DOLT_DATA_DIR = process.env.MNEME_DOLT_DATA_DIR
|
|
245
|
-
|| join(process.env.HOME, ".dolt", "databases");
|
|
246
|
-
|
|
247
|
-
const DOLT_PORT = parseInt(process.env.MNEME_DOLT_PORT || "3307", 10);
|
|
239
|
+
import { DOLT_DATA_DIR, DOLT_PORT, isPortOpen, findDoltProcess, startDoltServer, killDoltProcess } from "../dolt.mjs";
|
|
248
240
|
|
|
249
241
|
function ensureDoltServer() {
|
|
250
|
-
if
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
// Check if port is already in use (bash /dev/tcp returns 0 if open, 1 if refused)
|
|
255
|
-
const portInUse = run(`bash -c 'echo > /dev/tcp/127.0.0.1/${DOLT_PORT}' 2>&1`) !== null;
|
|
256
|
-
|
|
257
|
-
if (portInUse) {
|
|
258
|
-
// Port is occupied — check if it's our dolt server with the right data-dir
|
|
259
|
-
const psOutput = run(`ps aux 2>/dev/null`) ?? "";
|
|
260
|
-
const doltLines = psOutput.split("\n").filter((line) =>
|
|
261
|
-
line.includes("dolt") && line.includes("sql-server") && line.includes(`${DOLT_PORT}`)
|
|
262
|
-
&& !line.includes("grep")
|
|
263
|
-
);
|
|
264
|
-
// Prefer the actual dolt binary process over a bash wrapper
|
|
265
|
-
const doltProc = doltLines.find((l) => !l.includes("bash -c") && /\bdolt\s+sql-server\b/.test(l)) || doltLines[0];
|
|
242
|
+
// Check if port is already in use
|
|
243
|
+
if (isPortOpen()) {
|
|
244
|
+
const info = findDoltProcess();
|
|
266
245
|
|
|
267
|
-
if (
|
|
246
|
+
if (info && info.dataDir === DOLT_DATA_DIR) {
|
|
268
247
|
// Same data-dir, already running — nothing to do
|
|
269
248
|
log.ok(`dolt server already running ${color.dim(`(port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})`)}`);
|
|
270
249
|
return true;
|
|
271
250
|
}
|
|
272
251
|
|
|
273
|
-
if (
|
|
252
|
+
if (info) {
|
|
274
253
|
// Dolt running but with a different data-dir — kill and restart
|
|
275
254
|
log.warn(`dolt server on port ${DOLT_PORT} uses a different data-dir, restarting...`);
|
|
276
|
-
|
|
277
|
-
const pid = doltProc.trim().split(/\s+/)[1];
|
|
278
|
-
if (pid) run(`kill ${pid} 2>/dev/null`);
|
|
279
|
-
run("sleep 1");
|
|
255
|
+
killDoltProcess();
|
|
280
256
|
} else {
|
|
281
257
|
// Port occupied by something else entirely
|
|
282
258
|
log.fail(`Port ${DOLT_PORT} is in use by a non-dolt process. Set MNEME_DOLT_PORT to use a different port.`);
|
|
@@ -285,24 +261,12 @@ function ensureDoltServer() {
|
|
|
285
261
|
}
|
|
286
262
|
|
|
287
263
|
log.info(`Starting dolt server (port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})...`);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
run(
|
|
292
|
-
`nohup dolt sql-server --host 127.0.0.1 --port ${DOLT_PORT} --data-dir "${DOLT_DATA_DIR}" > "${logFile}" 2>&1 &`,
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Wait for server to be ready (up to 10s)
|
|
296
|
-
for (let i = 0; i < 10; i++) {
|
|
297
|
-
run("sleep 1");
|
|
298
|
-
const alive = run(`bash -c 'echo > /dev/tcp/127.0.0.1/${DOLT_PORT}' 2>&1`) !== null;
|
|
299
|
-
if (alive) {
|
|
300
|
-
log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
264
|
+
if (startDoltServer()) {
|
|
265
|
+
log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
266
|
+
return true;
|
|
303
267
|
}
|
|
304
268
|
|
|
305
|
-
log.fail(`dolt server failed to start. Check ${
|
|
269
|
+
log.fail(`dolt server failed to start. Check ${DOLT_DATA_DIR}/server.log`);
|
|
306
270
|
return false;
|
|
307
271
|
}
|
|
308
272
|
|
|
@@ -392,7 +356,7 @@ ${color.bold("===============================")}
|
|
|
392
356
|
|
|
393
357
|
${color.bold("Next steps:")}
|
|
394
358
|
mneme # Start coding with AI agent
|
|
395
|
-
|
|
359
|
+
mneme ready # Check available tasks
|
|
396
360
|
mneme doctor # Verify everything is healthy
|
|
397
361
|
`);
|
|
398
362
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mneme server — Manage the dolt SQL server.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* mneme server start Start the dolt server
|
|
6
|
+
* mneme server stop Stop the dolt server
|
|
7
|
+
* mneme server status Show server status
|
|
8
|
+
* mneme server restart Restart the dolt server
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { DOLT_DATA_DIR, DOLT_PORT, isPortOpen, findDoltProcess, startDoltServer, killDoltProcess } from "../dolt.mjs";
|
|
12
|
+
import { has, log, color } from "../utils.mjs";
|
|
13
|
+
|
|
14
|
+
function showStatus() {
|
|
15
|
+
if (!has("dolt")) {
|
|
16
|
+
log.fail("dolt is not installed");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const open = isPortOpen();
|
|
21
|
+
if (!open) {
|
|
22
|
+
log.info(`dolt server is ${color.red("stopped")} ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const info = findDoltProcess();
|
|
27
|
+
if (info) {
|
|
28
|
+
const dirMatch = info.dataDir === DOLT_DATA_DIR;
|
|
29
|
+
const dirStatus = dirMatch
|
|
30
|
+
? color.green(info.dataDir)
|
|
31
|
+
: `${color.red(info.dataDir)} (expected: ${DOLT_DATA_DIR})`;
|
|
32
|
+
|
|
33
|
+
console.log(`
|
|
34
|
+
${color.bold("dolt server")} — ${color.green("running")}
|
|
35
|
+
Port: ${DOLT_PORT}
|
|
36
|
+
PID: ${info.pid}
|
|
37
|
+
Data dir: ${dirStatus}
|
|
38
|
+
`);
|
|
39
|
+
|
|
40
|
+
if (!dirMatch) {
|
|
41
|
+
log.warn("Data dir mismatch. Run 'mneme server restart' to fix.");
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
log.info(`Port ${DOLT_PORT} is open but no dolt process found — may be a different service.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function doStart() {
|
|
51
|
+
if (!has("dolt")) {
|
|
52
|
+
log.fail("dolt is not installed. Run 'mneme init' or install manually.");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isPortOpen()) {
|
|
57
|
+
const info = findDoltProcess();
|
|
58
|
+
if (info && info.dataDir === DOLT_DATA_DIR) {
|
|
59
|
+
log.ok(`dolt server already running ${color.dim(`(port ${DOLT_PORT}, PID ${info.pid})`)}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (info) {
|
|
63
|
+
log.warn(`dolt server on port ${DOLT_PORT} uses different data-dir (${info.dataDir}), restarting...`);
|
|
64
|
+
killDoltProcess();
|
|
65
|
+
} else {
|
|
66
|
+
log.fail(`Port ${DOLT_PORT} is in use by a non-dolt process. Set MNEME_DOLT_PORT to use a different port.`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
log.info(`Starting dolt server (port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})...`);
|
|
72
|
+
if (startDoltServer()) {
|
|
73
|
+
log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
74
|
+
} else {
|
|
75
|
+
log.fail(`Failed to start dolt server. Check ${DOLT_DATA_DIR}/server.log`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function doStop() {
|
|
81
|
+
if (!isPortOpen()) {
|
|
82
|
+
log.info("dolt server is not running.");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const info = findDoltProcess();
|
|
87
|
+
if (!info) {
|
|
88
|
+
log.warn(`Port ${DOLT_PORT} is open but no dolt process found. Cannot stop.`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
log.info(`Stopping dolt server (PID ${info.pid})...`);
|
|
93
|
+
if (killDoltProcess()) {
|
|
94
|
+
log.ok("dolt server stopped.");
|
|
95
|
+
} else {
|
|
96
|
+
log.fail("Failed to stop dolt server. Try: kill " + info.pid);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function doRestart() {
|
|
101
|
+
if (isPortOpen()) {
|
|
102
|
+
const info = findDoltProcess();
|
|
103
|
+
if (info) {
|
|
104
|
+
log.info(`Stopping dolt server (PID ${info.pid})...`);
|
|
105
|
+
killDoltProcess();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
doStart();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function server(args = []) {
|
|
113
|
+
const sub = args[0];
|
|
114
|
+
|
|
115
|
+
switch (sub) {
|
|
116
|
+
case "start":
|
|
117
|
+
doStart();
|
|
118
|
+
break;
|
|
119
|
+
case "stop":
|
|
120
|
+
doStop();
|
|
121
|
+
break;
|
|
122
|
+
case "status":
|
|
123
|
+
showStatus();
|
|
124
|
+
break;
|
|
125
|
+
case "restart":
|
|
126
|
+
doRestart();
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
console.log(`
|
|
130
|
+
${color.bold("mneme server")} — Manage the dolt SQL server
|
|
131
|
+
|
|
132
|
+
Usage:
|
|
133
|
+
mneme server start Start the dolt server
|
|
134
|
+
mneme server stop Stop the dolt server
|
|
135
|
+
mneme server status Show server status
|
|
136
|
+
mneme server restart Restart the dolt server
|
|
137
|
+
|
|
138
|
+
Environment:
|
|
139
|
+
MNEME_DOLT_DATA_DIR Data directory (default: ~/.dolt/databases)
|
|
140
|
+
MNEME_DOLT_PORT Port (default: 3307)
|
|
141
|
+
`);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
package/src/dolt.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared dolt server utilities.
|
|
3
|
+
*
|
|
4
|
+
* Used by both `mneme init` and `mneme server`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { run } from "./utils.mjs";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Shared dolt data directory. All projects store their databases here,
|
|
13
|
+
* isolated by database name (e.g. beads_projectA, beads_projectB).
|
|
14
|
+
* One dolt server on port 3307 serves all projects on this machine.
|
|
15
|
+
*/
|
|
16
|
+
export const DOLT_DATA_DIR = process.env.MNEME_DOLT_DATA_DIR
|
|
17
|
+
|| join(process.env.HOME, ".dolt", "databases");
|
|
18
|
+
|
|
19
|
+
export const DOLT_PORT = parseInt(process.env.MNEME_DOLT_PORT || "3307", 10);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if a TCP port is accepting connections.
|
|
23
|
+
*/
|
|
24
|
+
export function isPortOpen(port = DOLT_PORT) {
|
|
25
|
+
return run(`bash -c 'echo > /dev/tcp/127.0.0.1/${port}' 2>&1`) !== null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Find dolt sql-server process(es) on the given port.
|
|
30
|
+
* Returns { proc, pid, dataDir } or null if not found.
|
|
31
|
+
*
|
|
32
|
+
* Prefers the actual dolt binary process over bash wrappers.
|
|
33
|
+
*/
|
|
34
|
+
export function findDoltProcess(port = DOLT_PORT) {
|
|
35
|
+
const psOutput = run(`ps aux 2>/dev/null`) ?? "";
|
|
36
|
+
const doltLines = psOutput.split("\n").filter((line) =>
|
|
37
|
+
line.includes("dolt") && line.includes("sql-server") && line.includes(`${port}`)
|
|
38
|
+
&& !line.includes("grep")
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (doltLines.length === 0) return null;
|
|
42
|
+
|
|
43
|
+
// Prefer the actual dolt binary process over a bash wrapper
|
|
44
|
+
const proc = doltLines.find((l) => !l.includes("bash -c") && /\bdolt\s+sql-server\b/.test(l))
|
|
45
|
+
|| doltLines[0];
|
|
46
|
+
|
|
47
|
+
const pid = proc.trim().split(/\s+/)[1];
|
|
48
|
+
|
|
49
|
+
// Extract --data-dir value from the process command line
|
|
50
|
+
const dataDirMatch = proc.match(/--data-dir\s+(\S+)/);
|
|
51
|
+
const dataDir = dataDirMatch ? dataDirMatch[1] : null;
|
|
52
|
+
|
|
53
|
+
return { proc, pid, dataDir, allLines: doltLines };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Ensure the dolt data directory exists.
|
|
58
|
+
*/
|
|
59
|
+
export function ensureDataDir() {
|
|
60
|
+
if (!existsSync(DOLT_DATA_DIR)) {
|
|
61
|
+
mkdirSync(DOLT_DATA_DIR, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Start the dolt server in the background. Returns true if started successfully.
|
|
67
|
+
* Does NOT check if already running — caller should check first.
|
|
68
|
+
*/
|
|
69
|
+
export function startDoltServer() {
|
|
70
|
+
ensureDataDir();
|
|
71
|
+
|
|
72
|
+
const logFile = join(DOLT_DATA_DIR, "server.log");
|
|
73
|
+
|
|
74
|
+
run(
|
|
75
|
+
`nohup dolt sql-server --host 127.0.0.1 --port ${DOLT_PORT} --data-dir "${DOLT_DATA_DIR}" > "${logFile}" 2>&1 &`,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Wait for server to be ready (up to 10s)
|
|
79
|
+
for (let i = 0; i < 10; i++) {
|
|
80
|
+
run("sleep 1");
|
|
81
|
+
if (isPortOpen()) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Kill a dolt process by PID. Also attempts to kill related wrapper processes.
|
|
91
|
+
*/
|
|
92
|
+
export function killDoltProcess(port = DOLT_PORT) {
|
|
93
|
+
const info = findDoltProcess(port);
|
|
94
|
+
if (!info) return false;
|
|
95
|
+
|
|
96
|
+
// Kill all matching dolt lines (binary + wrapper)
|
|
97
|
+
const pids = info.allLines
|
|
98
|
+
.map((l) => l.trim().split(/\s+/)[1])
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
|
|
101
|
+
for (const pid of pids) {
|
|
102
|
+
run(`kill ${pid} 2>/dev/null`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Wait briefly for process to exit
|
|
106
|
+
run("sleep 1");
|
|
107
|
+
return !isPortOpen(port);
|
|
108
|
+
}
|