import.mjs
111 lines 5.1 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Import from external sources into vault. Phase 6.
3 * Each importer produces vault notes with SPEC §1-2 frontmatter.
4 */
5
6 import fs from 'fs';
7 import path from 'path';
8 import { loadConfig } from './config.mjs';
9 import { writeNote } from './write.mjs';
10 import { normalizeSlug } from './vault.mjs';
11 import { IMPORT_SOURCE_TYPES, isValidImportSourceType } from './import-source-types.mjs';
12 import { getRepoRoot } from './repo-root.mjs';
13
14 const projectRoot = getRepoRoot();
15
16 /** @typedef {{
17 * project?: string,
18 * outputDir?: string,
19 * tags?: string[],
20 * dryRun?: boolean,
21 * vaultPath?: string,
22 * onProgress?: (p: { progress: number, total?: number, message?: string }) => void | Promise<void>,
23 * onMemoryEvent?: (data: object) => void,
24 * urlMode?: 'auto' | 'bookmark' | 'extract',
25 * sheetsRange?: string
26 * }} ImportOptions */
27
28 /**
29 * Run import for a source type.
30 * @param {string} sourceType - markdown, chatgpt-export, claude-export, mif, mem0-export, audio, video, etc.
31 * @param {string} input - Path to file, folder, or ZIP
32 * @param {ImportOptions} options
33 * @returns {Promise<{ imported: { path: string, source_id?: string }[], count: number }>}
34 */
35 export async function runImport(sourceType, input, options = {}) {
36 if (!isValidImportSourceType(sourceType)) {
37 throw new Error(
38 `Unknown source type: ${sourceType}. Valid: ${IMPORT_SOURCE_TYPES.join(', ')}.`
39 );
40 }
41 /** Hosted bridge passes vaultPath (temp dir); skip loadConfig so Netlify is not tied to repo config/local.yaml. */
42 let vaultPath = options.vaultPath;
43 if (!vaultPath) {
44 const config = loadConfig(projectRoot);
45 vaultPath = config.vault_path;
46 }
47 const project = options.project ? normalizeSlug(options.project) : null;
48 const outputBase = resolveOutputDir(vaultPath, options.outputDir, project);
49 const tags = options.tags || [];
50 const dryRun = options.dryRun === true;
51
52 const ctx = {
53 vaultPath,
54 outputBase,
55 project,
56 tags: Array.isArray(tags) ? tags : String(tags).split(',').map((t) => t.trim()).filter(Boolean),
57 dryRun,
58 onProgress: options.onProgress,
59 onMemoryEvent: options.onMemoryEvent,
60 urlMode: options.urlMode,
61 ...(options.sheetsRange != null && String(options.sheetsRange).trim() !== ''
62 ? { sheetsRange: String(options.sheetsRange).trim() }
63 : {}),
64 };
65 const importers = {
66 markdown: () => import('./importers/markdown.mjs').then((m) => m.importMarkdown(input, ctx)),
67 pdf: () => import('./importers/pdf.mjs').then((m) => m.importPdf(input, ctx)),
68 docx: () => import('./importers/docx.mjs').then((m) => m.importDocx(input, ctx)),
69 url: () => import('./importers/url.mjs').then((m) => m.importUrl(input, ctx)),
70 'chatgpt-export': () => import('./importers/chatgpt.mjs').then((m) => m.importChatGPT(input, ctx)),
71 'claude-export': () => import('./importers/claude.mjs').then((m) => m.importClaude(input, ctx)),
72 mif: () => import('./importers/mif.mjs').then((m) => m.importMIF(input, ctx)),
73 'mem0-export': () => import('./importers/mem0.mjs').then((m) => m.importMem0(input, ctx)),
74 'supabase-memory': () => import('./importers/supabase-memory.mjs').then((m) => m.importSupabaseMemory(input, ctx)),
75 audio: () => import('./importers/audio.mjs').then((m) => m.importAudio(input, ctx)),
76 video: () => import('./importers/audio.mjs').then((m) => m.importVideo(input, ctx)),
77 notion: () => import('./importers/notion.mjs').then((m) => m.importNotion(input, ctx)),
78 'jira-export': () => import('./importers/jira.mjs').then((m) => m.importJira(input, ctx)),
79 notebooklm: () => import('./importers/notebooklm.mjs').then((m) => m.importNotebookLM(input, ctx)),
80 gdrive: () => import('./importers/gdrive.mjs').then((m) => m.importGDrive(input, ctx)),
81 'generic-csv': () => import('./importers/generic-csv.mjs').then((m) => m.importGenericCsv(input, ctx)),
82 'excel-xlsx': () => import('./importers/excel-xlsx.mjs').then((m) => m.importExcelXlsx(input, ctx)),
83 vcf: () => import('./importers/vcf.mjs').then((m) => m.importVcf(input, ctx)),
84 'google-sheets': () => import('./importers/google-sheets.mjs').then((m) => m.importGoogleSheets(input, ctx)),
85 'linear-export': () => import('./importers/linear.mjs').then((m) => m.importLinear(input, ctx)),
86 'wallet-csv': () => import('./importers/wallet-csv.mjs').then((m) => m.importWalletCSV(input, ctx)),
87 'json-rows': () => import('./importers/json-rows.mjs').then((m) => m.importJsonRows(input, ctx)),
88 };
89
90 const fn = importers[sourceType];
91 if (!fn) {
92 throw new Error(`No importer registered for source type: ${sourceType}`);
93 }
94
95 return fn();
96 }
97
98 /**
99 * Resolve output directory: options.outputDir (vault-relative), or inbox/projects/<project>/inbox
100 * @param {string} vaultPath
101 * @param {string|undefined} outputDir - vault-relative
102 * @param {string|null} project
103 * @returns {string} vault-relative path
104 */
105 function resolveOutputDir(vaultPath, outputDir, project) {
106 if (outputDir) {
107 const normalized = outputDir.replace(/\\/g, '/').replace(/\/$/, '');
108 return normalized || 'inbox';
109 }
110 return project ? `projects/${project}/inbox` : 'inbox';
111 }
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago