gnosys 4.4.6 → 5.0.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.
Files changed (64) hide show
  1. package/README.md +12 -7
  2. package/dist/cli.js +140 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +65 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/attachments.d.ts +43 -0
  7. package/dist/lib/attachments.d.ts.map +1 -0
  8. package/dist/lib/attachments.js +154 -0
  9. package/dist/lib/attachments.js.map +1 -0
  10. package/dist/lib/audioExtract.d.ts +39 -0
  11. package/dist/lib/audioExtract.d.ts.map +1 -0
  12. package/dist/lib/audioExtract.js +220 -0
  13. package/dist/lib/audioExtract.js.map +1 -0
  14. package/dist/lib/chunkSplitter.d.ts +46 -0
  15. package/dist/lib/chunkSplitter.d.ts.map +1 -0
  16. package/dist/lib/chunkSplitter.js +233 -0
  17. package/dist/lib/chunkSplitter.js.map +1 -0
  18. package/dist/lib/config.d.ts +70 -1
  19. package/dist/lib/config.d.ts.map +1 -1
  20. package/dist/lib/config.js +62 -6
  21. package/dist/lib/config.js.map +1 -1
  22. package/dist/lib/db.d.ts +7 -1
  23. package/dist/lib/db.d.ts.map +1 -1
  24. package/dist/lib/db.js +31 -4
  25. package/dist/lib/db.js.map +1 -1
  26. package/dist/lib/dbWrite.d.ts.map +1 -1
  27. package/dist/lib/dbWrite.js +11 -0
  28. package/dist/lib/dbWrite.js.map +1 -1
  29. package/dist/lib/docxExtract.d.ts +27 -0
  30. package/dist/lib/docxExtract.d.ts.map +1 -0
  31. package/dist/lib/docxExtract.js +80 -0
  32. package/dist/lib/docxExtract.js.map +1 -0
  33. package/dist/lib/fileDetect.d.ts +20 -0
  34. package/dist/lib/fileDetect.d.ts.map +1 -0
  35. package/dist/lib/fileDetect.js +124 -0
  36. package/dist/lib/fileDetect.js.map +1 -0
  37. package/dist/lib/imageExtract.d.ts +26 -0
  38. package/dist/lib/imageExtract.d.ts.map +1 -0
  39. package/dist/lib/imageExtract.js +113 -0
  40. package/dist/lib/imageExtract.js.map +1 -0
  41. package/dist/lib/llm.d.ts +9 -0
  42. package/dist/lib/llm.d.ts.map +1 -1
  43. package/dist/lib/llm.js +102 -0
  44. package/dist/lib/llm.js.map +1 -1
  45. package/dist/lib/multimodalIngest.d.ts +68 -0
  46. package/dist/lib/multimodalIngest.d.ts.map +1 -0
  47. package/dist/lib/multimodalIngest.js +463 -0
  48. package/dist/lib/multimodalIngest.js.map +1 -0
  49. package/dist/lib/pdfExtract.d.ts +29 -0
  50. package/dist/lib/pdfExtract.d.ts.map +1 -0
  51. package/dist/lib/pdfExtract.js +163 -0
  52. package/dist/lib/pdfExtract.js.map +1 -0
  53. package/dist/lib/setup.d.ts +9 -0
  54. package/dist/lib/setup.d.ts.map +1 -1
  55. package/dist/lib/setup.js +241 -16
  56. package/dist/lib/setup.js.map +1 -1
  57. package/dist/lib/store.d.ts +3 -0
  58. package/dist/lib/store.d.ts.map +1 -1
  59. package/dist/lib/store.js.map +1 -1
  60. package/dist/lib/videoExtract.d.ts +30 -0
  61. package/dist/lib/videoExtract.d.ts.map +1 -0
  62. package/dist/lib/videoExtract.js +92 -0
  63. package/dist/lib/videoExtract.js.map +1 -0
  64. package/package.json +3 -1
package/README.md CHANGED
@@ -274,7 +274,7 @@ type = "local"
274
274
  command = ["gnosys", "serve"]
275
275
  ```
276
276
 
277
- > **Note:** API keys are configured globally in `~/.config/gnosys/.env`, not per-IDE. See [LLM Provider Setup](https://gnosys.ai/guide.html#guide-installation) in the User Guide.
277
+ > **Note:** API keys are configured via `gnosys setup` (macOS Keychain, environment variable, or `~/.config/gnosys/.env`). See [LLM Provider Setup](https://gnosys.ai/guide.html#guide-llm-provider-setup) in the User Guide.
278
278
 
279
279
  ---
280
280
 
@@ -345,14 +345,18 @@ Eight providers behind a single interface — switch between cloud and local wit
345
345
 
346
346
  | Provider | Type | Default Model | API Key Env Var |
347
347
  |----------|------|---------------|-----------------|
348
- | **Anthropic** | Cloud | claude-sonnet-4-6 | `ANTHROPIC_API_KEY` |
348
+ | **Anthropic** | Cloud | claude-sonnet-4-6 | `GNOSYS_ANTHROPIC_KEY` |
349
349
  | **Ollama** | Local | llama3.2 | — (runs locally) |
350
- | **Groq** | Cloud | llama-3.3-70b-versatile | `GROQ_API_KEY` |
351
- | **OpenAI** | Cloud | gpt-5.4-mini | `OPENAI_API_KEY` |
350
+ | **Groq** | Cloud | llama-3.3-70b-versatile | `GNOSYS_GROQ_KEY` |
351
+ | **OpenAI** | Cloud | gpt-5.4-mini | `GNOSYS_OPENAI_KEY` |
352
352
  | **LM Studio** | Local | default | — (runs locally) |
353
- | **xAI** | Cloud | grok-4.20 | `XAI_API_KEY` |
354
- | **Mistral** | Cloud | mistral-small-4 | `MISTRAL_API_KEY` |
355
- | **Custom** | Any | (user-defined) | `GNOSYS_LLM_API_KEY` |
353
+ | **xAI** | Cloud | grok-4.20 | `GNOSYS_XAI_KEY` |
354
+ | **Mistral** | Cloud | mistral-small-4 | `GNOSYS_MISTRAL_KEY` |
355
+ | **Custom** | Any | (user-defined) | `GNOSYS_CUSTOM_KEY` |
356
+
357
+ > Model lists and pricing are fetched dynamically from [OpenRouter](https://openrouter.ai) during `gnosys setup` and cached for 24 hours. Bundled defaults are used when offline.
358
+
359
+ > **API Key Security:** `gnosys setup` offers three storage options: macOS Keychain (recommended — encrypted, no plaintext), environment variable (shell profile), or `~/.config/gnosys/.env` (least secure). Legacy env var names (`ANTHROPIC_API_KEY`, `GROQ_API_KEY`, `OPENAI_API_KEY`, etc.) are still supported for backward compatibility.
356
360
 
357
361
  Route tasks to different providers — a cheap model for structuring, a powerful model for synthesis:
358
362
 
@@ -561,6 +565,7 @@ Gnosys is open source (MIT) and actively developed. Here's how to get involved:
561
565
  - Graph visualization in the dashboard
562
566
  - Obsidian community plugin for native vault integration
563
567
  - Docker Hub published image for one-line deployment
568
+ - Multimodal memory ingestion (PDFs, images, audio/video transcription)
564
569
 
565
570
  ---
566
571
 
package/dist/cli.js CHANGED
@@ -350,6 +350,36 @@ program
350
350
  console.error("No writable store found. Create a .gnosys/ directory or set GNOSYS_PERSONAL.");
351
351
  process.exit(1);
352
352
  }
353
+ // Check if input is a file path — if so, route through multimodal ingestion
354
+ if (existsSync(input)) {
355
+ const { ingestFile } = await import("./lib/multimodalIngest.js");
356
+ const storePath = writeTarget.store.getStorePath();
357
+ console.log(`Detected file: ${input}`);
358
+ console.log("Ingesting via multimodal pipeline...");
359
+ const result = await ingestFile({
360
+ filePath: path.resolve(input),
361
+ storePath,
362
+ mode: "llm",
363
+ author: opts.author,
364
+ authority: opts.authority,
365
+ onProgress: (p) => {
366
+ console.log(` [${p.current}/${p.total}] ${p.title || "Processing..."}`);
367
+ },
368
+ });
369
+ console.log(`\nFile type: ${result.fileType}`);
370
+ console.log(`Memories created: ${result.memories.length}`);
371
+ console.log(`Duration: ${(result.duration / 1000).toFixed(1)}s`);
372
+ for (const mem of result.memories) {
373
+ console.log(` ${mem.id}: ${mem.title}`);
374
+ }
375
+ if (result.errors.length > 0) {
376
+ console.error(`\nErrors (${result.errors.length}):`);
377
+ for (const err of result.errors) {
378
+ console.error(` Chunk ${err.chunk}: ${err.error}`);
379
+ }
380
+ }
381
+ return;
382
+ }
353
383
  const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
354
384
  await tagRegistry.load();
355
385
  const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
@@ -439,6 +469,12 @@ program
439
469
  await fs.writeFile(path.join(storePath, ".config", "tags.json"), JSON.stringify(defaultRegistry, null, 2), "utf-8");
440
470
  // Write default gnosys.json config (LLM settings)
441
471
  await fs.writeFile(path.join(storePath, ".config", "gnosys-config.json"), generateConfigTemplate() + "\n", "utf-8");
472
+ // v5.0: Create attachments directory and empty manifest
473
+ await fs.mkdir(path.join(storePath, "attachments"), { recursive: true });
474
+ await fs.writeFile(path.join(storePath, "attachments", "attachments.json"), JSON.stringify({ attachments: [] }, null, 2) + "\n", "utf-8");
475
+ // Create .gitignore inside .gnosys to exclude large binary attachments
476
+ const storeGitignore = "# Large binary attachments (tracked via manifest, not git)\nattachments/\n";
477
+ await fs.writeFile(path.join(storePath, ".gitignore"), storeGitignore, "utf-8");
442
478
  const changelog = `# Gnosys Changelog\n\n## ${new Date().toISOString().split("T")[0]}\n\n- Store initialized\n`;
443
479
  await fs.writeFile(path.join(storePath, "CHANGELOG.md"), changelog, "utf-8");
444
480
  try {
@@ -793,6 +829,110 @@ program
793
829
  console.log(`Memory added to [${writeTarget.label}]: ${opts.title}`);
794
830
  console.log(`Path: ${writeTarget.label}:${relPath}`);
795
831
  });
832
+ // ─── gnosys ingest <file> ─────────────────────────────────────────────────
833
+ program
834
+ .command("ingest <fileOrGlob>")
835
+ .description("Ingest a file (PDF, DOCX, TXT, MD) into Gnosys memory. Extracts text, splits into chunks, and creates atomic memories.")
836
+ .option("--mode <mode>", "Ingestion mode: llm or structured", "llm")
837
+ .option("-s, --store <store>", "Target store: project, personal, global")
838
+ .option("-a, --author <author>", "Author", "human")
839
+ .option("--authority <authority>", "Authority level", "imported")
840
+ .option("--dry-run", "Preview what would be created without writing")
841
+ .option("--list-attachments", "List all stored attachments")
842
+ .option("-d, --directory <dir>", "Project directory")
843
+ .action(async (fileOrGlob, opts) => {
844
+ // List attachments mode
845
+ if (opts.listAttachments) {
846
+ const { listAttachments } = await import("./lib/attachments.js");
847
+ const resolver = await getResolver();
848
+ const writeTarget = resolver.getWriteTarget(opts.store || undefined);
849
+ if (!writeTarget) {
850
+ console.error("No writable store found.");
851
+ process.exit(1);
852
+ }
853
+ const attachments = await listAttachments(writeTarget.store.getStorePath());
854
+ if (attachments.length === 0) {
855
+ console.log("No attachments found.");
856
+ return;
857
+ }
858
+ console.log(`Found ${attachments.length} attachment(s):\n`);
859
+ for (const a of attachments) {
860
+ const sizeMb = (a.sizeBytes / (1024 * 1024)).toFixed(2);
861
+ console.log(` ${a.originalName} (${sizeMb}MB, ${a.extension})`);
862
+ console.log(` UUID: ${a.uuid}`);
863
+ console.log(` Hash: ${a.contentHash.slice(0, 16)}...`);
864
+ console.log(` Memories: ${a.memoryIds.length > 0 ? a.memoryIds.join(", ") : "none"}`);
865
+ console.log(` Created: ${a.createdAt}\n`);
866
+ }
867
+ return;
868
+ }
869
+ // Resolve the file path
870
+ const resolvedPath = path.resolve(opts.directory || process.cwd(), fileOrGlob);
871
+ // Check the file exists
872
+ try {
873
+ await fs.access(resolvedPath);
874
+ }
875
+ catch {
876
+ console.error(`File not found: ${resolvedPath}`);
877
+ process.exit(1);
878
+ }
879
+ // Resolve the store
880
+ const resolver = await getResolver();
881
+ const writeTarget = resolver.getWriteTarget(opts.store || undefined);
882
+ if (!writeTarget) {
883
+ console.error("No writable store found. Create a .gnosys/ directory or set GNOSYS_PERSONAL.");
884
+ process.exit(1);
885
+ }
886
+ const storePath = writeTarget.store.getStorePath();
887
+ // Run ingestion
888
+ const { ingestFile } = await import("./lib/multimodalIngest.js");
889
+ console.log(`Ingesting: ${path.basename(resolvedPath)}`);
890
+ if (opts.dryRun) {
891
+ console.log("(dry run — no files will be written)\n");
892
+ }
893
+ try {
894
+ const result = await ingestFile({
895
+ filePath: resolvedPath,
896
+ storePath,
897
+ mode: opts.mode,
898
+ store: opts.store || undefined,
899
+ author: opts.author,
900
+ authority: opts.authority,
901
+ dryRun: opts.dryRun,
902
+ projectRoot: opts.directory,
903
+ onProgress: (p) => {
904
+ process.stdout.write(`\r Processing chunk ${p.current}/${p.total}...`);
905
+ },
906
+ });
907
+ // Clear the progress line
908
+ if (result.memories.length > 0) {
909
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
910
+ }
911
+ // Print results
912
+ console.log(`\nFile type: ${result.fileType}`);
913
+ console.log(`Attachment: ${result.attachment.originalName} (${result.attachment.uuid.slice(0, 8)}...)`);
914
+ console.log(`Duration: ${(result.duration / 1000).toFixed(1)}s`);
915
+ console.log(`Memories created: ${result.memories.length}`);
916
+ if (result.memories.length > 0) {
917
+ console.log("\nMemories:");
918
+ for (const m of result.memories) {
919
+ const extra = m.page ? ` [page ${m.page}]` : "";
920
+ console.log(` ${m.id}: ${m.title}${extra}`);
921
+ console.log(` Path: ${m.path}`);
922
+ }
923
+ }
924
+ if (result.errors.length > 0) {
925
+ console.log(`\nErrors (${result.errors.length}):`);
926
+ for (const e of result.errors) {
927
+ console.log(` Chunk ${e.chunk}: ${e.error}`);
928
+ }
929
+ }
930
+ }
931
+ catch (err) {
932
+ console.error(`\nIngestion failed: ${err instanceof Error ? err.message : err}`);
933
+ process.exit(1);
934
+ }
935
+ });
796
936
  // ─── gnosys tags-add ────────────────────────────────────────────────────
797
937
  program
798
938
  .command("tags-add")