ocean-brain 0.13.2 → 0.14.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 +66 -2
- package/dist/mcp.js +439 -480
- package/package.json +2 -2
- package/server/client/dist/assets/{ArrowRight.es-BlNsZQFs.js → ArrowRight.es-nBJws3ws.js} +1 -1
- package/server/client/dist/assets/{AuxiliaryPanelHeader-HAttwVby.js → AuxiliaryPanelHeader-BN_2axQu.js} +1 -1
- package/server/client/dist/assets/{Calendar-DH3eSSRs.js → Calendar-vA4Xa4Qr.js} +1 -1
- package/server/client/dist/assets/{Callout-CcYyvHQP.js → Callout-Bo2P960P.js} +1 -1
- package/server/client/dist/assets/{CaretUp.es-C8iY5OlB.js → CaretUp.es-DDbqKn78.js} +1 -1
- package/server/client/dist/assets/{CheckCircle.es-Dqr3WImn.js → CheckCircle.es-CyY9aLBF.js} +1 -1
- package/server/client/dist/assets/{Copy.es-fMv0ETir.js → Copy.es-BwX4tEoU.js} +1 -1
- package/server/client/dist/assets/{File.es-CHtXXafR.js → File.es-DeTgvUnI.js} +1 -1
- package/server/client/dist/assets/{FileText.es-DPIwDCEe.js → FileText.es-C-bpw8dQ.js} +1 -1
- package/server/client/dist/assets/{FlowArrow.es-Dxee98ir.js → FlowArrow.es-CKcMBOmX.js} +1 -1
- package/server/client/dist/assets/{Graph-hVj6vAYJ.js → Graph-BNoSRJP3.js} +1 -1
- package/server/client/dist/assets/{Graph-CdEu8eAn.js → Graph-DYYxNAWn.js} +1 -1
- package/server/client/dist/assets/{Image.es-CjXY1JVf.js → Image.es-d4jWnijM.js} +1 -1
- package/server/client/dist/assets/{Info.es-CYxg-Vku.js → Info.es-DVzN2joA.js} +1 -1
- package/server/client/dist/assets/Integration-gDDtIJYJ.js +1 -0
- package/server/client/dist/assets/{LinkSimple.es-B-P-7Iem.js → LinkSimple.es-BD4aGTc3.js} +1 -1
- package/server/client/dist/assets/Note-FL31jb2U.js +19 -0
- package/server/client/dist/assets/{NotePropertiesPanel-6P7UfRBR.js → NotePropertiesPanel-os22Ppnu.js} +1 -1
- package/server/client/dist/assets/{Plus.es-Dy6onD-Z.js → Plus.es-Cl8zOOZa.js} +1 -1
- package/server/client/dist/assets/Reminders-Dt2JuGci.js +1 -0
- package/server/client/dist/assets/{Search-D9detGWk.js → Search-CPPTW2PO.js} +2 -2
- package/server/client/dist/assets/{SurfaceCard-D5QxOte_.js → SurfaceCard-jL2qzrzF.js} +1 -1
- package/server/client/dist/assets/{Tag-Bquqtx23.js → Tag-3DbGmuxa.js} +1 -1
- package/server/client/dist/assets/{TagNotes-CbjfxqAN.js → TagNotes-DLwRF8pT.js} +1 -1
- package/server/client/dist/assets/Trash.es-fxukjAO6.js +1 -0
- package/server/client/dist/assets/{ViewChip-BVkZAXyx.js → ViewChip-CVe_HNyd.js} +1 -1
- package/server/client/dist/assets/Views-8ghLRax7.js +1 -0
- package/server/client/dist/assets/{abnfDiagram-N423BO3Z-D44O4T7q.js → abnfDiagram-N423BO3Z-CPgGmCbn.js} +1 -1
- package/server/client/dist/assets/app-Bcl0U7r9.css +1 -0
- package/server/client/dist/assets/app-DzChh-V0.js +151 -0
- package/server/client/dist/assets/{arc-Cil9fXuW.js → arc-DqTgRpuF.js} +1 -1
- package/server/client/dist/assets/{architectureDiagram-T3A2C74G-Cjq6ayF2.js → architectureDiagram-T3A2C74G-rTYMAQ-J.js} +1 -1
- package/server/client/dist/assets/{blockDiagram-VBNYF7ZC-B2Xm5N0i.js → blockDiagram-VBNYF7ZC-BOluytVz.js} +1 -1
- package/server/client/dist/assets/{c4Diagram-5PPSVZJV-Dp0y-lCR.js → c4Diagram-5PPSVZJV-C8gbPpcb.js} +1 -1
- package/server/client/dist/assets/{calendar-data-wB0ifXdo.js → calendar-data-BQ24n348.js} +1 -1
- package/server/client/dist/assets/channel--aI9fJWX.js +1 -0
- package/server/client/dist/assets/{chunk-2GRJ4B5K-CWaaNkFx.js → chunk-2GRJ4B5K-CRER5Ka0.js} +1 -1
- package/server/client/dist/assets/{chunk-2Q5K7J3B-Ces-xz2S.js → chunk-2Q5K7J3B-hxmJWGu_.js} +1 -1
- package/server/client/dist/assets/{chunk-5RXB4S5H-B-4Chlfc.js → chunk-5RXB4S5H-Bpr5Us8Y.js} +1 -1
- package/server/client/dist/assets/{chunk-5VM5RSS4-C8_HVwuz.js → chunk-5VM5RSS4-CKUz9fAb.js} +1 -1
- package/server/client/dist/assets/{chunk-6Q2QTUOP-xZ8ofRqk.js → chunk-6Q2QTUOP-B8XEOHPN.js} +1 -1
- package/server/client/dist/assets/{chunk-GF5L2VYU-Bpa43a7s.js → chunk-GF5L2VYU-BBUD-HJi.js} +1 -1
- package/server/client/dist/assets/{chunk-JWPE2WC7-DsyTXbuq.js → chunk-JWPE2WC7-Dq6_B8-d.js} +1 -1
- package/server/client/dist/assets/{chunk-KBJHAD2P-BDhjm9-8.js → chunk-KBJHAD2P--2VZEwNJ.js} +1 -1
- package/server/client/dist/assets/{chunk-RYQCIY6F-Cn-jdp6I.js → chunk-RYQCIY6F-CKCwnKiC.js} +1 -1
- package/server/client/dist/assets/{chunk-XXDRQBXY-BKsc_0wh.js → chunk-XXDRQBXY-VDufR1Oi.js} +1 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-BsbwE38V.js +1 -0
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-BsbwE38V.js +1 -0
- package/server/client/dist/assets/{cose-bilkent-JH36ORCC-jmn3Q4zj.js → cose-bilkent-JH36ORCC-D4nqOx8S.js} +1 -1
- package/server/client/dist/assets/{cynefinDiagram-MW4NZA55-BpLstuBJ.js → cynefinDiagram-MW4NZA55-CUuLMs-6.js} +1 -1
- package/server/client/dist/assets/{dagre-VZM6K2ZE-B5ft5Yzm.js → dagre-VZM6K2ZE-CUSkhZgS.js} +1 -1
- package/server/client/dist/assets/{diagram-7IWD3JNH-B_W9IsWZ.js → diagram-7IWD3JNH-D95eVcKx.js} +4 -4
- package/server/client/dist/assets/{diagram-B4RE2ZJO-32chS3GX.js → diagram-B4RE2ZJO-7OH_fb0K.js} +1 -1
- package/server/client/dist/assets/{diagram-LBJQPF4R-Bqupdb3-.js → diagram-LBJQPF4R-D9UwEIMd.js} +1 -1
- package/server/client/dist/assets/{diagram-Q27KOJAE-BD8jKBsv.js → diagram-Q27KOJAE-Dx7WFDuX.js} +1 -1
- package/server/client/dist/assets/{diagram-UB23O5K3-C40jAU8D.js → diagram-UB23O5K3-D2BYwAKT.js} +1 -1
- package/server/client/dist/assets/{ebnfDiagram-BXEA7PRR-BQfW9mFA.js → ebnfDiagram-BXEA7PRR-DbeA-fk4.js} +1 -1
- package/server/client/dist/assets/{erDiagram-JOGREHBK-ILZnLzz2.js → erDiagram-JOGREHBK-jWW-ICGO.js} +1 -1
- package/server/client/dist/assets/{flowDiagram-UKHOOZJN-CDmwr2dI.js → flowDiagram-UKHOOZJN-68iZdxxw.js} +1 -1
- package/server/client/dist/assets/{ganttDiagram-PKOTCBZU-DBcrUc49.js → ganttDiagram-PKOTCBZU-CxwY3VXg.js} +1 -1
- package/server/client/dist/assets/{gitGraphDiagram-DS77QQ5N-_xO6__FK.js → gitGraphDiagram-DS77QQ5N-Ci6o5MfZ.js} +1 -1
- package/server/client/dist/assets/{image.api-ChvlfuZg.js → image.api-CyIIEGkG.js} +1 -1
- package/server/client/dist/assets/index-Iy_BgP1R.js +1 -0
- package/server/client/dist/assets/{infoDiagram-6WML65LV-CuIIcu-L.js → infoDiagram-6WML65LV-DY7aMdjV.js} +1 -1
- package/server/client/dist/assets/integrations-LOdTs1T6.js +1 -0
- package/server/client/dist/assets/{ishikawaDiagram-WSZJBQD7-BVtRGTOj.js → ishikawaDiagram-WSZJBQD7-Bh5qKh1d.js} +1 -1
- package/server/client/dist/assets/{journeyDiagram-NVQOT4AX-D5-Nzb2N.js → journeyDiagram-NVQOT4AX-B_svXTTT.js} +1 -1
- package/server/client/dist/assets/{kanban-definition-27J2QSJJ-DmOhgC8H.js → kanban-definition-27J2QSJJ-CaJslHGg.js} +1 -1
- package/server/client/dist/assets/manage-image-DXedfr7U.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-BIDHUR5-.js → manage-image-detail-DmifOPNU.js} +1 -1
- package/server/client/dist/assets/{mcp-admin-adapter-D0R8p9az.js → mcp-admin-adapter-CmKsg1vd.js} +1 -1
- package/server/client/dist/assets/mcp-d8IzsoRz.js +5 -0
- package/server/client/dist/assets/{mermaid.core-C2tL9tq7.js → mermaid.core-BfUQM5vF.js} +4 -4
- package/server/client/dist/assets/{mindmap-definition-FAOFIHXS-DuvsPqrh.js → mindmap-definition-FAOFIHXS-DGbkJda2.js} +1 -1
- package/server/client/dist/assets/{note-core-YhYEmI_R.js → note-core-DRBsBf9H.js} +9 -9
- package/server/client/dist/assets/{note-runtime-B3ats-dC.js → note-runtime-ztZvXRIu.js} +16 -16
- package/server/client/dist/assets/{note-ui-BtzPk8gA.js → note-ui-DRnAnwKv.js} +1 -1
- package/server/client/dist/assets/{pegDiagram-VL7TDLO6-D64crEZV.js → pegDiagram-VL7TDLO6-Bip6zQct.js} +1 -1
- package/server/client/dist/assets/{pieDiagram-7S7Q4E2Y--kl7vopC.js → pieDiagram-7S7Q4E2Y-CjfjGY8Y.js} +1 -1
- package/server/client/dist/assets/{placeholder-DKgxoe_f.js → placeholder-G6iEBbDy.js} +1 -1
- package/server/client/dist/assets/{properties-B7Fv95qY.js → properties-BPobx_Qk.js} +1 -1
- package/server/client/dist/assets/{quadrantDiagram-CIZ2JOQS-DuvJ4M2y.js → quadrantDiagram-CIZ2JOQS-B6F1yUT-.js} +1 -1
- package/server/client/dist/assets/{railroadDiagram-AXF67PYL-BMKmCOY7.js → railroadDiagram-AXF67PYL-D-0fLe0F.js} +1 -1
- package/server/client/dist/assets/{requirementDiagram-LRYGKXZP-DY1eJcy0.js → requirementDiagram-LRYGKXZP-tm4mnCyJ.js} +1 -1
- package/server/client/dist/assets/route-preload.js +2 -2
- package/server/client/dist/assets/{sankeyDiagram-W5VNT64P-C3Ka4GKM.js → sankeyDiagram-W5VNT64P-DSBWFurb.js} +1 -1
- package/server/client/dist/assets/{search-ddYl3qH-.js → search-DanMKCC8.js} +1 -1
- package/server/client/dist/assets/{search-admin-adapter-Daf7e2YQ.js → search-admin-adapter-7sTcWTmv.js} +1 -1
- package/server/client/dist/assets/{sequenceDiagram-SI44F4Z6-Bhpwtk2s.js → sequenceDiagram-SI44F4Z6-KrwjjJIV.js} +1 -1
- package/server/client/dist/assets/{sizeCapture-X5ZJPWSS-BFta71TW.js → sizeCapture-X5ZJPWSS-DL2cn_Q6.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-OKZ733FA-D6GCn1io.js → stateDiagram-OKZ733FA-CKuNDH85.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-v2-UEYNNEHI-g4yinGDn.js → stateDiagram-v2-UEYNNEHI-BFaj2wbM.js} +1 -1
- package/server/client/dist/assets/{swimlanes-SLNWSIFB-DLFvOjUl.js → swimlanes-SLNWSIFB-5S17nqhK.js} +2 -2
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-dexsHJq2.js +8 -0
- package/server/client/dist/assets/{tag.api-DEUROVKQ.js → tag.api-kKZl04yY.js} +1 -1
- package/server/client/dist/assets/{time-PxWila4-.js → time-BwwQGbK-.js} +38 -38
- package/server/client/dist/assets/{timeline-definition-Z64GVDOM-DTSIekSM.js → timeline-definition-Z64GVDOM-CU1hp_7d.js} +3 -3
- package/server/client/dist/assets/{trash-BHida7BG.js → trash-Cepvi7G8.js} +1 -1
- package/server/client/dist/assets/url-Bp8sXMb4.js +1 -0
- package/server/client/dist/assets/{useNoteMutate-BjvZUmtX.js → useNoteMutate-D-mZKXjl.js} +3 -3
- package/server/client/dist/assets/{useReminderMutate-C5n-5LM-.js → useReminderMutate-Cexk1ydX.js} +1 -1
- package/server/client/dist/assets/{vennDiagram-T6HMQDX7-CMNqSI2X.js → vennDiagram-T6HMQDX7-MoiVLnil.js} +1 -1
- package/server/client/dist/assets/{wardleyDiagram-T6FBY63Y-DV-JOYqB.js → wardleyDiagram-T6FBY63Y-cZkfdI6C.js} +1 -1
- package/server/client/dist/assets/{xychartDiagram-ELKLHX3M-BUIniF4m.js → xychartDiagram-ELKLHX3M-B63hxD-r.js} +1 -1
- package/server/client/dist/index.html +6 -6
- package/server/dist/app.js +2 -0
- package/server/dist/features/integration/auth.js +21 -0
- package/server/dist/features/integration/manifest.js +87 -0
- package/server/dist/features/integration/schema.js +54 -0
- package/server/dist/features/integration/service.js +173 -0
- package/server/dist/features/mcp-admin/service.js +19 -83
- package/server/dist/features/note/graphql/note.field.resolver.js +1 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +6 -0
- package/server/dist/features/note/graphql/note.type-defs.js +13 -1
- package/server/dist/features/note/http/authoring.js +493 -0
- package/server/dist/features/note/http/mcp.js +15 -463
- package/server/dist/features/note/services/authoring.js +17 -1
- package/server/dist/features/note/services/markdown-intent-write.js +1 -0
- package/server/dist/features/note/services/markdown-read.js +85 -0
- package/server/dist/features/note/services/properties.js +36 -30
- package/server/dist/features/note/services/search.js +17 -0
- package/server/dist/features/search/graphql/resolver.js +10 -1
- package/server/dist/features/search/graphql/type-defs.js +3 -0
- package/server/dist/features/tag/graphql/tag.query.resolver.js +10 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js +1 -0
- package/server/dist/features/view/graphql/mcp.query.resolver.js +22 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +2 -0
- package/server/dist/features/view/graphql/view.type-defs.js +27 -0
- package/server/dist/features/view/services/mcp-read.js +142 -0
- package/server/dist/features/view/services/workspace.js +12 -4
- package/server/dist/modules/auth-guard.js +24 -1
- package/server/dist/modules/integration-token.js +27 -0
- package/server/dist/modules/mcp-auth.js +37 -23
- package/server/dist/modules/mcp-token.js +6 -25
- package/server/dist/routes/api.js +2 -0
- package/server/dist/routes/graphql.js +2 -1
- package/server/dist/routes/integrations.js +117 -0
- package/server/dist/routes/mcp.js +29 -9
- package/server/prisma/migrations/20260917120000_0020_plugin_installations/migration.sql +39 -0
- package/server/prisma/migrations/20260917150000_0021_integration_connections/migration.sql +29 -0
- package/server/prisma/schema.prisma +34 -10
- package/server/client/dist/assets/Note-CpzLtGVM.js +0 -19
- package/server/client/dist/assets/Reminders-DnuALBL6.js +0 -1
- package/server/client/dist/assets/Trash.es-XEcWG_aq.js +0 -1
- package/server/client/dist/assets/Views-L4eC96Lx.js +0 -1
- package/server/client/dist/assets/app-BFM2V_Rx.js +0 -151
- package/server/client/dist/assets/app-DjDwGe0t.css +0 -1
- package/server/client/dist/assets/channel-DZFalh-h.js +0 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/index-CJPl3MJE.js +0 -1
- package/server/client/dist/assets/manage-image-CSJ3Flgc.js +0 -1
- package/server/client/dist/assets/mcp-D8footel.js +0 -5
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-Ci4mcvmo.js +0 -8
- package/server/client/dist/assets/url-DKUMNVCI.js +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import models from "../../models.js";
|
|
3
|
+
import { createAppError } from "../../modules/error-handler.js";
|
|
4
|
+
import { issueIntegrationToken } from "../../modules/integration-token.js";
|
|
5
|
+
import {
|
|
6
|
+
MCP_CONNECTION_ID,
|
|
7
|
+
NATIVE_INTEGRATIONS,
|
|
8
|
+
parseManifest,
|
|
9
|
+
parsePermissions,
|
|
10
|
+
validateGrants
|
|
11
|
+
} from "./manifest.js";
|
|
12
|
+
const resolveManifest = (row) => {
|
|
13
|
+
if (row.native) {
|
|
14
|
+
const definition = NATIVE_INTEGRATIONS.find((integration) => integration.connectionId === row.id);
|
|
15
|
+
if (definition) return definition.manifest;
|
|
16
|
+
}
|
|
17
|
+
return parseManifest(JSON.parse(row.manifest));
|
|
18
|
+
};
|
|
19
|
+
const toConnection = (row) => ({
|
|
20
|
+
id: row.id,
|
|
21
|
+
native: row.native,
|
|
22
|
+
manifest: resolveManifest(row),
|
|
23
|
+
grantedPermissions: parsePermissions(JSON.parse(row.grantedPermissions)),
|
|
24
|
+
enabled: row.enabled,
|
|
25
|
+
pinned: row.pinned,
|
|
26
|
+
createdAt: row.createdAt.toISOString(),
|
|
27
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
28
|
+
token: row.credential ? {
|
|
29
|
+
id: row.credential.id,
|
|
30
|
+
createdAt: row.credential.createdAt.toISOString(),
|
|
31
|
+
lastUsedAt: row.credential.lastUsedAt?.toISOString() ?? null
|
|
32
|
+
} : null
|
|
33
|
+
});
|
|
34
|
+
const credentialSummary = { select: { id: true, createdAt: true, lastUsedAt: true } };
|
|
35
|
+
const createIntegrationService = (db = models) => {
|
|
36
|
+
const get = async (id) => {
|
|
37
|
+
const row = await db.integrationConnection.findUnique({
|
|
38
|
+
where: { id },
|
|
39
|
+
include: { credential: credentialSummary }
|
|
40
|
+
});
|
|
41
|
+
if (!row) throw createAppError(404, "INTEGRATION_NOT_FOUND", "The integration connection was not found.");
|
|
42
|
+
return toConnection(row);
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
get,
|
|
46
|
+
async ensureNativeConnections() {
|
|
47
|
+
for (const integration of NATIVE_INTEGRATIONS) {
|
|
48
|
+
await db.integrationConnection.upsert({
|
|
49
|
+
where: { id: integration.connectionId },
|
|
50
|
+
create: {
|
|
51
|
+
id: integration.connectionId,
|
|
52
|
+
integrationId: integration.manifest.id,
|
|
53
|
+
native: true,
|
|
54
|
+
manifest: JSON.stringify(integration.manifest),
|
|
55
|
+
grantedPermissions: JSON.stringify(integration.manifest.permissions)
|
|
56
|
+
},
|
|
57
|
+
// Keep the owner's grants, enabled state and credentials on upgrade.
|
|
58
|
+
update: {}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
async list() {
|
|
63
|
+
const rows = await db.integrationConnection.findMany({
|
|
64
|
+
orderBy: [{ native: "desc" }, { createdAt: "asc" }],
|
|
65
|
+
include: { credential: credentialSummary }
|
|
66
|
+
});
|
|
67
|
+
return rows.map(toConnection);
|
|
68
|
+
},
|
|
69
|
+
async connect(input) {
|
|
70
|
+
const manifest = parseManifest(input.manifest);
|
|
71
|
+
if (manifest.id.startsWith("ocean-brain."))
|
|
72
|
+
throw createAppError(
|
|
73
|
+
400,
|
|
74
|
+
"RESERVED_INTEGRATION_ID",
|
|
75
|
+
"The ocean-brain namespace is reserved for native integrations."
|
|
76
|
+
);
|
|
77
|
+
const grantedPermissions = validateGrants(input.grantedPermissions ?? [], manifest);
|
|
78
|
+
const row = await db.integrationConnection.create({
|
|
79
|
+
data: {
|
|
80
|
+
integrationId: manifest.id,
|
|
81
|
+
manifest: JSON.stringify(manifest),
|
|
82
|
+
grantedPermissions: JSON.stringify(grantedPermissions)
|
|
83
|
+
},
|
|
84
|
+
include: { credential: credentialSummary }
|
|
85
|
+
});
|
|
86
|
+
return toConnection(row);
|
|
87
|
+
},
|
|
88
|
+
async update(id, input) {
|
|
89
|
+
return db.$transaction(async (tx) => {
|
|
90
|
+
const current = await tx.integrationConnection.findUnique({ where: { id } });
|
|
91
|
+
if (!current)
|
|
92
|
+
throw createAppError(404, "INTEGRATION_NOT_FOUND", "The integration connection was not found.");
|
|
93
|
+
if (input.manifest !== void 0 && current.native)
|
|
94
|
+
throw createAppError(
|
|
95
|
+
400,
|
|
96
|
+
"NATIVE_INTEGRATION_MANIFEST",
|
|
97
|
+
"Native integration manifests are managed by Ocean Brain."
|
|
98
|
+
);
|
|
99
|
+
const manifest = input.manifest === void 0 ? resolveManifest(current) : parseManifest(input.manifest);
|
|
100
|
+
if (manifest.id !== current.integrationId)
|
|
101
|
+
throw createAppError(400, "INTEGRATION_ID_CHANGED", "An upgrade cannot change the integration id.");
|
|
102
|
+
const previousGrants = parsePermissions(JSON.parse(current.grantedPermissions)).filter(
|
|
103
|
+
(permission) => manifest.permissions.includes(permission)
|
|
104
|
+
);
|
|
105
|
+
const grantedPermissions = validateGrants(input.grantedPermissions ?? previousGrants, manifest);
|
|
106
|
+
for (const key of ["enabled", "pinned"]) {
|
|
107
|
+
if (input[key] !== void 0 && typeof input[key] !== "boolean")
|
|
108
|
+
throw createAppError(400, "INVALID_INTEGRATION_SETTING", `${key} must be a boolean.`);
|
|
109
|
+
}
|
|
110
|
+
const row = await tx.integrationConnection.update({
|
|
111
|
+
where: { id },
|
|
112
|
+
data: {
|
|
113
|
+
manifest: JSON.stringify(manifest),
|
|
114
|
+
grantedPermissions: JSON.stringify(grantedPermissions),
|
|
115
|
+
...typeof input.enabled === "boolean" ? { enabled: input.enabled } : {},
|
|
116
|
+
...typeof input.pinned === "boolean" ? { pinned: input.pinned } : {}
|
|
117
|
+
},
|
|
118
|
+
include: { credential: credentialSummary }
|
|
119
|
+
});
|
|
120
|
+
return toConnection(row);
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
async disconnect(id) {
|
|
124
|
+
const connection = await get(id);
|
|
125
|
+
if (connection.native || id === MCP_CONNECTION_ID)
|
|
126
|
+
throw createAppError(
|
|
127
|
+
400,
|
|
128
|
+
"NATIVE_INTEGRATION_REQUIRED",
|
|
129
|
+
"Native integrations can be disabled, but cannot be disconnected."
|
|
130
|
+
);
|
|
131
|
+
await db.integrationConnection.delete({ where: { id } });
|
|
132
|
+
},
|
|
133
|
+
async rotateToken(id) {
|
|
134
|
+
await get(id);
|
|
135
|
+
const token = issueIntegrationToken();
|
|
136
|
+
await db.integrationCredential.upsert({
|
|
137
|
+
where: { connectionId: id },
|
|
138
|
+
create: { connectionId: id, tokenHash: token.hash },
|
|
139
|
+
update: { tokenHash: token.hash, createdAt: /* @__PURE__ */ new Date(), lastUsedAt: null }
|
|
140
|
+
});
|
|
141
|
+
return { token: token.plaintext };
|
|
142
|
+
},
|
|
143
|
+
async revokeToken(id) {
|
|
144
|
+
await get(id);
|
|
145
|
+
await db.integrationCredential.deleteMany({ where: { connectionId: id } });
|
|
146
|
+
},
|
|
147
|
+
async authenticate(token) {
|
|
148
|
+
const tokenHash = createHash("sha256").update(token, "utf8").digest("hex");
|
|
149
|
+
const credential = await db.integrationCredential.findUnique({
|
|
150
|
+
where: { tokenHash },
|
|
151
|
+
include: { connection: true }
|
|
152
|
+
});
|
|
153
|
+
if (!credential?.connection.enabled) return null;
|
|
154
|
+
const connection = credential.connection;
|
|
155
|
+
const touched = await db.integrationCredential.updateMany({
|
|
156
|
+
where: { connectionId: connection.id, tokenHash },
|
|
157
|
+
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
158
|
+
});
|
|
159
|
+
if (!touched.count) return null;
|
|
160
|
+
return {
|
|
161
|
+
connectionId: connection.id,
|
|
162
|
+
integrationId: connection.integrationId,
|
|
163
|
+
native: connection.native,
|
|
164
|
+
permissions: parsePermissions(JSON.parse(connection.grantedPermissions)).filter(
|
|
165
|
+
(permission) => resolveManifest(connection).permissions.includes(permission)
|
|
166
|
+
)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
export {
|
|
172
|
+
createIntegrationService
|
|
173
|
+
};
|
|
@@ -1,87 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { MCP_CONNECTION_ID } from "../integration/manifest.js";
|
|
2
|
+
import { createIntegrationService } from "../integration/service.js";
|
|
3
|
+
const createMcpAdminService = (integrations = createIntegrationService()) => ({
|
|
4
|
+
async getStatus() {
|
|
5
|
+
const integration = await integrations.get(MCP_CONNECTION_ID);
|
|
6
|
+
return { enabled: integration.enabled, hasActiveToken: integration.token !== null, token: integration.token };
|
|
7
|
+
},
|
|
8
|
+
async setEnabled(enabled) {
|
|
9
|
+
await integrations.update(MCP_CONNECTION_ID, { enabled });
|
|
10
|
+
},
|
|
11
|
+
rotateToken: () => integrations.rotateToken(MCP_CONNECTION_ID),
|
|
12
|
+
revokeActiveToken: () => integrations.revokeToken(MCP_CONNECTION_ID),
|
|
13
|
+
async validatePresentedToken(token) {
|
|
14
|
+
const integration = await integrations.get(MCP_CONNECTION_ID);
|
|
15
|
+
if (!integration.token) return { ok: false, reason: "not_configured" };
|
|
16
|
+
const principal = await integrations.authenticate(token);
|
|
17
|
+
if (!principal || principal.connectionId !== MCP_CONNECTION_ID) return { ok: false, reason: "forbidden" };
|
|
18
|
+
return { ok: true, permissions: principal.permissions };
|
|
11
19
|
}
|
|
12
|
-
|
|
13
|
-
enabled,
|
|
14
|
-
hasActiveToken: true,
|
|
15
|
-
token: {
|
|
16
|
-
id: String(activeToken.id),
|
|
17
|
-
createdAt: activeToken.createdAt.toISOString(),
|
|
18
|
-
lastUsedAt: activeToken.lastUsedAt ? activeToken.lastUsedAt.toISOString() : null
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
const createMcpAdminService = (db = models) => {
|
|
23
|
-
return {
|
|
24
|
-
async getStatus() {
|
|
25
|
-
const enabledCache = await db.cache.findUnique({ where: { key: MCP_ENABLED_CACHE_KEY } });
|
|
26
|
-
const activeToken = await db.mcpToken.findFirst({
|
|
27
|
-
where: { revokedAt: null },
|
|
28
|
-
orderBy: { createdAt: "desc" }
|
|
29
|
-
});
|
|
30
|
-
return toStatus(enabledCache?.value === "true", activeToken);
|
|
31
|
-
},
|
|
32
|
-
async setEnabled(enabled) {
|
|
33
|
-
await db.cache.upsert({
|
|
34
|
-
where: { key: MCP_ENABLED_CACHE_KEY },
|
|
35
|
-
update: { value: String(enabled) },
|
|
36
|
-
create: {
|
|
37
|
-
key: MCP_ENABLED_CACHE_KEY,
|
|
38
|
-
value: String(enabled)
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
async rotateToken() {
|
|
43
|
-
const now = /* @__PURE__ */ new Date();
|
|
44
|
-
const issued = issueMcpToken();
|
|
45
|
-
await db.$transaction(async (transaction) => {
|
|
46
|
-
await transaction.mcpToken.updateMany({
|
|
47
|
-
where: { revokedAt: null },
|
|
48
|
-
data: { revokedAt: now }
|
|
49
|
-
});
|
|
50
|
-
await transaction.mcpToken.create({ data: { tokenHash: issued.hash } });
|
|
51
|
-
});
|
|
52
|
-
return { token: issued.plaintext };
|
|
53
|
-
},
|
|
54
|
-
async revokeActiveToken() {
|
|
55
|
-
await db.mcpToken.updateMany({
|
|
56
|
-
where: { revokedAt: null },
|
|
57
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
async validatePresentedToken(token) {
|
|
61
|
-
const activeToken = await db.mcpToken.findFirst({
|
|
62
|
-
where: { revokedAt: null },
|
|
63
|
-
orderBy: { createdAt: "desc" }
|
|
64
|
-
});
|
|
65
|
-
if (!activeToken) {
|
|
66
|
-
return {
|
|
67
|
-
ok: false,
|
|
68
|
-
reason: "not_configured"
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
if (!verifyMcpToken(activeToken.tokenHash, token)) {
|
|
72
|
-
return {
|
|
73
|
-
ok: false,
|
|
74
|
-
reason: "forbidden"
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
await db.mcpToken.update({
|
|
78
|
-
where: { id: activeToken.id },
|
|
79
|
-
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
80
|
-
});
|
|
81
|
-
return { ok: true };
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
};
|
|
20
|
+
});
|
|
85
21
|
export {
|
|
86
22
|
createMcpAdminService
|
|
87
23
|
};
|
|
@@ -4,7 +4,7 @@ import { renderNoteSnapshotContentAsMarkdown } from "../../../features/note/serv
|
|
|
4
4
|
import models from "../../../models.js";
|
|
5
5
|
const noteFieldResolvers = {
|
|
6
6
|
tags: async (note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),
|
|
7
|
-
properties: async (note) => listNoteProperties(note.id),
|
|
7
|
+
properties: async (note, { keys }) => listNoteProperties(note.id, keys ?? note.selectedPropertyKeys),
|
|
8
8
|
contentPreview: (note) => buildNoteContentPreview(note.content),
|
|
9
9
|
contentAsMarkdown: async (note) => {
|
|
10
10
|
const { blocksToMarkdown } = await import("../../../modules/blocknote.js");
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import { getTrashedNoteById, listTrashedNotes } from "../../../features/note/services/trash.js";
|
|
22
22
|
import models from "../../../models.js";
|
|
23
23
|
import { runDataMaintenanceInBackground } from "../../../modules/data-maintenance.js";
|
|
24
|
+
import { readMarkdownRange } from "../services/markdown-read.js";
|
|
24
25
|
const MAX_SQLITE_INT_ID = 2147483647;
|
|
25
26
|
const isValidNoteIdString = (id) => {
|
|
26
27
|
if (!/^[1-9]\d*$/.test(id)) {
|
|
@@ -386,6 +387,11 @@ const noteQueryResolvers = {
|
|
|
386
387
|
}),
|
|
387
388
|
backReferences: createBackReferencesQueryResolver(),
|
|
388
389
|
note: createNoteQueryResolver(),
|
|
390
|
+
noteRead: async (_, { id, ...input }) => {
|
|
391
|
+
const note = await createNoteQueryResolver()(_, { id });
|
|
392
|
+
const { blocksToMarkdown } = await import("../../../modules/blocknote.js");
|
|
393
|
+
return { note, ...readMarkdownRange(await blocksToMarkdown(note.content), note.updatedAt, input) };
|
|
394
|
+
},
|
|
389
395
|
noteCleanupCandidates: async (_, {
|
|
390
396
|
query,
|
|
391
397
|
pagination = {
|
|
@@ -106,7 +106,7 @@ const noteType = gql`
|
|
|
106
106
|
order: Int!
|
|
107
107
|
layout: NoteLayout!
|
|
108
108
|
tags: [Tag!]!
|
|
109
|
-
properties: [NoteProperty!]!
|
|
109
|
+
properties(keys: [String!]): [NoteProperty!]!
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
type NoteProperty {
|
|
@@ -269,6 +269,17 @@ const noteQuery = gql`
|
|
|
269
269
|
links: [GraphLink!]!
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
type NoteContentRange { start: Int!, end: Int!, totalLength: Int!, sectionEnd: Int!, hasMore: Boolean!, nextOffset: Int }
|
|
273
|
+
type NoteHeadingCandidate { heading: String!, level: Int!, start: Int!, end: Int! }
|
|
274
|
+
type NoteReadResult {
|
|
275
|
+
status: String!
|
|
276
|
+
reason: String
|
|
277
|
+
message: String
|
|
278
|
+
note: Note!
|
|
279
|
+
markdown: String!
|
|
280
|
+
contentRange: NoteContentRange
|
|
281
|
+
candidates: [NoteHeadingCandidate!]!
|
|
282
|
+
}
|
|
272
283
|
type Query {
|
|
273
284
|
allNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!
|
|
274
285
|
tagNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!
|
|
@@ -278,6 +289,7 @@ const noteQuery = gql`
|
|
|
278
289
|
imageNotes(src: String!): [Note!]!
|
|
279
290
|
backReferences(id: ID!): [Note]!
|
|
280
291
|
note(id: ID!): Note!
|
|
292
|
+
noteRead(id: ID!, offset: Int, maxLength: Int, heading: String, expectedUpdatedAt: String): NoteReadResult!
|
|
281
293
|
noteCleanupCandidates(query: String, pagination: PaginationInput): [NoteCleanupCandidate!]!
|
|
282
294
|
noteCleanupPreview(id: ID!): NoteCleanupPreview
|
|
283
295
|
noteSnapshots(id: ID!, limit: Int): [NoteSnapshot!]!
|