modular-studio 1.0.4 → 1.0.5
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 +101 -20
- package/dist/assets/Badge-22Ai0eyi.js +1 -0
- package/dist/assets/Input-Bgp734xs.js +1 -0
- package/dist/assets/KnowledgeTab-DABxirZh.js +4 -0
- package/dist/assets/MemoryTab-DZeYElIT.js +16 -0
- package/dist/assets/QualificationTab-Dfpy3J30.js +1 -0
- package/dist/assets/ReviewTab-SD8lQuCc.js +103 -0
- package/dist/assets/Section-DoJrmytO.js +1 -0
- package/dist/assets/TestTab-PDyMF8Fw.js +33 -0
- package/dist/assets/ToolsTab-B83qGCmG.js +1 -0
- package/dist/assets/conversationStore-CkfEU2eV.js +1 -0
- package/dist/assets/icons-C2EV-le6.js +1 -0
- package/dist/assets/index-DkpMAxX7.css +1 -0
- package/dist/assets/index-q24ug5Qs.js +143 -0
- package/dist/assets/{jszip.min-BK6ZQWkj.js → jszip.min-wf-D3Ix_.js} +1 -1
- package/dist/assets/markdown-DWF7F0i0.js +29 -0
- package/dist/assets/services-BaKotDf0.js +343 -0
- package/dist/assets/stores-CeKWz7ou.js +1 -0
- package/dist/assets/vendor-D1h_O76p.js +9 -0
- package/dist/index.html +8 -4
- package/dist-server/bin/modular-mcp.js +0 -1
- package/dist-server/bin/modular-studio.js +0 -1
- package/dist-server/server/config.js +0 -1
- package/dist-server/server/data/mcp-tokens.json +3 -3
- package/dist-server/server/index.d.ts.map +1 -1
- package/dist-server/server/index.js +2 -1
- package/dist-server/server/mcp/manager.js +0 -1
- package/dist-server/server/mcp/modular-server.js +0 -1
- package/dist-server/server/mcp/transport.js +0 -1
- package/dist-server/server/routes/agent-sdk.js +0 -1
- package/dist-server/server/routes/agents.d.ts +9 -5
- package/dist-server/server/routes/agents.d.ts.map +1 -1
- package/dist-server/server/routes/agents.js +81 -8
- package/dist-server/server/routes/auth-codex.js +0 -1
- package/dist-server/server/routes/capabilities.js +0 -1
- package/dist-server/server/routes/claude-config.js +0 -1
- package/dist-server/server/routes/connectors.d.ts.map +1 -1
- package/dist-server/server/routes/connectors.js +194 -1
- package/dist-server/server/routes/conversations.js +0 -1
- package/dist-server/server/routes/embeddings.js +0 -1
- package/dist-server/server/routes/health.js +0 -1
- package/dist-server/server/routes/knowledge.js +0 -1
- package/dist-server/server/routes/llm.js +0 -1
- package/dist-server/server/routes/mcp-oauth.js +0 -1
- package/dist-server/server/routes/mcp.js +0 -1
- package/dist-server/server/routes/memory.d.ts +3 -0
- package/dist-server/server/routes/memory.d.ts.map +1 -0
- package/dist-server/server/routes/memory.js +283 -0
- package/dist-server/server/routes/pipeline.js +0 -1
- package/dist-server/server/routes/providers.js +0 -1
- package/dist-server/server/routes/qualification.d.ts.map +1 -1
- package/dist-server/server/routes/qualification.js +382 -74
- package/dist-server/server/routes/repo-index.js +0 -1
- package/dist-server/server/routes/runtime.js +0 -1
- package/dist-server/server/routes/skills-search.d.ts.map +1 -1
- package/dist-server/server/routes/skills-search.js +39 -5
- package/dist-server/server/routes/worktrees.js +0 -1
- package/dist-server/server/services/__tests__/embeddingService.test.js +0 -1
- package/dist-server/server/services/adapters/postgresAdapter.d.ts +29 -0
- package/dist-server/server/services/adapters/postgresAdapter.d.ts.map +1 -0
- package/dist-server/server/services/adapters/postgresAdapter.js +224 -0
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts +28 -0
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts.map +1 -0
- package/dist-server/server/services/adapters/sqliteAdapter.js +219 -0
- package/dist-server/server/services/adapters/storageAdapter.d.ts +22 -0
- package/dist-server/server/services/adapters/storageAdapter.d.ts.map +1 -0
- package/dist-server/server/services/adapters/storageAdapter.js +1 -0
- package/dist-server/server/services/agentRunner.js +0 -1
- package/dist-server/server/services/agentStore.d.ts +18 -3
- package/dist-server/server/services/agentStore.d.ts.map +1 -1
- package/dist-server/server/services/agentStore.js +116 -23
- package/dist-server/server/services/contentStore.js +0 -1
- package/dist-server/server/services/embeddingService.d.ts +2 -0
- package/dist-server/server/services/embeddingService.d.ts.map +1 -1
- package/dist-server/server/services/embeddingService.js +30 -19
- package/dist-server/server/services/factExtractor.js +0 -1
- package/dist-server/server/services/githubIndexer.js +0 -1
- package/dist-server/server/services/mcpOAuth.js +0 -1
- package/dist-server/server/services/memoryScorer.js +0 -1
- package/dist-server/server/services/repoIndexer.js +0 -1
- package/dist-server/server/services/sqliteStore.js +0 -1
- package/dist-server/server/services/teamRunner.js +0 -1
- package/dist-server/server/services/worktreeManager.js +0 -1
- package/dist-server/server/types.d.ts +5 -0
- package/dist-server/server/types.d.ts.map +1 -1
- package/dist-server/server/types.js +0 -1
- package/dist-server/server/utils/pathSecurity.js +0 -1
- package/dist-server/src/services/budgetAllocator.js +0 -1
- package/dist-server/src/services/contradictionDetector.js +0 -1
- package/dist-server/src/services/treeIndexer.js +0 -1
- package/dist-server/src/store/knowledgeBase.d.ts +10 -0
- package/dist-server/src/store/knowledgeBase.d.ts.map +1 -1
- package/dist-server/src/store/knowledgeBase.js +13 -1
- package/dist-server/src/store/memoryStore.d.ts +107 -0
- package/dist-server/src/store/memoryStore.d.ts.map +1 -0
- package/dist-server/src/store/memoryStore.js +263 -0
- package/dist-server/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +104 -97
- package/dist/assets/graphPopulator-B3rQxb5A.js +0 -1
- package/dist/assets/index-BA_J-aHx.js +0 -686
- package/dist/assets/index-C7vpqKVZ.css +0 -1
|
@@ -26,6 +26,7 @@ import pipelineRoutes from './routes/pipeline.js';
|
|
|
26
26
|
import embeddingRoutes from './routes/embeddings.js';
|
|
27
27
|
import embeddingService from './services/embeddingService.js';
|
|
28
28
|
import conversationRoutes from './routes/conversations.js';
|
|
29
|
+
import memoryRoutes from './routes/memory.js';
|
|
29
30
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
export function createApp() {
|
|
31
32
|
const app = express();
|
|
@@ -87,6 +88,7 @@ export function createApp() {
|
|
|
87
88
|
app.use('/api/pipeline', pipelineRoutes);
|
|
88
89
|
app.use('/api/embeddings', embeddingRoutes);
|
|
89
90
|
app.use('/api/conversations', conversationRoutes);
|
|
91
|
+
app.use('/api/memory', memoryRoutes);
|
|
90
92
|
// API 404 catch-all — log unmatched API routes for debugging
|
|
91
93
|
app.use('/api', (_req, res) => {
|
|
92
94
|
console.warn(`[API 404] ${_req.method} ${_req.originalUrl}`);
|
|
@@ -185,4 +187,3 @@ if (isMainModule && !globalThis.__modularStudioStarted) {
|
|
|
185
187
|
process.on('SIGINT', () => { clearInterval(keepAlive); server.close(); process.exit(0); });
|
|
186
188
|
process.on('SIGTERM', () => { clearInterval(keepAlive); server.close(); process.exit(0); });
|
|
187
189
|
}
|
|
188
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent CRUD Routes
|
|
3
|
-
* GET /api/agents
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Agent CRUD Routes with Versioning
|
|
3
|
+
* GET /api/agents — list all (summary only)
|
|
4
|
+
* POST /api/agents — create new agent (returns agentId)
|
|
5
|
+
* GET /api/agents/:id — full latest state
|
|
6
|
+
* PUT /api/agents/:id — save/update (creates new version)
|
|
7
|
+
* DELETE /api/agents/:id — delete
|
|
8
|
+
* GET /api/agents/:id/versions — list all versions
|
|
9
|
+
* POST /api/agents/:id/versions/:version/restore — restore to specific version
|
|
10
|
+
* DELETE /api/agents/:id/versions/:version — delete a version
|
|
7
11
|
*/
|
|
8
12
|
declare const router: import("express-serve-static-core").Router;
|
|
9
13
|
export default router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../server/routes/agents.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../server/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA6HxB,eAAe,MAAM,CAAC"}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent CRUD Routes
|
|
3
|
-
* GET /api/agents
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Agent CRUD Routes with Versioning
|
|
3
|
+
* GET /api/agents — list all (summary only)
|
|
4
|
+
* POST /api/agents — create new agent (returns agentId)
|
|
5
|
+
* GET /api/agents/:id — full latest state
|
|
6
|
+
* PUT /api/agents/:id — save/update (creates new version)
|
|
7
|
+
* DELETE /api/agents/:id — delete
|
|
8
|
+
* GET /api/agents/:id/versions — list all versions
|
|
9
|
+
* POST /api/agents/:id/versions/:version/restore — restore to specific version
|
|
10
|
+
* DELETE /api/agents/:id/versions/:version — delete a version
|
|
7
11
|
*/
|
|
8
12
|
import { Router } from 'express';
|
|
9
|
-
import { saveAgent, loadAgent, listAgents, deleteAgent } from '../services/agentStore.js';
|
|
13
|
+
import { saveAgent, loadAgent, listAgents, deleteAgent, createAgentVersion, listAgentVersions, restoreAgentVersion, deleteAgentVersion } from '../services/agentStore.js';
|
|
10
14
|
const router = Router();
|
|
15
|
+
// List all agents
|
|
11
16
|
router.get('/', (_req, res) => {
|
|
12
17
|
try {
|
|
13
18
|
const agents = listAgents();
|
|
@@ -17,6 +22,24 @@ router.get('/', (_req, res) => {
|
|
|
17
22
|
res.status(500).json({ status: 'error', error: err.message });
|
|
18
23
|
}
|
|
19
24
|
});
|
|
25
|
+
// Create new agent
|
|
26
|
+
router.post('/', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const state = req.body;
|
|
29
|
+
if (!state || typeof state !== 'object') {
|
|
30
|
+
res.status(400).json({ status: 'error', error: 'Invalid body' });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const agentId = state.id || `agent-${Date.now()}`;
|
|
34
|
+
state.version = state.version || '0.1.0';
|
|
35
|
+
saveAgent(agentId, state);
|
|
36
|
+
res.json({ status: 'ok', data: { id: agentId } });
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
res.status(500).json({ status: 'error', error: err.message });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// Get latest agent state
|
|
20
43
|
router.get('/:id', (req, res) => {
|
|
21
44
|
try {
|
|
22
45
|
const agent = loadAgent(req.params.id);
|
|
@@ -30,6 +53,7 @@ router.get('/:id', (req, res) => {
|
|
|
30
53
|
res.status(500).json({ status: 'error', error: err.message });
|
|
31
54
|
}
|
|
32
55
|
});
|
|
56
|
+
// Save/update agent (creates version)
|
|
33
57
|
router.put('/:id', (req, res) => {
|
|
34
58
|
try {
|
|
35
59
|
const state = req.body;
|
|
@@ -37,13 +61,19 @@ router.put('/:id', (req, res) => {
|
|
|
37
61
|
res.status(400).json({ status: 'error', error: 'Invalid body' });
|
|
38
62
|
return;
|
|
39
63
|
}
|
|
64
|
+
// Create version if agent exists and version changed
|
|
65
|
+
const existing = loadAgent(req.params.id);
|
|
66
|
+
if (existing && state.version && state.version !== existing.version) {
|
|
67
|
+
createAgentVersion(req.params.id, existing.version, 'Auto-saved version');
|
|
68
|
+
}
|
|
40
69
|
saveAgent(req.params.id, state);
|
|
41
|
-
res.json({ status: 'ok', data: { id: req.params.id } });
|
|
70
|
+
res.json({ status: 'ok', data: { id: req.params.id, version: state.version } });
|
|
42
71
|
}
|
|
43
72
|
catch (err) {
|
|
44
73
|
res.status(500).json({ status: 'error', error: err.message });
|
|
45
74
|
}
|
|
46
75
|
});
|
|
76
|
+
// Delete agent
|
|
47
77
|
router.delete('/:id', (req, res) => {
|
|
48
78
|
try {
|
|
49
79
|
const deleted = deleteAgent(req.params.id);
|
|
@@ -57,5 +87,48 @@ router.delete('/:id', (req, res) => {
|
|
|
57
87
|
res.status(500).json({ status: 'error', error: err.message });
|
|
58
88
|
}
|
|
59
89
|
});
|
|
90
|
+
// List agent versions
|
|
91
|
+
router.get('/:id/versions', (req, res) => {
|
|
92
|
+
try {
|
|
93
|
+
const versions = listAgentVersions(req.params.id);
|
|
94
|
+
const formatted = versions.map(v => ({
|
|
95
|
+
id: v.id,
|
|
96
|
+
version: v.version,
|
|
97
|
+
timestamp: v.timestamp,
|
|
98
|
+
label: v.label,
|
|
99
|
+
}));
|
|
100
|
+
res.json({ status: 'ok', data: formatted });
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
res.status(500).json({ status: 'error', error: err.message });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Restore agent version
|
|
107
|
+
router.post('/:id/versions/:version/restore', (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const restored = restoreAgentVersion(req.params.id, req.params.version);
|
|
110
|
+
if (!restored) {
|
|
111
|
+
res.status(404).json({ status: 'error', error: 'Version not found' });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
res.json({ status: 'ok', data: { restored: req.params.version } });
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
res.status(500).json({ status: 'error', error: err.message });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Delete agent version
|
|
121
|
+
router.delete('/:id/versions/:version', (req, res) => {
|
|
122
|
+
try {
|
|
123
|
+
const deleted = deleteAgentVersion(req.params.id, req.params.version);
|
|
124
|
+
if (!deleted) {
|
|
125
|
+
res.status(404).json({ status: 'error', error: 'Version not found' });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
res.json({ status: 'ok' });
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
res.status(500).json({ status: 'error', error: err.message });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
60
134
|
export default router;
|
|
61
|
-
//# sourceMappingURL=agents.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../../server/routes/connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,QAAA,MAAM,MAAM,4CAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../../server/routes/connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2jBxB,eAAe,MAAM,CAAC"}
|
|
@@ -324,5 +324,198 @@ router.delete('/auth/:service', (req, res) => {
|
|
|
324
324
|
saveAuth(auth);
|
|
325
325
|
res.json({ status: 'ok' });
|
|
326
326
|
});
|
|
327
|
+
// ── In-memory session keys (never persisted to disk) ──
|
|
328
|
+
const sessionKeys = new Map();
|
|
329
|
+
// ── Notion helpers ──
|
|
330
|
+
function extractPageId(url) {
|
|
331
|
+
const hex32 = url.match(/([a-f0-9]{32})(?:[?#/]|$)/i);
|
|
332
|
+
if (hex32)
|
|
333
|
+
return hex32[1];
|
|
334
|
+
const uuid = url.match(/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i);
|
|
335
|
+
return uuid ? uuid[1].replace(/-/g, '') : null;
|
|
336
|
+
}
|
|
337
|
+
function notionHeaders(key) {
|
|
338
|
+
return { 'Authorization': `Bearer ${key}`, 'Notion-Version': '2022-06-28', 'Content-Type': 'application/json' };
|
|
339
|
+
}
|
|
340
|
+
function getBlockText(block) {
|
|
341
|
+
const content = block[block.type];
|
|
342
|
+
if (typeof content !== 'object' || !content || Array.isArray(content))
|
|
343
|
+
return '';
|
|
344
|
+
const rt = content.rich_text;
|
|
345
|
+
if (!Array.isArray(rt))
|
|
346
|
+
return '';
|
|
347
|
+
return rt.map(r => (typeof r === 'object' && r !== null ? String(r.plain_text ?? '') : '')).join('');
|
|
348
|
+
}
|
|
349
|
+
function blocksToMarkdown(blocks) {
|
|
350
|
+
const lines = [];
|
|
351
|
+
for (const b of blocks) {
|
|
352
|
+
const text = getBlockText(b);
|
|
353
|
+
if (!text)
|
|
354
|
+
continue;
|
|
355
|
+
if (b.type === 'heading_1')
|
|
356
|
+
lines.push(`# ${text}`);
|
|
357
|
+
else if (b.type === 'heading_2')
|
|
358
|
+
lines.push(`## ${text}`);
|
|
359
|
+
else if (b.type === 'heading_3')
|
|
360
|
+
lines.push(`### ${text}`);
|
|
361
|
+
else if (b.type === 'bulleted_list_item')
|
|
362
|
+
lines.push(`- ${text}`);
|
|
363
|
+
else if (b.type === 'numbered_list_item')
|
|
364
|
+
lines.push(`1. ${text}`);
|
|
365
|
+
else if (b.type === 'code')
|
|
366
|
+
lines.push(`\`\`\`\n${text}\n\`\`\``);
|
|
367
|
+
else
|
|
368
|
+
lines.push(text);
|
|
369
|
+
}
|
|
370
|
+
return lines.join('\n');
|
|
371
|
+
}
|
|
372
|
+
function getPageTitle(page) {
|
|
373
|
+
const titleProp = Object.values(page.properties).find(p => p.type === 'title');
|
|
374
|
+
return titleProp?.title?.[0]?.plain_text ?? page.id;
|
|
375
|
+
}
|
|
376
|
+
async function fetchAllBlocks(pageId, key) {
|
|
377
|
+
const blocks = [];
|
|
378
|
+
let cursor;
|
|
379
|
+
do {
|
|
380
|
+
const qs = cursor ? `?start_cursor=${encodeURIComponent(cursor)}` : '';
|
|
381
|
+
const resp = await fetch(`https://api.notion.com/v1/blocks/${pageId}/children${qs}`, { headers: notionHeaders(key) });
|
|
382
|
+
if (!resp.ok)
|
|
383
|
+
break;
|
|
384
|
+
const data = await resp.json();
|
|
385
|
+
blocks.push(...data.results);
|
|
386
|
+
cursor = data.has_more && data.next_cursor ? data.next_cursor : undefined;
|
|
387
|
+
} while (cursor);
|
|
388
|
+
return blocks;
|
|
389
|
+
}
|
|
390
|
+
async function fetchPage(pageId, key) {
|
|
391
|
+
const resp = await fetch(`https://api.notion.com/v1/pages/${pageId}`, { headers: notionHeaders(key) });
|
|
392
|
+
if (!resp.ok)
|
|
393
|
+
return null;
|
|
394
|
+
const page = await resp.json();
|
|
395
|
+
const title = getPageTitle(page);
|
|
396
|
+
const blocks = await fetchAllBlocks(pageId, key);
|
|
397
|
+
const content = blocksToMarkdown(blocks);
|
|
398
|
+
return { id: pageId, title, content, tokens: Math.ceil(content.length / 4) };
|
|
399
|
+
}
|
|
400
|
+
async function queryDatabase(dbId, key) {
|
|
401
|
+
const items = [];
|
|
402
|
+
let cursor;
|
|
403
|
+
do {
|
|
404
|
+
const body = JSON.stringify(cursor ? { start_cursor: cursor } : {});
|
|
405
|
+
const resp = await fetch(`https://api.notion.com/v1/databases/${dbId}/query`, { method: 'POST', headers: notionHeaders(key), body });
|
|
406
|
+
if (!resp.ok)
|
|
407
|
+
break;
|
|
408
|
+
const data = await resp.json();
|
|
409
|
+
for (const row of data.results) {
|
|
410
|
+
const title = getPageTitle(row);
|
|
411
|
+
items.push({ id: row.id, title, content: `# ${title}`, tokens: Math.ceil(title.length / 4) + 10 });
|
|
412
|
+
}
|
|
413
|
+
cursor = data.has_more && data.next_cursor ? data.next_cursor : undefined;
|
|
414
|
+
} while (cursor);
|
|
415
|
+
return items;
|
|
416
|
+
}
|
|
417
|
+
async function searchWorkspace(key) {
|
|
418
|
+
const body = JSON.stringify({ sort: { direction: 'descending', timestamp: 'last_edited_time' }, page_size: 10 });
|
|
419
|
+
const resp = await fetch('https://api.notion.com/v1/search', { method: 'POST', headers: notionHeaders(key), body });
|
|
420
|
+
if (!resp.ok)
|
|
421
|
+
return [];
|
|
422
|
+
const data = await resp.json();
|
|
423
|
+
const items = [];
|
|
424
|
+
for (const result of data.results) {
|
|
425
|
+
if (result.object !== 'page')
|
|
426
|
+
continue;
|
|
427
|
+
const page = await fetchPage(result.id, key);
|
|
428
|
+
if (page)
|
|
429
|
+
items.push(page);
|
|
430
|
+
}
|
|
431
|
+
return items;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* POST /api/connectors/notion/test
|
|
435
|
+
* Validate a Notion API key by calling users/me.
|
|
436
|
+
*/
|
|
437
|
+
router.post('/notion/test', async (req, res) => {
|
|
438
|
+
const body = req.body;
|
|
439
|
+
const apiKey = typeof body.apiKey === 'string' ? body.apiKey : '';
|
|
440
|
+
if (!apiKey) {
|
|
441
|
+
res.status(400).json({ status: 'error', error: 'Missing apiKey' });
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const resp = await fetch('https://api.notion.com/v1/users/me', {
|
|
446
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Notion-Version': '2022-06-28' },
|
|
447
|
+
});
|
|
448
|
+
if (resp.status === 401) {
|
|
449
|
+
res.status(401).json({ status: 'error', error: 'Invalid Notion API key. Create one at notion.so/my-integrations' });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (resp.status === 429) {
|
|
453
|
+
const retryAfter = resp.headers.get('Retry-After') ?? '60';
|
|
454
|
+
res.status(429).json({ status: 'error', error: `Notion rate limit hit. Retry in ${retryAfter}s` });
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (!resp.ok) {
|
|
458
|
+
res.status(resp.status).json({ status: 'error', error: `Notion API error: ${resp.status}` });
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const user = await resp.json();
|
|
462
|
+
sessionKeys.set('notion', apiKey);
|
|
463
|
+
res.json({ status: 'ok', data: { user: user.name ?? user.id } });
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
res.status(500).json({ status: 'error', error: 'Connection error. Check your network.' });
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
/**
|
|
470
|
+
* POST /api/connectors/notion/fetch
|
|
471
|
+
* Fetch pages/databases from Notion and return as markdown items.
|
|
472
|
+
* Body: { apiKey?, databaseIds?: string[], pageUrls?: string[] }
|
|
473
|
+
*/
|
|
474
|
+
router.post('/notion/fetch', async (req, res) => {
|
|
475
|
+
const body = req.body;
|
|
476
|
+
const apiKey = typeof body.apiKey === 'string' && body.apiKey
|
|
477
|
+
? body.apiKey
|
|
478
|
+
: (sessionKeys.get('notion') ?? '');
|
|
479
|
+
if (!apiKey) {
|
|
480
|
+
res.status(401).json({ status: 'error', error: 'No API key. Test connection first.' });
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const databaseIds = Array.isArray(body.databaseIds)
|
|
484
|
+
? body.databaseIds.filter((s) => typeof s === 'string')
|
|
485
|
+
: [];
|
|
486
|
+
const pageUrls = Array.isArray(body.pageUrls)
|
|
487
|
+
? body.pageUrls.filter((s) => typeof s === 'string')
|
|
488
|
+
: [];
|
|
489
|
+
try {
|
|
490
|
+
let items = [];
|
|
491
|
+
if (databaseIds.length > 0) {
|
|
492
|
+
for (const dbId of databaseIds) {
|
|
493
|
+
const rows = await queryDatabase(dbId.trim(), apiKey);
|
|
494
|
+
items.push(...rows);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else if (pageUrls.length > 0) {
|
|
498
|
+
for (const url of pageUrls) {
|
|
499
|
+
const pageId = extractPageId(url);
|
|
500
|
+
if (!pageId)
|
|
501
|
+
continue;
|
|
502
|
+
const page = await fetchPage(pageId, apiKey);
|
|
503
|
+
if (page)
|
|
504
|
+
items.push(page);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
items = await searchWorkspace(apiKey);
|
|
509
|
+
}
|
|
510
|
+
res.json({ status: 'ok', data: items });
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
const msg = err instanceof Error ? err.message : '';
|
|
514
|
+
if (msg.includes('429')) {
|
|
515
|
+
res.status(429).json({ status: 'error', error: 'Notion rate limit hit. Retry in 60s' });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
res.status(500).json({ status: 'error', error: 'Failed to fetch from Notion. Check API key permissions.' });
|
|
519
|
+
}
|
|
520
|
+
});
|
|
327
521
|
export default router;
|
|
328
|
-
//# sourceMappingURL=connectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../server/routes/memory.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmSxB,eAAe,MAAM,CAAC"}
|