@andespindola/brainlink 1.9.0 → 1.10.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 CHANGED
@@ -564,6 +564,7 @@ Available tools:
564
564
  - `brainlink_vault_status` / `brainlink_vault_init` / `brainlink_vault_commit` / `brainlink_vault_history` / `brainlink_vault_pull` / `brainlink_vault_restore`: version the vault's canonical Markdown with git (the derived index is git-ignored). Pull and restore reindex changed files automatically so the vault stays immediately searchable.
565
565
  - `brainlink_vault_export` / `brainlink_vault_import`: write and read a portable `.blvault` snapshot (gzip envelope of the Markdown with per-file SHA-256). Encrypted with AES-256-GCM when a `passphrase` (or `BRAINLINK_VAULT_KEY`) is available, otherwise plaintext gzip; import auto-detects and decrypts. Import verifies checksums, preserves divergent local files as `*.conflict-<ts>.md`, and reindexes.
566
566
  - `brainlink_vault_export_bundle` / `brainlink_vault_bundle_list` / `brainlink_vault_import_bundle`: export **every known vault** (the config vault plus `allowedVaults`) into a single portable `.blbundle`, inspect the vaults inside a bundle, and import them back — all vaults to their recorded paths, or a single one via `only` (optionally into `targetVault`). Same encryption (via `passphrase`/`BRAINLINK_VAULT_KEY`), checksum verification and conflict preservation as the single-vault snapshot.
567
+ - `brainlink_vault_transfer_export` / `brainlink_vault_transfer_import`: move a vault **between two Brainlinks over MCP** without a file on disk. An agent connected to both servers calls `transfer_export` on the source (returns the vault as an encrypted, base64 payload) and `transfer_import` on the target (applies the payload and reindexes). A shared `passphrase` (min 8 chars) is mandatory since the payload travels through the agent's context. Same checksum verification and `*.conflict-<ts>.md` preservation as snapshot import.
567
568
  - `brainlink_vault_merge_agents`: losslessly merge one agent namespace into another within the same vault (e.g. `claude-code` → `shared`). Identical notes are skipped, colliding slugs with different content are kept under a `-from-<agent>` name, everything else is moved, and merged notes are aggregated into domain `<context> Hub` notes. Source notes are kept unless `deleteSource` is set; `dryRun` previews the plan.
568
569
  - `brainlink_graph`: read indexed graph nodes and weighted links.
569
570
  - `brainlink_graph_contexts`: list the visual graph contexts used by the local server.
@@ -49,16 +49,30 @@ export const buildSnapshotEnvelope = async (vaultPath) => {
49
49
  files: entries.map((entry) => entry.file)
50
50
  };
51
51
  };
52
- export const exportVaultSnapshot = async (vaultPath, outputFile, options = {}) => {
52
+ // Build the in-memory snapshot artifact (gzip envelope, optionally encrypted).
53
+ // Shared by the file-based export and the MCP payload transfer so both formats
54
+ // are byte-identical. `requireEncryption` refuses to emit plaintext (used by the
55
+ // transfer path, where the payload travels through an agent's context).
56
+ export const packVaultSnapshot = async (vaultPath, options = {}) => {
53
57
  const envelope = await buildSnapshotEnvelope(vaultPath);
54
58
  const gzipped = gzipSync(Buffer.from(JSON.stringify(envelope), 'utf8'), { level: 9 });
55
59
  const key = options.encrypt !== false ? resolveSnapshotKey(options.passphrase) : null;
56
- if (options.encrypt !== false && !key) {
60
+ if (options.requireEncryption && !key) {
61
+ throw new Error('A passphrase (or BRAINLINK_VAULT_KEY) is required to produce an encrypted snapshot.');
62
+ }
63
+ const payload = key ? encryptVaultContent(key, gzipped) : gzipped;
64
+ return { payload, fileCount: envelope.files.length, encrypted: Boolean(key) };
65
+ };
66
+ // Import a snapshot straight from an in-memory payload (no file on disk). Used by
67
+ // the MCP vault-transfer import so a snapshot can move between Brainlinks.
68
+ export const applyVaultSnapshotPayload = async (payload, vaultPath, options = {}) => applySnapshotEnvelope(parseEnvelope(payload, options.passphrase), vaultPath, options);
69
+ export const exportVaultSnapshot = async (vaultPath, outputFile, options = {}) => {
70
+ const artifact = await packVaultSnapshot(vaultPath, options);
71
+ if (options.encrypt !== false && !artifact.encrypted) {
57
72
  process.stderr.write('brainlink: snapshot exported unencrypted — pass --passphrase (or set BRAINLINK_VAULT_KEY) to encrypt a portable snapshot.\n');
58
73
  }
59
- const packed = key ? encryptVaultContent(key, gzipped) : gzipped;
60
- await writeFile(outputFile, packed, { mode: 0o600 });
61
- return { file: outputFile, fileCount: envelope.files.length, bytes: packed.byteLength, encrypted: Boolean(key) };
74
+ await writeFile(outputFile, artifact.payload, { mode: 0o600 });
75
+ return { file: outputFile, fileCount: artifact.fileCount, bytes: artifact.payload.byteLength, encrypted: artifact.encrypted };
62
76
  };
63
77
  const parseEnvelope = (raw, passphrase) => {
64
78
  let gzipped = raw;
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { addNoteInputSchema, addFileInputSchema, addFileTool, addImageInputSchema, addImageTool, addNoteTool, addNotesInputSchema, addNotesTool, deleteNoteInputSchema, deleteNoteTool, deleteNotesInputSchema, deleteNotesTool, volatileAddInputSchema, volatileAddTool, volatileClearInputSchema, volatileClearTool, dedupeInputSchema, dedupeResolveInputSchema, dedupeResolveTool, dedupeTool, brokenLinksInputSchema, brokenLinksTool, bootstrapInputSchema, bootstrapTool, canonicalizeContextLinksInputSchema, canonicalizeContextLinksTool, contextInputSchema, contextPacksInputSchema, contextPacksTool, contextTool, doctorActionsInputSchema, doctorActionsTool, graphContextsInputSchema, graphContextsTool, graphInputSchema, graphTool, getImageInputSchema, getImageTool, explainInputSchema, explainTool, indexInputSchema, indexTool, inboxAddInputSchema, inboxAddTool, inboxListInputSchema, inboxListTool, inboxProcessInputSchema, inboxProcessTool, orphansInputSchema, orphansTool, policyInputSchema, policyTool, projectInitInputSchema, projectInitTool, recommendationsInputSchema, recommendationsTool, rememberInputSchema, rememberTool, repairLinksInputSchema, repairLinksTool, searchInputSchema, searchTool, similarNotesInputSchema, similarNotesTool, sessionCloseInputSchema, sessionCloseTool, statsInputSchema, statsTool, suggestLinksInputSchema, suggestLinksTool, syncInputSchema, syncTool, validateInputSchema, validateTool, vaultBundleListInputSchema, vaultBundleListTool, vaultCommitInputSchema, vaultCommitTool, vaultExportBundleInputSchema, vaultExportBundleTool, vaultExportInputSchema, vaultExportTool, vaultHistoryInputSchema, vaultHistoryTool, vaultImportBundleInputSchema, vaultImportBundleTool, vaultImportInputSchema, vaultImportTool, vaultInitInputSchema, vaultInitTool, vaultMergeAgentsInputSchema, vaultMergeAgentsTool, vaultProvisionInputSchema, vaultProvisionTool, vaultPullInputSchema, vaultPullTool, vaultRestoreInputSchema, vaultRestoreTool, vaultStatusInputSchema, vaultStatusTool, versionInputSchema, versionTool } from './tools.js';
2
+ import { addNoteInputSchema, addFileInputSchema, addFileTool, addImageInputSchema, addImageTool, addNoteTool, addNotesInputSchema, addNotesTool, deleteNoteInputSchema, deleteNoteTool, deleteNotesInputSchema, deleteNotesTool, volatileAddInputSchema, volatileAddTool, volatileClearInputSchema, volatileClearTool, dedupeInputSchema, dedupeResolveInputSchema, dedupeResolveTool, dedupeTool, brokenLinksInputSchema, brokenLinksTool, bootstrapInputSchema, bootstrapTool, canonicalizeContextLinksInputSchema, canonicalizeContextLinksTool, contextInputSchema, contextPacksInputSchema, contextPacksTool, contextTool, doctorActionsInputSchema, doctorActionsTool, graphContextsInputSchema, graphContextsTool, graphInputSchema, graphTool, getImageInputSchema, getImageTool, explainInputSchema, explainTool, indexInputSchema, indexTool, inboxAddInputSchema, inboxAddTool, inboxListInputSchema, inboxListTool, inboxProcessInputSchema, inboxProcessTool, orphansInputSchema, orphansTool, policyInputSchema, policyTool, projectInitInputSchema, projectInitTool, recommendationsInputSchema, recommendationsTool, rememberInputSchema, rememberTool, repairLinksInputSchema, repairLinksTool, searchInputSchema, searchTool, similarNotesInputSchema, similarNotesTool, sessionCloseInputSchema, sessionCloseTool, statsInputSchema, statsTool, suggestLinksInputSchema, suggestLinksTool, syncInputSchema, syncTool, validateInputSchema, validateTool, vaultBundleListInputSchema, vaultBundleListTool, vaultCommitInputSchema, vaultCommitTool, vaultExportBundleInputSchema, vaultExportBundleTool, vaultExportInputSchema, vaultExportTool, vaultHistoryInputSchema, vaultHistoryTool, vaultImportBundleInputSchema, vaultImportBundleTool, vaultImportInputSchema, vaultImportTool, vaultInitInputSchema, vaultInitTool, vaultMergeAgentsInputSchema, vaultMergeAgentsTool, vaultProvisionInputSchema, vaultProvisionTool, vaultPullInputSchema, vaultPullTool, vaultRestoreInputSchema, vaultRestoreTool, vaultStatusInputSchema, vaultStatusTool, vaultTransferExportInputSchema, vaultTransferExportTool, vaultTransferImportInputSchema, vaultTransferImportTool, versionInputSchema, versionTool } from './tools.js';
3
3
  import { getRuntimeVersion } from './runtime.js';
4
4
  import { brainlinkServerInstructions } from './server-instructions.js';
5
5
  import { guardToolHandler } from './tool-guard.js';
@@ -231,6 +231,16 @@ export const createBrainlinkMcpServer = () => {
231
231
  description: 'Import a .blbundle: restore every vault to its recorded path, or a single one via "only" (optionally into "targetVault"). Checksums are verified; divergent local files are preserved as *.conflict-<ts>.md.',
232
232
  inputSchema: vaultImportBundleInputSchema
233
233
  }, vaultImportBundleTool);
234
+ server.registerTool('brainlink_vault_transfer_export', {
235
+ title: 'Export Vault for MCP Transfer',
236
+ description: 'Export this vault as an encrypted, base64 transfer payload (no file on disk) so an agent connected to two Brainlinks can move it. Requires a shared passphrase; import the payload on the target with brainlink_vault_transfer_import and the same passphrase.',
237
+ inputSchema: vaultTransferExportInputSchema
238
+ }, vaultTransferExportTool);
239
+ server.registerTool('brainlink_vault_transfer_import', {
240
+ title: 'Import Vault from MCP Transfer',
241
+ description: 'Import a base64 transfer payload produced by brainlink_vault_transfer_export on another Brainlink, using the same passphrase. Checksums are verified; divergent local files are preserved as *.conflict-<ts>.md, then the vault is reindexed.',
242
+ inputSchema: vaultTransferImportInputSchema
243
+ }, vaultTransferImportTool);
234
244
  server.registerTool('brainlink_graph', {
235
245
  title: 'Read Brainlink Graph',
236
246
  description: 'Read indexed graph nodes and wiki-link edges. Edges include weight and priority fields so agents can rank importance and priority.',
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { commitVault, initVaultGit, pullVault, pushVault, restoreVault, vaultGitStatus, vaultHistory } from '../../application/vault-git.js';
3
3
  import { provisionVaultRepo } from '../../application/provision-vault.js';
4
- import { exportVaultSnapshot, importVaultSnapshot } from '../../application/vault-snapshot.js';
4
+ import { applyVaultSnapshotPayload, exportVaultSnapshot, importVaultSnapshot, packVaultSnapshot } from '../../application/vault-snapshot.js';
5
5
  import { exportVaultsBundle, importVaultsBundle, listBundleVaults } from '../../application/vault-bundle.js';
6
6
  import { mergeAgentNamespace } from '../../application/merge-agent-namespace.js';
7
7
  import { getKnownVaults, loadBrainlinkConfig } from '../../infrastructure/config.js';
@@ -68,6 +68,26 @@ export const vaultImportBundleInputSchema = {
68
68
  passphrase: z.string().min(1).optional().describe('Passphrase to decrypt an encrypted bundle.'),
69
69
  skipIndex: z.boolean().optional().default(false).describe('Skip the automatic reindex after importing Markdown.')
70
70
  };
71
+ // MCP-to-MCP vault transfer (agent-mediated): the agent, connected to both
72
+ // Brainlinks, exports an encrypted payload from the source and imports it into
73
+ // the target. The payload travels through the agent's context, so encryption
74
+ // with a shared passphrase is mandatory on both ends.
75
+ export const vaultTransferExportInputSchema = {
76
+ ...vaultInput,
77
+ passphrase: z
78
+ .string()
79
+ .min(8)
80
+ .describe('Shared passphrase (min 8 chars) used to encrypt the transfer payload. The receiving Brainlink must import with the same passphrase.')
81
+ };
82
+ export const vaultTransferImportInputSchema = {
83
+ ...vaultInput,
84
+ payloadBase64: z
85
+ .string()
86
+ .min(1)
87
+ .describe('Base64 transfer payload produced by brainlink_vault_transfer_export on the source Brainlink.'),
88
+ passphrase: z.string().min(8).describe('The same passphrase used on the source Brainlink to export the payload.'),
89
+ skipIndex: z.boolean().optional().default(false).describe('Skip the automatic reindex after importing Markdown.')
90
+ };
71
91
  export const vaultProvisionInputSchema = {
72
92
  ...vaultInput,
73
93
  name: z.string().min(1).optional().describe('Repository name to create. Defaults to brainlink-vault.'),
@@ -236,6 +256,43 @@ export const vaultImportBundleTool = async (input) => {
236
256
  return jsonResult({ ok: false, bundleFile: input.bundleFile, error: error instanceof Error ? error.message : String(error) });
237
257
  }
238
258
  };
259
+ export const vaultTransferExportTool = async (input) => {
260
+ const context = await resolveExecutionContext(input);
261
+ try {
262
+ const artifact = await packVaultSnapshot(context.vault, { passphrase: input.passphrase, requireEncryption: true });
263
+ return jsonResult({
264
+ vault: context.vault,
265
+ ok: true,
266
+ encrypted: artifact.encrypted,
267
+ fileCount: artifact.fileCount,
268
+ bytes: artifact.payload.byteLength,
269
+ payloadBase64: artifact.payload.toString('base64')
270
+ });
271
+ }
272
+ catch (error) {
273
+ return failure(context.vault, error);
274
+ }
275
+ };
276
+ export const vaultTransferImportTool = async (input) => {
277
+ const context = await resolveExecutionContext(input);
278
+ try {
279
+ const payload = Buffer.from(input.payloadBase64, 'base64');
280
+ const result = await applyVaultSnapshotPayload(payload, context.vault, {
281
+ skipIndex: input.skipIndex === true,
282
+ passphrase: input.passphrase
283
+ });
284
+ return jsonResult({
285
+ vault: context.vault,
286
+ ok: true,
287
+ imported: result.imported,
288
+ conflicted: result.conflicted,
289
+ reindexed: result.index != null
290
+ });
291
+ }
292
+ catch (error) {
293
+ return failure(context.vault, error);
294
+ }
295
+ };
239
296
  export const vaultMergeAgentsTool = async (input) => {
240
297
  const context = await resolveExecutionContext(input);
241
298
  try {
package/dist/mcp/tools.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { brokenLinksInputSchema, brokenLinksTool, contextInputSchema, contextPacksInputSchema, contextPacksTool, contextTool, doctorActionsInputSchema, doctorActionsTool, explainInputSchema, explainTool, graphContextsInputSchema, graphContextsTool, graphInputSchema, graphTool, getImageInputSchema, getImageTool, orphansInputSchema, orphansTool, recommendationsInputSchema, recommendationsTool, searchInputSchema, searchTool, similarNotesInputSchema, similarNotesTool, statsInputSchema, statsTool, suggestLinksInputSchema, suggestLinksTool, validateInputSchema, validateTool, versionInputSchema, versionTool } from './tools/read-tools.js';
2
2
  export { addFileInputSchema, addFileTool, addImageInputSchema, addImageTool, addNoteInputSchema, addNoteTool, addNotesInputSchema, addNotesTool, deleteNoteInputSchema, deleteNoteTool, deleteNotesInputSchema, deleteNotesTool, inboxAddInputSchema, inboxAddTool, inboxListInputSchema, inboxListTool, inboxProcessInputSchema, inboxProcessTool, rememberInputSchema, rememberTool, volatileAddInputSchema, volatileAddTool, volatileClearInputSchema, volatileClearTool } from './tools/write-tools.js';
3
3
  export { bootstrapInputSchema, bootstrapTool, canonicalizeContextLinksInputSchema, canonicalizeContextLinksTool, dedupeInputSchema, dedupeResolveInputSchema, dedupeResolveTool, dedupeTool, indexInputSchema, indexTool, policyInputSchema, policyTool, projectInitInputSchema, projectInitTool, repairLinksInputSchema, repairLinksTool, sessionCloseInputSchema, sessionCloseTool, syncInputSchema, syncTool } from './tools/maintenance-tools.js';
4
- export { vaultBundleListInputSchema, vaultBundleListTool, vaultCommitInputSchema, vaultCommitTool, vaultExportBundleInputSchema, vaultExportBundleTool, vaultExportInputSchema, vaultExportTool, vaultHistoryInputSchema, vaultHistoryTool, vaultImportBundleInputSchema, vaultImportBundleTool, vaultImportInputSchema, vaultImportTool, vaultInitInputSchema, vaultInitTool, vaultMergeAgentsInputSchema, vaultMergeAgentsTool, vaultProvisionInputSchema, vaultProvisionTool, vaultPullInputSchema, vaultPullTool, vaultRestoreInputSchema, vaultRestoreTool, vaultStatusInputSchema, vaultStatusTool } from './tools/vault-tools.js';
4
+ export { vaultBundleListInputSchema, vaultBundleListTool, vaultCommitInputSchema, vaultCommitTool, vaultExportBundleInputSchema, vaultExportBundleTool, vaultExportInputSchema, vaultExportTool, vaultHistoryInputSchema, vaultHistoryTool, vaultImportBundleInputSchema, vaultImportBundleTool, vaultImportInputSchema, vaultImportTool, vaultInitInputSchema, vaultInitTool, vaultMergeAgentsInputSchema, vaultMergeAgentsTool, vaultProvisionInputSchema, vaultProvisionTool, vaultPullInputSchema, vaultPullTool, vaultRestoreInputSchema, vaultRestoreTool, vaultStatusInputSchema, vaultStatusTool, vaultTransferExportInputSchema, vaultTransferExportTool, vaultTransferImportInputSchema, vaultTransferImportTool } from './tools/vault-tools.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",