@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 +19 -2
- package/dist/agent-setup.d.ts.map +1 -1
- package/dist/bridge.d.ts +50 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/cli.js +358 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +263 -2
- package/dist/mcp-runtime.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
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
|
|
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,
|
|
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"}
|
package/dist/bridge.d.ts
ADDED
|
@@ -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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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.
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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,
|
package/dist/mcp-runtime.d.ts
CHANGED
package/dist/mcp.js
CHANGED