Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 2c33ecdf94c8d90b421cc222b310e1e2d179f062 294 lines 8.1 kB view raw
1#!/usr/bin/env node 2// memory/cli.mjs 3// Local-first memory CLI for Claude/Codex session continuity. 4 5import { 6 buildEnvProfile, 7 commitEvent, 8 createCheckpoint, 9 createSession, 10 flushRemote, 11 inspectStore, 12 listSessionCheckpoints, 13 listSessions, 14 rememberSession, 15 setDeviceId, 16} from "./index.mjs"; 17 18function parseArgs(argv) { 19 const out = { _: [] }; 20 for (let i = 0; i < argv.length; i += 1) { 21 const token = argv[i]; 22 if (!token.startsWith("--")) { 23 out._.push(token); 24 continue; 25 } 26 27 const key = token.slice(2); 28 const next = argv[i + 1]; 29 if (next && !next.startsWith("--")) { 30 out[key] = next; 31 i += 1; 32 } else { 33 out[key] = true; 34 } 35 } 36 return out; 37} 38 39async function readStdinIfAny() { 40 if (process.stdin.isTTY) return ""; 41 return new Promise((resolve, reject) => { 42 let data = ""; 43 process.stdin.setEncoding("utf8"); 44 process.stdin.on("data", (chunk) => { 45 data += chunk; 46 }); 47 process.stdin.on("end", () => resolve(data)); 48 process.stdin.on("error", reject); 49 }); 50} 51 52function toInt(value, fallback) { 53 const parsed = Number(value); 54 if (Number.isFinite(parsed)) return parsed; 55 return fallback; 56} 57 58function printUsage() { 59 console.log(`agent-memory CLI 60 61Commands: 62 create [--session <id>] [--title <title>] [--provider <name>] [--project <name>] 63 event [--session <id>] [--provider <name>] [--model <name>] [--role <role>] [--text <text>] [--source <source>] [--project <name>] [--ticket <id>] 64 checkpoint --session <id> [--reason <reason>] [--summary <text>] [--max-events <n>] 65 checkpoints --session <id> 66 list [--limit <n>] [--project <name>] [--json] 67 remember --from <session-id> [--checkpoint <id>] [--session <new-id>] [--title <title>] [--project <name>] [--provider <name>] 68 set-device --id <device-id> 69 doctor [--json] 70 profile [--include-key] [--exports] [--project <name>] [--provider <name>] [--ticket <id>] [--session <id>] [--device <id>] 71 flush-remote 72`); 73} 74 75async function commandCreate(args) { 76 const session = await createSession({ 77 sessionId: args.session || process.env.AGENT_SESSION_ID, 78 title: args.title, 79 provider: args.provider || process.env.AGENT_MEMORY_PROVIDER, 80 project: args.project || process.env.AGENT_MEMORY_PROJECT, 81 }); 82 console.log(JSON.stringify(session, null, 2)); 83} 84 85async function commandEvent(args) { 86 const stdinRaw = await readStdinIfAny(); 87 let stdinPayload = {}; 88 89 if (stdinRaw.trim()) { 90 try { 91 stdinPayload = JSON.parse(stdinRaw); 92 } catch { 93 stdinPayload = { text: stdinRaw.trim() }; 94 } 95 } 96 97 const payload = { 98 ...stdinPayload, 99 sessionId: 100 args.session || 101 stdinPayload.sessionId || 102 stdinPayload.session_id || 103 process.env.AGENT_SESSION_ID, 104 provider: 105 args.provider || 106 stdinPayload.provider || 107 process.env.AGENT_MEMORY_PROVIDER, 108 model: args.model || stdinPayload.model, 109 role: args.role || stdinPayload.role || "user", 110 source: args.source || stdinPayload.source || "manual", 111 project: 112 args.project || 113 stdinPayload.project || 114 process.env.AGENT_MEMORY_PROJECT, 115 text: args.text || stdinPayload.text || stdinPayload.content || stdinPayload.message, 116 content: stdinPayload.content, 117 context: stdinPayload.context, 118 tool_calls: stdinPayload.tool_calls, 119 metadata: { 120 ...(stdinPayload.metadata || {}), 121 ...(args.ticket ? { ticket: args.ticket } : {}), 122 }, 123 title: args.title || stdinPayload.title, 124 }; 125 126 const result = await commitEvent(payload); 127 console.log(JSON.stringify(result, null, 2)); 128} 129 130async function commandCheckpoint(args) { 131 const sessionId = args.session || process.env.AGENT_SESSION_ID; 132 if (!sessionId) { 133 throw new Error("--session is required (or set AGENT_SESSION_ID)"); 134 } 135 136 const stdinRaw = await readStdinIfAny(); 137 const summary = args.summary || stdinRaw.trim() || undefined; 138 139 const checkpoint = await createCheckpoint({ 140 sessionId, 141 reason: args.reason || "manual", 142 summary, 143 maxEvents: toInt(args["max-events"], 40), 144 }); 145 146 console.log(JSON.stringify(checkpoint, null, 2)); 147} 148 149async function commandCheckpoints(args) { 150 const sessionId = args.session || process.env.AGENT_SESSION_ID; 151 if (!sessionId) { 152 throw new Error("--session is required (or set AGENT_SESSION_ID)"); 153 } 154 155 const checkpoints = await listSessionCheckpoints(sessionId); 156 console.log(JSON.stringify(checkpoints, null, 2)); 157} 158 159async function commandList(args) { 160 const sessions = await listSessions({ 161 limit: toInt(args.limit, 30), 162 project: args.project || process.env.AGENT_MEMORY_PROJECT, 163 }); 164 165 if (args.json) { 166 console.log(JSON.stringify(sessions, null, 2)); 167 return; 168 } 169 170 if (sessions.length === 0) { 171 console.log("No sessions found."); 172 return; 173 } 174 175 for (const session of sessions) { 176 const remembered = session.remembered_from?.session_id 177 ? ` remembered_from=${session.remembered_from.session_id}` 178 : ""; 179 console.log( 180 `${session.session_id} project=${session.project} updated=${session.updated_at} seq=${session.last_seq} title="${session.title}"${remembered}` 181 ); 182 } 183} 184 185async function commandRemember(args) { 186 if (!args.from) { 187 throw new Error("--from is required"); 188 } 189 190 const result = await rememberSession({ 191 fromSessionId: args.from, 192 checkpointId: args.checkpoint, 193 sessionId: args.session || process.env.AGENT_SESSION_ID, 194 title: args.title, 195 project: args.project || process.env.AGENT_MEMORY_PROJECT, 196 provider: args.provider || process.env.AGENT_MEMORY_PROVIDER, 197 }); 198 199 console.log(JSON.stringify(result, null, 2)); 200} 201 202async function commandFlushRemote() { 203 const result = await flushRemote(); 204 console.log(JSON.stringify(result, null, 2)); 205} 206 207async function commandSetDevice(args) { 208 if (!args.id) { 209 throw new Error("--id is required"); 210 } 211 212 const normalized = await setDeviceId(args.id); 213 console.log(`AGENT_DEVICE_ID set to ${normalized}`); 214} 215 216async function commandDoctor(args) { 217 const info = await inspectStore(); 218 if (args.json) { 219 console.log(JSON.stringify(info, null, 2)); 220 return; 221 } 222 223 console.log(`home: ${info.home}`); 224 console.log(`device: ${info.device_id}`); 225 console.log(`key source: ${info.key_source}`); 226 console.log(`key fingerprint: ${info.key_fingerprint}`); 227 console.log(`sessions: ${info.sessions_count}`); 228 console.log(`event streams: ${info.event_streams_count}`); 229 console.log(`checkpoints: ${info.checkpoint_streams_count}`); 230 console.log(`remote enabled: ${info.remote_enabled}`); 231 if (info.remote_enabled) { 232 console.log(`remote endpoint: ${info.remote_endpoint}`); 233 } 234} 235 236function formatProfileLine(line, useExports) { 237 return useExports ? `export ${line}` : line; 238} 239 240async function commandProfile(args) { 241 const includeKey = Boolean(args["include-key"]); 242 const useExports = Boolean(args.exports); 243 244 const profile = await buildEnvProfile({ 245 includeKey, 246 deviceId: args.device, 247 project: args.project, 248 provider: args.provider, 249 ticket: args.ticket, 250 sessionId: args.session, 251 }); 252 253 console.log(`# Agent memory profile (${profile.home})`); 254 for (const line of profile.lines) { 255 console.log(formatProfileLine(line, useExports)); 256 } 257} 258 259async function main() { 260 const args = parseArgs(process.argv.slice(2)); 261 const command = args._[0] || "help"; 262 263 if (command === "help" || command === "--help" || command === "-h") { 264 printUsage(); 265 return; 266 } 267 268 const handlers = { 269 create: commandCreate, 270 event: commandEvent, 271 checkpoint: commandCheckpoint, 272 checkpoints: commandCheckpoints, 273 list: commandList, 274 remember: commandRemember, 275 "set-device": commandSetDevice, 276 doctor: commandDoctor, 277 profile: commandProfile, 278 "flush-remote": commandFlushRemote, 279 }; 280 281 const handler = handlers[command]; 282 if (!handler) { 283 printUsage(); 284 process.exitCode = 1; 285 return; 286 } 287 288 await handler(args); 289} 290 291main().catch((error) => { 292 console.error(`agent-memory: ${error.message}`); 293 process.exit(1); 294});