@safekb/mcp 0.9.0 → 0.9.1

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 (2) hide show
  1. package/dist/safekb-mcp.js +42 -5
  2. package/package.json +1 -1
@@ -2581,7 +2581,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
2581
2581
  // package.json
2582
2582
  var package_default = {
2583
2583
  name: "@safekb/mcp",
2584
- version: "0.9.0",
2584
+ version: "0.9.1",
2585
2585
  description: "SafeKB local MCP client: proxies the hosted SafeKB tools and adds zero-knowledge sealed documents.",
2586
2586
  license: "SEE LICENSE IN LICENSE",
2587
2587
  author: "Mustafa Öztürk",
@@ -8353,6 +8353,8 @@ function passphraseNote(seal2) {
8353
8353
  var POSITION_DECIMALS = 6;
8354
8354
  var IMPORTED = "imported";
8355
8355
  var OBJECT_ID4 = /^[0-9a-f]{24}$/;
8356
+ var ATTACHMENT_IDS_OF_BLOCK = /"attachmentIds":\[([^\]]*)\]/g;
8357
+ var QUOTED_ID = /[0-9a-f]{24}/g;
8356
8358
 
8357
8359
  class SealedImporter {
8358
8360
  ctx;
@@ -8361,6 +8363,7 @@ class SealedImporter {
8361
8363
  rootOf = new Map;
8362
8364
  seqOf = new Map;
8363
8365
  spaceOf = new Map;
8366
+ reservedAttachments = new Set;
8364
8367
  constructor(ctx, sealed, services) {
8365
8368
  this.ctx = ctx;
8366
8369
  this.sealed = sealed;
@@ -8375,6 +8378,7 @@ class SealedImporter {
8375
8378
  }
8376
8379
  async document(record, spaceId) {
8377
8380
  const place = await this.placeOf(record, spaceId);
8381
+ this.reserveAttachmentIds(record.body);
8378
8382
  if (this.ctx.ids.mentionsUnknown(record.body)) {
8379
8383
  this.ctx.report.note("sealed document(s) name something by an id the import did not know yet; that id stays as exported");
8380
8384
  }
@@ -8388,9 +8392,21 @@ class SealedImporter {
8388
8392
  ...place.args
8389
8393
  }));
8390
8394
  }
8395
+ reserveAttachmentIds(body) {
8396
+ const { ids } = this.ctx;
8397
+ for (const block of body.matchAll(ATTACHMENT_IDS_OF_BLOCK)) {
8398
+ for (const [exported] of (block[1] ?? "").matchAll(QUOTED_ID)) {
8399
+ if (ids.get("attachment", exported) === null) {
8400
+ ids.set("attachment", exported, newObjectId());
8401
+ this.reservedAttachments.add(exported);
8402
+ }
8403
+ }
8404
+ }
8405
+ }
8391
8406
  async attachment(entry) {
8392
8407
  const { ids, report } = this.ctx;
8393
8408
  const { record } = entry;
8409
+ this.reservedAttachments.delete(record.id);
8394
8410
  const documentId = ids.get("node", record.owner_id);
8395
8411
  if (documentId === null) {
8396
8412
  report.note("attachment(s) left out: what they belong to was not imported");
@@ -8401,6 +8417,7 @@ class SealedImporter {
8401
8417
  document_id: documentId,
8402
8418
  expected_seq: this.seqOf.get(documentId),
8403
8419
  size: bytes.length,
8420
+ attachment_id: ids.get("attachment", record.id) ?? undefined,
8404
8421
  file: {
8405
8422
  filename: record.file_name,
8406
8423
  mime: record.mime_type,
@@ -8411,6 +8428,11 @@ class SealedImporter {
8411
8428
  ids.set("attachment", record.id, attached.attachment_id);
8412
8429
  report.count("attachment");
8413
8430
  }
8431
+ reportUnmatchedAttachments() {
8432
+ if (this.reservedAttachments.size === 0)
8433
+ return;
8434
+ this.ctx.report.note(`${this.reservedAttachments.size} attachment(s) named by a sealed body are not in the export; the body points at nothing there`);
8435
+ }
8414
8436
  async credential(record, spaceId) {
8415
8437
  const { kb, ids, report } = this.ctx;
8416
8438
  const rootId = ids.get("seal-root", record.seal_root_id);
@@ -8837,6 +8859,7 @@ ${WHAT_STAYS}`, this.ctx.report);
8837
8859
  await this.databases.relations();
8838
8860
  for (const rewrite of this.ctx.rewrites)
8839
8861
  await rewrite();
8862
+ this.sealed.reportUnmatchedAttachments();
8840
8863
  for (const archive of this.ctx.archivals.reverse())
8841
8864
  await archive();
8842
8865
  }
@@ -30861,6 +30884,16 @@ var CALLOUT_ALERTS = {
30861
30884
  error: "CAUTION"
30862
30885
  };
30863
30886
  var ALERT_CALLOUTS = Object.fromEntries(Object.entries(CALLOUT_ALERTS).map(([callout, alert]) => [alert, callout]));
30887
+ var NAMED_ALERT_PREFIX = "CALLOUT:";
30888
+ var CALLOUT_TYPE_NAME = /^[A-Za-z][\w-]*$/;
30889
+ function alertOfCallout(type) {
30890
+ if (Object.hasOwn(CALLOUT_ALERTS, type))
30891
+ return CALLOUT_ALERTS[type];
30892
+ return CALLOUT_TYPE_NAME.test(type) ? `${NAMED_ALERT_PREFIX}${type}` : null;
30893
+ }
30894
+ function calloutOfAlert(alert) {
30895
+ return alert.startsWith(NAMED_ALERT_PREFIX) ? alert.slice(NAMED_ALERT_PREFIX.length) : ALERT_CALLOUTS[alert];
30896
+ }
30864
30897
  var DEFAULT_TOGGLE_SUMMARY = "Details";
30865
30898
  var MENTION_SCHEME = "kb-mention://";
30866
30899
  var DIRECTIVE_OPEN = "<!--canvas ";
@@ -31074,10 +31107,14 @@ function imageAsParagraph(node) {
31074
31107
  ]
31075
31108
  } : node;
31076
31109
  }
31110
+ var BARE_INLINE_TYPES = new Set(["text", "hardBreak", "mention"]);
31077
31111
  function canonicalChildren(content, inCell = false) {
31078
31112
  if (typeof content === "string") {
31079
31113
  return nonEmpty(canonicalNodes([{ type: "paragraph", content }]));
31080
31114
  }
31115
+ if (content && content.length > 0 && content.every((child) => BARE_INLINE_TYPES.has(child.type))) {
31116
+ return nonEmpty(canonicalNodes([{ type: "paragraph", content }]));
31117
+ }
31081
31118
  const children = (content ?? []).map((child) => canonicalNode(child, inCell));
31082
31119
  return nonEmpty(inCell ? children.map(imageAsParagraph) : children);
31083
31120
  }
@@ -31339,7 +31376,7 @@ function inlineNodes(tokens) {
31339
31376
  }
31340
31377
 
31341
31378
  // ../shared/js/canvas-markdown/src/parse.ts
31342
- var ALERT_MARKER = /^\[!(NOTE|TIP|WARNING|CAUTION)\]$/;
31379
+ var ALERT_MARKER = /^\[!(NOTE|TIP|WARNING|CAUTION|CALLOUT:[A-Za-z][\w-]*)\]$/;
31343
31380
  var TASK_MARKER = /^\[([ xX])\](?:[ \t]+|$)/;
31344
31381
  var DETAILS_OPEN = /^<details(?:\s+open)?\s*>\s*(?:<summary>([\s\S]*?)<\/summary>\s*)?$/i;
31345
31382
  var DETAILS_CLOSE = /^<\/details>$/i;
@@ -31503,7 +31540,7 @@ class BlockParser {
31503
31540
  const children = this.blocks((token) => token.type === "blockquote_close");
31504
31541
  this.pos++;
31505
31542
  if (alert) {
31506
- return node("callout", { type: ALERT_CALLOUTS[alert] }, children);
31543
+ return node("callout", { type: calloutOfAlert(alert) }, children);
31507
31544
  }
31508
31545
  return node("blockquote", undefined, children);
31509
31546
  }
@@ -31958,7 +31995,7 @@ function checkBlock(node2, path2, out) {
31958
31995
  return;
31959
31996
  }
31960
31997
  case "callout":
31961
- if (!(String(node2.attrs?.type ?? "info") in CALLOUT_ALERTS)) {
31998
+ if (alertOfCallout(String(node2.attrs?.type ?? "info")) === null) {
31962
31999
  out.add(path2, `callout type '${String(node2.attrs?.type)}' has no markdown alert`);
31963
32000
  }
31964
32001
  break;
@@ -32140,7 +32177,7 @@ function renderTable(node2, context2) {
32140
32177
  `);
32141
32178
  }
32142
32179
  function renderCallout(node2, context2) {
32143
- const alert = CALLOUT_ALERTS[String(node2.attrs?.type ?? "info")];
32180
+ const alert = alertOfCallout(String(node2.attrs?.type ?? "info"));
32144
32181
  const children = node2.content ?? [];
32145
32182
  const first = children[0];
32146
32183
  const marker = `[!${alert}]`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safekb/mcp",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "SafeKB local MCP client: proxies the hosted SafeKB tools and adds zero-knowledge sealed documents.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Mustafa Öztürk",