gnosys 5.2.24 → 5.3.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/README.md +43 -5
- package/dist/cli.js +303 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/db.d.ts +21 -0
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +61 -0
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/remote.d.ts +112 -0
- package/dist/lib/remote.d.ts.map +1 -0
- package/dist/lib/remote.js +511 -0
- package/dist/lib/remote.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://www.npmjs.com/package/gnosys"><img src="https://img.shields.io/npm/v/gnosys.svg" alt="npm version"></a>
|
|
7
7
|
<a href="https://github.com/proticom/gnosys/actions"><img src="https://github.com/proticom/gnosys/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
8
|
-
<img src="https://img.shields.io/badge/tests-
|
|
8
|
+
<img src="https://img.shields.io/badge/tests-738%20passing-brightgreen" alt="tests">
|
|
9
9
|
<img src="https://img.shields.io/badge/coverage-lib%2040%25%20|%20sandbox%2045%25-yellow" alt="coverage">
|
|
10
10
|
<a href="https://gnosys.ai"><img src="https://img.shields.io/badge/docs-gnosys.ai-C04C4C" alt="docs"></a>
|
|
11
11
|
<a href="https://gnosys.ai/guide.html"><img src="https://img.shields.io/badge/user%20guide-gnosys.ai%2Fguide-555560" alt="user guide"></a>
|
|
@@ -48,6 +48,7 @@ Gnosys takes a different approach: the central brain is a single SQLite database
|
|
|
48
48
|
- **Reflection API** — `gnosys.reflect(outcome)` updates confidence and consolidates memories based on real-world outcomes.
|
|
49
49
|
- **Bulk import** — CSV, JSON, JSONL. Import entire datasets in seconds.
|
|
50
50
|
- **Obsidian-native** — `gnosys export` generates a full vault with YAML frontmatter, `[[wikilinks]]`, summaries, and graph data.
|
|
51
|
+
- **Multi-machine sync (v5.3.0)** — share your `gnosys.db` across machines via NAS or shared drive. Local cache for speed, remote source of truth for consistency. Built-in conflict detection with skip-and-flag resolution. Run `gnosys remote configure` to set up.
|
|
51
52
|
- **MCP-compatible** — also runs as a full MCP server that drops into Cursor, Claude Desktop, Claude Code, Cowork, Codex, or any MCP client.
|
|
52
53
|
- **Zero infrastructure** — no external databases, no Docker (unless you want it), no cloud services. Just `npm install`.
|
|
53
54
|
|
|
@@ -334,6 +335,11 @@ command = ["gnosys", "serve"]
|
|
|
334
335
|
| `gnosys_detect_ambiguity` | Check if a query matches multiple projects |
|
|
335
336
|
| `gnosys_briefing` | Generate project briefing (categories, activity, tags, summary) |
|
|
336
337
|
| `gnosys_working_set` | Get recently modified memories for the current project |
|
|
338
|
+
| **Multi-Machine Sync** | |
|
|
339
|
+
| `gnosys_remote_status` | Check sync state (pending changes, conflicts, reachability) |
|
|
340
|
+
| `gnosys_remote_push` | Push local changes to remote |
|
|
341
|
+
| `gnosys_remote_pull` | Pull remote changes to local |
|
|
342
|
+
| `gnosys_remote_resolve` | Resolve a conflict by choosing local or remote |
|
|
337
343
|
|
|
338
344
|
---
|
|
339
345
|
|
|
@@ -343,6 +349,35 @@ command = ["gnosys", "serve"]
|
|
|
343
349
|
|
|
344
350
|
All memories live in a single `~/.gnosys/gnosys.db` with `project_id` and `scope` columns. SQLite is the sole source of truth — no dual-write, no markdown files on disk. Sub-10ms reads, WAL mode for concurrent access. Use `gnosys export` to generate an Obsidian vault on demand. See the [User Guide](https://gnosys.ai/guide.html) for the full schema and memory format.
|
|
345
351
|
|
|
352
|
+
### Multi-Machine Sync
|
|
353
|
+
|
|
354
|
+
Gnosys v5.3.0 supports running across multiple machines with a shared database on a NAS or network share.
|
|
355
|
+
|
|
356
|
+
**How it works:**
|
|
357
|
+
- Local DB at `~/.gnosys/gnosys.db` is your fast working cache
|
|
358
|
+
- Remote DB on shared storage (e.g. `/Volumes/synology/gnosys/`) is the canonical source of truth
|
|
359
|
+
- Reads always hit local for speed
|
|
360
|
+
- Writes go to local first, then sync to remote
|
|
361
|
+
- Per-memory `modified` timestamps detect conflicts
|
|
362
|
+
- Skip-and-flag is the safe default; `--newer-wins` for unattended sync
|
|
363
|
+
|
|
364
|
+
**Setup:**
|
|
365
|
+
```bash
|
|
366
|
+
gnosys remote configure
|
|
367
|
+
# interactive: validates path, tests SQLite locking, checks latency
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Daily commands:**
|
|
371
|
+
```bash
|
|
372
|
+
gnosys remote status # pending changes, conflicts, last sync
|
|
373
|
+
gnosys remote sync # two-way sync (push then pull)
|
|
374
|
+
gnosys remote push # local → remote only
|
|
375
|
+
gnosys remote pull # remote → local only
|
|
376
|
+
gnosys remote resolve <id> --keep <local|remote>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**AI-mediated conflict resolution:** Agents using gnosys via MCP can detect sync state and prompt the user when conflicts arise, rather than silently picking a winner. The agent presents both versions and asks which to keep.
|
|
380
|
+
|
|
346
381
|
### LLM Providers
|
|
347
382
|
|
|
348
383
|
Eight providers behind a single interface — switch between cloud and local with one command:
|
|
@@ -471,6 +506,8 @@ All commands support `--json` for programmatic output. See the [User Guide](http
|
|
|
471
506
|
|
|
472
507
|
**Web knowledge base:** `web init`, `web ingest`, `web build-index`, `web build`, `web add`, `web remove`, `web status`
|
|
473
508
|
|
|
509
|
+
**Multi-machine sync:** `remote configure`, `remote status`, `remote sync`, `remote push`, `remote pull`, `remote resolve`
|
|
510
|
+
|
|
474
511
|
**Server:** `serve`, `serve --with-maintenance`
|
|
475
512
|
|
|
476
513
|
---
|
|
@@ -480,13 +517,13 @@ All commands support `--json` for programmatic output. See the [User Guide](http
|
|
|
480
517
|
```bash
|
|
481
518
|
npm install # Install dependencies
|
|
482
519
|
npm run build # Compile TypeScript
|
|
483
|
-
npm test # Run test suite (
|
|
520
|
+
npm test # Run test suite (738 tests)
|
|
484
521
|
npm run test:watch # Run tests in watch mode
|
|
485
522
|
npm run test:coverage # Run tests with v8 coverage report (HTML in coverage/)
|
|
486
523
|
npm run dev # Run MCP server in dev mode (tsx)
|
|
487
524
|
```
|
|
488
525
|
|
|
489
|
-
|
|
526
|
+
738 tests across 35+ files. CI runs on Node 20 + 22 with multi-project scenario testing, network-share simulation, and TypeScript strict checking. Publishing uses OIDC trusted publishing via GitHub Actions — no npm tokens needed.
|
|
490
527
|
|
|
491
528
|
---
|
|
492
529
|
|
|
@@ -558,7 +595,7 @@ Real numbers from a 120-memory test vault:
|
|
|
558
595
|
| Graph reindex (120 memories) | <1s |
|
|
559
596
|
| Storage per memory | ~1 KB (SQLite row) |
|
|
560
597
|
| Embedding storage (120 memories) | ~0.3 MB |
|
|
561
|
-
| Test suite |
|
|
598
|
+
| Test suite | 738 tests, 0 errors |
|
|
562
599
|
|
|
563
600
|
All benchmarks on Apple M-series hardware, Node.js 20+. Structured imports bypass LLM entirely.
|
|
564
601
|
|
|
@@ -578,7 +615,8 @@ Gnosys is open source (MIT) and actively developed. Here's how to get involved:
|
|
|
578
615
|
- PRs welcome — especially for new import connectors, LLM providers, and Obsidian plugins
|
|
579
616
|
|
|
580
617
|
**What's next:**
|
|
581
|
-
-
|
|
618
|
+
- Improved network share latency (write-ahead batching for high-latency NAS)
|
|
619
|
+
- Automated background sync via LaunchAgent (macOS) / systemd (Linux)
|
|
582
620
|
- Temporal memory versioning (valid_from / valid_until)
|
|
583
621
|
- Cross-session "deep dream" overnight consolidation
|
|
584
622
|
- Graph visualization in the dashboard
|
package/dist/cli.js
CHANGED
|
@@ -2469,6 +2469,309 @@ program
|
|
|
2469
2469
|
}
|
|
2470
2470
|
});
|
|
2471
2471
|
// NOTE: gnosys migrate is defined below (near the end) with --to-central support
|
|
2472
|
+
// ─── gnosys remote (multi-machine sync) ────────────────────────────────
|
|
2473
|
+
const remoteCmd = program
|
|
2474
|
+
.command("remote")
|
|
2475
|
+
.description("Multi-machine sync — share gnosys.db across machines via NAS or shared drive");
|
|
2476
|
+
remoteCmd
|
|
2477
|
+
.command("status")
|
|
2478
|
+
.description("Show remote sync status: pending changes, conflicts, last sync")
|
|
2479
|
+
.option("--json", "Output as JSON")
|
|
2480
|
+
.action(async (opts) => {
|
|
2481
|
+
let centralDb = null;
|
|
2482
|
+
try {
|
|
2483
|
+
centralDb = GnosysDB.openCentral();
|
|
2484
|
+
if (!centralDb.isAvailable()) {
|
|
2485
|
+
console.error("Central DB not available.");
|
|
2486
|
+
process.exit(1);
|
|
2487
|
+
}
|
|
2488
|
+
const remotePath = centralDb.getMeta("remote_path");
|
|
2489
|
+
if (!remotePath) {
|
|
2490
|
+
if (opts.json) {
|
|
2491
|
+
console.log(JSON.stringify({ configured: false, message: "Remote not configured. Run 'gnosys remote configure'." }, null, 2));
|
|
2492
|
+
}
|
|
2493
|
+
else {
|
|
2494
|
+
console.log("Remote sync: not configured.");
|
|
2495
|
+
console.log("Run 'gnosys remote configure' to set up multi-machine sync.");
|
|
2496
|
+
}
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
const { RemoteSync, formatStatus } = await import("./lib/remote.js");
|
|
2500
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2501
|
+
const status = await sync.getStatus();
|
|
2502
|
+
sync.closeRemote();
|
|
2503
|
+
if (opts.json) {
|
|
2504
|
+
console.log(JSON.stringify(status, null, 2));
|
|
2505
|
+
}
|
|
2506
|
+
else {
|
|
2507
|
+
console.log(formatStatus(status));
|
|
2508
|
+
if (status.conflicts.length > 0) {
|
|
2509
|
+
console.log("\nConflicts:");
|
|
2510
|
+
for (const c of status.conflicts) {
|
|
2511
|
+
console.log(` ${c.memoryId}: ${c.title}`);
|
|
2512
|
+
console.log(` local: ${c.localModified}`);
|
|
2513
|
+
console.log(` remote: ${c.remoteModified}`);
|
|
2514
|
+
}
|
|
2515
|
+
console.log("\nResolve with: gnosys remote resolve <memory-id> --keep <local|remote>");
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
catch (err) {
|
|
2520
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2521
|
+
process.exit(1);
|
|
2522
|
+
}
|
|
2523
|
+
finally {
|
|
2524
|
+
centralDb?.close();
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
remoteCmd
|
|
2528
|
+
.command("push")
|
|
2529
|
+
.description("Push local changes to remote")
|
|
2530
|
+
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
2531
|
+
.action(async (opts) => {
|
|
2532
|
+
let centralDb = null;
|
|
2533
|
+
try {
|
|
2534
|
+
centralDb = GnosysDB.openCentral();
|
|
2535
|
+
if (!centralDb.isAvailable()) {
|
|
2536
|
+
console.error("Central DB not available.");
|
|
2537
|
+
process.exit(1);
|
|
2538
|
+
}
|
|
2539
|
+
const remotePath = centralDb.getMeta("remote_path");
|
|
2540
|
+
if (!remotePath) {
|
|
2541
|
+
console.error("Remote not configured.");
|
|
2542
|
+
process.exit(1);
|
|
2543
|
+
}
|
|
2544
|
+
const { RemoteSync } = await import("./lib/remote.js");
|
|
2545
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2546
|
+
const result = await sync.push({ strategy: opts.newerWins ? "newer-wins" : "skip-and-flag" });
|
|
2547
|
+
sync.closeRemote();
|
|
2548
|
+
console.log(`Pushed: ${result.pushed} | Skipped: ${result.skipped} | Conflicts: ${result.conflicts.length}`);
|
|
2549
|
+
if (result.errors.length > 0) {
|
|
2550
|
+
console.log("\nErrors:");
|
|
2551
|
+
for (const e of result.errors)
|
|
2552
|
+
console.log(` ${e}`);
|
|
2553
|
+
}
|
|
2554
|
+
if (result.conflicts.length > 0) {
|
|
2555
|
+
console.log("\nConflicts flagged (run 'gnosys remote status' for details):");
|
|
2556
|
+
for (const c of result.conflicts)
|
|
2557
|
+
console.log(` ${c.memoryId} — ${c.title}`);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
catch (err) {
|
|
2561
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2562
|
+
process.exit(1);
|
|
2563
|
+
}
|
|
2564
|
+
finally {
|
|
2565
|
+
centralDb?.close();
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
2568
|
+
remoteCmd
|
|
2569
|
+
.command("pull")
|
|
2570
|
+
.description("Pull remote changes to local")
|
|
2571
|
+
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
2572
|
+
.action(async (opts) => {
|
|
2573
|
+
let centralDb = null;
|
|
2574
|
+
try {
|
|
2575
|
+
centralDb = GnosysDB.openCentral();
|
|
2576
|
+
if (!centralDb.isAvailable()) {
|
|
2577
|
+
console.error("Central DB not available.");
|
|
2578
|
+
process.exit(1);
|
|
2579
|
+
}
|
|
2580
|
+
const remotePath = centralDb.getMeta("remote_path");
|
|
2581
|
+
if (!remotePath) {
|
|
2582
|
+
console.error("Remote not configured.");
|
|
2583
|
+
process.exit(1);
|
|
2584
|
+
}
|
|
2585
|
+
const { RemoteSync } = await import("./lib/remote.js");
|
|
2586
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2587
|
+
const result = await sync.pull({ strategy: opts.newerWins ? "newer-wins" : "skip-and-flag" });
|
|
2588
|
+
sync.closeRemote();
|
|
2589
|
+
console.log(`Pulled: ${result.pulled} | Skipped: ${result.skipped} | Conflicts: ${result.conflicts.length}`);
|
|
2590
|
+
if (result.errors.length > 0) {
|
|
2591
|
+
console.log("\nErrors:");
|
|
2592
|
+
for (const e of result.errors)
|
|
2593
|
+
console.log(` ${e}`);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
catch (err) {
|
|
2597
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2598
|
+
process.exit(1);
|
|
2599
|
+
}
|
|
2600
|
+
finally {
|
|
2601
|
+
centralDb?.close();
|
|
2602
|
+
}
|
|
2603
|
+
});
|
|
2604
|
+
remoteCmd
|
|
2605
|
+
.command("sync")
|
|
2606
|
+
.description("Two-way sync: push local changes then pull remote changes")
|
|
2607
|
+
.option("--auto", "Run silently for cron/LaunchAgent (skip-and-flag for conflicts)")
|
|
2608
|
+
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
2609
|
+
.action(async (opts) => {
|
|
2610
|
+
let centralDb = null;
|
|
2611
|
+
try {
|
|
2612
|
+
centralDb = GnosysDB.openCentral();
|
|
2613
|
+
if (!centralDb.isAvailable()) {
|
|
2614
|
+
if (!opts.auto)
|
|
2615
|
+
console.error("Central DB not available.");
|
|
2616
|
+
process.exit(1);
|
|
2617
|
+
}
|
|
2618
|
+
const remotePath = centralDb.getMeta("remote_path");
|
|
2619
|
+
if (!remotePath) {
|
|
2620
|
+
if (!opts.auto)
|
|
2621
|
+
console.error("Remote not configured.");
|
|
2622
|
+
process.exit(opts.auto ? 0 : 1);
|
|
2623
|
+
}
|
|
2624
|
+
const { RemoteSync } = await import("./lib/remote.js");
|
|
2625
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2626
|
+
const result = await sync.sync({
|
|
2627
|
+
auto: opts.auto,
|
|
2628
|
+
strategy: opts.newerWins ? "newer-wins" : "skip-and-flag",
|
|
2629
|
+
});
|
|
2630
|
+
sync.closeRemote();
|
|
2631
|
+
if (!opts.auto || result.conflicts.length > 0 || result.errors.length > 0) {
|
|
2632
|
+
console.log(`Pushed: ${result.pushed} | Pulled: ${result.pulled} | Conflicts: ${result.conflicts.length}`);
|
|
2633
|
+
if (result.errors.length > 0) {
|
|
2634
|
+
console.log("\nErrors:");
|
|
2635
|
+
for (const e of result.errors)
|
|
2636
|
+
console.log(` ${e}`);
|
|
2637
|
+
}
|
|
2638
|
+
if (result.conflicts.length > 0) {
|
|
2639
|
+
console.log("\nConflicts need resolution (run 'gnosys remote status' for details).");
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
catch (err) {
|
|
2644
|
+
if (!opts.auto)
|
|
2645
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2646
|
+
process.exit(1);
|
|
2647
|
+
}
|
|
2648
|
+
finally {
|
|
2649
|
+
centralDb?.close();
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
remoteCmd
|
|
2653
|
+
.command("resolve <memoryId>")
|
|
2654
|
+
.description("Resolve a sync conflict by choosing local, remote, or merged content")
|
|
2655
|
+
.option("--keep <choice>", "Choice: local | remote", "local")
|
|
2656
|
+
.action(async (memoryId, opts) => {
|
|
2657
|
+
let centralDb = null;
|
|
2658
|
+
try {
|
|
2659
|
+
centralDb = GnosysDB.openCentral();
|
|
2660
|
+
if (!centralDb.isAvailable()) {
|
|
2661
|
+
console.error("Central DB not available.");
|
|
2662
|
+
process.exit(1);
|
|
2663
|
+
}
|
|
2664
|
+
const remotePath = centralDb.getMeta("remote_path");
|
|
2665
|
+
if (!remotePath) {
|
|
2666
|
+
console.error("Remote not configured.");
|
|
2667
|
+
process.exit(1);
|
|
2668
|
+
}
|
|
2669
|
+
if (opts.keep !== "local" && opts.keep !== "remote") {
|
|
2670
|
+
console.error(`--keep must be 'local' or 'remote' (got: ${opts.keep})`);
|
|
2671
|
+
process.exit(1);
|
|
2672
|
+
}
|
|
2673
|
+
const { RemoteSync } = await import("./lib/remote.js");
|
|
2674
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2675
|
+
const result = await sync.resolve(memoryId, opts.keep);
|
|
2676
|
+
sync.closeRemote();
|
|
2677
|
+
if (result.ok) {
|
|
2678
|
+
console.log(`Resolved ${memoryId}: kept ${opts.keep} version.`);
|
|
2679
|
+
}
|
|
2680
|
+
else {
|
|
2681
|
+
console.error(`Failed to resolve: ${result.error}`);
|
|
2682
|
+
process.exit(1);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
catch (err) {
|
|
2686
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2687
|
+
process.exit(1);
|
|
2688
|
+
}
|
|
2689
|
+
finally {
|
|
2690
|
+
centralDb?.close();
|
|
2691
|
+
}
|
|
2692
|
+
});
|
|
2693
|
+
remoteCmd
|
|
2694
|
+
.command("configure")
|
|
2695
|
+
.description("Configure or change the remote sync location (interactive)")
|
|
2696
|
+
.option("--path <path>", "Set remote path non-interactively")
|
|
2697
|
+
.option("--migrate", "Copy current local DB to remote on first setup")
|
|
2698
|
+
.action(async (opts) => {
|
|
2699
|
+
let centralDb = null;
|
|
2700
|
+
try {
|
|
2701
|
+
centralDb = GnosysDB.openCentral();
|
|
2702
|
+
if (!centralDb.isAvailable()) {
|
|
2703
|
+
console.error("Central DB not available.");
|
|
2704
|
+
process.exit(1);
|
|
2705
|
+
}
|
|
2706
|
+
const { validateLocation, RemoteSync } = await import("./lib/remote.js");
|
|
2707
|
+
let remotePath = opts.path;
|
|
2708
|
+
if (!remotePath) {
|
|
2709
|
+
const { createInterface } = await import("readline/promises");
|
|
2710
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2711
|
+
try {
|
|
2712
|
+
const current = centralDb.getMeta("remote_path");
|
|
2713
|
+
if (current)
|
|
2714
|
+
console.log(`Current remote: ${current}`);
|
|
2715
|
+
remotePath = (await rl.question("Remote path (e.g. /Volumes/synology/gnosys): ")).trim();
|
|
2716
|
+
}
|
|
2717
|
+
finally {
|
|
2718
|
+
rl.close();
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
if (!remotePath) {
|
|
2722
|
+
console.error("No path provided.");
|
|
2723
|
+
process.exit(1);
|
|
2724
|
+
}
|
|
2725
|
+
console.log(`\nValidating ${remotePath}...`);
|
|
2726
|
+
const validation = await validateLocation(remotePath);
|
|
2727
|
+
console.log(` Path exists: ${validation.checks.pathExists ? "✓" : "✗"}`);
|
|
2728
|
+
console.log(` Writable: ${validation.checks.writable ? "✓" : "✗"}`);
|
|
2729
|
+
console.log(` SQLite compatible: ${validation.checks.sqliteCompatible ? "✓" : "✗"}`);
|
|
2730
|
+
if (validation.checks.latencyMs !== null) {
|
|
2731
|
+
console.log(` Latency: ${validation.checks.latencyMs}ms`);
|
|
2732
|
+
}
|
|
2733
|
+
if (validation.checks.existingDb.found) {
|
|
2734
|
+
const c = validation.checks.existingDb;
|
|
2735
|
+
console.log(` Existing DB found: ${c.memoryCount ?? "unknown"} memories (last modified ${c.lastModified ?? "unknown"})`);
|
|
2736
|
+
}
|
|
2737
|
+
for (const w of validation.warnings)
|
|
2738
|
+
console.log(` ⚠ ${w}`);
|
|
2739
|
+
for (const e of validation.errors)
|
|
2740
|
+
console.log(` ✗ ${e}`);
|
|
2741
|
+
if (!validation.ok) {
|
|
2742
|
+
console.error("\nValidation failed. Remote not configured.");
|
|
2743
|
+
process.exit(1);
|
|
2744
|
+
}
|
|
2745
|
+
// Save config
|
|
2746
|
+
centralDb.setMeta("remote_path", remotePath);
|
|
2747
|
+
console.log(`\nRemote configured: ${remotePath}`);
|
|
2748
|
+
// Optional initial migration
|
|
2749
|
+
if (opts.migrate && !validation.checks.existingDb.found) {
|
|
2750
|
+
console.log("\nMigrating local DB to remote...");
|
|
2751
|
+
const sync = new RemoteSync(centralDb, remotePath);
|
|
2752
|
+
const result = await sync.migrate();
|
|
2753
|
+
sync.closeRemote();
|
|
2754
|
+
if (result.ok) {
|
|
2755
|
+
console.log(` ✓ Copied ${result.copied} memories to remote.`);
|
|
2756
|
+
}
|
|
2757
|
+
else {
|
|
2758
|
+
console.error(` ✗ Migration had errors:`);
|
|
2759
|
+
for (const e of result.errors)
|
|
2760
|
+
console.error(` ${e}`);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
else if (validation.checks.existingDb.found) {
|
|
2764
|
+
console.log("\nExisting DB found at remote. Run 'gnosys remote sync' to merge.");
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
catch (err) {
|
|
2768
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
2769
|
+
process.exit(1);
|
|
2770
|
+
}
|
|
2771
|
+
finally {
|
|
2772
|
+
centralDb?.close();
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2472
2775
|
// ─── gnosys upgrade ─────────────────────────────────────────────────────
|
|
2473
2776
|
program
|
|
2474
2777
|
.command("upgrade")
|