cf-memory-mcp 3.60.0 → 3.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cf-memory-mcp.js +110 -6
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -2672,6 +2672,42 @@ class CFMemoryMCP {
|
|
|
2672
2672
|
}
|
|
2673
2673
|
}
|
|
2674
2674
|
|
|
2675
|
+
/**
|
|
2676
|
+
* Drop the implicit-session disk cache iff the cached session id is
|
|
2677
|
+
* in `deletedSessionIds`. Called from the delete CLI so a subsequent
|
|
2678
|
+
* end_session against the implicit cache won't write to a session
|
|
2679
|
+
* that no longer exists on the server. Also clears the in-memory map
|
|
2680
|
+
* for the matching cwd. Returns true when something was dropped.
|
|
2681
|
+
*/
|
|
2682
|
+
invalidateImplicitSessionIfMatches(deletedSessionIds) {
|
|
2683
|
+
if (!deletedSessionIds || !deletedSessionIds.length) return false;
|
|
2684
|
+
const matchSet = new Set(deletedSessionIds);
|
|
2685
|
+
let dropped = false;
|
|
2686
|
+
try {
|
|
2687
|
+
const implicitPath = this.getImplicitSessionDiskPath();
|
|
2688
|
+
if (implicitPath && fs.existsSync(implicitPath)) {
|
|
2689
|
+
const entry = JSON.parse(fs.readFileSync(implicitPath, 'utf8'));
|
|
2690
|
+
if (matchSet.has(entry.session_id)) {
|
|
2691
|
+
fs.unlinkSync(implicitPath);
|
|
2692
|
+
dropped = true;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
} catch (_) { /* non-fatal */ }
|
|
2696
|
+
// Also evict in-memory entries so a long-lived bridge stops
|
|
2697
|
+
// auto-filling the deleted id.
|
|
2698
|
+
try {
|
|
2699
|
+
if (this._implicitSessionByCwd && this._implicitSessionByCwd.size) {
|
|
2700
|
+
for (const [cwd, sid] of this._implicitSessionByCwd.entries()) {
|
|
2701
|
+
if (matchSet.has(sid)) {
|
|
2702
|
+
this._implicitSessionByCwd.delete(cwd);
|
|
2703
|
+
dropped = true;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
} catch (_) { /* non-fatal */ }
|
|
2708
|
+
return dropped;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2675
2711
|
/**
|
|
2676
2712
|
* Return the implicit session_id for the current cwd, creating one via
|
|
2677
2713
|
* start_session when no session is active. Lets agents skip explicit
|
|
@@ -4051,6 +4087,14 @@ function parseCliArgs(rest) {
|
|
|
4051
4087
|
flags.raw = true;
|
|
4052
4088
|
} else if (a === '--copy') {
|
|
4053
4089
|
flags.copy = true;
|
|
4090
|
+
} else if (a === '--watch') {
|
|
4091
|
+
flags.watch = true;
|
|
4092
|
+
} else if (a === '--interval') {
|
|
4093
|
+
const n = parseInt(rest[++i], 10);
|
|
4094
|
+
flags.interval = Number.isFinite(n) && n >= 5 ? Math.min(n, 600) : 30;
|
|
4095
|
+
} else if (a.startsWith('--interval=')) {
|
|
4096
|
+
const n = parseInt(a.slice('--interval='.length), 10);
|
|
4097
|
+
flags.interval = Number.isFinite(n) && n >= 5 ? Math.min(n, 600) : 30;
|
|
4054
4098
|
} else if (a === '--card') {
|
|
4055
4099
|
flags.card = true;
|
|
4056
4100
|
} else if (a === '--short') {
|
|
@@ -4127,6 +4171,42 @@ async function runResumeCli() {
|
|
|
4127
4171
|
process.exit(1);
|
|
4128
4172
|
}
|
|
4129
4173
|
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
4174
|
+
|
|
4175
|
+
// --watch: re-spawn this command without --watch on an interval,
|
|
4176
|
+
// clearing the screen between renders. Bounded by --interval (5-600s,
|
|
4177
|
+
// default 30s). Ctrl+C exits cleanly.
|
|
4178
|
+
if (flags.watch) {
|
|
4179
|
+
const interval = (flags.interval || 30) * 1000;
|
|
4180
|
+
const { spawnSync } = require('child_process');
|
|
4181
|
+
// Strip --watch from argv for the inner call. Pass everything else.
|
|
4182
|
+
const innerArgs = process.argv.slice(3).filter(a => a !== '--watch' && a !== '--interval' && !a.startsWith('--interval='))
|
|
4183
|
+
.filter((_, i, arr) => {
|
|
4184
|
+
// Also drop the value after --interval if it was separate.
|
|
4185
|
+
const prev = arr[i - 1];
|
|
4186
|
+
return prev !== '--interval';
|
|
4187
|
+
});
|
|
4188
|
+
let stopped = false;
|
|
4189
|
+
const onSig = () => { stopped = true; process.stderr.write('\n[watch] stopped\n'); process.exit(0); };
|
|
4190
|
+
process.on('SIGINT', onSig);
|
|
4191
|
+
process.on('SIGTERM', onSig);
|
|
4192
|
+
while (!stopped) {
|
|
4193
|
+
// Clear screen (ANSI). Skip when not a TTY (just append).
|
|
4194
|
+
if (process.stdout.isTTY) process.stdout.write('\x1b[2J\x1b[H');
|
|
4195
|
+
process.stdout.write(`[watch] ${new Date().toISOString()} (every ${interval/1000}s, Ctrl+C to exit)\n\n`);
|
|
4196
|
+
try {
|
|
4197
|
+
const res = spawnSync(process.execPath, [__filename, 'resume', ...innerArgs], {
|
|
4198
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
4199
|
+
});
|
|
4200
|
+
if (res.status !== 0 && res.status !== 3) {
|
|
4201
|
+
process.stderr.write(`\n[watch] resume exited ${res.status}\n`);
|
|
4202
|
+
}
|
|
4203
|
+
} catch (err) {
|
|
4204
|
+
process.stderr.write(`\n[watch] error: ${err.message}\n`);
|
|
4205
|
+
}
|
|
4206
|
+
await new Promise(r => setTimeout(r, interval));
|
|
4207
|
+
}
|
|
4208
|
+
process.exit(0);
|
|
4209
|
+
}
|
|
4130
4210
|
const server = new CFMemoryMCP();
|
|
4131
4211
|
server.logDebug = () => {};
|
|
4132
4212
|
server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
|
|
@@ -5171,6 +5251,12 @@ async function runDeleteCli() {
|
|
|
5171
5251
|
});
|
|
5172
5252
|
const deleteText = deleteRes?.result?.content?.[0]?.text;
|
|
5173
5253
|
const payload = JSON.parse(deleteText || '{}');
|
|
5254
|
+
// Implicit-cache invalidation: if any deleted id matches the
|
|
5255
|
+
// current cwd's implicit session, drop the cache so the next
|
|
5256
|
+
// end_session creates a fresh one.
|
|
5257
|
+
if (payload.deleted) {
|
|
5258
|
+
server.invalidateImplicitSessionIfMatches(payload.deleted_session_ids || []);
|
|
5259
|
+
}
|
|
5174
5260
|
if (flags.json) {
|
|
5175
5261
|
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
5176
5262
|
process.exit(payload.deleted ? 0 : 3);
|
|
@@ -5210,6 +5296,12 @@ async function runDeleteCli() {
|
|
|
5210
5296
|
});
|
|
5211
5297
|
const deleteText = deleteRes?.result?.content?.[0]?.text;
|
|
5212
5298
|
const payload = JSON.parse(deleteText || '{}');
|
|
5299
|
+
// Drop the implicit-session disk cache if the deleted id matches
|
|
5300
|
+
// it. Otherwise a subsequent end_session would try to write to a
|
|
5301
|
+
// session that no longer exists.
|
|
5302
|
+
if (payload.deleted) {
|
|
5303
|
+
server.invalidateImplicitSessionIfMatches([fullId]);
|
|
5304
|
+
}
|
|
5213
5305
|
if (flags.json) {
|
|
5214
5306
|
process.stdout.write(JSON.stringify({ ...payload, session_id: fullId }, null, 2) + '\n');
|
|
5215
5307
|
process.exit(payload.deleted ? 0 : 3);
|
|
@@ -5467,6 +5559,7 @@ const PER_COMMAND_HELP = {
|
|
|
5467
5559
|
--md <path> Write the markdown to a file instead of stdout.
|
|
5468
5560
|
--copy Pipe the markdown to the platform clipboard (pbcopy/xclip/wl-copy/clip).
|
|
5469
5561
|
--card Compact 2-3 line status card (for terminal status widgets).
|
|
5562
|
+
--watch [--interval N] Re-render every N seconds (default 30, range 5-600). Ctrl+C to exit.
|
|
5470
5563
|
Extract flags (pick one; each exits 3 if the section is empty):
|
|
5471
5564
|
--next-only First next_step only (for shell prompts).
|
|
5472
5565
|
--next-steps All next_steps, numbered.
|
|
@@ -6052,11 +6145,15 @@ async function runCleanCli() {
|
|
|
6052
6145
|
const removed = [];
|
|
6053
6146
|
try {
|
|
6054
6147
|
if (all) {
|
|
6055
|
-
// Clear ALL disk caches in ~/.cf-memory
|
|
6148
|
+
// Clear ALL disk caches in ~/.cf-memory/: handoff-*.json
|
|
6149
|
+
// (offline resume cache) AND implicit-*.json (persistent
|
|
6150
|
+
// implicit session id per cwd).
|
|
6056
6151
|
const dir = path.join(os.homedir(), '.cf-memory');
|
|
6057
6152
|
if (fs.existsSync(dir)) {
|
|
6058
6153
|
for (const entry of fs.readdirSync(dir)) {
|
|
6059
|
-
|
|
6154
|
+
const isHandoff = entry.startsWith('handoff-') && entry.endsWith('.json');
|
|
6155
|
+
const isImplicit = entry.startsWith('implicit-') && entry.endsWith('.json');
|
|
6156
|
+
if (isHandoff || isImplicit) {
|
|
6060
6157
|
const full = path.join(dir, entry);
|
|
6061
6158
|
fs.unlinkSync(full);
|
|
6062
6159
|
removed.push(full);
|
|
@@ -6064,10 +6161,17 @@ async function runCleanCli() {
|
|
|
6064
6161
|
}
|
|
6065
6162
|
}
|
|
6066
6163
|
} else {
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6164
|
+
// cwd-only mode: drop both the handoff cache AND the
|
|
6165
|
+
// implicit session cache for this cwd.
|
|
6166
|
+
const handoffPath = server.getDiskCachePath();
|
|
6167
|
+
if (handoffPath && fs.existsSync(handoffPath)) {
|
|
6168
|
+
fs.unlinkSync(handoffPath);
|
|
6169
|
+
removed.push(handoffPath);
|
|
6170
|
+
}
|
|
6171
|
+
const implicitPath = server.getImplicitSessionDiskPath();
|
|
6172
|
+
if (implicitPath && fs.existsSync(implicitPath)) {
|
|
6173
|
+
fs.unlinkSync(implicitPath);
|
|
6174
|
+
removed.push(implicitPath);
|
|
6071
6175
|
}
|
|
6072
6176
|
}
|
|
6073
6177
|
if (flags.json) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-memory-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.62.0",
|
|
4
4
|
"description": "Cloudflare-hosted MCP server for code indexing, retrieval, and assistant memory with a direct remote MCP endpoint and local stdio bridge.",
|
|
5
5
|
"main": "bin/cf-memory-mcp.js",
|
|
6
6
|
"bin": {
|