@hasna/mementos 0.14.29 → 0.14.30

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
@@ -47,3 +47,16 @@ integration snippets. OpenCode and Antigravity receive the same MCP registration
47
47
  plus host instruction files that tell the agent to use `memory.search`,
48
48
  `memory.save`, and `mementos inject` as the hosted fallback path when native
49
49
  hooks are unavailable.
50
+
51
+ ## Bridge
52
+
53
+ ```bash
54
+ mementos bridge status
55
+ mementos bridge start --agent <name>
56
+ mementos bridge stop
57
+ mementos bridge install
58
+ ```
59
+
60
+ The bridge is remote-only. It keeps PID/log/service metadata under user config
61
+ paths and heartbeats the hosted agent when `--agent` or `MEMENTOS_AGENT_NAME` is
62
+ set; it does not own a local memory store.
@@ -0,0 +1,50 @@
1
+ export interface BridgeOptions {
2
+ agentName?: string;
3
+ apiUrl?: string;
4
+ homeDir?: string;
5
+ intervalMs?: number;
6
+ platform?: NodeJS.Platform;
7
+ }
8
+ export interface BridgeStartOptions extends BridgeOptions {
9
+ command?: string[];
10
+ }
11
+ export interface BridgeServiceOptions extends BridgeOptions {
12
+ command?: string;
13
+ }
14
+ export interface BridgePaths {
15
+ baseDir: string;
16
+ logFile: string;
17
+ pidFile: string;
18
+ serviceFile: string;
19
+ }
20
+ export interface BridgeStatus {
21
+ agentName?: string;
22
+ apiUrl: string;
23
+ localDatabase: false;
24
+ logFile: string;
25
+ pid?: number;
26
+ pidFile: string;
27
+ remoteOnly: true;
28
+ running: boolean;
29
+ startedAt?: string;
30
+ }
31
+ export interface BridgeServiceResult {
32
+ path: string;
33
+ remoteOnly: true;
34
+ status: "installed" | "removed" | "absent";
35
+ }
36
+ export declare function buildBridgePaths(homeDir?: string, platform?: NodeJS.Platform): BridgePaths;
37
+ export declare function getBridgeStatus(options?: BridgeOptions): BridgeStatus;
38
+ export declare function startBridge(options?: BridgeStartOptions): BridgeStatus;
39
+ export declare function stopBridge(options?: BridgeOptions): BridgeServiceResult;
40
+ export declare function installBridgeService(options?: BridgeServiceOptions): BridgeServiceResult;
41
+ export declare function uninstallBridgeService(options?: BridgeOptions): BridgeServiceResult;
42
+ export declare function readBridgeLogs(options?: BridgeOptions & {
43
+ lines?: number;
44
+ }): string[];
45
+ export declare function writeBridgePid(options: BridgeOptions & {
46
+ pid: number;
47
+ startedAt: string;
48
+ }): void;
49
+ export declare function clearBridgePid(options?: BridgeOptions): void;
50
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,KAAK,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,IAAI,CAAC;IACjB,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;CAC5C;AASD,wBAAgB,gBAAgB,CAAC,OAAO,SAAY,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,WAAW,CAU/G;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,aAAkB,GAAG,YAAY,CAYzE;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,YAAY,CA8B1E;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,aAAkB,GAAG,mBAAmB,CAO3E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAW5F;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,aAAkB,GAAG,mBAAmB,CAKvF;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,EAAE,CAOzF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAShG;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,aAAkB,GAAG,IAAI,CAEhE"}
package/dist/cli.js CHANGED
@@ -573,6 +573,185 @@ function findLocalDatabaseFiles(dir) {
573
573
  });
574
574
  }
575
575
 
576
+ // src/bridge.ts
577
+ import { spawn } from "child_process";
578
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
579
+ import { homedir as homedir2 } from "os";
580
+ import { dirname as dirname2, join as join2 } from "path";
581
+ function buildBridgePaths(homeDir = homedir2(), platform = process.platform) {
582
+ const baseDir = join2(homeDir, ".config", "mementos", "bridge");
583
+ return {
584
+ baseDir,
585
+ logFile: join2(baseDir, "bridge.log"),
586
+ pidFile: join2(baseDir, "bridge.pid.json"),
587
+ serviceFile: platform === "darwin" ? join2(homeDir, "Library", "LaunchAgents", "md.mementos.bridge.plist") : join2(homeDir, ".config", "systemd", "user", "mementos-bridge.service")
588
+ };
589
+ }
590
+ function getBridgeStatus(options = {}) {
591
+ const paths = buildBridgePaths(options.homeDir, options.platform);
592
+ const pidFile = readBridgePid(paths.pidFile);
593
+ if (pidFile && !isRunning(pidFile.pid)) {
594
+ rmSync2(paths.pidFile, { force: true });
595
+ return baseStatus(paths, options, false);
596
+ }
597
+ return {
598
+ ...baseStatus(paths, options, Boolean(pidFile)),
599
+ ...pidFile?.agentName ? { agentName: pidFile.agentName } : {},
600
+ ...pidFile ? { pid: pidFile.pid, startedAt: pidFile.startedAt } : {}
601
+ };
602
+ }
603
+ function startBridge(options = {}) {
604
+ const current = getBridgeStatus(options);
605
+ if (current.running)
606
+ return current;
607
+ const paths = buildBridgePaths(options.homeDir, options.platform);
608
+ mkdirSync2(paths.baseDir, { recursive: true });
609
+ const out = openSync(paths.logFile, "a");
610
+ const err = openSync(paths.logFile, "a");
611
+ const command = options.command ?? [process.execPath, process.argv[1] ?? "mementos", "bridge", "run"];
612
+ const child = spawn(command[0] ?? "mementos", command.slice(1), {
613
+ detached: true,
614
+ env: {
615
+ ...process.env,
616
+ ...options.agentName ? { MEMENTOS_AGENT_NAME: options.agentName } : {},
617
+ ...options.apiUrl ? { MEMENTOS_API_URL: options.apiUrl } : {},
618
+ ...options.homeDir ? { MEMENTOS_BRIDGE_HOME: options.homeDir } : {},
619
+ ...options.intervalMs ? { MEMENTOS_BRIDGE_INTERVAL_MS: String(options.intervalMs) } : {}
620
+ },
621
+ stdio: ["ignore", out, err]
622
+ });
623
+ child.unref();
624
+ writeBridgePid({
625
+ agentName: options.agentName,
626
+ apiUrl: options.apiUrl,
627
+ homeDir: options.homeDir,
628
+ pid: child.pid ?? 0,
629
+ startedAt: new Date().toISOString()
630
+ });
631
+ return getBridgeStatus(options);
632
+ }
633
+ function stopBridge(options = {}) {
634
+ const paths = buildBridgePaths(options.homeDir, options.platform);
635
+ const pidFile = readBridgePid(paths.pidFile);
636
+ if (!pidFile)
637
+ return { path: paths.pidFile, remoteOnly: true, status: "absent" };
638
+ if (isRunning(pidFile.pid))
639
+ process.kill(pidFile.pid, "SIGTERM");
640
+ clearBridgePid(options);
641
+ return { path: paths.pidFile, remoteOnly: true, status: "removed" };
642
+ }
643
+ function installBridgeService(options = {}) {
644
+ const platform = options.platform ?? process.platform;
645
+ const paths = buildBridgePaths(options.homeDir, platform);
646
+ mkdirSync2(dirname2(paths.serviceFile), { recursive: true });
647
+ const command = options.command ?? "mementos bridge run";
648
+ const apiUrl = options.apiUrl ?? "https://mementos.md/api/v1";
649
+ const content = platform === "darwin" ? renderLaunchdService(command, apiUrl, options.agentName) : renderSystemdService(command, apiUrl, options.agentName);
650
+ writeFileSync2(paths.serviceFile, content, { mode: 420 });
651
+ return { path: paths.serviceFile, remoteOnly: true, status: "installed" };
652
+ }
653
+ function uninstallBridgeService(options = {}) {
654
+ const paths = buildBridgePaths(options.homeDir, options.platform);
655
+ if (!existsSync2(paths.serviceFile))
656
+ return { path: paths.serviceFile, remoteOnly: true, status: "absent" };
657
+ rmSync2(paths.serviceFile, { force: true });
658
+ return { path: paths.serviceFile, remoteOnly: true, status: "removed" };
659
+ }
660
+ function readBridgeLogs(options = {}) {
661
+ const paths = buildBridgePaths(options.homeDir, options.platform);
662
+ if (!existsSync2(paths.logFile))
663
+ return [];
664
+ return readFileSync2(paths.logFile, "utf8").split(`
665
+ `).filter(Boolean).slice(-(options.lines ?? 40));
666
+ }
667
+ function writeBridgePid(options) {
668
+ const paths = buildBridgePaths(options.homeDir, options.platform);
669
+ mkdirSync2(paths.baseDir, { recursive: true });
670
+ writeFileSync2(paths.pidFile, `${JSON.stringify({
671
+ ...options.agentName ? { agentName: options.agentName } : {},
672
+ ...options.apiUrl ? { apiUrl: options.apiUrl } : {},
673
+ pid: options.pid,
674
+ startedAt: options.startedAt
675
+ }, null, 2)}
676
+ `, { mode: 420 });
677
+ }
678
+ function clearBridgePid(options = {}) {
679
+ rmSync2(buildBridgePaths(options.homeDir, options.platform).pidFile, { force: true });
680
+ }
681
+ function baseStatus(paths, options, running) {
682
+ return {
683
+ ...options.agentName ? { agentName: options.agentName } : {},
684
+ apiUrl: options.apiUrl ?? "https://mementos.md/api/v1",
685
+ localDatabase: false,
686
+ logFile: paths.logFile,
687
+ pidFile: paths.pidFile,
688
+ remoteOnly: true,
689
+ running
690
+ };
691
+ }
692
+ function readBridgePid(path) {
693
+ if (!existsSync2(path))
694
+ return;
695
+ try {
696
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
697
+ return Number.isInteger(parsed.pid) && parsed.pid > 0 ? parsed : undefined;
698
+ } catch {
699
+ return;
700
+ }
701
+ }
702
+ function isRunning(pid) {
703
+ try {
704
+ process.kill(pid, 0);
705
+ return true;
706
+ } catch {
707
+ return false;
708
+ }
709
+ }
710
+ function renderSystemdService(command, apiUrl, agentName) {
711
+ return `[Unit]
712
+ Description=mementos.md remote bridge
713
+
714
+ [Service]
715
+ Type=simple
716
+ Environment=MEMENTOS_API_URL=${apiUrl}
717
+ ${agentName ? `Environment=MEMENTOS_AGENT_NAME=${agentName}
718
+ ` : ""}ExecStart=${command}
719
+ Restart=on-failure
720
+
721
+ [Install]
722
+ WantedBy=default.target
723
+ `;
724
+ }
725
+ function renderLaunchdService(command, apiUrl, agentName) {
726
+ const args = command.split(/\s+/).map((part) => ` <string>${escapeXml(part)}</string>`).join(`
727
+ `);
728
+ return `<?xml version="1.0" encoding="UTF-8"?>
729
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
730
+ <plist version="1.0">
731
+ <dict>
732
+ <key>Label</key>
733
+ <string>md.mementos.bridge</string>
734
+ <key>ProgramArguments</key>
735
+ <array>
736
+ ${args}
737
+ </array>
738
+ <key>EnvironmentVariables</key>
739
+ <dict>
740
+ <key>MEMENTOS_API_URL</key>
741
+ <string>${escapeXml(apiUrl)}</string>
742
+ ${agentName ? ` <key>MEMENTOS_AGENT_NAME</key>
743
+ <string>${escapeXml(agentName)}</string>
744
+ ` : ""} </dict>
745
+ <key>RunAtLoad</key>
746
+ <true/>
747
+ </dict>
748
+ </plist>
749
+ `;
750
+ }
751
+ function escapeXml(value) {
752
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
753
+ }
754
+
576
755
  // src/remote-client.ts
577
756
  var DEFAULT_API_URL = "https://mementos.md/api/v1";
578
757
 
@@ -753,6 +932,8 @@ async function run(name, args) {
753
932
  return injectMemory(args);
754
933
  case "hook":
755
934
  return runHook(args);
935
+ case "bridge":
936
+ return runBridge(args);
756
937
  case "billing":
757
938
  return runBilling(args);
758
939
  case "inbox":
@@ -778,6 +959,37 @@ async function run(name, args) {
778
959
  ${helpText()}`);
779
960
  }
780
961
  }
962
+ async function runBridge(args) {
963
+ const subcommand = args[0] ?? "status";
964
+ const options = parseBridgeOptions(args.slice(1));
965
+ const bridgeOptions = {
966
+ agentName: options.agentName ?? process.env.MEMENTOS_AGENT_NAME,
967
+ apiUrl: baseApiUrl().endsWith("/api/v1") ? baseApiUrl() : `${baseApiUrl()}/api/v1`,
968
+ homeDir: options.homeDir ?? process.env.MEMENTOS_BRIDGE_HOME,
969
+ intervalMs: options.intervalMs ?? readPositiveInteger(process.env.MEMENTOS_BRIDGE_INTERVAL_MS)
970
+ };
971
+ switch (subcommand) {
972
+ case "start":
973
+ return startBridge(bridgeOptions);
974
+ case "status":
975
+ return getBridgeStatus(bridgeOptions);
976
+ case "stop":
977
+ return stopBridge(bridgeOptions);
978
+ case "install":
979
+ return installBridgeService({
980
+ ...bridgeOptions,
981
+ command: "mementos bridge run"
982
+ });
983
+ case "uninstall":
984
+ return uninstallBridgeService(bridgeOptions);
985
+ case "logs":
986
+ return { lines: readBridgeLogs({ ...bridgeOptions, lines: options.lines }) };
987
+ case "run":
988
+ return runBridgeDaemon(bridgeOptions, Boolean(options.once));
989
+ default:
990
+ throw new Error("usage: mementos bridge <start|status|stop|install|uninstall|logs|run>");
991
+ }
992
+ }
781
993
  async function runInbox(args) {
782
994
  const subcommand = args[0] ?? "list";
783
995
  switch (subcommand) {
@@ -803,6 +1015,44 @@ async function runInbox(args) {
803
1015
  throw new Error("usage: mementos inbox <send|list|claim>");
804
1016
  }
805
1017
  }
1018
+ async function runBridgeDaemon(options, once) {
1019
+ writeBridgePid({
1020
+ agentName: options.agentName,
1021
+ apiUrl: options.apiUrl,
1022
+ homeDir: options.homeDir,
1023
+ pid: process.pid,
1024
+ startedAt: new Date().toISOString()
1025
+ });
1026
+ const cleanup = () => clearBridgePid(options);
1027
+ process.once("SIGINT", () => {
1028
+ cleanup();
1029
+ process.exit(0);
1030
+ });
1031
+ process.once("SIGTERM", () => {
1032
+ cleanup();
1033
+ process.exit(0);
1034
+ });
1035
+ try {
1036
+ const intervalMs = options.intervalMs ?? 30000;
1037
+ do {
1038
+ if (options.agentName) {
1039
+ await client.heartbeatAgent({
1040
+ name: options.agentName,
1041
+ source: "mementos-bridge"
1042
+ });
1043
+ }
1044
+ if (once)
1045
+ break;
1046
+ await sleep(intervalMs);
1047
+ } while (true);
1048
+ if (once)
1049
+ cleanup();
1050
+ return getBridgeStatus(options);
1051
+ } finally {
1052
+ if (once)
1053
+ cleanup();
1054
+ }
1055
+ }
806
1056
  async function runBilling(args) {
807
1057
  const subcommand = args[0] ?? "status";
808
1058
  switch (subcommand) {
@@ -1017,6 +1267,26 @@ function parseSetupOptions(args) {
1017
1267
  }
1018
1268
  return options;
1019
1269
  }
1270
+ function parseBridgeOptions(args) {
1271
+ const options = {};
1272
+ for (let index = 0;index < args.length; index += 1) {
1273
+ const arg = args[index];
1274
+ if (arg === "--agent" || arg === "--agent-name") {
1275
+ options.agentName = args[++index];
1276
+ } else if (arg === "--home") {
1277
+ options.homeDir = args[++index];
1278
+ } else if (arg === "--interval-ms") {
1279
+ options.intervalMs = readPositiveInteger(args[++index]);
1280
+ } else if (arg === "--lines") {
1281
+ options.lines = readPositiveInteger(args[++index]);
1282
+ } else if (arg === "--once") {
1283
+ options.once = true;
1284
+ } else {
1285
+ throw new Error(`Unknown bridge option: ${arg}`);
1286
+ }
1287
+ }
1288
+ return options;
1289
+ }
1020
1290
  function parseInboxOptions(args) {
1021
1291
  const options = {};
1022
1292
  for (let index = 0;index < args.length; index += 1) {
@@ -1049,6 +1319,18 @@ function parseHosts(input) {
1049
1319
  function baseProductUrl() {
1050
1320
  return (process.env.MEMENTOS_API_URL ?? DEFAULT_API_URL).replace(/\/api\/v1\/?$/, "").replace(/\/+$/, "");
1051
1321
  }
1322
+ function baseApiUrl() {
1323
+ return (process.env.MEMENTOS_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
1324
+ }
1325
+ function readPositiveInteger(input) {
1326
+ if (!input)
1327
+ return;
1328
+ const value = Number(input);
1329
+ return Number.isInteger(value) && value > 0 ? value : undefined;
1330
+ }
1331
+ function sleep(ms) {
1332
+ return new Promise((resolve) => setTimeout(resolve, ms));
1333
+ }
1052
1334
  function readTextPayload(payload) {
1053
1335
  if (typeof payload !== "object" || payload === null)
1054
1336
  return;
@@ -1063,6 +1345,7 @@ Usage:
1063
1345
  mementos setup agents [--dry-run|--verify|--uninstall]
1064
1346
  mementos inject <query>
1065
1347
  mementos hook <stop|user-prompt-submit|session-start|subagent-start|subagent-stop|pre-tool-use|post-tool-use|notification|setup|doctor>
1348
+ mementos bridge <start|status|stop|install|uninstall|logs|run>
1066
1349
  mementos inbox <send|list|claim>
1067
1350
  mementos billing <status|credits|buy>
1068
1351
  mementos capabilities
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { DEFAULT_API_URL, MementosRemoteClient, MementosRemoteError, createRemoteClientFromEnv, normalizeApiUrl, type RemoteClientConfig, type RemoteRequestOptions, } from "./remote-client";
2
2
  export { REMOTE_MCP_TOOLS, callRemoteMcpTool, handleMcpMessage, startMcpStdio, type RemoteMcpTool, } from "./mcp-runtime";
3
3
  export { SUPPORTED_AGENT_HOSTS, buildAgentSetupPlan, diagnoseAgentSetup, formatMemoryInjection, installAgentSetup, renderSignupInstructions, type AgentSetupFile, type AgentSetupOptions, type AgentSetupPlan, type AgentSetupDiagnostic, type AgentSetupDiagnosticOptions, type SupportedAgentHost, } from "./agent-setup";
4
+ export { buildBridgePaths, clearBridgePid, getBridgeStatus, installBridgeService, readBridgeLogs, startBridge, stopBridge, uninstallBridgeService, writeBridgePid, type BridgeOptions, type BridgePaths, type BridgeServiceOptions, type BridgeServiceResult, type BridgeStartOptions, type BridgeStatus, } from "./bridge";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -146,7 +146,7 @@ function readErrorMessage(payload, status) {
146
146
  }
147
147
  // src/mcp-runtime.ts
148
148
  import { createInterface } from "readline";
149
- var MEMENTOS_MCP_VERSION = "0.14.29";
149
+ var MEMENTOS_MCP_VERSION = "0.14.30";
150
150
  var AUTHLESS_SCHEMA = {
151
151
  type: "object",
152
152
  properties: {},
@@ -915,16 +915,203 @@ function findLocalDatabaseFiles(dir) {
915
915
  return localDbPattern.test(entry) ? [path] : [];
916
916
  });
917
917
  }
918
+ // src/bridge.ts
919
+ import { spawn } from "child_process";
920
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
921
+ import { homedir as homedir2 } from "os";
922
+ import { dirname as dirname2, join as join2 } from "path";
923
+ function buildBridgePaths(homeDir = homedir2(), platform = process.platform) {
924
+ const baseDir = join2(homeDir, ".config", "mementos", "bridge");
925
+ return {
926
+ baseDir,
927
+ logFile: join2(baseDir, "bridge.log"),
928
+ pidFile: join2(baseDir, "bridge.pid.json"),
929
+ serviceFile: platform === "darwin" ? join2(homeDir, "Library", "LaunchAgents", "md.mementos.bridge.plist") : join2(homeDir, ".config", "systemd", "user", "mementos-bridge.service")
930
+ };
931
+ }
932
+ function getBridgeStatus(options = {}) {
933
+ const paths = buildBridgePaths(options.homeDir, options.platform);
934
+ const pidFile = readBridgePid(paths.pidFile);
935
+ if (pidFile && !isRunning(pidFile.pid)) {
936
+ rmSync2(paths.pidFile, { force: true });
937
+ return baseStatus(paths, options, false);
938
+ }
939
+ return {
940
+ ...baseStatus(paths, options, Boolean(pidFile)),
941
+ ...pidFile?.agentName ? { agentName: pidFile.agentName } : {},
942
+ ...pidFile ? { pid: pidFile.pid, startedAt: pidFile.startedAt } : {}
943
+ };
944
+ }
945
+ function startBridge(options = {}) {
946
+ const current = getBridgeStatus(options);
947
+ if (current.running)
948
+ return current;
949
+ const paths = buildBridgePaths(options.homeDir, options.platform);
950
+ mkdirSync2(paths.baseDir, { recursive: true });
951
+ const out = openSync(paths.logFile, "a");
952
+ const err = openSync(paths.logFile, "a");
953
+ const command = options.command ?? [process.execPath, process.argv[1] ?? "mementos", "bridge", "run"];
954
+ const child = spawn(command[0] ?? "mementos", command.slice(1), {
955
+ detached: true,
956
+ env: {
957
+ ...process.env,
958
+ ...options.agentName ? { MEMENTOS_AGENT_NAME: options.agentName } : {},
959
+ ...options.apiUrl ? { MEMENTOS_API_URL: options.apiUrl } : {},
960
+ ...options.homeDir ? { MEMENTOS_BRIDGE_HOME: options.homeDir } : {},
961
+ ...options.intervalMs ? { MEMENTOS_BRIDGE_INTERVAL_MS: String(options.intervalMs) } : {}
962
+ },
963
+ stdio: ["ignore", out, err]
964
+ });
965
+ child.unref();
966
+ writeBridgePid({
967
+ agentName: options.agentName,
968
+ apiUrl: options.apiUrl,
969
+ homeDir: options.homeDir,
970
+ pid: child.pid ?? 0,
971
+ startedAt: new Date().toISOString()
972
+ });
973
+ return getBridgeStatus(options);
974
+ }
975
+ function stopBridge(options = {}) {
976
+ const paths = buildBridgePaths(options.homeDir, options.platform);
977
+ const pidFile = readBridgePid(paths.pidFile);
978
+ if (!pidFile)
979
+ return { path: paths.pidFile, remoteOnly: true, status: "absent" };
980
+ if (isRunning(pidFile.pid))
981
+ process.kill(pidFile.pid, "SIGTERM");
982
+ clearBridgePid(options);
983
+ return { path: paths.pidFile, remoteOnly: true, status: "removed" };
984
+ }
985
+ function installBridgeService(options = {}) {
986
+ const platform = options.platform ?? process.platform;
987
+ const paths = buildBridgePaths(options.homeDir, platform);
988
+ mkdirSync2(dirname2(paths.serviceFile), { recursive: true });
989
+ const command = options.command ?? "mementos bridge run";
990
+ const apiUrl = options.apiUrl ?? "https://mementos.md/api/v1";
991
+ const content = platform === "darwin" ? renderLaunchdService(command, apiUrl, options.agentName) : renderSystemdService(command, apiUrl, options.agentName);
992
+ writeFileSync2(paths.serviceFile, content, { mode: 420 });
993
+ return { path: paths.serviceFile, remoteOnly: true, status: "installed" };
994
+ }
995
+ function uninstallBridgeService(options = {}) {
996
+ const paths = buildBridgePaths(options.homeDir, options.platform);
997
+ if (!existsSync2(paths.serviceFile))
998
+ return { path: paths.serviceFile, remoteOnly: true, status: "absent" };
999
+ rmSync2(paths.serviceFile, { force: true });
1000
+ return { path: paths.serviceFile, remoteOnly: true, status: "removed" };
1001
+ }
1002
+ function readBridgeLogs(options = {}) {
1003
+ const paths = buildBridgePaths(options.homeDir, options.platform);
1004
+ if (!existsSync2(paths.logFile))
1005
+ return [];
1006
+ return readFileSync2(paths.logFile, "utf8").split(`
1007
+ `).filter(Boolean).slice(-(options.lines ?? 40));
1008
+ }
1009
+ function writeBridgePid(options) {
1010
+ const paths = buildBridgePaths(options.homeDir, options.platform);
1011
+ mkdirSync2(paths.baseDir, { recursive: true });
1012
+ writeFileSync2(paths.pidFile, `${JSON.stringify({
1013
+ ...options.agentName ? { agentName: options.agentName } : {},
1014
+ ...options.apiUrl ? { apiUrl: options.apiUrl } : {},
1015
+ pid: options.pid,
1016
+ startedAt: options.startedAt
1017
+ }, null, 2)}
1018
+ `, { mode: 420 });
1019
+ }
1020
+ function clearBridgePid(options = {}) {
1021
+ rmSync2(buildBridgePaths(options.homeDir, options.platform).pidFile, { force: true });
1022
+ }
1023
+ function baseStatus(paths, options, running) {
1024
+ return {
1025
+ ...options.agentName ? { agentName: options.agentName } : {},
1026
+ apiUrl: options.apiUrl ?? "https://mementos.md/api/v1",
1027
+ localDatabase: false,
1028
+ logFile: paths.logFile,
1029
+ pidFile: paths.pidFile,
1030
+ remoteOnly: true,
1031
+ running
1032
+ };
1033
+ }
1034
+ function readBridgePid(path) {
1035
+ if (!existsSync2(path))
1036
+ return;
1037
+ try {
1038
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
1039
+ return Number.isInteger(parsed.pid) && parsed.pid > 0 ? parsed : undefined;
1040
+ } catch {
1041
+ return;
1042
+ }
1043
+ }
1044
+ function isRunning(pid) {
1045
+ try {
1046
+ process.kill(pid, 0);
1047
+ return true;
1048
+ } catch {
1049
+ return false;
1050
+ }
1051
+ }
1052
+ function renderSystemdService(command, apiUrl, agentName) {
1053
+ return `[Unit]
1054
+ Description=mementos.md remote bridge
1055
+
1056
+ [Service]
1057
+ Type=simple
1058
+ Environment=MEMENTOS_API_URL=${apiUrl}
1059
+ ${agentName ? `Environment=MEMENTOS_AGENT_NAME=${agentName}
1060
+ ` : ""}ExecStart=${command}
1061
+ Restart=on-failure
1062
+
1063
+ [Install]
1064
+ WantedBy=default.target
1065
+ `;
1066
+ }
1067
+ function renderLaunchdService(command, apiUrl, agentName) {
1068
+ const args = command.split(/\s+/).map((part) => ` <string>${escapeXml(part)}</string>`).join(`
1069
+ `);
1070
+ return `<?xml version="1.0" encoding="UTF-8"?>
1071
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1072
+ <plist version="1.0">
1073
+ <dict>
1074
+ <key>Label</key>
1075
+ <string>md.mementos.bridge</string>
1076
+ <key>ProgramArguments</key>
1077
+ <array>
1078
+ ${args}
1079
+ </array>
1080
+ <key>EnvironmentVariables</key>
1081
+ <dict>
1082
+ <key>MEMENTOS_API_URL</key>
1083
+ <string>${escapeXml(apiUrl)}</string>
1084
+ ${agentName ? ` <key>MEMENTOS_AGENT_NAME</key>
1085
+ <string>${escapeXml(agentName)}</string>
1086
+ ` : ""} </dict>
1087
+ <key>RunAtLoad</key>
1088
+ <true/>
1089
+ </dict>
1090
+ </plist>
1091
+ `;
1092
+ }
1093
+ function escapeXml(value) {
1094
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1095
+ }
918
1096
  export {
1097
+ writeBridgePid,
1098
+ uninstallBridgeService,
1099
+ stopBridge,
919
1100
  startMcpStdio,
1101
+ startBridge,
920
1102
  renderSignupInstructions,
1103
+ readBridgeLogs,
921
1104
  normalizeApiUrl,
1105
+ installBridgeService,
922
1106
  installAgentSetup,
923
1107
  handleMcpMessage,
1108
+ getBridgeStatus,
924
1109
  formatMemoryInjection,
925
1110
  diagnoseAgentSetup,
926
1111
  createRemoteClientFromEnv,
1112
+ clearBridgePid,
927
1113
  callRemoteMcpTool,
1114
+ buildBridgePaths,
928
1115
  buildAgentSetupPlan,
929
1116
  SUPPORTED_AGENT_HOSTS,
930
1117
  REMOTE_MCP_TOOLS,
@@ -1,5 +1,5 @@
1
1
  import { MementosRemoteClient } from "./remote-client";
2
- export declare const MEMENTOS_MCP_VERSION = "0.14.29";
2
+ export declare const MEMENTOS_MCP_VERSION = "0.14.30";
3
3
  export interface RemoteMcpTool {
4
4
  name: string;
5
5
  description: string;
package/dist/mcp.js CHANGED
@@ -151,7 +151,7 @@ function readErrorMessage(payload, status) {
151
151
  }
152
152
 
153
153
  // src/mcp-runtime.ts
154
- var MEMENTOS_MCP_VERSION = "0.14.29";
154
+ var MEMENTOS_MCP_VERSION = "0.14.30";
155
155
  var AUTHLESS_SCHEMA = {
156
156
  type: "object",
157
157
  properties: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.29",
3
+ "version": "0.14.30",
4
4
  "description": "Remote-only mementos.md SaaS CLI and MCP client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",