@justfortytwo/installer 1.0.0-1 → 1.0.0-10
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 +160 -11
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +10 -7
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/forget.d.ts.map +1 -1
- package/dist/commands/forget.js +6 -4
- package/dist/commands/forget.js.map +1 -1
- package/dist/commands/init.d.ts +86 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +254 -56
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pair.d.ts +8 -0
- package/dist/commands/pair.d.ts.map +1 -1
- package/dist/commands/pair.js +37 -3
- package/dist/commands/pair.js.map +1 -1
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +35 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +14 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +54 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +4 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +34 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/update.d.ts +6 -2
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +50 -11
- package/dist/commands/update.js.map +1 -1
- package/dist/engine.d.ts +32 -8
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +117 -27
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -5
- package/dist/index.js.map +1 -1
- package/dist/oneiros.d.ts +2 -0
- package/dist/oneiros.d.ts.map +1 -0
- package/dist/oneiros.js +28 -0
- package/dist/oneiros.js.map +1 -0
- package/dist/render.d.ts +4 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +13 -10
- package/dist/render.js.map +1 -1
- package/dist/runtime.d.ts +75 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +219 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shim.d.ts +52 -0
- package/dist/shim.d.ts.map +1 -0
- package/dist/shim.js +163 -0
- package/dist/shim.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// start — run fortytwo's long-running services in the foreground.
|
|
2
|
+
//
|
|
3
|
+
// Loads the project .env (the shell/container env wins over it), starts every
|
|
4
|
+
// service that is installed and configured (the Telegram bridge needs
|
|
5
|
+
// TELEGRAM_BOT_TOKEN; the scheduler needs nothing) and supervises them until
|
|
6
|
+
// Ctrl+C / SIGTERM or until one exits — see runtime.ts. Never daemonizes: use it
|
|
7
|
+
// directly as the container/service command (`fortytwo start`) and let
|
|
8
|
+
// Docker/Podman/systemd own restarts. The services' own bins
|
|
9
|
+
// (fortytwo-telegram-bridge, fortytwo-scheduler) stay available for debugging.
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { parseEnvContent } from './init.js';
|
|
13
|
+
import { buildServiceEnv, liveRuntime, planServices, supervise } from '../runtime.js';
|
|
14
|
+
export async function runStart(_argv) {
|
|
15
|
+
const root = process.cwd();
|
|
16
|
+
const running = liveRuntime(root);
|
|
17
|
+
if (running) {
|
|
18
|
+
process.stderr.write(`start: fortytwo is already running (pid ${running.pid}). Stop it with \`fortytwo stop\`.\n`);
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
const envPath = join(root, '.env');
|
|
22
|
+
const fileEnv = existsSync(envPath) ? parseEnvContent(readFileSync(envPath, 'utf8')) : {};
|
|
23
|
+
const env = buildServiceEnv(process.env, fileEnv, root);
|
|
24
|
+
const { run, skipped } = planServices(root, env);
|
|
25
|
+
for (const s of skipped)
|
|
26
|
+
process.stdout.write(`[fortytwo] ${s.name}: not started (${s.reason})\n`);
|
|
27
|
+
if (run.length === 0) {
|
|
28
|
+
process.stderr.write('start: no fortytwo service can run here. Run `fortytwo init` in this project first, ' +
|
|
29
|
+
'and set TELEGRAM_BOT_TOKEN in .env for the Telegram channel.\n');
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
process.stdout.write(`[fortytwo] starting ${run.map((s) => s.name).join(' + ')} (Ctrl+C to stop)\n`);
|
|
33
|
+
return supervise(run, { root, env, skipped });
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,sEAAsE;AACtE,6EAA6E;AAC7E,iFAAiF;AACjF,uEAAuE;AACvE,6DAA6D;AAC7D,+EAA+E;AAE/E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAe;IAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,OAAO,CAAC,GAAG,sCAAsC,CAAC,CAAC;QACnH,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1F,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IACnG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sFAAsF;YACtF,gEAAgE,CACjE,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrG,OAAO,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RuntimeState } from '../runtime.js';
|
|
2
|
+
export interface StatusInput {
|
|
3
|
+
runtime: RuntimeState | null;
|
|
4
|
+
schedulerHeartbeat: {
|
|
5
|
+
pid: number;
|
|
6
|
+
ts: string;
|
|
7
|
+
} | null;
|
|
8
|
+
memoryConfigured: boolean;
|
|
9
|
+
nowMs: number;
|
|
10
|
+
}
|
|
11
|
+
/** The status lines (pure, for tests). */
|
|
12
|
+
export declare function statusLines(i: StatusInput): string[];
|
|
13
|
+
export declare function runStatus(_argv: string[]): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAWA,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAG/E,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE,CAkBpD;AAaD,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAWhE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// status — is fortytwo running, and which of its services?
|
|
2
|
+
//
|
|
3
|
+
// Reads what already exists instead of searching for processes: the runtime
|
|
4
|
+
// state `fortytwo start` keeps in .fortytwo/runtime.json (Telegram bridge), the
|
|
5
|
+
// scheduler's own heartbeat file (so a scheduler run by hand shows up too), and
|
|
6
|
+
// .mcp.json for memory, which Claude Code itself starts on demand.
|
|
7
|
+
// Exit code: 0 when `fortytwo start` is running, 3 when it is not (LSB "not
|
|
8
|
+
// running"), so `fortytwo status` works as a container health check.
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { liveRuntime, serviceRunning } from '../runtime.js';
|
|
12
|
+
import { defaultDoctorDeps, HEARTBEAT_STALE_MS } from './doctor.js';
|
|
13
|
+
/** The status lines (pure, for tests). */
|
|
14
|
+
export function statusLines(i) {
|
|
15
|
+
const row = (label, value) => `${label.padEnd(11)}${value}`;
|
|
16
|
+
const lines = [];
|
|
17
|
+
lines.push(row('Fortytwo', i.runtime ? `running (pid ${i.runtime.pid}, since ${i.runtime.startedAt})` : 'not running (start it with `fortytwo start`)'));
|
|
18
|
+
const skippedTg = i.runtime?.skipped.find((s) => s.name === 'telegram');
|
|
19
|
+
lines.push(row('Telegram', serviceRunning(i.runtime, 'telegram') ? 'running'
|
|
20
|
+
: skippedTg ? `not started (${skippedTg.reason})`
|
|
21
|
+
: 'not running'));
|
|
22
|
+
const hb = i.schedulerHeartbeat;
|
|
23
|
+
const age = hb ? i.nowMs - new Date(hb.ts).getTime() : Infinity;
|
|
24
|
+
lines.push(row('Scheduler', hb && age <= HEARTBEAT_STALE_MS ? `running (heartbeat ${Math.max(0, Math.round(age / 1000))}s ago)`
|
|
25
|
+
: hb ? `not running (last heartbeat ${hb.ts})`
|
|
26
|
+
: 'not running'));
|
|
27
|
+
lines.push(row('Memory', i.memoryConfigured ? 'configured via MCP (Claude Code starts it on demand)' : 'not configured (run `fortytwo init`)'));
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
30
|
+
function memoryConfigured(root) {
|
|
31
|
+
const path = join(root, '.mcp.json');
|
|
32
|
+
if (!existsSync(path))
|
|
33
|
+
return false;
|
|
34
|
+
try {
|
|
35
|
+
const cfg = JSON.parse(readFileSync(path, 'utf8'));
|
|
36
|
+
return !!cfg.mcpServers?.['fortytwo-memory'];
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function runStatus(_argv) {
|
|
43
|
+
const root = process.cwd();
|
|
44
|
+
const runtime = liveRuntime(root);
|
|
45
|
+
const lines = statusLines({
|
|
46
|
+
runtime,
|
|
47
|
+
schedulerHeartbeat: defaultDoctorDeps().schedulerHeartbeat(),
|
|
48
|
+
memoryConfigured: memoryConfigured(root),
|
|
49
|
+
nowMs: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
52
|
+
return runtime ? 0 : 3;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAWA,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBjG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// stop — stop a `fortytwo start` running for this project.
|
|
2
|
+
//
|
|
3
|
+
// Signals ONLY the supervisor recorded in .fortytwo/runtime.json, and only while
|
|
4
|
+
// its heartbeat is fresh (see runtime.ts), so a stale file can never hit a
|
|
5
|
+
// reused pid. It never searches for or kills processes by name. The supervisor
|
|
6
|
+
// then stops its own services. Under Docker/systemd prefer their own stop.
|
|
7
|
+
import { isPidAlive, liveRuntime } from '../runtime.js';
|
|
8
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9
|
+
export async function runStop(_argv, opts = {}) {
|
|
10
|
+
const runtime = liveRuntime(process.cwd());
|
|
11
|
+
if (!runtime) {
|
|
12
|
+
process.stdout.write('fortytwo is not running.\n');
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
process.stdout.write(`Stopping fortytwo (pid ${runtime.pid})…\n`);
|
|
16
|
+
try {
|
|
17
|
+
process.kill(runtime.pid, 'SIGTERM');
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
process.stderr.write(`stop: could not signal pid ${runtime.pid}: ${err.message}\n`);
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 20_000);
|
|
24
|
+
while (Date.now() < deadline) {
|
|
25
|
+
if (!isPidAlive(runtime.pid)) {
|
|
26
|
+
process.stdout.write('fortytwo stopped.\n');
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
await sleep(200);
|
|
30
|
+
}
|
|
31
|
+
process.stderr.write(`stop: fortytwo (pid ${runtime.pid}) is still running after SIGTERM.\n`);
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=stop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,2EAA2E;AAC3E,+EAA+E;AAC/E,2EAA2E;AAE3E,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAExD,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAe,EAAE,OAA+B,EAAE;IAC9E,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,OAAO,CAAC,GAAG,KAAM,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAC/F,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;YAC5C,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,OAAO,CAAC,GAAG,qCAAqC,CAAC,CAAC;IAC9F,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -11,7 +11,11 @@ export interface InstallResult {
|
|
|
11
11
|
detail: string;
|
|
12
12
|
}
|
|
13
13
|
export interface UpdateDeps {
|
|
14
|
+
/** Running CLI, not the target project package; optional for embedded callers. */
|
|
15
|
+
runningInstallerVersion?: () => string | null;
|
|
16
|
+
persistInstaller?: (exactSpec: string) => InstallResult;
|
|
14
17
|
readState: () => InstallState | null;
|
|
18
|
+
runtimeActive?: () => boolean;
|
|
15
19
|
installedVersion: (spec: string) => string | null;
|
|
16
20
|
/** spec -> declared semver range (this CLI's fortytwo.compat). */
|
|
17
21
|
compatRanges: Record<string, string>;
|
|
@@ -26,8 +30,8 @@ export interface UpdateDeps {
|
|
|
26
30
|
}>;
|
|
27
31
|
/** Persist the resolved set, rotating current -> previous. */
|
|
28
32
|
record: (pins: VersionPin[]) => void;
|
|
29
|
-
/**
|
|
30
|
-
|
|
33
|
+
/** Reconcile machine-owned runtime assets; never render persona context. */
|
|
34
|
+
reconcile: () => Promise<void>;
|
|
31
35
|
out: (s: string) => void;
|
|
32
36
|
err: (s: string) => void;
|
|
33
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/update.js
CHANGED
|
@@ -9,17 +9,20 @@
|
|
|
9
9
|
// the install lands, so rollback always targets the set that was running.
|
|
10
10
|
// 4. POST-VERIFY with doctor. On failure: leave the new set in place but LOUDLY
|
|
11
11
|
// point at `fortytwo rollback` — rollback is MANUAL by design (never auto-
|
|
12
|
-
// revert, so the user can inspect first). On success:
|
|
13
|
-
//
|
|
12
|
+
// revert, so the user can inspect first). On success: reconcile machine-owned runtime assets only.
|
|
13
|
+
// Existing context is never re-rendered by update.
|
|
14
14
|
//
|
|
15
15
|
// rollback re-installs state.json's `previous` set and rotates the ledger back.
|
|
16
16
|
// Both share the resolve/install primitives below and are unit-tested through an
|
|
17
17
|
// injected `*Deps` (so no test touches the network or the real npm).
|
|
18
18
|
import { spawnSync } from 'node:child_process';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { isPidAlive, liveRuntime } from '../runtime.js';
|
|
20
|
+
import { HEARTBEAT_STALE_MS } from './doctor.js';
|
|
21
|
+
import { recordVersionSet, readState, } from '../state.js';
|
|
22
|
+
import { compareSemver, readInstalledVersion, readSelfCompatRanges, readSelfVersion, satisfiesRange } from '../engine.js';
|
|
21
23
|
import { runDoctorChecks, defaultDoctorDeps } from './doctor.js';
|
|
22
|
-
import {
|
|
24
|
+
import { reconcileOneiros } from '../oneiros.js';
|
|
25
|
+
import { readProjectSetting, DEFAULT_DB_PATH, CLI_PACKAGE, cliInstallSpec } from './init.js';
|
|
23
26
|
// --- pure: pick the latest published version satisfying a range ---
|
|
24
27
|
/** Semver precedence (-1 / 0 / 1), pre-release aware (`1.0.0-0 < 1.0.0`). */
|
|
25
28
|
export function compareVersions(a, b) {
|
|
@@ -54,6 +57,33 @@ export async function performUpdate(deps, flags) {
|
|
|
54
57
|
deps.err('update: no install found — run `fortytwo init` first.\n');
|
|
55
58
|
return 2;
|
|
56
59
|
}
|
|
60
|
+
if (!flags.dryRun && deps.runtimeActive?.()) {
|
|
61
|
+
deps.err('update: stop the running runtime first (`fortytwo stop`, or your service manager), run `fortytwo update`, then `fortytwo start`. No packages or context were changed.\n');
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
if (deps.runningInstallerVersion) {
|
|
65
|
+
const running = deps.runningInstallerVersion();
|
|
66
|
+
const installed = deps.installedVersion(CLI_PACKAGE);
|
|
67
|
+
if (!running || !deps.persistInstaller) {
|
|
68
|
+
deps.err('update: cannot determine or persist the running installer version.\n');
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
if (installed && compareVersions(running, installed) < 0) {
|
|
72
|
+
deps.err('update: the project installer is newer than this command. Use the project-local fortytwo update.\n');
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const spec = cliInstallSpec(running, installed);
|
|
76
|
+
if (spec) {
|
|
77
|
+
deps.out(`update: pin project installer to ${spec}${flags.dryRun ? ' (dry-run)' : ''}\n`);
|
|
78
|
+
if (!flags.dryRun) {
|
|
79
|
+
const result = deps.persistInstaller(spec);
|
|
80
|
+
if (!result.ok) {
|
|
81
|
+
deps.err(`update: installer persistence failed (${result.detail}). Retry update before starting services.\n`);
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
57
87
|
// The engine = the declared compat siblings that are CURRENTLY installed. We
|
|
58
88
|
// upgrade what's there; we don't pull in siblings the user deliberately omitted.
|
|
59
89
|
const ranges = {};
|
|
@@ -73,6 +103,8 @@ export async function performUpdate(deps, flags) {
|
|
|
73
103
|
const versionOf = (pins, name) => pins.find((p) => p.name === name)?.resolved;
|
|
74
104
|
const changed = target.filter((t) => versionOf(current, t.name) !== t.resolved);
|
|
75
105
|
if (changed.length === 0) {
|
|
106
|
+
if (!flags.dryRun)
|
|
107
|
+
await deps.reconcile();
|
|
76
108
|
deps.out('update: already at the latest in-range version:\n' +
|
|
77
109
|
current.map((p) => ` ${p.name}@${p.resolved} (${p.range})`).join('\n') + '\n');
|
|
78
110
|
return 0;
|
|
@@ -101,8 +133,8 @@ export async function performUpdate(deps, flags) {
|
|
|
101
133
|
deps.err('\nupdate: installed, but doctor reports required checks FAILED. Inspect, then run `fortytwo rollback` to restore the previous set.\n');
|
|
102
134
|
return 1;
|
|
103
135
|
}
|
|
104
|
-
deps.
|
|
105
|
-
deps.out('\nupdate: healthy — engine upgraded.\n');
|
|
136
|
+
await deps.reconcile();
|
|
137
|
+
deps.out('\nupdate: healthy — engine upgraded. Start services with `fortytwo start` (or your service manager).\n');
|
|
106
138
|
return 0;
|
|
107
139
|
}
|
|
108
140
|
export async function performRollback(deps) {
|
|
@@ -144,8 +176,8 @@ export function fetchPublishedVersions(pkg) {
|
|
|
144
176
|
}
|
|
145
177
|
});
|
|
146
178
|
}
|
|
147
|
-
function npmInstall(specs, root) {
|
|
148
|
-
const res = spawnSync('npm', ['install', '--no-audit', '--no-fund', ...specs], { cwd: root, stdio: 'inherit' });
|
|
179
|
+
function npmInstall(specs, root, flags = []) {
|
|
180
|
+
const res = spawnSync('npm', ['install', '--no-audit', '--no-fund', ...flags, ...specs], { cwd: root, stdio: 'inherit' });
|
|
149
181
|
if (res.status === 0)
|
|
150
182
|
return { ok: true, detail: 'ok' };
|
|
151
183
|
return { ok: false, detail: res.error ? res.error.message : `npm exited ${res.status}` };
|
|
@@ -156,15 +188,22 @@ function parseUpdateArgs(argv) {
|
|
|
156
188
|
export async function runUpdate(argv) {
|
|
157
189
|
const root = process.cwd();
|
|
158
190
|
const deps = {
|
|
191
|
+
runningInstallerVersion: readSelfVersion,
|
|
192
|
+
persistInstaller: (spec) => npmInstall([spec], root, ['--save-exact']),
|
|
159
193
|
readState: () => readState(root),
|
|
194
|
+
runtimeActive: () => {
|
|
195
|
+
if (liveRuntime(root))
|
|
196
|
+
return true;
|
|
197
|
+
const heartbeat = defaultDoctorDeps().schedulerHeartbeat();
|
|
198
|
+
return !!heartbeat && Date.now() - Date.parse(heartbeat.ts) <= HEARTBEAT_STALE_MS && isPidAlive(heartbeat.pid);
|
|
199
|
+
},
|
|
160
200
|
installedVersion: readInstalledVersion,
|
|
161
201
|
compatRanges: readSelfCompatRanges(),
|
|
162
202
|
fetchVersions: fetchPublishedVersions,
|
|
163
203
|
install: (specs) => npmInstall(specs, root),
|
|
164
204
|
doctor: () => runDoctorChecks(defaultDoctorDeps()),
|
|
165
205
|
record: (pins) => { recordVersionSet(pins, root); },
|
|
166
|
-
|
|
167
|
-
renderPersona(id, { root }); },
|
|
206
|
+
reconcile: () => reconcileOneiros(root, readProjectSetting('DB_PATH', root) ?? DEFAULT_DB_PATH),
|
|
168
207
|
out: (s) => process.stdout.write(s),
|
|
169
208
|
err: (s) => process.stderr.write(s),
|
|
170
209
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,+EAA+E;AAC/E,6BAA6B;AAC7B,2EAA2E;AAC3E,mDAAmD;AACnD,+BAA+B;AAC/B,iFAAiF;AACjF,+EAA+E;AAC/E,kFAAkF;AAClF,gFAAgF;AAChF,
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Directory of an installed sibling, found by walking up `<dir>/node_modules`
|
|
3
|
+
* from the project root exactly like Node's resolver; null if absent. Pure fs,
|
|
4
|
+
* so import-only `exports` maps (gate, telegram) can't defeat it the way they
|
|
5
|
+
* defeat require.resolve.
|
|
6
|
+
*/
|
|
7
|
+
export declare function findSiblingDir(spec: string, projectRoot?: string): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* The ESM entry file of a package's root export, relative to its dir: `exports`
|
|
10
|
+
* (string, `"."`, or a bare conditions object; conditions `import` > `node` >
|
|
11
|
+
* `default`, nested allowed), then `main`, then `index.js`. Null if the root is
|
|
12
|
+
* not exported for import.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveEsmEntry(pkg: {
|
|
15
|
+
exports?: unknown;
|
|
16
|
+
main?: unknown;
|
|
17
|
+
}): string | null;
|
|
18
|
+
/** Dynamically import an engine sibling from the project; null if it is not installed. */
|
|
19
|
+
export declare function loadSibling<T = Record<string, unknown>>(spec: string, projectRoot?: string): Promise<T | null>;
|
|
3
20
|
export declare const loadGate: () => Promise<{
|
|
4
21
|
POLICY_SCHEMA_VERSION?: number;
|
|
5
22
|
} | null>;
|
|
@@ -16,13 +33,12 @@ export declare const loadMemory: () => Promise<{
|
|
|
16
33
|
} | null>;
|
|
17
34
|
export declare const loadTelegram: () => Promise<Record<string, unknown> | null>;
|
|
18
35
|
/**
|
|
19
|
-
* Installed version of a sibling (its package.json `version`), or
|
|
20
|
-
*
|
|
21
|
-
* immune to import-only `exports` maps (which defeat require.resolve) and to
|
|
22
|
-
* bundled test runners where import.meta.resolve is unavailable. Matches where
|
|
23
|
-
* loadSibling's dynamic import would find the package.
|
|
36
|
+
* Installed version of a sibling in the project (its package.json `version`), or
|
|
37
|
+
* null. Same lookup as loadSibling, so the two always agree.
|
|
24
38
|
*/
|
|
25
|
-
export declare function readInstalledVersion(spec: string): string | null;
|
|
39
|
+
export declare function readInstalledVersion(spec: string, projectRoot?: string): string | null;
|
|
40
|
+
/** This CLI's own version (package.json `version`), or null. */
|
|
41
|
+
export declare function readSelfVersion(): string | null;
|
|
26
42
|
/** The CLI's declared compat ranges (package.json `fortytwo.compat`). */
|
|
27
43
|
export declare function readSelfCompatRanges(): Record<string, string>;
|
|
28
44
|
/**
|
|
@@ -42,6 +58,14 @@ export declare function compareSemver(a: string, b: string): number;
|
|
|
42
58
|
export declare function satisfiesRange(version: string, range: string): boolean;
|
|
43
59
|
/** GET {baseUrl}/api/tags; return installed model names, or null if unreachable. */
|
|
44
60
|
export declare function fetchOllamaModels(baseUrl: string): Promise<string[] | null>;
|
|
61
|
+
/**
|
|
62
|
+
* POST {baseUrl}/api/pull and wait for it to finish. Talks to the server over
|
|
63
|
+
* HTTP — not the local `ollama` CLI, which only reaches localhost — so a remote
|
|
64
|
+
* or sibling-container endpoint works. Streams progress (so a multi-minute pull
|
|
65
|
+
* never sits idle long enough to trip fetch's timeouts). Returns null on success,
|
|
66
|
+
* else a short error.
|
|
67
|
+
*/
|
|
68
|
+
export declare function pullOllamaModel(baseUrl: string, model: string): Promise<string | null>;
|
|
45
69
|
export type MigrationState = 'ok' | 'missing' | 'pending' | 'unavailable';
|
|
46
70
|
/**
|
|
47
71
|
* Migration health for the memory DB: 'missing' (no DB file → run init),
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAoBA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAU/F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAoBzF;AAED,0FAA0F;AAC1F,wBAAsB,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAWnB;AAED,eAAO,MAAM,QAAQ;4BAA+C,MAAM;SAAyB,CAAC;AACpG,eAAO,MAAM,UAAU;mCAEY,MAAM;aAG5B,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,CAAC,EAAE;YAAE,MAAM,EAAE;gBAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE;oBAC1E,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC;SACrB,CAAC;AAC7B,eAAO,MAAM,YAAY,+CAA8C,CAAC;AAExE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAQrG;AAoBD,gEAAgE;AAChE,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAG/C;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7D;AAWD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA0B1D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAYtE;AAED,oFAAoF;AACpF,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CASjF;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkB5F;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAE1E;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAehF"}
|
package/dist/engine.js
CHANGED
|
@@ -7,12 +7,73 @@
|
|
|
7
7
|
// crashes the CLI — a missing sibling makes the dependent command/check report
|
|
8
8
|
// "not installed" instead of throwing.
|
|
9
9
|
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { dirname, join, parse } from 'node:path';
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
11
|
+
import { dirname, join, parse, resolve } from 'node:path';
|
|
12
|
+
// PROJECT-ROOTED RESOLUTION. Siblings live in the TARGET PROJECT's node_modules,
|
|
13
|
+
// never next to the installer: under `npx create-fortytwo` the installer runs from
|
|
14
|
+
// npm's exec cache (~/.npm/_npx/<hash>/node_modules), a separate tree. So every
|
|
15
|
+
// lookup below starts at the project root (the cwd every command operates on),
|
|
16
|
+
// not at import.meta.url — a bare `import(spec)` / `createRequire(import.meta.url)`
|
|
17
|
+
// would resolve from the exec cache and miss the engine `init` just installed.
|
|
18
|
+
/**
|
|
19
|
+
* Directory of an installed sibling, found by walking up `<dir>/node_modules`
|
|
20
|
+
* from the project root exactly like Node's resolver; null if absent. Pure fs,
|
|
21
|
+
* so import-only `exports` maps (gate, telegram) can't defeat it the way they
|
|
22
|
+
* defeat require.resolve.
|
|
23
|
+
*/
|
|
24
|
+
export function findSiblingDir(spec, projectRoot = process.cwd()) {
|
|
25
|
+
const segments = spec.split('/'); // e.g. ['@justfortytwo', 'gate']
|
|
26
|
+
let dir = resolve(projectRoot);
|
|
27
|
+
const root = parse(dir).root;
|
|
28
|
+
while (true) {
|
|
29
|
+
const candidate = join(dir, 'node_modules', ...segments);
|
|
30
|
+
if (existsSync(join(candidate, 'package.json')))
|
|
31
|
+
return candidate;
|
|
32
|
+
if (dir === root)
|
|
33
|
+
return null;
|
|
34
|
+
dir = dirname(dir);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The ESM entry file of a package's root export, relative to its dir: `exports`
|
|
39
|
+
* (string, `"."`, or a bare conditions object; conditions `import` > `node` >
|
|
40
|
+
* `default`, nested allowed), then `main`, then `index.js`. Null if the root is
|
|
41
|
+
* not exported for import.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveEsmEntry(pkg) {
|
|
44
|
+
const pick = (target) => {
|
|
45
|
+
if (typeof target === 'string')
|
|
46
|
+
return target;
|
|
47
|
+
if (!target || typeof target !== 'object' || Array.isArray(target))
|
|
48
|
+
return null;
|
|
49
|
+
const conditions = target;
|
|
50
|
+
for (const c of ['import', 'node', 'default']) {
|
|
51
|
+
if (c in conditions) {
|
|
52
|
+
const hit = pick(conditions[c]);
|
|
53
|
+
if (hit)
|
|
54
|
+
return hit;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
const { exports } = pkg;
|
|
60
|
+
if (exports !== undefined && exports !== null) {
|
|
61
|
+
const isSubpathMap = typeof exports === 'object' && !Array.isArray(exports) && Object.keys(exports).some((k) => k.startsWith('.'));
|
|
62
|
+
return pick(isSubpathMap ? exports['.'] : exports);
|
|
63
|
+
}
|
|
64
|
+
return typeof pkg.main === 'string' && pkg.main ? pkg.main : 'index.js';
|
|
65
|
+
}
|
|
66
|
+
/** Dynamically import an engine sibling from the project; null if it is not installed. */
|
|
67
|
+
export async function loadSibling(spec, projectRoot = process.cwd()) {
|
|
68
|
+
const dir = findSiblingDir(spec, projectRoot);
|
|
69
|
+
if (!dir)
|
|
70
|
+
return null;
|
|
14
71
|
try {
|
|
15
|
-
|
|
72
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
|
73
|
+
const entry = resolveEsmEntry(pkg);
|
|
74
|
+
if (!entry)
|
|
75
|
+
return null;
|
|
76
|
+
return (await import(pathToFileURL(join(dir, entry)).href));
|
|
16
77
|
}
|
|
17
78
|
catch {
|
|
18
79
|
return null;
|
|
@@ -22,29 +83,18 @@ export const loadGate = () => loadSibling('@justfortytwo/gate');
|
|
|
22
83
|
export const loadMemory = () => loadSibling('@justfortytwo/memory');
|
|
23
84
|
export const loadTelegram = () => loadSibling('@justfortytwo/telegram');
|
|
24
85
|
/**
|
|
25
|
-
* Installed version of a sibling (its package.json `version`), or
|
|
26
|
-
*
|
|
27
|
-
* immune to import-only `exports` maps (which defeat require.resolve) and to
|
|
28
|
-
* bundled test runners where import.meta.resolve is unavailable. Matches where
|
|
29
|
-
* loadSibling's dynamic import would find the package.
|
|
86
|
+
* Installed version of a sibling in the project (its package.json `version`), or
|
|
87
|
+
* null. Same lookup as loadSibling, so the two always agree.
|
|
30
88
|
*/
|
|
31
|
-
export function readInstalledVersion(spec) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (dir === root)
|
|
46
|
-
return null;
|
|
47
|
-
dir = dirname(dir);
|
|
89
|
+
export function readInstalledVersion(spec, projectRoot = process.cwd()) {
|
|
90
|
+
const dir = findSiblingDir(spec, projectRoot);
|
|
91
|
+
if (!dir)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version ?? null;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
48
98
|
}
|
|
49
99
|
}
|
|
50
100
|
/** Walk up from this module to find the installer's own package.json. */
|
|
@@ -66,6 +116,11 @@ function findSelfPackageJson() {
|
|
|
66
116
|
dir = dirname(dir);
|
|
67
117
|
}
|
|
68
118
|
}
|
|
119
|
+
/** This CLI's own version (package.json `version`), or null. */
|
|
120
|
+
export function readSelfVersion() {
|
|
121
|
+
const v = findSelfPackageJson()?.version;
|
|
122
|
+
return typeof v === 'string' ? v : null;
|
|
123
|
+
}
|
|
69
124
|
/** The CLI's declared compat ranges (package.json `fortytwo.compat`). */
|
|
70
125
|
export function readSelfCompatRanges() {
|
|
71
126
|
const pkg = findSelfPackageJson();
|
|
@@ -159,6 +214,41 @@ export async function fetchOllamaModels(baseUrl) {
|
|
|
159
214
|
return null;
|
|
160
215
|
}
|
|
161
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* POST {baseUrl}/api/pull and wait for it to finish. Talks to the server over
|
|
219
|
+
* HTTP — not the local `ollama` CLI, which only reaches localhost — so a remote
|
|
220
|
+
* or sibling-container endpoint works. Streams progress (so a multi-minute pull
|
|
221
|
+
* never sits idle long enough to trip fetch's timeouts). Returns null on success,
|
|
222
|
+
* else a short error.
|
|
223
|
+
*/
|
|
224
|
+
export async function pullOllamaModel(baseUrl, model) {
|
|
225
|
+
try {
|
|
226
|
+
const res = await fetch(`${baseUrl.replace(/\/$/, '')}/api/pull`, {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'content-type': 'application/json' },
|
|
229
|
+
body: JSON.stringify({ model, name: model, stream: true }), // `name`: older Ollama servers
|
|
230
|
+
});
|
|
231
|
+
if (!res.ok)
|
|
232
|
+
return `HTTP ${res.status}`;
|
|
233
|
+
let last = {};
|
|
234
|
+
for (const line of (await res.text()).split('\n')) {
|
|
235
|
+
if (!line.trim())
|
|
236
|
+
continue;
|
|
237
|
+
try {
|
|
238
|
+
last = JSON.parse(line);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (last.error)
|
|
244
|
+
return last.error;
|
|
245
|
+
}
|
|
246
|
+
return last.status === 'success' ? null : `pull ended with status "${last.status ?? 'unknown'}"`;
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
return err.message;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
162
252
|
/**
|
|
163
253
|
* Migration health for the memory DB: 'missing' (no DB file → run init),
|
|
164
254
|
* 'ok' (the migrated `memories` table exists), 'pending' (DB present but not
|