@mneme-ai/core 2.19.96-lite → 2.19.97-lite
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/pulse.d.ts.map +1 -1
- package/dist/pulse.js +29 -5
- package/dist/pulse.js.map +1 -1
- package/dist/super_nova/index.d.ts +118 -0
- package/dist/super_nova/index.d.ts.map +1 -0
- package/dist/super_nova/index.js +240 -0
- package/dist/super_nova/index.js.map +1 -0
- package/dist/super_nova/super_nova.test.d.ts +2 -0
- package/dist/super_nova/super_nova.test.d.ts.map +1 -0
- package/dist/super_nova/super_nova.test.js +90 -0
- package/dist/super_nova/super_nova.test.js.map +1 -0
- package/dist/superlock/index.d.ts +84 -0
- package/dist/superlock/index.d.ts.map +1 -0
- package/dist/superlock/index.js +218 -0
- package/dist/superlock/index.js.map +1 -0
- package/dist/superlock/superlock.test.d.ts +2 -0
- package/dist/superlock/superlock.test.d.ts.map +1 -0
- package/dist/superlock/superlock.test.js +105 -0
- package/dist/superlock/superlock.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.19.97 — SUPERLOCK + DEV-SOURCE GUARD.
|
|
3
|
+
*
|
|
4
|
+
* Solves the race condition class user reported in the post-mortem of
|
|
5
|
+
* their broken install:
|
|
6
|
+
*
|
|
7
|
+
* 1. Pulse banner advertises [AUTO-ACTION] mneme.system.upgrade
|
|
8
|
+
* 2. Daemon (or shepherd) starts auto-upgrade in background
|
|
9
|
+
* → deletes bot/platforms/index.js to rewrite
|
|
10
|
+
* 3. User simultaneously runs `npm install -g mneme-ai` from terminal
|
|
11
|
+
* 4. EBUSY on libvips DLL because daemon is mid-write
|
|
12
|
+
* 5. Half-finished install: missing files; every `mneme` command
|
|
13
|
+
* throws ERR_MODULE_NOT_FOUND
|
|
14
|
+
*
|
|
15
|
+
* Plus the user discovered a SECOND install source: when Mneme is
|
|
16
|
+
* checked out as a dev repo (D:\lib_ai_git\packages\cli\bin\mneme.js),
|
|
17
|
+
* THAT daemon ALSO honours pulse auto-upgrade mandates — and tries to
|
|
18
|
+
* upgrade the *global npm install*, racing with whatever the user is
|
|
19
|
+
* doing. Two install roots → two daemons → two upgrade triggers →
|
|
20
|
+
* guaranteed race.
|
|
21
|
+
*
|
|
22
|
+
* Two-layer fix:
|
|
23
|
+
*
|
|
24
|
+
* DEV-SOURCE GUARD
|
|
25
|
+
* Detect when the running mneme binary is from a source checkout
|
|
26
|
+
* (path contains `packages/cli/bin/`) rather than a published
|
|
27
|
+
* `node_modules/mneme-ai/` tree. When detected, refuse ALL
|
|
28
|
+
* auto-upgrade attempts + drop the upgrade [AUTO-ACTION] from
|
|
29
|
+
* pulse output entirely. Dev source manages its own upgrades
|
|
30
|
+
* via git pull.
|
|
31
|
+
*
|
|
32
|
+
* SUPERLOCK
|
|
33
|
+
* Single global file mutex at `~/.mneme-global/superlock.flag`
|
|
34
|
+
* that EVERY install/upgrade path must grab before mutating the
|
|
35
|
+
* on-disk install. Lock content is JSON with { pid, startedAt,
|
|
36
|
+
* intent, role } so observers can see who's holding it + why.
|
|
37
|
+
* Stale locks (>10 min) are auto-broken. Lock acquisition is
|
|
38
|
+
* atomic-rename (POSIX + Windows safe).
|
|
39
|
+
*
|
|
40
|
+
* Composes with v2.19.58 install-incoming.flag (5-min flag window),
|
|
41
|
+
* v2.19.57 shepherd, v2.19.62 phoenix DLL extraction, but is the
|
|
42
|
+
* single source of truth that serialises EVERYTHING.
|
|
43
|
+
*/
|
|
44
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, renameSync } from "node:fs";
|
|
45
|
+
import { join, normalize } from "node:path";
|
|
46
|
+
import { homedir } from "node:os";
|
|
47
|
+
import { randomBytes } from "node:crypto";
|
|
48
|
+
const GLOBAL_DIR = ".mneme-global";
|
|
49
|
+
const LOCK = "superlock.flag";
|
|
50
|
+
const STALE_MS = 10 * 60 * 1000; // 10 min — anything older is presumed dead
|
|
51
|
+
function lockDir() {
|
|
52
|
+
const d = join(homedir(), GLOBAL_DIR);
|
|
53
|
+
if (!existsSync(d)) {
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(d, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
catch { /* */ }
|
|
58
|
+
}
|
|
59
|
+
return d;
|
|
60
|
+
}
|
|
61
|
+
function lockPath() { return join(lockDir(), LOCK); }
|
|
62
|
+
/** Read current lock state if any. Returns null if no lock or unreadable. */
|
|
63
|
+
export function readLock() {
|
|
64
|
+
const p = lockPath();
|
|
65
|
+
if (!existsSync(p))
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** True iff a lock exists AND is fresh enough to honour. Stale locks
|
|
75
|
+
* (older than STALE_MS) are ignored because the holder probably died. */
|
|
76
|
+
export function isLockHeld() {
|
|
77
|
+
const s = readLock();
|
|
78
|
+
if (!s)
|
|
79
|
+
return false;
|
|
80
|
+
try {
|
|
81
|
+
const age = Date.now() - new Date(s.startedAt).getTime();
|
|
82
|
+
return age < STALE_MS;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Atomic acquire. Returns true if we now hold the lock; false if
|
|
89
|
+
* somebody else holds a fresh one. Uses tmpfile + rename for atomicity
|
|
90
|
+
* on both POSIX and Windows. */
|
|
91
|
+
export function acquireLock(state) {
|
|
92
|
+
// Check existing lock.
|
|
93
|
+
const existing = readLock();
|
|
94
|
+
if (existing) {
|
|
95
|
+
const age = Date.now() - new Date(existing.startedAt).getTime();
|
|
96
|
+
if (age < STALE_MS && existing.pid !== state.pid)
|
|
97
|
+
return false;
|
|
98
|
+
// Stale — break it.
|
|
99
|
+
try {
|
|
100
|
+
unlinkSync(lockPath());
|
|
101
|
+
}
|
|
102
|
+
catch { /* */ }
|
|
103
|
+
}
|
|
104
|
+
// Atomic: write to tmp then rename. Two concurrent acquirers race on
|
|
105
|
+
// rename; one wins, the other's rename overwrites — both think they
|
|
106
|
+
// won. Belt-and-suspenders: re-read after writing and check pid.
|
|
107
|
+
const tmp = join(lockDir(), `superlock.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
|
|
108
|
+
const full = { ...state, startedAt: new Date().toISOString() };
|
|
109
|
+
try {
|
|
110
|
+
writeFileSync(tmp, JSON.stringify(full, null, 2), "utf8");
|
|
111
|
+
renameSync(tmp, lockPath());
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
try {
|
|
115
|
+
unlinkSync(tmp);
|
|
116
|
+
}
|
|
117
|
+
catch { /* */ }
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
// Verify we're the winner.
|
|
121
|
+
const s = readLock();
|
|
122
|
+
return !!s && s.pid === state.pid;
|
|
123
|
+
}
|
|
124
|
+
/** Release the lock if we still hold it. Caller MUST always call this
|
|
125
|
+
* in a `finally` block to avoid leaks. */
|
|
126
|
+
export function releaseLock(pid = process.pid) {
|
|
127
|
+
const s = readLock();
|
|
128
|
+
if (!s)
|
|
129
|
+
return true;
|
|
130
|
+
if (s.pid !== pid)
|
|
131
|
+
return false; // somebody else owns it now
|
|
132
|
+
try {
|
|
133
|
+
unlinkSync(lockPath());
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Wrap any async install/upgrade action with the superlock. Throws
|
|
141
|
+
* with a descriptive error if the lock is held by another fresh
|
|
142
|
+
* process. */
|
|
143
|
+
export async function withSuperlock(state, fn) {
|
|
144
|
+
const acquired = acquireLock({ ...state, pid: process.pid });
|
|
145
|
+
if (!acquired) {
|
|
146
|
+
const held = readLock();
|
|
147
|
+
throw new Error(`SUPERLOCK: another mneme install/upgrade is in progress` +
|
|
148
|
+
(held ? ` (pid=${held.pid}, role=${held.role}, intent=${held.intent}, since ${held.startedAt})` : "") +
|
|
149
|
+
`. Wait for it to finish, or run \`mneme superlock --break\` if you believe it is stuck.`);
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return await fn();
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
releaseLock();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ─── DEV-SOURCE GUARD ──────────────────────────────────────────────────
|
|
159
|
+
/** True iff the currently-running mneme binary lives in a source
|
|
160
|
+
* checkout (path contains `/packages/cli/` or `\packages\cli\`) rather
|
|
161
|
+
* than a node_modules install. Dev-source must NEVER auto-upgrade
|
|
162
|
+
* itself — git pull is the right tool for that. */
|
|
163
|
+
export function isDevSource(scriptPath) {
|
|
164
|
+
// Distinguish "caller passed nothing" (use argv default) from "caller
|
|
165
|
+
// explicitly passed empty" (refuse to guess) — the latter should
|
|
166
|
+
// return false so callers can opt out of process-state inspection.
|
|
167
|
+
const raw = scriptPath === undefined ? (process.argv[1] ?? "") : scriptPath;
|
|
168
|
+
if (!raw)
|
|
169
|
+
return false;
|
|
170
|
+
const path = normalize(raw);
|
|
171
|
+
// Patterns that indicate dev checkout:
|
|
172
|
+
// .../packages/cli/bin/mneme.js
|
|
173
|
+
// .../packages/cli/dist/index.js
|
|
174
|
+
// .../packages/cli/src/index.ts
|
|
175
|
+
const devPatterns = [
|
|
176
|
+
/[\\/]packages[\\/]cli[\\/]/,
|
|
177
|
+
/[\\/]lib_ai_git[\\/]/,
|
|
178
|
+
/[\\/]mneme-ai[\\/]packages[\\/]/,
|
|
179
|
+
];
|
|
180
|
+
// Conversely, definitely-not-dev-source patterns (real install):
|
|
181
|
+
const installPatterns = [
|
|
182
|
+
/[\\/]node_modules[\\/]mneme-ai[\\/]/,
|
|
183
|
+
/[\\/]\.npm-global[\\/]/,
|
|
184
|
+
/[\\/]npm[\\/]node_modules[\\/]mneme-ai[\\/]/,
|
|
185
|
+
];
|
|
186
|
+
for (const p of installPatterns)
|
|
187
|
+
if (p.test(path))
|
|
188
|
+
return false;
|
|
189
|
+
for (const p of devPatterns)
|
|
190
|
+
if (p.test(path))
|
|
191
|
+
return true;
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
/** Human-readable explanation surfaced when dev-source guard fires. */
|
|
195
|
+
export function devSourceMessage(scriptPath) {
|
|
196
|
+
const path = scriptPath ?? process.argv[1] ?? "(unknown)";
|
|
197
|
+
return [
|
|
198
|
+
`Mneme is running from a dev source checkout, not a published npm install.`,
|
|
199
|
+
` binary path: ${path}`,
|
|
200
|
+
` auto-upgrade: REFUSED (dev source manages itself via git pull)`,
|
|
201
|
+
`To pull latest dev changes: cd <repo> && git pull && pnpm install`,
|
|
202
|
+
`To use the published version: install via npm install -g mneme-ai in a separate shell.`,
|
|
203
|
+
].join("\n");
|
|
204
|
+
}
|
|
205
|
+
/** The decision the pulse renderer + auto-upgrade hooks call before
|
|
206
|
+
* emitting `[AUTO-ACTION] mneme.system.upgrade`. Returns true when
|
|
207
|
+
* the auto-action is safe to advertise + execute. */
|
|
208
|
+
export function autoUpgradeAllowed(scriptPath) {
|
|
209
|
+
if (isDevSource(scriptPath)) {
|
|
210
|
+
return { allowed: false, reason: "dev-source-detected" };
|
|
211
|
+
}
|
|
212
|
+
if (isLockHeld()) {
|
|
213
|
+
const s = readLock();
|
|
214
|
+
return { allowed: false, reason: `superlock-held-by-${s?.role ?? "unknown"}` };
|
|
215
|
+
}
|
|
216
|
+
return { allowed: true, reason: "ok" };
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/superlock/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superlock.test.d.ts","sourceRoot":"","sources":["../../src/superlock/superlock.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { isDevSource, autoUpgradeAllowed, acquireLock, releaseLock, isLockHeld, readLock, withSuperlock, devSourceMessage } from "./index.js";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
6
|
+
const LOCK_PATH = join(homedir(), ".mneme-global", "superlock.flag");
|
|
7
|
+
describe("superlock", () => {
|
|
8
|
+
describe("isDevSource", () => {
|
|
9
|
+
it("recognises a dev checkout path", () => {
|
|
10
|
+
expect(isDevSource("D:\\lib_ai_git\\packages\\cli\\bin\\mneme.js")).toBe(true);
|
|
11
|
+
expect(isDevSource("/Users/dev/lib_ai_git/packages/cli/bin/mneme.js")).toBe(true);
|
|
12
|
+
expect(isDevSource("C:\\src\\mneme-ai\\packages\\cli\\dist\\index.js")).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it("recognises a node_modules install as NOT dev", () => {
|
|
15
|
+
expect(isDevSource("C:\\nvm4w\\nodejs\\node_modules\\mneme-ai\\bin\\mneme.js")).toBe(false);
|
|
16
|
+
expect(isDevSource("/usr/local/lib/node_modules/mneme-ai/bin/mneme.js")).toBe(false);
|
|
17
|
+
expect(isDevSource("/Users/x/.npm-global/lib/node_modules/mneme-ai/bin/mneme.js")).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it("returns false on explicit empty path (no argv fallback when caller opts out)", () => {
|
|
20
|
+
expect(isDevSource("")).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("autoUpgradeAllowed", () => {
|
|
24
|
+
beforeEach(() => { try {
|
|
25
|
+
if (existsSync(LOCK_PATH))
|
|
26
|
+
unlinkSync(LOCK_PATH);
|
|
27
|
+
}
|
|
28
|
+
catch { /* */ } });
|
|
29
|
+
afterEach(() => { try {
|
|
30
|
+
if (existsSync(LOCK_PATH))
|
|
31
|
+
unlinkSync(LOCK_PATH);
|
|
32
|
+
}
|
|
33
|
+
catch { /* */ } });
|
|
34
|
+
it("refuses upgrade when running from dev source", () => {
|
|
35
|
+
const r = autoUpgradeAllowed("D:\\lib_ai_git\\packages\\cli\\bin\\mneme.js");
|
|
36
|
+
expect(r.allowed).toBe(false);
|
|
37
|
+
expect(r.reason).toBe("dev-source-detected");
|
|
38
|
+
});
|
|
39
|
+
it("allows upgrade from real npm install when no lock held", () => {
|
|
40
|
+
const r = autoUpgradeAllowed("/usr/local/lib/node_modules/mneme-ai/bin/mneme.js");
|
|
41
|
+
expect(r.allowed).toBe(true);
|
|
42
|
+
expect(r.reason).toBe("ok");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("acquire/release lock", () => {
|
|
46
|
+
beforeEach(() => { try {
|
|
47
|
+
if (existsSync(LOCK_PATH))
|
|
48
|
+
unlinkSync(LOCK_PATH);
|
|
49
|
+
}
|
|
50
|
+
catch { /* */ } });
|
|
51
|
+
afterEach(() => { try {
|
|
52
|
+
if (existsSync(LOCK_PATH))
|
|
53
|
+
unlinkSync(LOCK_PATH);
|
|
54
|
+
}
|
|
55
|
+
catch { /* */ } });
|
|
56
|
+
it("first acquire succeeds, second from different pid fails", () => {
|
|
57
|
+
expect(acquireLock({ pid: 11111, role: "test", intent: "first try", holderPath: "/tmp/a" })).toBe(true);
|
|
58
|
+
expect(isLockHeld()).toBe(true);
|
|
59
|
+
// Try to acquire as a different pid while fresh lock is held.
|
|
60
|
+
expect(acquireLock({ pid: 22222, role: "test", intent: "second try", holderPath: "/tmp/b" })).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it("release lets the next acquirer in", () => {
|
|
63
|
+
expect(acquireLock({ pid: process.pid, role: "test", intent: "self", holderPath: "/tmp/x" })).toBe(true);
|
|
64
|
+
expect(releaseLock(process.pid)).toBe(true);
|
|
65
|
+
expect(isLockHeld()).toBe(false);
|
|
66
|
+
expect(acquireLock({ pid: 33333, role: "test", intent: "next", holderPath: "/tmp/y" })).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it("readLock returns null when no lock exists", () => {
|
|
69
|
+
expect(readLock()).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("withSuperlock", () => {
|
|
73
|
+
beforeEach(() => { try {
|
|
74
|
+
if (existsSync(LOCK_PATH))
|
|
75
|
+
unlinkSync(LOCK_PATH);
|
|
76
|
+
}
|
|
77
|
+
catch { /* */ } });
|
|
78
|
+
afterEach(() => { try {
|
|
79
|
+
if (existsSync(LOCK_PATH))
|
|
80
|
+
unlinkSync(LOCK_PATH);
|
|
81
|
+
}
|
|
82
|
+
catch { /* */ } });
|
|
83
|
+
it("runs fn, releases lock on success", async () => {
|
|
84
|
+
const result = await withSuperlock({ role: "test", intent: "demo", holderPath: "/tmp/z" }, async () => {
|
|
85
|
+
expect(isLockHeld()).toBe(true);
|
|
86
|
+
return 42;
|
|
87
|
+
});
|
|
88
|
+
expect(result).toBe(42);
|
|
89
|
+
expect(isLockHeld()).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it("releases lock even when fn throws", async () => {
|
|
92
|
+
await expect(withSuperlock({ role: "test", intent: "boom", holderPath: "/tmp/z" }, async () => { throw new Error("boom"); })).rejects.toThrow("boom");
|
|
93
|
+
expect(isLockHeld()).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe("devSourceMessage", () => {
|
|
97
|
+
it("includes path, refusal, and remediation", () => {
|
|
98
|
+
const msg = devSourceMessage("D:\\lib_ai_git\\packages\\cli\\bin\\mneme.js");
|
|
99
|
+
expect(msg).toContain("dev source");
|
|
100
|
+
expect(msg).toContain("REFUSED");
|
|
101
|
+
expect(msg).toContain("git pull");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=superlock.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superlock.test.js","sourceRoot":"","sources":["../../src/superlock/superlock.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mneme-ai/core",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.97-lite",
|
|
4
4
|
"description": "Core indexing, retrieval, and graph engine for Mneme — LITE variant (no bundled WASM embedder, no native deps).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|