@xqli02/mneme 0.1.4 → 0.1.6
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/init.mjs +23 -33
- 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/init.mjs
CHANGED
|
@@ -236,47 +236,37 @@ 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
|
-
|
|
242
|
+
// Check if port is already in use
|
|
243
|
+
if (isPortOpen()) {
|
|
244
|
+
const info = findDoltProcess();
|
|
253
245
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
246
|
+
if (info && info.dataDir === DOLT_DATA_DIR) {
|
|
247
|
+
// Same data-dir, already running — nothing to do
|
|
248
|
+
log.ok(`dolt server already running ${color.dim(`(port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})`)}`);
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (info) {
|
|
253
|
+
// Dolt running but with a different data-dir — kill and restart
|
|
254
|
+
log.warn(`dolt server on port ${DOLT_PORT} uses a different data-dir, restarting...`);
|
|
255
|
+
killDoltProcess();
|
|
256
|
+
} else {
|
|
257
|
+
// Port occupied by something else entirely
|
|
258
|
+
log.fail(`Port ${DOLT_PORT} is in use by a non-dolt process. Set MNEME_DOLT_PORT to use a different port.`);
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
log.info(`Starting dolt server (port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})...`);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
run(
|
|
266
|
-
`nohup dolt sql-server --host 127.0.0.1 --port ${DOLT_PORT} --data-dir "${DOLT_DATA_DIR}" > "${logFile}" 2>&1 &`,
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
// Wait for server to be ready (up to 10s)
|
|
270
|
-
for (let i = 0; i < 10; i++) {
|
|
271
|
-
run("sleep 1");
|
|
272
|
-
const check = run("bd list --status=open 2>&1");
|
|
273
|
-
if (check !== null && !check.includes("unreachable") && !check.includes("connection refused")) {
|
|
274
|
-
log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
275
|
-
return true;
|
|
276
|
-
}
|
|
264
|
+
if (startDoltServer()) {
|
|
265
|
+
log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
|
|
266
|
+
return true;
|
|
277
267
|
}
|
|
278
268
|
|
|
279
|
-
log.fail(`dolt server failed to start. Check ${
|
|
269
|
+
log.fail(`dolt server failed to start. Check ${DOLT_DATA_DIR}/server.log`);
|
|
280
270
|
return false;
|
|
281
271
|
}
|
|
282
272
|
|
|
@@ -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
|
+
}
|