gnosys 5.11.3 → 5.12.0
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/cli.js +332 -5151
- package/dist/index.js +364 -235
- package/dist/lib/addCommand.d.ts +9 -0
- package/dist/lib/addCommand.js +103 -0
- package/dist/lib/addStructuredCommand.d.ts +16 -0
- package/dist/lib/addStructuredCommand.js +103 -0
- package/dist/lib/ambiguityCommand.d.ts +4 -0
- package/dist/lib/ambiguityCommand.js +36 -0
- package/dist/lib/apiKeyVault.d.ts +78 -0
- package/dist/lib/apiKeyVault.js +447 -0
- package/dist/lib/askCommand.d.ts +13 -0
- package/dist/lib/askCommand.js +145 -0
- package/dist/lib/audioExtract.js +4 -1
- package/dist/lib/auditCommand.d.ts +7 -0
- package/dist/lib/auditCommand.js +27 -0
- package/dist/lib/backupCommand.d.ts +6 -0
- package/dist/lib/backupCommand.js +54 -0
- package/dist/lib/bootstrapCommand.d.ts +15 -0
- package/dist/lib/bootstrapCommand.js +51 -0
- package/dist/lib/briefingCommand.d.ts +7 -0
- package/dist/lib/briefingCommand.js +92 -0
- package/dist/lib/centralizeCommand.d.ts +5 -0
- package/dist/lib/centralizeCommand.js +16 -0
- package/dist/lib/chatCommand.d.ts +12 -0
- package/dist/lib/chatCommand.js +46 -0
- package/dist/lib/checkCommand.d.ts +4 -0
- package/dist/lib/checkCommand.js +133 -0
- package/dist/lib/clientReadOverlay.d.ts +27 -0
- package/dist/lib/clientReadOverlay.js +73 -0
- package/dist/lib/clientReadResolve.d.ts +32 -0
- package/dist/lib/clientReadResolve.js +84 -0
- package/dist/lib/commitContextCommand.d.ts +9 -0
- package/dist/lib/commitContextCommand.js +142 -0
- package/dist/lib/config.d.ts +43 -3
- package/dist/lib/config.js +58 -57
- package/dist/lib/configCommand.d.ts +10 -0
- package/dist/lib/configCommand.js +321 -0
- package/dist/lib/connectCommand.d.ts +8 -0
- package/dist/lib/connectCommand.js +19 -0
- package/dist/lib/db.d.ts +52 -0
- package/dist/lib/db.js +169 -1
- package/dist/lib/dearchiveCommand.d.ts +7 -0
- package/dist/lib/dearchiveCommand.js +41 -0
- package/dist/lib/discoverCommand.d.ts +9 -0
- package/dist/lib/discoverCommand.js +87 -0
- package/dist/lib/doctorCommand.d.ts +6 -0
- package/dist/lib/doctorCommand.js +256 -0
- package/dist/lib/dream.d.ts +42 -2
- package/dist/lib/dream.js +290 -30
- package/dist/lib/dreamCommand.d.ts +10 -0
- package/dist/lib/dreamCommand.js +195 -0
- package/dist/lib/dreamLaunchd.d.ts +2 -0
- package/dist/lib/dreamLaunchd.js +72 -0
- package/dist/lib/dreamLogCommand.d.ts +10 -0
- package/dist/lib/dreamLogCommand.js +58 -0
- package/dist/lib/dreamReport.d.ts +7 -0
- package/dist/lib/dreamReport.js +114 -0
- package/dist/lib/dreamRunLog.d.ts +121 -0
- package/dist/lib/dreamRunLog.js +212 -0
- package/dist/lib/embeddings.js +3 -0
- package/dist/lib/exportCommand.d.ts +18 -0
- package/dist/lib/exportCommand.js +101 -0
- package/dist/lib/fsearchCommand.d.ts +8 -0
- package/dist/lib/fsearchCommand.js +44 -0
- package/dist/lib/graphCommand.d.ts +4 -0
- package/dist/lib/graphCommand.js +68 -0
- package/dist/lib/helperGenerateCommand.d.ts +5 -0
- package/dist/lib/helperGenerateCommand.js +27 -0
- package/dist/lib/historyCommand.d.ts +5 -0
- package/dist/lib/historyCommand.js +51 -0
- package/dist/lib/hybridSearchCommand.d.ts +12 -0
- package/dist/lib/hybridSearchCommand.js +95 -0
- package/dist/lib/ideMcpInstall.d.ts +30 -0
- package/dist/lib/ideMcpInstall.js +102 -0
- package/dist/lib/importCommand.d.ts +16 -0
- package/dist/lib/importCommand.js +89 -0
- package/dist/lib/importProjectCommand.d.ts +6 -0
- package/dist/lib/importProjectCommand.js +43 -0
- package/dist/lib/ingestCommand.d.ts +13 -0
- package/dist/lib/ingestCommand.js +95 -0
- package/dist/lib/installOutput.d.ts +36 -0
- package/dist/lib/installOutput.js +55 -0
- package/dist/lib/lensCommand.d.ts +20 -0
- package/dist/lib/lensCommand.js +61 -0
- package/dist/lib/lensing.d.ts +1 -0
- package/dist/lib/lensing.js +50 -9
- package/dist/lib/linksCommand.d.ts +7 -0
- package/dist/lib/linksCommand.js +48 -0
- package/dist/lib/listCommand.d.ts +8 -0
- package/dist/lib/listCommand.js +74 -0
- package/dist/lib/llm.d.ts +1 -1
- package/dist/lib/llm.js +26 -8
- package/dist/lib/localDiskCheck.d.ts +17 -0
- package/dist/lib/localDiskCheck.js +54 -0
- package/dist/lib/machineConfig.d.ts +11 -1
- package/dist/lib/machineConfig.js +16 -0
- package/dist/lib/machineRegistry.d.ts +61 -0
- package/dist/lib/machineRegistry.js +80 -0
- package/dist/lib/maintainCommand.d.ts +8 -0
- package/dist/lib/maintainCommand.js +34 -0
- package/dist/lib/masterLease.d.ts +20 -0
- package/dist/lib/masterLease.js +68 -0
- package/dist/lib/migrateCommand.d.ts +7 -0
- package/dist/lib/migrateCommand.js +158 -0
- package/dist/lib/migrateDbCommand.d.ts +9 -0
- package/dist/lib/migrateDbCommand.js +94 -0
- package/dist/lib/modelValidation.d.ts +5 -0
- package/dist/lib/modelValidation.js +27 -0
- package/dist/lib/openrouterTiers.d.ts +29 -0
- package/dist/lib/openrouterTiers.js +113 -0
- package/dist/lib/prefCommand.d.ts +10 -0
- package/dist/lib/prefCommand.js +118 -0
- package/dist/lib/projectsCommand.d.ts +8 -0
- package/dist/lib/projectsCommand.js +131 -0
- package/dist/lib/readCommand.d.ts +7 -0
- package/dist/lib/readCommand.js +62 -0
- package/dist/lib/recall.d.ts +3 -0
- package/dist/lib/recall.js +19 -4
- package/dist/lib/recallCommand.d.ts +11 -0
- package/dist/lib/recallCommand.js +112 -0
- package/dist/lib/reflectCommand.d.ts +8 -0
- package/dist/lib/reflectCommand.js +61 -0
- package/dist/lib/reindexCommand.d.ts +4 -0
- package/dist/lib/reindexCommand.js +34 -0
- package/dist/lib/reindexGraphCommand.d.ts +4 -0
- package/dist/lib/reindexGraphCommand.js +12 -0
- package/dist/lib/reinforceCommand.d.ts +8 -0
- package/dist/lib/reinforceCommand.js +40 -0
- package/dist/lib/remote.d.ts +5 -1
- package/dist/lib/remote.js +5 -1
- package/dist/lib/remoteWizard.d.ts +24 -5
- package/dist/lib/remoteWizard.js +308 -319
- package/dist/lib/restoreCommand.d.ts +5 -0
- package/dist/lib/restoreCommand.js +35 -0
- package/dist/lib/sandboxStartCommand.d.ts +6 -0
- package/dist/lib/sandboxStartCommand.js +25 -0
- package/dist/lib/sandboxStatusCommand.d.ts +4 -0
- package/dist/lib/sandboxStatusCommand.js +24 -0
- package/dist/lib/sandboxStopCommand.d.ts +4 -0
- package/dist/lib/sandboxStopCommand.js +21 -0
- package/dist/lib/searchCommand.d.ts +9 -0
- package/dist/lib/searchCommand.js +90 -0
- package/dist/lib/semanticSearchCommand.d.ts +8 -0
- package/dist/lib/semanticSearchCommand.js +52 -0
- package/dist/lib/setup/configSetRender.js +2 -0
- package/dist/lib/setup/providerGlyphs.d.ts +19 -0
- package/dist/lib/setup/providerGlyphs.js +42 -0
- package/dist/lib/setup/remoteRender.d.ts +31 -1
- package/dist/lib/setup/remoteRender.js +95 -4
- package/dist/lib/setup/sections/ides.d.ts +7 -0
- package/dist/lib/setup/sections/ides.js +24 -2
- package/dist/lib/setup/sections/providers.d.ts +17 -0
- package/dist/lib/setup/sections/providers.js +255 -0
- package/dist/lib/setup/sections/routing.d.ts +2 -6
- package/dist/lib/setup/sections/routing.js +33 -85
- package/dist/lib/setup/sections/taskRoutingEditor.d.ts +17 -0
- package/dist/lib/setup/sections/taskRoutingEditor.js +149 -0
- package/dist/lib/setup/summary.d.ts +9 -0
- package/dist/lib/setup/summary.js +51 -37
- package/dist/lib/setup/ui/status.d.ts +1 -0
- package/dist/lib/setup/ui/status.js +2 -0
- package/dist/lib/setup.d.ts +108 -3
- package/dist/lib/setup.js +813 -303
- package/dist/lib/setupKeys.d.ts +42 -0
- package/dist/lib/setupKeys.js +564 -0
- package/dist/lib/setupRemoteCommand.d.ts +4 -0
- package/dist/lib/setupRemoteCommand.js +28 -0
- package/dist/lib/setupRemotePullCommand.d.ts +5 -0
- package/dist/lib/setupRemotePullCommand.js +52 -0
- package/dist/lib/setupRemotePushCommand.d.ts +5 -0
- package/dist/lib/setupRemotePushCommand.js +57 -0
- package/dist/lib/setupRemoteResolveCommand.d.ts +4 -0
- package/dist/lib/setupRemoteResolveCommand.js +48 -0
- package/dist/lib/setupRemoteStatusCommand.d.ts +4 -0
- package/dist/lib/setupRemoteStatusCommand.js +73 -0
- package/dist/lib/setupRemoteSyncCommand.d.ts +6 -0
- package/dist/lib/setupRemoteSyncCommand.js +65 -0
- package/dist/lib/setupSyncProjectsCommand.d.ts +4 -0
- package/dist/lib/setupSyncProjectsCommand.js +292 -0
- package/dist/lib/staleCommand.d.ts +8 -0
- package/dist/lib/staleCommand.js +34 -0
- package/dist/lib/statsCommand.d.ts +6 -0
- package/dist/lib/statsCommand.js +142 -0
- package/dist/lib/statusCommand.d.ts +18 -0
- package/dist/lib/statusCommand.js +250 -0
- package/dist/lib/storesCommand.d.ts +2 -0
- package/dist/lib/storesCommand.js +4 -0
- package/dist/lib/syncClient.d.ts +47 -0
- package/dist/lib/syncClient.js +212 -0
- package/dist/lib/syncCommand.d.ts +6 -0
- package/dist/lib/syncCommand.js +57 -0
- package/dist/lib/syncDoctorCommand.d.ts +5 -0
- package/dist/lib/syncDoctorCommand.js +100 -0
- package/dist/lib/syncIngest.d.ts +19 -0
- package/dist/lib/syncIngest.js +152 -0
- package/dist/lib/syncIngestLaunchd.d.ts +8 -0
- package/dist/lib/syncIngestLaunchd.js +93 -0
- package/dist/lib/syncIngestStartup.d.ts +5 -0
- package/dist/lib/syncIngestStartup.js +29 -0
- package/dist/lib/syncIngestSystemd.d.ts +10 -0
- package/dist/lib/syncIngestSystemd.js +97 -0
- package/dist/lib/syncIngestTimer.d.ts +8 -0
- package/dist/lib/syncIngestTimer.js +27 -0
- package/dist/lib/syncIngestTimerCommand.d.ts +7 -0
- package/dist/lib/syncIngestTimerCommand.js +83 -0
- package/dist/lib/syncLock.d.ts +6 -0
- package/dist/lib/syncLock.js +74 -0
- package/dist/lib/syncSnapshot.d.ts +30 -0
- package/dist/lib/syncSnapshot.js +184 -0
- package/dist/lib/syncStaging.d.ts +81 -0
- package/dist/lib/syncStaging.js +239 -0
- package/dist/lib/tagsAddCommand.d.ts +8 -0
- package/dist/lib/tagsAddCommand.js +18 -0
- package/dist/lib/tagsCommand.d.ts +4 -0
- package/dist/lib/tagsCommand.js +16 -0
- package/dist/lib/timelineCommand.d.ts +7 -0
- package/dist/lib/timelineCommand.js +49 -0
- package/dist/lib/traceCommand.d.ts +6 -0
- package/dist/lib/traceCommand.js +39 -0
- package/dist/lib/traverseCommand.d.ts +6 -0
- package/dist/lib/traverseCommand.js +58 -0
- package/dist/lib/updateCommand.d.ts +13 -0
- package/dist/lib/updateCommand.js +67 -0
- package/dist/lib/updateStatusCommand.d.ts +5 -0
- package/dist/lib/updateStatusCommand.js +38 -0
- package/dist/lib/webAddCommand.d.ts +8 -0
- package/dist/lib/webAddCommand.js +55 -0
- package/dist/lib/webBuildCommand.d.ts +10 -0
- package/dist/lib/webBuildCommand.js +65 -0
- package/dist/lib/webBuildIndexCommand.d.ts +8 -0
- package/dist/lib/webBuildIndexCommand.js +37 -0
- package/dist/lib/webIngestCommand.d.ts +11 -0
- package/dist/lib/webIngestCommand.js +51 -0
- package/dist/lib/webInitCommand.d.ts +9 -0
- package/dist/lib/webInitCommand.js +167 -0
- package/dist/lib/webRemoveCommand.d.ts +5 -0
- package/dist/lib/webRemoveCommand.js +41 -0
- package/dist/lib/webStatusCommand.d.ts +5 -0
- package/dist/lib/webStatusCommand.js +94 -0
- package/dist/lib/webUpdateCommand.d.ts +7 -0
- package/dist/lib/webUpdateCommand.js +72 -0
- package/dist/lib/workingSetCommand.d.ts +6 -0
- package/dist/lib/workingSetCommand.js +37 -0
- package/package.json +2 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
export async function runMaintainCommand(getResolver, opts) {
|
|
3
|
+
const { GnosysMaintenanceEngine, formatMaintenanceReport } = await import("./maintenance.js");
|
|
4
|
+
const resolver = await getResolver();
|
|
5
|
+
const stores = resolver.getStores();
|
|
6
|
+
if (stores.length === 0) {
|
|
7
|
+
console.error("No Gnosys stores found. Run gnosys init first.");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
const cfg = await loadConfig(stores[0].path);
|
|
11
|
+
const engine = new GnosysMaintenanceEngine(resolver, cfg);
|
|
12
|
+
const report = await engine.maintain({
|
|
13
|
+
dryRun: opts.dryRun,
|
|
14
|
+
autoApply: opts.autoApply,
|
|
15
|
+
onLog: (level, message) => {
|
|
16
|
+
if (level === "warn") {
|
|
17
|
+
console.error(`⚠ ${message}`);
|
|
18
|
+
}
|
|
19
|
+
else if (level === "action") {
|
|
20
|
+
console.log(`→ ${message}`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.log(message);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
onProgress: (step, current, total) => {
|
|
27
|
+
process.stdout.write(`\r[${current}/${total}] ${step}...`);
|
|
28
|
+
if (current === total)
|
|
29
|
+
process.stdout.write("\n");
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
console.log("");
|
|
33
|
+
console.log(formatMaintenanceReport(report));
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v13 master.json ownership marker — epoch-fenced lease on the master folder.
|
|
3
|
+
*/
|
|
4
|
+
export declare const MASTER_MARKER_FILE = "master.json";
|
|
5
|
+
export interface MasterMarker {
|
|
6
|
+
epoch: number;
|
|
7
|
+
holderMachineId: string;
|
|
8
|
+
hostname: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function masterMarkerPath(masterPath: string): string;
|
|
12
|
+
export declare function readMasterMarker(masterPath: string): MasterMarker | null;
|
|
13
|
+
/** Bump epoch when claiming; reuse epoch+1 on stale takeover. */
|
|
14
|
+
export declare function writeMasterMarker(masterPath: string, machineId: string, opts?: {
|
|
15
|
+
previousEpoch?: number;
|
|
16
|
+
}): MasterMarker;
|
|
17
|
+
/** Heartbeat refresh — same epoch, new updatedAt (transient write failures must not demote). */
|
|
18
|
+
export declare function touchMasterMarkerHeartbeat(masterPath: string): MasterMarker | null;
|
|
19
|
+
export declare function assertMasterLeaseHeld(masterPath: string, machineId: string): void;
|
|
20
|
+
export declare function validateLeaseEpochBeforeWrite(masterPath: string, expectedEpoch: number, machineId: string): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v13 master.json ownership marker — epoch-fenced lease on the master folder.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { atomicWriteFileSync } from "./atomicWrite.js";
|
|
7
|
+
import { ensureMachineConfig } from "./machineConfig.js";
|
|
8
|
+
export const MASTER_MARKER_FILE = "master.json";
|
|
9
|
+
export function masterMarkerPath(masterPath) {
|
|
10
|
+
return path.join(masterPath, MASTER_MARKER_FILE);
|
|
11
|
+
}
|
|
12
|
+
export function readMasterMarker(masterPath) {
|
|
13
|
+
const p = masterMarkerPath(masterPath);
|
|
14
|
+
if (!existsSync(p))
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
const raw = readFileSync(p, "utf-8");
|
|
18
|
+
const o = JSON.parse(raw);
|
|
19
|
+
if (typeof o.epoch !== "number" || !o.holderMachineId)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
epoch: o.epoch,
|
|
23
|
+
holderMachineId: o.holderMachineId,
|
|
24
|
+
hostname: String(o.hostname ?? ""),
|
|
25
|
+
updatedAt: String(o.updatedAt ?? ""),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Bump epoch when claiming; reuse epoch+1 on stale takeover. */
|
|
33
|
+
export function writeMasterMarker(masterPath, machineId, opts) {
|
|
34
|
+
const nextEpoch = (opts?.previousEpoch ?? 0) + 1;
|
|
35
|
+
const marker = {
|
|
36
|
+
epoch: nextEpoch,
|
|
37
|
+
holderMachineId: machineId,
|
|
38
|
+
hostname: ensureMachineConfig().config.hostname,
|
|
39
|
+
updatedAt: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
atomicWriteFileSync(masterMarkerPath(masterPath), JSON.stringify(marker, null, 2) + "\n");
|
|
42
|
+
return marker;
|
|
43
|
+
}
|
|
44
|
+
/** Heartbeat refresh — same epoch, new updatedAt (transient write failures must not demote). */
|
|
45
|
+
export function touchMasterMarkerHeartbeat(masterPath) {
|
|
46
|
+
const existing = readMasterMarker(masterPath);
|
|
47
|
+
if (!existing)
|
|
48
|
+
return null;
|
|
49
|
+
const refreshed = { ...existing, updatedAt: new Date().toISOString() };
|
|
50
|
+
atomicWriteFileSync(masterMarkerPath(masterPath), JSON.stringify(refreshed, null, 2) + "\n");
|
|
51
|
+
return refreshed;
|
|
52
|
+
}
|
|
53
|
+
export function assertMasterLeaseHeld(masterPath, machineId) {
|
|
54
|
+
const marker = readMasterMarker(masterPath);
|
|
55
|
+
if (!marker) {
|
|
56
|
+
throw new Error("master.json missing — this folder is not an active master");
|
|
57
|
+
}
|
|
58
|
+
if (marker.holderMachineId !== machineId) {
|
|
59
|
+
throw new Error(`master lease held by ${marker.holderMachineId} (epoch ${marker.epoch}), not this machine`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function validateLeaseEpochBeforeWrite(masterPath, expectedEpoch, machineId) {
|
|
63
|
+
assertMasterLeaseHeld(masterPath, machineId);
|
|
64
|
+
const marker = readMasterMarker(masterPath);
|
|
65
|
+
if (!marker || marker.epoch !== expectedEpoch) {
|
|
66
|
+
throw new Error("master lease epoch changed — aborting write");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { GnosysDB } from "./db.js";
|
|
4
|
+
import { findProjectIdentity, readProjectIdentity, migrateProject } from "./projectIdentity.js";
|
|
5
|
+
export async function runMigrateCommand(opts) {
|
|
6
|
+
const { createInterface } = await import("readline/promises");
|
|
7
|
+
const rl = opts.yes ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
+
let centralDb = null;
|
|
9
|
+
const ask = async (question, defaultValue) => {
|
|
10
|
+
if (!rl)
|
|
11
|
+
return defaultValue || "";
|
|
12
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
13
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
14
|
+
return answer || defaultValue || "";
|
|
15
|
+
};
|
|
16
|
+
const fail = (message) => {
|
|
17
|
+
console.error(message);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
console.log("\n── Gnosys Project Migration ──\n");
|
|
22
|
+
let sourceDir;
|
|
23
|
+
if (opts.from) {
|
|
24
|
+
sourceDir = path.resolve(opts.from);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const found = await findProjectIdentity(process.cwd());
|
|
28
|
+
const defaultSource = found ? found.projectRoot : "";
|
|
29
|
+
const sourceInput = await ask("Source directory (contains .gnosys/)", defaultSource);
|
|
30
|
+
if (!sourceInput) {
|
|
31
|
+
fail("No source directory provided.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
sourceDir = path.resolve(sourceInput);
|
|
35
|
+
}
|
|
36
|
+
const storePath = path.join(sourceDir, ".gnosys");
|
|
37
|
+
try {
|
|
38
|
+
await fs.stat(storePath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
fail(`No .gnosys/ directory found at ${sourceDir}`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const identity = await readProjectIdentity(sourceDir);
|
|
45
|
+
const { glob } = await import("glob");
|
|
46
|
+
const memFiles = await glob("**/*.md", {
|
|
47
|
+
cwd: storePath,
|
|
48
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
49
|
+
});
|
|
50
|
+
console.log("\nSource project:");
|
|
51
|
+
if (identity) {
|
|
52
|
+
console.log(` Name: ${identity.projectName}`);
|
|
53
|
+
console.log(` ID: ${identity.projectId}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(` Name: (unregistered — pre-v3 store)`);
|
|
57
|
+
}
|
|
58
|
+
console.log(` Directory: ${sourceDir}`);
|
|
59
|
+
console.log(` Memories: ${memFiles.length} markdown files`);
|
|
60
|
+
let targetDir;
|
|
61
|
+
if (opts.to) {
|
|
62
|
+
targetDir = path.resolve(opts.to);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const targetInput = await ask("\nTarget directory (where .gnosys/ should live)");
|
|
66
|
+
if (!targetInput) {
|
|
67
|
+
fail("No target directory provided.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
targetDir = path.resolve(targetInput);
|
|
71
|
+
}
|
|
72
|
+
const defaultName = opts.name || path.basename(targetDir);
|
|
73
|
+
const newName = opts.yes
|
|
74
|
+
? defaultName
|
|
75
|
+
: await ask("Project name", defaultName);
|
|
76
|
+
let doSync = true;
|
|
77
|
+
let doDelete = true;
|
|
78
|
+
if (!opts.yes) {
|
|
79
|
+
const syncAnswer = await ask("\nSync memories to central DB?", "Y");
|
|
80
|
+
doSync = syncAnswer.toLowerCase() !== "n" && syncAnswer.toLowerCase() !== "no";
|
|
81
|
+
const deleteAnswer = await ask("Delete old .gnosys/ after migration?", "Y");
|
|
82
|
+
doDelete = deleteAnswer.toLowerCase() !== "n" && deleteAnswer.toLowerCase() !== "no";
|
|
83
|
+
}
|
|
84
|
+
console.log("\n── Migration Summary ──");
|
|
85
|
+
console.log(` From: ${sourceDir}/.gnosys/`);
|
|
86
|
+
console.log(` To: ${targetDir}/.gnosys/`);
|
|
87
|
+
console.log(` Name: ${identity?.projectName || "(new)"} → ${newName}`);
|
|
88
|
+
console.log(` Memories: ${memFiles.length} files`);
|
|
89
|
+
console.log(` Sync to DB: ${doSync ? "yes" : "no"}`);
|
|
90
|
+
console.log(` Delete old: ${doDelete ? "yes" : "no"}`);
|
|
91
|
+
if (!opts.yes) {
|
|
92
|
+
const confirm = await ask("\nProceed?", "Y");
|
|
93
|
+
if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") {
|
|
94
|
+
console.log("Aborted.");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
centralDb = GnosysDB.openCentral();
|
|
100
|
+
if (!centralDb.isAvailable())
|
|
101
|
+
centralDb = null;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
centralDb = null;
|
|
105
|
+
}
|
|
106
|
+
console.log("\nMigrating...");
|
|
107
|
+
const result = await migrateProject({
|
|
108
|
+
sourcePath: sourceDir,
|
|
109
|
+
targetPath: targetDir,
|
|
110
|
+
newName,
|
|
111
|
+
deleteSource: doDelete,
|
|
112
|
+
centralDb: centralDb || undefined,
|
|
113
|
+
});
|
|
114
|
+
console.log(` Copied ${result.memoryFileCount} memory files`);
|
|
115
|
+
console.log(` Project: ${result.newIdentity.projectName} (${result.newIdentity.projectId})`);
|
|
116
|
+
console.log(` Path: ${result.newIdentity.workingDirectory}`);
|
|
117
|
+
console.log(` Central DB: ${centralDb ? "updated ✓" : "not available"}`);
|
|
118
|
+
if (doSync && centralDb) {
|
|
119
|
+
console.log("\nSyncing memories to central DB...");
|
|
120
|
+
const matter = (await import("gray-matter")).default;
|
|
121
|
+
const { syncMemoryToDb } = await import("./dbWrite.js");
|
|
122
|
+
const newStorePath = path.join(targetDir, ".gnosys");
|
|
123
|
+
const mdFiles = await glob("**/*.md", {
|
|
124
|
+
cwd: newStorePath,
|
|
125
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
126
|
+
});
|
|
127
|
+
let synced = 0;
|
|
128
|
+
for (const file of mdFiles) {
|
|
129
|
+
try {
|
|
130
|
+
const filePath = path.join(newStorePath, file);
|
|
131
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
132
|
+
const parsed = matter(raw);
|
|
133
|
+
if (parsed.data?.id) {
|
|
134
|
+
syncMemoryToDb(centralDb, parsed.data, parsed.content, filePath, result.newIdentity.projectId, "project");
|
|
135
|
+
synced++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Skip files that fail to parse
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log(` Synced ${synced} memories to central DB`);
|
|
143
|
+
}
|
|
144
|
+
if (doDelete) {
|
|
145
|
+
console.log(`\nOld .gnosys/ at ${sourceDir} removed.`);
|
|
146
|
+
}
|
|
147
|
+
console.log(`\nMigration complete! Run 'gnosys projects' to verify.`);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
151
|
+
console.error(`\nMigration failed: ${msg}`);
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
rl?.close();
|
|
156
|
+
centralDb?.close();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GnosysResolver } from "./resolver.js";
|
|
2
|
+
export type MigrateDbCommandOptions = {
|
|
3
|
+
toCentral?: boolean;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type MigrateDbCommandContext = {
|
|
7
|
+
getResolver: () => Promise<GnosysResolver>;
|
|
8
|
+
};
|
|
9
|
+
export declare function runMigrateDbCommand(opts: MigrateDbCommandOptions, context: MigrateDbCommandContext): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { GnosysDB } from "./db.js";
|
|
3
|
+
import { createProjectIdentity } from "./projectIdentity.js";
|
|
4
|
+
export async function runMigrateDbCommand(opts, context) {
|
|
5
|
+
if (!opts.toCentral) {
|
|
6
|
+
const resolver = await context.getResolver();
|
|
7
|
+
const writeTarget = resolver.getWriteTarget();
|
|
8
|
+
if (!writeTarget) {
|
|
9
|
+
console.error("No writable store found. Run 'gnosys init' first.");
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const { migrate, formatMigrationReport } = await import("./migrate.js");
|
|
14
|
+
const stats = await migrate(writeTarget.store.getStorePath(), { verbose: opts.verbose });
|
|
15
|
+
console.log(formatMigrationReport(stats));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log("Migrating per-project stores to central DB (~/.gnosys/gnosys.db)...\n");
|
|
19
|
+
let centralDb = null;
|
|
20
|
+
try {
|
|
21
|
+
try {
|
|
22
|
+
centralDb = GnosysDB.openCentral();
|
|
23
|
+
if (!centralDb.isAvailable()) {
|
|
24
|
+
console.error("Central DB not available (better-sqlite3 missing).");
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
console.error(`Cannot open central DB: ${err instanceof Error ? err.message : err}`);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const resolver = await context.getResolver();
|
|
35
|
+
const detectedStores = await resolver.detectAllStores();
|
|
36
|
+
const projectDirs = detectedStores
|
|
37
|
+
.filter(s => s.hasGnosys)
|
|
38
|
+
.map(s => s.path);
|
|
39
|
+
if (projectDirs.length === 0) {
|
|
40
|
+
console.log("No per-project stores found to migrate.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(`Found ${projectDirs.length} project store(s) to migrate:\n`);
|
|
44
|
+
let totalMemories = 0;
|
|
45
|
+
let totalProjects = 0;
|
|
46
|
+
for (const projectDir of projectDirs) {
|
|
47
|
+
const storePath = path.join(projectDir, ".gnosys");
|
|
48
|
+
const log = opts.verbose ? console.log : () => { };
|
|
49
|
+
let projectDb = null;
|
|
50
|
+
try {
|
|
51
|
+
const identity = await createProjectIdentity(projectDir, {
|
|
52
|
+
centralDb: centralDb,
|
|
53
|
+
});
|
|
54
|
+
log(` [${identity.projectName}] ID: ${identity.projectId}`);
|
|
55
|
+
projectDb = new GnosysDB(storePath);
|
|
56
|
+
if (!projectDb.isAvailable() || !projectDb.isMigrated()) {
|
|
57
|
+
log(` [${identity.projectName}] No migrated gnosys.db — skipping`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const memories = projectDb.getAllMemories();
|
|
61
|
+
let count = 0;
|
|
62
|
+
centralDb.transaction(() => {
|
|
63
|
+
for (const mem of memories) {
|
|
64
|
+
centralDb.insertMemory({
|
|
65
|
+
...mem,
|
|
66
|
+
project_id: identity.projectId,
|
|
67
|
+
scope: "project",
|
|
68
|
+
});
|
|
69
|
+
count++;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
totalMemories += count;
|
|
73
|
+
totalProjects++;
|
|
74
|
+
console.log(` ✓ ${identity.projectName}: ${count} memories migrated`);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error(` ✗ ${projectDir}: ${err instanceof Error ? err.message : err}`);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
projectDb?.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
console.log(`\n╔════════════════════════════════════════╗`);
|
|
84
|
+
console.log(`║ Central Migration Complete ║`);
|
|
85
|
+
console.log(`╚════════════════════════════════════════╝`);
|
|
86
|
+
console.log(` Projects migrated: ${totalProjects}`);
|
|
87
|
+
console.log(` Memories imported: ${totalMemories}`);
|
|
88
|
+
console.log(`\n Per-project gnosys.db files are untouched.`);
|
|
89
|
+
console.log(` Central DB: ${GnosysDB.getCentralDbPath()}`);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
centralDb?.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -11,6 +11,11 @@ export interface ValidationResult {
|
|
|
11
11
|
error?: string;
|
|
12
12
|
latencyMs?: number;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* True when a validation error likely means the API key is wrong or expired
|
|
16
|
+
* (as opposed to model name, network, or rate-limit issues).
|
|
17
|
+
*/
|
|
18
|
+
export declare function isApiKeyValidationError(error?: string): boolean;
|
|
14
19
|
/**
|
|
15
20
|
* Validate a provider/model/key combo by sending a tiny test request.
|
|
16
21
|
* Times out after 15 seconds.
|
|
@@ -6,6 +6,22 @@
|
|
|
6
6
|
* model names, expired keys, and reachability problems before the user
|
|
7
7
|
* finishes setup.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* True when a validation error likely means the API key is wrong or expired
|
|
11
|
+
* (as opposed to model name, network, or rate-limit issues).
|
|
12
|
+
*/
|
|
13
|
+
export function isApiKeyValidationError(error) {
|
|
14
|
+
if (!error)
|
|
15
|
+
return false;
|
|
16
|
+
const lower = error.toLowerCase();
|
|
17
|
+
if (/http\s+(401|403)\b/i.test(error))
|
|
18
|
+
return true;
|
|
19
|
+
if (/incorrect\s+api\s+key|invalid\s+api\s+key|invalid_api_key|authentication\s+failed|unauthorized|permission\s+denied/.test(lower)) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
// Some providers (e.g. xAI) return HTTP 400 with an API-key message.
|
|
23
|
+
return /http\s+400\b/.test(error) && /api\s+key/.test(lower);
|
|
24
|
+
}
|
|
9
25
|
/**
|
|
10
26
|
* Build a provider-specific minimal chat request.
|
|
11
27
|
* Returns null for providers we can't validate (e.g. "custom" without baseUrl).
|
|
@@ -68,6 +84,17 @@ function buildRequest(provider, model, apiKey, customBaseUrl) {
|
|
|
68
84
|
},
|
|
69
85
|
body: openaiBody,
|
|
70
86
|
};
|
|
87
|
+
case "openrouter":
|
|
88
|
+
return {
|
|
89
|
+
url: "https://openrouter.ai/api/v1/chat/completions",
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
93
|
+
"HTTP-Referer": "https://github.com/proticom/gnosys",
|
|
94
|
+
"X-Title": "Gnosys",
|
|
95
|
+
},
|
|
96
|
+
body: openaiBody,
|
|
97
|
+
};
|
|
71
98
|
case "ollama":
|
|
72
99
|
return {
|
|
73
100
|
url: "http://localhost:11434/api/chat",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build setup-wizard model tiers from the OpenRouter catalog.
|
|
3
|
+
* Includes :free models (excluded from other provider tier builders).
|
|
4
|
+
*/
|
|
5
|
+
export interface OpenRouterCatalogModel {
|
|
6
|
+
id: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
pricing?: {
|
|
9
|
+
prompt?: string;
|
|
10
|
+
completion?: string;
|
|
11
|
+
};
|
|
12
|
+
context_length?: number;
|
|
13
|
+
created?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface OpenRouterModelTier {
|
|
16
|
+
name: string;
|
|
17
|
+
model: string;
|
|
18
|
+
input: number;
|
|
19
|
+
output: number;
|
|
20
|
+
recommended: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Default recommended free model on OpenRouter. */
|
|
23
|
+
export declare const OPENROUTER_DEFAULT_MODEL = "nvidia/nemotron-3-super-120b-a12b:free";
|
|
24
|
+
export declare const OPENROUTER_STATIC_TIERS: OpenRouterModelTier[];
|
|
25
|
+
/**
|
|
26
|
+
* Turn OpenRouter /models JSON into wizard tiers: free models first, then
|
|
27
|
+
* a few paid budget/balanced/premium picks.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildOpenRouterTiers(catalog: OpenRouterCatalogModel[]): OpenRouterModelTier[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build setup-wizard model tiers from the OpenRouter catalog.
|
|
3
|
+
* Includes :free models (excluded from other provider tier builders).
|
|
4
|
+
*/
|
|
5
|
+
const SKIP_ID = /guard|embed|tts|audio|vision|image|code-|router/i;
|
|
6
|
+
/** Default recommended free model on OpenRouter. */
|
|
7
|
+
export const OPENROUTER_DEFAULT_MODEL = "nvidia/nemotron-3-super-120b-a12b:free";
|
|
8
|
+
export const OPENROUTER_STATIC_TIERS = [
|
|
9
|
+
{
|
|
10
|
+
name: "Free · Nemotron 3 Super",
|
|
11
|
+
model: OPENROUTER_DEFAULT_MODEL,
|
|
12
|
+
input: 0,
|
|
13
|
+
output: 0,
|
|
14
|
+
recommended: true,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "Free · Devstral Small",
|
|
18
|
+
model: "mistralai/devstral-small-2505:free",
|
|
19
|
+
input: 0,
|
|
20
|
+
output: 0,
|
|
21
|
+
recommended: false,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "Free · Llama 3.3 70B",
|
|
25
|
+
model: "meta-llama/llama-3.3-70b-instruct:free",
|
|
26
|
+
input: 0,
|
|
27
|
+
output: 0,
|
|
28
|
+
recommended: false,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
function tierLabel(m) {
|
|
32
|
+
const raw = m.name?.trim() || m.id.split("/").pop() || m.id;
|
|
33
|
+
const short = raw.length > 48 ? `${raw.slice(0, 45)}…` : raw;
|
|
34
|
+
return m.isFree ? `Free · ${short}` : short;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Turn OpenRouter /models JSON into wizard tiers: free models first, then
|
|
38
|
+
* a few paid budget/balanced/premium picks.
|
|
39
|
+
*/
|
|
40
|
+
export function buildOpenRouterTiers(catalog) {
|
|
41
|
+
const parsed = catalog
|
|
42
|
+
.filter((m) => m.id.includes("/") && !SKIP_ID.test(m.id))
|
|
43
|
+
.map((m) => {
|
|
44
|
+
const input = parseFloat(m.pricing?.prompt ?? "0") * 1e6;
|
|
45
|
+
const output = parseFloat(m.pricing?.completion ?? "0") * 1e6;
|
|
46
|
+
const isFree = m.id.includes(":free") || (input === 0 && output === 0);
|
|
47
|
+
return {
|
|
48
|
+
id: m.id,
|
|
49
|
+
name: m.name,
|
|
50
|
+
input,
|
|
51
|
+
output,
|
|
52
|
+
isFree,
|
|
53
|
+
created: m.created ?? 0,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
const free = parsed
|
|
57
|
+
.filter((m) => m.isFree)
|
|
58
|
+
.sort((a, b) => b.created - a.created);
|
|
59
|
+
const paid = parsed
|
|
60
|
+
.filter((m) => !m.isFree && m.input > 0)
|
|
61
|
+
.filter((m) => !/preview|beta/i.test(m.id))
|
|
62
|
+
.sort((a, b) => b.created - a.created);
|
|
63
|
+
const tiers = [];
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
const recommendedFree = free.find((m) => m.id === OPENROUTER_DEFAULT_MODEL) ??
|
|
66
|
+
free.find((m) => m.id.includes("nemotron-3-super")) ??
|
|
67
|
+
free[0];
|
|
68
|
+
for (const m of free.slice(0, 8)) {
|
|
69
|
+
if (seen.has(m.id))
|
|
70
|
+
continue;
|
|
71
|
+
seen.add(m.id);
|
|
72
|
+
tiers.push({
|
|
73
|
+
name: tierLabel(m),
|
|
74
|
+
model: m.id,
|
|
75
|
+
input: 0,
|
|
76
|
+
output: 0,
|
|
77
|
+
recommended: recommendedFree?.id === m.id,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const BUDGET_MAX = 1.5;
|
|
81
|
+
const BALANCED_MAX = 6;
|
|
82
|
+
const budget = paid.find((m) => m.input <= BUDGET_MAX);
|
|
83
|
+
const balanced = paid.find((m) => m.input > BUDGET_MAX && m.input <= BALANCED_MAX);
|
|
84
|
+
const premium = paid.find((m) => m.input > BALANCED_MAX);
|
|
85
|
+
for (const [m, label] of [
|
|
86
|
+
[budget, "Paid · Budget"],
|
|
87
|
+
[balanced, "Paid · Balanced"],
|
|
88
|
+
[premium, "Paid · Premium"],
|
|
89
|
+
]) {
|
|
90
|
+
if (!m || seen.has(m.id))
|
|
91
|
+
continue;
|
|
92
|
+
seen.add(m.id);
|
|
93
|
+
tiers.push({
|
|
94
|
+
name: label,
|
|
95
|
+
model: m.id,
|
|
96
|
+
input: Math.round(m.input * 100) / 100,
|
|
97
|
+
output: Math.round(m.output * 100) / 100,
|
|
98
|
+
recommended: false,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (tiers.length === 0) {
|
|
102
|
+
return [...OPENROUTER_STATIC_TIERS];
|
|
103
|
+
}
|
|
104
|
+
if (!tiers.some((t) => t.recommended)) {
|
|
105
|
+
tiers[0].recommended = true;
|
|
106
|
+
}
|
|
107
|
+
// Ensure the canonical Nemotron free tier is always listed.
|
|
108
|
+
if (!seen.has(OPENROUTER_DEFAULT_MODEL)) {
|
|
109
|
+
const nemotron = OPENROUTER_STATIC_TIERS[0];
|
|
110
|
+
tiers.unshift({ ...nemotron, recommended: !tiers.some((t) => t.recommended) });
|
|
111
|
+
}
|
|
112
|
+
return tiers;
|
|
113
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type PrefSetCommandOptions = {
|
|
2
|
+
title?: string;
|
|
3
|
+
tags?: string;
|
|
4
|
+
};
|
|
5
|
+
export type PrefGetCommandOptions = {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function runPrefSetCommand(key: string, value: string, opts: PrefSetCommandOptions): Promise<void>;
|
|
9
|
+
export declare function runPrefGetCommand(key: string | undefined, opts: PrefGetCommandOptions): Promise<void>;
|
|
10
|
+
export declare function runPrefDeleteCommand(key: string): Promise<void>;
|