hub-integration-guides.mjs
654 lines 20.1 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Settings → Integrations tile guides: setup text and copyable commands.
3 * Used by hub.js to open the integration detail modal on tile click.
4 */
5
6 export const IMPORT_SOURCES_DOC =
7 'https://github.com/aaronrene/knowtation/blob/main/docs/IMPORT-SOURCES.md';
8 export const CAPTURE_CONTRACT_DOC =
9 'https://github.com/aaronrene/knowtation/blob/main/docs/CAPTURE-CONTRACT.md';
10 export const MESSAGING_DOC =
11 'https://github.com/aaronrene/knowtation/blob/main/docs/MESSAGING-INTEGRATION.md';
12 export const TEAMS_DOC =
13 'https://github.com/aaronrene/knowtation/blob/main/docs/TEAMS-AND-COLLABORATION.md';
14 export const HERMES_MEMORY_DOC =
15 'https://hermes-agent.nousresearch.com/docs/user-guide/features/memory';
16
17 export const MODEL_ROUTING_DOC =
18 'https://github.com/aaronrene/knowtation/blob/main/docs/COMPANION-APP-MODEL-ROUTING-AND-ENRICHMENT-ARCHITECTURE.md';
19 export const OPENROUTER_DOC = 'https://openrouter.ai/docs';
20
21 /**
22 * @typedef {'capture' | 'import' | 'provider'} IntegrationKind
23 * @typedef {{ type: 'text', html: string } | { type: 'code', label?: string, code: string }} IntegrationSection
24 * @typedef {{
25 * id: string,
26 * icon: string,
27 * name: string,
28 * desc: string,
29 * kind: IntegrationKind,
30 * sourceType?: string,
31 * sections: IntegrationSection[],
32 * docUrl?: string,
33 * docLabel?: string,
34 * hubImport?: boolean,
35 * }} IntegrationGuide
36 */
37
38 /** @type {Record<string, IntegrationGuide>} */
39 export const INTEGRATION_GUIDES = Object.freeze({
40 openrouter: {
41 id: 'openrouter',
42 icon: '🧭',
43 name: 'OpenRouter',
44 desc: 'BYO key; one API, many models',
45 kind: 'provider',
46 docUrl: OPENROUTER_DOC,
47 docLabel: 'OpenRouter docs',
48 sections: [
49 {
50 type: 'text',
51 html:
52 'OpenRouter is a <strong>bring-your-own-key</strong> model lane (OpenAI-compatible): you pay OpenRouter directly, so it is <strong>never metered against Knowtation packs</strong>. Use it to reach many models (OpenAI, Anthropic, Llama, Mistral, …) through one key. A failed OpenRouter call is <strong>not</strong> silently re-routed to a managed provider — your note text stays on the lane you chose.',
53 },
54 {
55 type: 'text',
56 html:
57 'Select <strong>OpenRouter</strong> under <strong>Settings → Integrations → AI model provider</strong> (self-hosted admins; or set <code>KNOWTATION_CHAT_PROVIDER=openrouter</code> as an operator env lock). The key and model live in server env, never in this UI.',
58 },
59 {
60 type: 'code',
61 label: 'Server env',
62 code: [
63 'KNOWTATION_CHAT_PROVIDER=openrouter',
64 'OPENROUTER_API_KEY=sk-or-...',
65 '# optional model (default openai/gpt-4o-mini):',
66 'OPENROUTER_CHAT_MODEL=anthropic/claude-3.5-haiku',
67 '# optional attribution headers:',
68 'OPENROUTER_SITE_URL=https://your-hub.example',
69 'OPENROUTER_APP_TITLE=Knowtation',
70 ].join('\n'),
71 },
72 ],
73 },
74 slack: {
75 id: 'slack',
76 icon: '💬',
77 name: 'Slack',
78 desc: 'Team chat → Inbox',
79 kind: 'capture',
80 docUrl: MESSAGING_DOC,
81 docLabel: 'MESSAGING-INTEGRATION.md',
82 sections: [
83 {
84 type: 'text',
85 html:
86 'Run the Slack adapter on your Hub host. It verifies Slack signatures and forwards messages to <code>POST /api/v1/capture</code>.',
87 },
88 {
89 type: 'code',
90 label: 'Adapter',
91 code: [
92 'export CAPTURE_URL=https://your-hub.example/api/v1/capture',
93 'export SLACK_SIGNING_SECRET=your_slack_signing_secret',
94 '# Optional when Hub sets CAPTURE_WEBHOOK_SECRET:',
95 'export CAPTURE_WEBHOOK_SECRET=your_shared_secret',
96 'node scripts/capture-slack-adapter.mjs --port 3132',
97 ].join('\n'),
98 },
99 {
100 type: 'code',
101 label: 'Capture body (JSON)',
102 code: '{"body": "message text", "source": "slack", "project": "optional-slug"}',
103 },
104 ],
105 },
106 discord: {
107 id: 'discord',
108 icon: '🎮',
109 name: 'Discord',
110 desc: 'Server messages → Inbox',
111 kind: 'capture',
112 docUrl: MESSAGING_DOC,
113 docLabel: 'MESSAGING-INTEGRATION.md',
114 sections: [
115 {
116 type: 'text',
117 html:
118 'Run the Discord adapter and point your bot or webhook at it. Messages are POSTed to the Hub capture endpoint.',
119 },
120 {
121 type: 'code',
122 label: 'Adapter',
123 code: [
124 'export CAPTURE_URL=https://your-hub.example/api/v1/capture',
125 'export CAPTURE_WEBHOOK_SECRET=your_shared_secret',
126 'node scripts/capture-discord-adapter.mjs --port 3133',
127 ].join('\n'),
128 },
129 {
130 type: 'code',
131 label: 'Capture body (JSON)',
132 code: '{"body": "message text", "source": "discord"}',
133 },
134 ],
135 },
136 telegram: {
137 id: 'telegram',
138 icon: '✈️',
139 name: 'Telegram',
140 desc: 'Bot or webhook → Inbox',
141 kind: 'capture',
142 docUrl: MESSAGING_DOC,
143 docLabel: 'MESSAGING-INTEGRATION.md',
144 sections: [
145 {
146 type: 'text',
147 html:
148 'Use the Telegram adapter, or any bot that POSTs JSON to <code>/api/v1/capture</code> with <code>source: telegram</code>.',
149 },
150 {
151 type: 'code',
152 label: 'Adapter',
153 code: [
154 'export CAPTURE_URL=https://your-hub.example/api/v1/capture',
155 'export CAPTURE_WEBHOOK_SECRET=your_shared_secret',
156 'node scripts/capture-telegram-adapter.mjs --port 3134',
157 ].join('\n'),
158 },
159 {
160 type: 'code',
161 label: 'Capture body (JSON)',
162 code: '{"body": "message text", "source": "telegram"}',
163 },
164 ],
165 },
166 whatsapp: {
167 id: 'whatsapp',
168 icon: '📱',
169 name: 'WhatsApp',
170 desc: 'Automation → Inbox',
171 kind: 'capture',
172 docUrl: MESSAGING_DOC,
173 docLabel: 'MESSAGING-INTEGRATION.md',
174 sections: [
175 {
176 type: 'text',
177 html:
178 'Knowtation has no first-party WhatsApp adapter. Use <strong>Zapier</strong>, <strong>n8n</strong>, or Meta Cloud API automation to POST message text to your Hub capture URL.',
179 },
180 {
181 type: 'code',
182 label: 'HTTP POST target',
183 code: 'POST https://your-hub.example/api/v1/capture',
184 },
185 {
186 type: 'code',
187 label: 'Capture body (JSON)',
188 code: '{"body": "message text", "source": "whatsapp"}',
189 },
190 ],
191 },
192 'chatgpt-export': {
193 id: 'chatgpt-export',
194 icon: '🤖',
195 name: 'ChatGPT',
196 desc: 'ZIP or folder export',
197 kind: 'import',
198 sourceType: 'chatgpt-export',
199 hubImport: true,
200 docUrl: IMPORT_SOURCES_DOC,
201 sections: [
202 {
203 type: 'text',
204 html:
205 'In ChatGPT: <strong>Settings → Data controls → Export data</strong>. Download the ZIP (contains <code>conversations.json</code>) and import the extracted folder or ZIP.',
206 },
207 {
208 type: 'code',
209 label: 'CLI',
210 code:
211 'knowtation import chatgpt-export /path/to/export-folder --output-dir imports/chatgpt --tags chatgpt',
212 },
213 ],
214 },
215 'claude-export': {
216 id: 'claude-export',
217 icon: '🧠',
218 name: 'Claude',
219 desc: 'Chat + memory export',
220 kind: 'import',
221 sourceType: 'claude-export',
222 hubImport: true,
223 docUrl: IMPORT_SOURCES_DOC,
224 sections: [
225 {
226 type: 'text',
227 html:
228 'Export from <strong>Claude → Settings → Privacy → Export data</strong> (chats and/or memory). Accepts Anthropic export folders or third-party JSON/Markdown exports.',
229 },
230 {
231 type: 'code',
232 label: 'CLI',
233 code:
234 'knowtation import claude-export /path/to/claude-export --project myproject --tags claude',
235 },
236 ],
237 },
238 'mem0-export': {
239 id: 'mem0-export',
240 icon: '💾',
241 name: 'Mem0',
242 desc: 'JSON memory export',
243 kind: 'import',
244 sourceType: 'mem0-export',
245 hubImport: true,
246 docUrl: IMPORT_SOURCES_DOC,
247 sections: [
248 {
249 type: 'text',
250 html: 'Export memories from Mem0 (<code>create_memory_export</code> / <code>get_memory_export</code>). One vault note per memory entry.',
251 },
252 {
253 type: 'code',
254 label: 'CLI',
255 code: 'knowtation import mem0-export ./mem0-export.json --project memories --tags mem0',
256 },
257 ],
258 },
259 notion: {
260 id: 'notion',
261 icon: '📝',
262 name: 'Notion',
263 desc: 'API; page IDs + key',
264 kind: 'import',
265 sourceType: 'notion',
266 hubImport: true,
267 docUrl: IMPORT_SOURCES_DOC,
268 sections: [
269 {
270 type: 'text',
271 html:
272 'Create a Notion integration, share pages with it, then pass comma-separated page IDs. Requires <code>NOTION_API_KEY</code> on the machine running the import.',
273 },
274 {
275 type: 'code',
276 label: 'CLI',
277 code: [
278 'export NOTION_API_KEY=your_integration_secret',
279 'knowtation import notion "page-uuid-1,page-uuid-2" --output-dir imports/notion',
280 ].join('\n'),
281 },
282 ],
283 },
284 'jira-export': {
285 id: 'jira-export',
286 icon: '🎫',
287 name: 'Jira',
288 desc: 'CSV export',
289 kind: 'import',
290 sourceType: 'jira-export',
291 hubImport: true,
292 docUrl: IMPORT_SOURCES_DOC,
293 sections: [
294 {
295 type: 'text',
296 html: 'Export issues from Jira as CSV. One note per issue with full row JSON preserved for search.',
297 },
298 {
299 type: 'code',
300 label: 'CLI',
301 code: 'knowtation import jira-export ./jira-export.csv --output-dir imports/jira --tags jira',
302 },
303 ],
304 },
305 notebooklm: {
306 id: 'notebooklm',
307 icon: '📓',
308 name: 'NotebookLM',
309 desc: 'Markdown or JSON',
310 kind: 'import',
311 sourceType: 'notebooklm',
312 hubImport: true,
313 docUrl: IMPORT_SOURCES_DOC,
314 sections: [
315 {
316 type: 'text',
317 html: 'Import a folder of Markdown files or a JSON export with sources/conversations.',
318 },
319 {
320 type: 'code',
321 label: 'CLI',
322 code: 'knowtation import notebooklm ./notebooklm-export-folder --output-dir imports/notebooklm',
323 },
324 ],
325 },
326 gdrive: {
327 id: 'gdrive',
328 icon: '📁',
329 name: 'Google Drive',
330 desc: 'Markdown folder',
331 kind: 'import',
332 sourceType: 'gdrive',
333 hubImport: true,
334 docUrl: IMPORT_SOURCES_DOC,
335 sections: [
336 {
337 type: 'text',
338 html: 'Point at a folder of <code>.md</code> files (export Docs to Markdown or sync with pandoc).',
339 },
340 {
341 type: 'code',
342 label: 'CLI',
343 code: 'knowtation import gdrive /path/to/docs-as-markdown --output-dir imports/gdrive',
344 },
345 ],
346 },
347 'linear-export': {
348 id: 'linear-export',
349 icon: '📋',
350 name: 'Linear',
351 desc: 'CSV export',
352 kind: 'import',
353 sourceType: 'linear-export',
354 hubImport: true,
355 docUrl: IMPORT_SOURCES_DOC,
356 sections: [
357 {
358 type: 'text',
359 html: 'Export from Linear (<strong>Command menu → Export data → CSV</strong>). One note per issue.',
360 },
361 {
362 type: 'code',
363 label: 'CLI',
364 code: 'knowtation import linear-export ./linear-export.csv --output-dir imports/linear',
365 },
366 ],
367 },
368 mif: {
369 id: 'mif',
370 icon: '🔗',
371 name: 'MIF',
372 desc: 'Memory Interchange Format',
373 kind: 'import',
374 sourceType: 'mif',
375 hubImport: true,
376 docUrl: IMPORT_SOURCES_DOC,
377 sections: [
378 {
379 type: 'text',
380 html:
381 '<a href="https://mif-spec.dev/" target="_blank" rel="noopener noreferrer">Memory Interchange Format</a> — copy <code>.memory.md</code> or normalize JSON-LD exports into the vault.',
382 },
383 {
384 type: 'code',
385 label: 'CLI',
386 code: 'knowtation import mif ./my-memories.memory.md --output-dir imports/mif',
387 },
388 ],
389 },
390 markdown: {
391 id: 'markdown',
392 icon: '📄',
393 name: 'Markdown',
394 desc: 'File or folder',
395 kind: 'import',
396 sourceType: 'markdown',
397 hubImport: true,
398 docUrl: IMPORT_SOURCES_DOC,
399 sections: [
400 {
401 type: 'text',
402 html: 'Generic Markdown import for a single file or folder tree. Hub supports ZIP, multi-file, and folder drop.',
403 },
404 {
405 type: 'code',
406 label: 'CLI',
407 code: [
408 'knowtation import markdown ./my-notes.md --output-dir imports/notes',
409 'knowtation import markdown ./exported-folder --project myproject',
410 ].join('\n'),
411 },
412 ],
413 },
414 audio: {
415 id: 'audio',
416 icon: '🎙️',
417 name: 'Audio',
418 desc: 'Whisper transcription',
419 kind: 'import',
420 sourceType: 'audio',
421 hubImport: true,
422 docUrl: IMPORT_SOURCES_DOC,
423 sections: [
424 {
425 type: 'text',
426 html:
427 'Self-hosted Hub transcribes with OpenAI Whisper (<strong>~25&nbsp;MB</strong> per file). Requires <code>OPENAI_API_KEY</code>. Larger files may transcode when <code>ffmpeg</code> is available.',
428 },
429 {
430 type: 'code',
431 label: 'CLI',
432 code: 'knowtation import audio ./recording.m4a --project myproject --output-dir media/audio',
433 },
434 ],
435 },
436 'wallet-csv': {
437 id: 'wallet-csv',
438 icon: '💰',
439 name: 'Wallet CSV',
440 desc: 'Tx history; 11 formats',
441 kind: 'import',
442 sourceType: 'wallet-csv',
443 hubImport: true,
444 docUrl: IMPORT_SOURCES_DOC,
445 sections: [
446 {
447 type: 'text',
448 html:
449 'Auto-detects Coinbase, Kraken, Binance, MetaMask/Etherscan, Phantom, Ledger Live, and more. One note per transaction row.',
450 },
451 {
452 type: 'code',
453 label: 'CLI',
454 code: 'knowtation import wallet-csv ./wallet-export.csv --tags payment,on-chain',
455 },
456 ],
457 },
458 'supabase-memory': {
459 id: 'supabase-memory',
460 icon: '🗄️',
461 name: 'Supabase',
462 desc: 'Memory table import',
463 kind: 'import',
464 sourceType: 'supabase-memory',
465 docUrl: IMPORT_SOURCES_DOC,
466 sections: [
467 {
468 type: 'text',
469 html:
470 'Import rows from a Supabase memory table (CLI / bridge). Connection credentials must live in server env — never paste secrets into the Hub UI.',
471 },
472 {
473 type: 'code',
474 label: 'CLI pattern',
475 code: 'knowtation import supabase-memory <connection-or-table-ref> --project memories',
476 },
477 ],
478 },
479 openclaw: {
480 id: 'openclaw',
481 icon: '🦞',
482 name: 'OpenClaw',
483 desc: 'Agent memory + chats',
484 kind: 'import',
485 sourceType: 'openclaw',
486 docUrl: IMPORT_SOURCES_DOC,
487 sections: [
488 {
489 type: 'text',
490 html:
491 'Import agent conversations and memory from <a href="https://github.com/openclaw/openclaw" target="_blank" rel="noopener noreferrer">OpenClaw</a>. Point at an export folder or memory dump; one note per conversation or memory entry with <code>source: openclaw</code>.',
492 },
493 {
494 type: 'code',
495 label: 'CLI',
496 code:
497 'knowtation import openclaw /path/to/openclaw-export --output-dir imports/openclaw --tags openclaw',
498 },
499 {
500 type: 'text',
501 html:
502 'For live capture from channels (Slack, WhatsApp, etc.), run OpenClaw with Knowtation Hub MCP — copy URL, JWT, and vault id from <strong>Hub API</strong> below.',
503 },
504 ],
505 },
506 hermes: {
507 id: 'hermes',
508 icon: '🪶',
509 name: 'Hermes Agent',
510 desc: 'Agent memory + profile',
511 kind: 'import',
512 docUrl: HERMES_MEMORY_DOC,
513 docLabel: 'Hermes memory docs',
514 sections: [
515 {
516 type: 'text',
517 html:
518 '<a href="https://github.com/NousResearch/hermes-agent" target="_blank" rel="noopener noreferrer">Hermes Agent</a> stores built-in memory in <code>~/.hermes/memories/MEMORY.md</code> and <code>USER.md</code>. Export a snapshot or copy those files into Knowtation.',
519 },
520 {
521 type: 'code',
522 label: 'Export from Hermes',
523 code: [
524 'hermes memory export --output ~/hermes-memory-backup.json',
525 '# Or full portable backup (config + memory + skills):',
526 'hermes backup',
527 ].join('\n'),
528 },
529 {
530 type: 'code',
531 label: 'Import into Knowtation',
532 code: [
533 'knowtation import markdown ~/.hermes/memories/MEMORY.md \\',
534 ' --output-dir imports/hermes --tags hermes,memory',
535 'knowtation import markdown ~/.hermes/memories/USER.md \\',
536 ' --output-dir imports/hermes --tags hermes,user-profile',
537 ].join('\n'),
538 },
539 {
540 type: 'text',
541 html:
542 'Wire Hermes to your vault for ongoing reads/writes: <strong>Settings → Integrations → Hub API</strong> → copy env vars into Hermes MCP config. External memory providers (Mem0, Honcho, etc.) can be mirrored the same way after export.',
543 },
544 ],
545 },
546 imports: {
547 id: 'imports',
548 icon: '📥',
549 name: 'Imports',
550 desc: 'Local files & team uploads',
551 kind: 'import',
552 hubImport: true,
553 docUrl: IMPORT_SOURCES_DOC,
554 docLabel: 'IMPORT-SOURCES.md',
555 sections: [
556 {
557 type: 'text',
558 html: '<strong>Local (your machine)</strong> — Use the Hub <strong>Import</strong> button: pick a source type, drop files or a folder, or upload a ZIP (~100&nbsp;MB per request). For large batches or automation, use the CLI.',
559 },
560 {
561 type: 'code',
562 label: 'CLI (local files)',
563 code: [
564 'knowtation import markdown ./my-folder --output-dir inbox',
565 'knowtation import chatgpt-export ./export.zip --tags import',
566 ].join('\n'),
567 },
568 {
569 type: 'text',
570 html:
571 '<strong>Team (shared vault)</strong> — Admins invite editors in <strong>Settings → Team</strong>. Teammates sign in to the same Hub, use <strong>Import</strong> or CLI with shared Hub credentials, and notes land in the team vault. Restrict vaults per person under <strong>Vault access</strong>. See TEAMS-AND-COLLABORATION.md.',
572 },
573 {
574 type: 'code',
575 label: 'Team Hub env (from Integrations → Hub API)',
576 code: [
577 'KNOWTATION_HUB_URL=https://your-hub.example',
578 'KNOWTATION_HUB_TOKEN=<JWT after sign-in>',
579 'KNOWTATION_HUB_VAULT_ID=default',
580 ].join('\n'),
581 },
582 ],
583 },
584 });
585
586 /** @returns {string[]} */
587 export function listIntegrationGuideIds() {
588 return Object.keys(INTEGRATION_GUIDES);
589 }
590
591 /**
592 * @param {string} id
593 * @returns {IntegrationGuide | null}
594 */
595 export function getIntegrationGuide(id) {
596 if (!id || typeof id !== 'string') return null;
597 return INTEGRATION_GUIDES[id] ?? null;
598 }
599
600 /**
601 * Escape plain text for safe HTML insertion.
602 * @param {string} s
603 */
604 export function escapeHtml(s) {
605 return String(s)
606 .replace(/&/g, '&amp;')
607 .replace(/</g, '&lt;')
608 .replace(/>/g, '&gt;')
609 .replace(/"/g, '&quot;');
610 }
611
612 /**
613 * Render guide sections to HTML (text sections are pre-sanitized author HTML; code is escaped).
614 * @param {IntegrationGuide} guide
615 * @returns {string}
616 */
617 export function renderIntegrationGuideHtml(guide) {
618 const parts = [];
619 for (const section of guide.sections) {
620 if (section.type === 'text') {
621 parts.push(`<p class="integ-guide-text">${section.html}</p>`);
622 continue;
623 }
624 const label = section.label ? `<span class="integ-guide-code-label">${escapeHtml(section.label)}</span>` : '';
625 parts.push(
626 `<div class="integ-guide-code-block">${label}<pre><code>${escapeHtml(section.code)}</code></pre>` +
627 `<button type="button" class="btn-secondary btn-copy-small integ-guide-copy" data-copy="${escapeHtml(section.code)}">Copy</button></div>`,
628 );
629 }
630 if (guide.docUrl) {
631 const label = guide.docLabel || 'Full documentation';
632 parts.push(
633 `<p class="integ-guide-doc"><a href="${escapeHtml(guide.docUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a></p>`,
634 );
635 }
636 return parts.join('\n');
637 }
638
639 /**
640 * Bind click handlers on integration tiles inside a root element.
641 * @param {ParentNode} root
642 * @param {{ onOpen: (guide: IntegrationGuide) => void }} handlers
643 */
644 export function wireIntegrationTiles(root, handlers) {
645 if (!root || !handlers || typeof handlers.onOpen !== 'function') return;
646 const tiles = root.querySelectorAll('[data-integ-id]');
647 for (const tile of tiles) {
648 tile.addEventListener('click', () => {
649 const id = tile.getAttribute('data-integ-id');
650 const guide = id ? getIntegrationGuide(id) : null;
651 if (guide) handlers.onOpen(guide);
652 });
653 }
654 }
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