kratos-memory 1.9.0 → 1.9.2
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/package.json +2 -2
- package/scripts/ensure-better-sqlite3.mjs +105 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kratos-memory",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2",
|
|
4
4
|
"description": "Persistent memory for AI coding agents — CLI-first, local, zero network calls",
|
|
5
5
|
"main": "dist/cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"author": "",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"better-sqlite3": "
|
|
38
|
+
"better-sqlite3": "12.9.0",
|
|
39
39
|
"chalk": "^5.3.0",
|
|
40
40
|
"commander": "^12.1.0",
|
|
41
41
|
"fs-extra": "^11.2.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, rmSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, statSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -9,6 +9,9 @@ const __dirname = path.dirname(__filename);
|
|
|
9
9
|
const packageRoot = path.resolve(__dirname, '..');
|
|
10
10
|
const requireFromPackage = createRequire(path.join(packageRoot, 'package.json'));
|
|
11
11
|
|
|
12
|
+
const LOCK_WAIT_MS = 180_000; // how long a process waits for another's rebuild
|
|
13
|
+
const LOCK_STALE_MS = 600_000; // locks older than this are abandoned crashes
|
|
14
|
+
|
|
12
15
|
function resolveBetterSqlite3Dir() {
|
|
13
16
|
const pkgPath = requireFromPackage.resolve('better-sqlite3/package.json');
|
|
14
17
|
return path.dirname(pkgPath);
|
|
@@ -70,6 +73,66 @@ function formatOutput(result) {
|
|
|
70
73
|
return chunks.join('\n');
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
function sleepSync(ms) {
|
|
77
|
+
spawnSync(process.execPath, ['-e', `setTimeout(() => process.exit(0), ${ms})`]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Cross-process lock via atomic mkdir. Concurrent kratos invocations on a
|
|
82
|
+
* fresh install must not rebuild the native module simultaneously — the
|
|
83
|
+
* second process waits for the first, then re-checks instead of rebuilding.
|
|
84
|
+
*/
|
|
85
|
+
function acquireLock(lockDir) {
|
|
86
|
+
const start = Date.now();
|
|
87
|
+
for (;;) {
|
|
88
|
+
try {
|
|
89
|
+
mkdirSync(lockDir);
|
|
90
|
+
return true;
|
|
91
|
+
} catch {
|
|
92
|
+
try {
|
|
93
|
+
const stat = statSync(lockDir);
|
|
94
|
+
if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {
|
|
95
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
continue; // lock vanished between attempts — retry immediately
|
|
100
|
+
}
|
|
101
|
+
if (Date.now() - start > LOCK_WAIT_MS) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
sleepSync(500);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function releaseLock(lockDir) {
|
|
110
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Fast path: download the official prebuilt binary (seconds, no compiler).
|
|
115
|
+
* This is what better-sqlite3's own install script does — it gets skipped
|
|
116
|
+
* when users install with --ignore-scripts.
|
|
117
|
+
*/
|
|
118
|
+
function tryPrebuildInstall(moduleDir) {
|
|
119
|
+
let prebuildBin;
|
|
120
|
+
try {
|
|
121
|
+
const requireFromModule = createRequire(path.join(moduleDir, 'package.json'));
|
|
122
|
+
const prebuildPkg = requireFromModule.resolve('prebuild-install/package.json');
|
|
123
|
+
const prebuildDir = path.dirname(prebuildPkg);
|
|
124
|
+
const binRel = JSON.parse(
|
|
125
|
+
spawnSync(process.execPath, ['-e', `console.log(JSON.stringify(require(${JSON.stringify(prebuildPkg)}).bin))`], { encoding: 'utf8' }).stdout.trim()
|
|
126
|
+
);
|
|
127
|
+
prebuildBin = path.join(prebuildDir, typeof binRel === 'string' ? binRel : Object.values(binRel)[0]);
|
|
128
|
+
} catch {
|
|
129
|
+
return false; // prebuild-install not resolvable — fall through to source build
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = runCommand(process.execPath, [prebuildBin], moduleDir);
|
|
133
|
+
return result.status === 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
73
136
|
export function ensureBetterSqlite3() {
|
|
74
137
|
let moduleDir;
|
|
75
138
|
|
|
@@ -84,30 +147,49 @@ export function ensureBetterSqlite3() {
|
|
|
84
147
|
return;
|
|
85
148
|
}
|
|
86
149
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const npm = findNpmCommand();
|
|
94
|
-
const rebuild = runCommand(
|
|
95
|
-
npm.command,
|
|
96
|
-
[...npm.argsPrefix, 'run', 'build-release', '--foreground-scripts'],
|
|
97
|
-
moduleDir
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
if (canLoadBetterSqlite3(moduleDir)) {
|
|
101
|
-
console.warn('[kratos-memory] better-sqlite3 rebuild succeeded.');
|
|
102
|
-
return;
|
|
150
|
+
const lockDir = path.join(moduleDir, '.kratos-setup-lock');
|
|
151
|
+
if (!acquireLock(lockDir)) {
|
|
152
|
+
// Another process held the lock the whole time — check its result
|
|
153
|
+
if (canLoadBetterSqlite3(moduleDir)) return;
|
|
154
|
+
throw new Error('Timed out waiting for another kratos process to finish native setup.');
|
|
103
155
|
}
|
|
104
156
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
157
|
+
try {
|
|
158
|
+
// Another process may have completed setup while we waited for the lock
|
|
159
|
+
if (canLoadBetterSqlite3(moduleDir)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
rmSync(path.join(moduleDir, 'build'), { recursive: true, force: true });
|
|
164
|
+
|
|
165
|
+
console.warn('[kratos-memory] one-time native setup: fetching prebuilt SQLite binary...');
|
|
166
|
+
if (tryPrebuildInstall(moduleDir) && canLoadBetterSqlite3(moduleDir)) {
|
|
167
|
+
console.warn('[kratos-memory] prebuilt binary installed.');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.warn('[kratos-memory] no prebuilt binary available — compiling from source (one time, may take a minute)...');
|
|
172
|
+
const npm = findNpmCommand();
|
|
173
|
+
const rebuild = runCommand(
|
|
174
|
+
npm.command,
|
|
175
|
+
[...npm.argsPrefix, 'run', 'build-release', '--foreground-scripts'],
|
|
176
|
+
moduleDir
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (canLoadBetterSqlite3(moduleDir)) {
|
|
180
|
+
console.warn('[kratos-memory] better-sqlite3 rebuild succeeded.');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const detail = formatOutput(rebuild);
|
|
185
|
+
throw new Error(
|
|
186
|
+
detail
|
|
187
|
+
? `Failed to build better-sqlite3.\n${detail}`
|
|
188
|
+
: 'Failed to build better-sqlite3.'
|
|
189
|
+
);
|
|
190
|
+
} finally {
|
|
191
|
+
releaseLock(lockDir);
|
|
192
|
+
}
|
|
111
193
|
}
|
|
112
194
|
|
|
113
195
|
if (process.argv[1] === __filename) {
|