@hasna/mementos 0.14.28 → 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
@@ -20,7 +20,8 @@ mementos setup agents
20
20
 
21
21
  `mementos setup agents` registers the hosted `mementos-mcp` server in Claude
22
22
  Code, Codex, OpenCode, and Antigravity config files. It also writes managed
23
- diagnostic snippets under `~/.config/mementos/agent-integrations`.
23
+ diagnostic snippets and host-specific memory instructions under
24
+ `~/.config/mementos/agent-integrations`.
24
25
 
25
26
  ## MCP
26
27
 
@@ -42,4 +43,20 @@ Claude Code also receives `UserPromptSubmit`, `SessionStart`, and `Stop` hook
42
43
  entries that call `mementos hook ...` and return hosted memory as hook context.
43
44
  Codex does not expose a native hook file in `~/.codex/config.toml`, so the
44
45
  installer registers MCP there and keeps the injection command in the managed
45
- integration snippets.
46
+ integration snippets. OpenCode and Antigravity receive the same MCP registration
47
+ plus host instruction files that tell the agent to use `memory.search`,
48
+ `memory.save`, and `mementos inject` as the hosted fallback path when native
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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-setup.d.ts","sourceRoot":"","sources":["../src/agent-setup.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,8DAA+D,CAAC;AAElG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;CAChF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;KAC9B,CAAC;IACF,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,IAAI,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CA0CnF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,2BAAgC,GAAG,oBAAoB,CAyBlG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAqCjF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,SAAwB;;;EAStE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAW9D"}
1
+ {"version":3,"file":"agent-setup.d.ts","sourceRoot":"","sources":["../src/agent-setup.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,8DAA+D,CAAC;AAElG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;CAChF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;KAC9B,CAAC;IACF,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,IAAI,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAkDnF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,2BAAgC,GAAG,oBAAoB,CAyBlG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAqCjF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,SAAwB;;;EAStE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAW9D"}
@@ -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
@@ -15,6 +15,10 @@ function buildAgentSetupPlan(options = {}) {
15
15
  const hookCommand = "mementos hook stop";
16
16
  const injectCommand = "mementos inject";
17
17
  const files = [
18
+ {
19
+ path: join(baseDir, "mementos-instructions.md"),
20
+ content: renderSharedInstructions({ apiUrl, injectCommand, mcpCommand })
21
+ },
18
22
  {
19
23
  path: join(baseDir, "mementos-injection.md"),
20
24
  content: renderInjectionInstructions({ apiUrl, injectCommand })
@@ -31,6 +35,10 @@ function buildAgentSetupPlan(options = {}) {
31
35
  }
32
36
  ];
33
37
  for (const host of hosts) {
38
+ files.push({
39
+ path: join(baseDir, `${host}-instructions.md`),
40
+ content: renderHostInstructions(host, { apiUrl, injectCommand, mcpCommand })
41
+ });
34
42
  files.push({
35
43
  path: join(baseDir, `${host}.json`),
36
44
  content: `${JSON.stringify(renderHostConfig(host, { apiUrl, hookCommand, injectCommand, mcpCommand }), null, 2)}
@@ -155,11 +163,13 @@ function renderHostConfig(host, commands) {
155
163
  host,
156
164
  install: "bun install -g @hasna/mementos",
157
165
  remoteOnly: true,
166
+ nativeHooks: hasNativeHooks(host),
158
167
  mcp: renderMcpServerConfig(commands).mcpServers.mementos,
159
168
  hooks: {
160
169
  stop: commands.hookCommand,
161
170
  injection: commands.injectCommand
162
- }
171
+ },
172
+ instructions: `${host}-instructions.md`
163
173
  };
164
174
  }
165
175
  function renderManagedHostConfig(host, commands) {
@@ -173,8 +183,15 @@ function renderManagedHostConfig(host, commands) {
173
183
  inject: commands.injectCommand,
174
184
  stopHook: commands.hookCommand
175
185
  },
186
+ injection: {
187
+ nativeHooks: hasNativeHooks(host),
188
+ fallbackCommand: commands.injectCommand,
189
+ mcpTools: ["memory.search", "memory.save", "memory.get", "memory.update", "memory.delete", "memory.usage"]
190
+ },
176
191
  files: {
177
192
  integrationDir: commands.baseDir,
193
+ sharedInstructions: join(commands.baseDir, "mementos-instructions.md"),
194
+ hostInstructions: join(commands.baseDir, `${host}-instructions.md`),
178
195
  injectionInstructions: join(commands.baseDir, "mementos-injection.md"),
179
196
  stopHook: join(commands.baseDir, "mementos-stop-hook.sh")
180
197
  }
@@ -455,6 +472,63 @@ MEMENTOS_API_URL=${apiUrl} MEMENTOS_API_TOKEN=$MEMENTOS_API_TOKEN ${injectComman
455
472
  This command reads hosted memory through the mementos.md API and prints a prompt-ready block.
456
473
  `;
457
474
  }
475
+ function renderSharedInstructions({
476
+ apiUrl,
477
+ injectCommand,
478
+ mcpCommand
479
+ }) {
480
+ return `# mementos.md Agent Instructions
481
+
482
+ Use mementos.md as hosted durable memory for this agent host.
483
+
484
+ - The MCP server command is \`${mcpCommand}\`.
485
+ - The hosted API is \`${apiUrl}\`.
486
+ - Use \`memory.search\` before non-trivial project work.
487
+ - Use \`memory.save\` for durable decisions, root causes, and reusable context.
488
+ - If the host does not provide a native hook, run \`${injectCommand} "<current task or question>"\` and include the returned hosted context in the next prompt.
489
+ - Do not create or depend on a local memory database for the SaaS package.
490
+ `;
491
+ }
492
+ function renderHostInstructions(host, commands) {
493
+ const common = renderSharedInstructions(commands);
494
+ if (host === "claude-code") {
495
+ return `${common}
496
+ ## Claude Code
497
+
498
+ Claude Code has native mementos hooks installed by \`mementos setup agents\`.
499
+ Use the mementos MCP server for explicit memory operations and let the
500
+ \`UserPromptSubmit\`, \`SessionStart\`, and \`Stop\` hooks inject hosted context.
501
+ `;
502
+ }
503
+ if (host === "codex") {
504
+ return `${common}
505
+ ## Codex
506
+
507
+ Codex does not have a mementos native hook in its config file. Use the
508
+ mementos MCP server for durable memory and run \`${commands.injectCommand}\`
509
+ when you need prompt-ready hosted context before a turn.
510
+ `;
511
+ }
512
+ if (host === "opencode") {
513
+ return `${common}
514
+ ## OpenCode
515
+
516
+ Use the mementos MCP server for durable memory. When OpenCode cannot invoke a
517
+ native lifecycle hook, run \`${commands.injectCommand}\` with the active task
518
+ or error text and paste the hosted context into the next prompt.
519
+ `;
520
+ }
521
+ return `${common}
522
+ ## Antigravity
523
+
524
+ Use the mementos MCP server for durable memory. When Antigravity cannot invoke
525
+ a native lifecycle hook, run \`${commands.injectCommand}\` with the active task
526
+ or error text and include the hosted context before continuing.
527
+ `;
528
+ }
529
+ function hasNativeHooks(host) {
530
+ return host === "claude-code";
531
+ }
458
532
  function renderHookScript(command) {
459
533
  return `#!/usr/bin/env bash
460
534
  set -euo pipefail
@@ -499,6 +573,185 @@ function findLocalDatabaseFiles(dir) {
499
573
  });
500
574
  }
501
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
+
502
755
  // src/remote-client.ts
503
756
  var DEFAULT_API_URL = "https://mementos.md/api/v1";
504
757
 
@@ -679,6 +932,8 @@ async function run(name, args) {
679
932
  return injectMemory(args);
680
933
  case "hook":
681
934
  return runHook(args);
935
+ case "bridge":
936
+ return runBridge(args);
682
937
  case "billing":
683
938
  return runBilling(args);
684
939
  case "inbox":
@@ -704,6 +959,37 @@ async function run(name, args) {
704
959
  ${helpText()}`);
705
960
  }
706
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
+ }
707
993
  async function runInbox(args) {
708
994
  const subcommand = args[0] ?? "list";
709
995
  switch (subcommand) {
@@ -729,6 +1015,44 @@ async function runInbox(args) {
729
1015
  throw new Error("usage: mementos inbox <send|list|claim>");
730
1016
  }
731
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
+ }
732
1056
  async function runBilling(args) {
733
1057
  const subcommand = args[0] ?? "status";
734
1058
  switch (subcommand) {
@@ -943,6 +1267,26 @@ function parseSetupOptions(args) {
943
1267
  }
944
1268
  return options;
945
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
+ }
946
1290
  function parseInboxOptions(args) {
947
1291
  const options = {};
948
1292
  for (let index = 0;index < args.length; index += 1) {
@@ -975,6 +1319,18 @@ function parseHosts(input) {
975
1319
  function baseProductUrl() {
976
1320
  return (process.env.MEMENTOS_API_URL ?? DEFAULT_API_URL).replace(/\/api\/v1\/?$/, "").replace(/\/+$/, "");
977
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
+ }
978
1334
  function readTextPayload(payload) {
979
1335
  if (typeof payload !== "object" || payload === null)
980
1336
  return;
@@ -989,6 +1345,7 @@ Usage:
989
1345
  mementos setup agents [--dry-run|--verify|--uninstall]
990
1346
  mementos inject <query>
991
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>
992
1349
  mementos inbox <send|list|claim>
993
1350
  mementos billing <status|credits|buy>
994
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.28";
149
+ var MEMENTOS_MCP_VERSION = "0.14.30";
150
150
  var AUTHLESS_SCHEMA = {
151
151
  type: "object",
152
152
  properties: {},
@@ -358,6 +358,10 @@ function buildAgentSetupPlan(options = {}) {
358
358
  const hookCommand = "mementos hook stop";
359
359
  const injectCommand = "mementos inject";
360
360
  const files = [
361
+ {
362
+ path: join(baseDir, "mementos-instructions.md"),
363
+ content: renderSharedInstructions({ apiUrl, injectCommand, mcpCommand })
364
+ },
361
365
  {
362
366
  path: join(baseDir, "mementos-injection.md"),
363
367
  content: renderInjectionInstructions({ apiUrl, injectCommand })
@@ -374,6 +378,10 @@ function buildAgentSetupPlan(options = {}) {
374
378
  }
375
379
  ];
376
380
  for (const host of hosts) {
381
+ files.push({
382
+ path: join(baseDir, `${host}-instructions.md`),
383
+ content: renderHostInstructions(host, { apiUrl, injectCommand, mcpCommand })
384
+ });
377
385
  files.push({
378
386
  path: join(baseDir, `${host}.json`),
379
387
  content: `${JSON.stringify(renderHostConfig(host, { apiUrl, hookCommand, injectCommand, mcpCommand }), null, 2)}
@@ -498,11 +506,13 @@ function renderHostConfig(host, commands) {
498
506
  host,
499
507
  install: "bun install -g @hasna/mementos",
500
508
  remoteOnly: true,
509
+ nativeHooks: hasNativeHooks(host),
501
510
  mcp: renderMcpServerConfig(commands).mcpServers.mementos,
502
511
  hooks: {
503
512
  stop: commands.hookCommand,
504
513
  injection: commands.injectCommand
505
- }
514
+ },
515
+ instructions: `${host}-instructions.md`
506
516
  };
507
517
  }
508
518
  function renderManagedHostConfig(host, commands) {
@@ -516,8 +526,15 @@ function renderManagedHostConfig(host, commands) {
516
526
  inject: commands.injectCommand,
517
527
  stopHook: commands.hookCommand
518
528
  },
529
+ injection: {
530
+ nativeHooks: hasNativeHooks(host),
531
+ fallbackCommand: commands.injectCommand,
532
+ mcpTools: ["memory.search", "memory.save", "memory.get", "memory.update", "memory.delete", "memory.usage"]
533
+ },
519
534
  files: {
520
535
  integrationDir: commands.baseDir,
536
+ sharedInstructions: join(commands.baseDir, "mementos-instructions.md"),
537
+ hostInstructions: join(commands.baseDir, `${host}-instructions.md`),
521
538
  injectionInstructions: join(commands.baseDir, "mementos-injection.md"),
522
539
  stopHook: join(commands.baseDir, "mementos-stop-hook.sh")
523
540
  }
@@ -798,6 +815,63 @@ MEMENTOS_API_URL=${apiUrl} MEMENTOS_API_TOKEN=$MEMENTOS_API_TOKEN ${injectComman
798
815
  This command reads hosted memory through the mementos.md API and prints a prompt-ready block.
799
816
  `;
800
817
  }
818
+ function renderSharedInstructions({
819
+ apiUrl,
820
+ injectCommand,
821
+ mcpCommand
822
+ }) {
823
+ return `# mementos.md Agent Instructions
824
+
825
+ Use mementos.md as hosted durable memory for this agent host.
826
+
827
+ - The MCP server command is \`${mcpCommand}\`.
828
+ - The hosted API is \`${apiUrl}\`.
829
+ - Use \`memory.search\` before non-trivial project work.
830
+ - Use \`memory.save\` for durable decisions, root causes, and reusable context.
831
+ - If the host does not provide a native hook, run \`${injectCommand} "<current task or question>"\` and include the returned hosted context in the next prompt.
832
+ - Do not create or depend on a local memory database for the SaaS package.
833
+ `;
834
+ }
835
+ function renderHostInstructions(host, commands) {
836
+ const common = renderSharedInstructions(commands);
837
+ if (host === "claude-code") {
838
+ return `${common}
839
+ ## Claude Code
840
+
841
+ Claude Code has native mementos hooks installed by \`mementos setup agents\`.
842
+ Use the mementos MCP server for explicit memory operations and let the
843
+ \`UserPromptSubmit\`, \`SessionStart\`, and \`Stop\` hooks inject hosted context.
844
+ `;
845
+ }
846
+ if (host === "codex") {
847
+ return `${common}
848
+ ## Codex
849
+
850
+ Codex does not have a mementos native hook in its config file. Use the
851
+ mementos MCP server for durable memory and run \`${commands.injectCommand}\`
852
+ when you need prompt-ready hosted context before a turn.
853
+ `;
854
+ }
855
+ if (host === "opencode") {
856
+ return `${common}
857
+ ## OpenCode
858
+
859
+ Use the mementos MCP server for durable memory. When OpenCode cannot invoke a
860
+ native lifecycle hook, run \`${commands.injectCommand}\` with the active task
861
+ or error text and paste the hosted context into the next prompt.
862
+ `;
863
+ }
864
+ return `${common}
865
+ ## Antigravity
866
+
867
+ Use the mementos MCP server for durable memory. When Antigravity cannot invoke
868
+ a native lifecycle hook, run \`${commands.injectCommand}\` with the active task
869
+ or error text and include the hosted context before continuing.
870
+ `;
871
+ }
872
+ function hasNativeHooks(host) {
873
+ return host === "claude-code";
874
+ }
801
875
  function renderHookScript(command) {
802
876
  return `#!/usr/bin/env bash
803
877
  set -euo pipefail
@@ -841,16 +915,203 @@ function findLocalDatabaseFiles(dir) {
841
915
  return localDbPattern.test(entry) ? [path] : [];
842
916
  });
843
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
+ }
844
1096
  export {
1097
+ writeBridgePid,
1098
+ uninstallBridgeService,
1099
+ stopBridge,
845
1100
  startMcpStdio,
1101
+ startBridge,
846
1102
  renderSignupInstructions,
1103
+ readBridgeLogs,
847
1104
  normalizeApiUrl,
1105
+ installBridgeService,
848
1106
  installAgentSetup,
849
1107
  handleMcpMessage,
1108
+ getBridgeStatus,
850
1109
  formatMemoryInjection,
851
1110
  diagnoseAgentSetup,
852
1111
  createRemoteClientFromEnv,
1112
+ clearBridgePid,
853
1113
  callRemoteMcpTool,
1114
+ buildBridgePaths,
854
1115
  buildAgentSetupPlan,
855
1116
  SUPPORTED_AGENT_HOSTS,
856
1117
  REMOTE_MCP_TOOLS,
@@ -1,5 +1,5 @@
1
1
  import { MementosRemoteClient } from "./remote-client";
2
- export declare const MEMENTOS_MCP_VERSION = "0.14.28";
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.28";
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.28",
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",