@agentmemory/agentmemory 0.9.20 → 0.9.22
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/.env.example +2 -0
- package/README.md +166 -12
- package/dist/.env.example +2 -0
- package/dist/cli.d.mts +5 -1
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +122 -693
- package/dist/cli.mjs.map +1 -1
- package/dist/connect-BQQXpyDS.mjs +763 -0
- package/dist/connect-BQQXpyDS.mjs.map +1 -0
- package/dist/hooks/post-tool-use.mjs +1 -1
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/stop.mjs +8 -0
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/{image-refs-R3tin9MR.mjs → image-refs-CJS5B9Gq.mjs} +2 -2
- package/dist/{image-refs-R3tin9MR.mjs.map → image-refs-CJS5B9Gq.mjs.map} +1 -1
- package/dist/{image-store-DyrKZKqZ.mjs → image-store-CdE0amb1.mjs} +1 -1
- package/dist/index.mjs +881 -281
- package/dist/index.mjs.map +1 -1
- package/dist/logger-xlVlvCWX.mjs +43 -0
- package/dist/logger-xlVlvCWX.mjs.map +1 -0
- package/dist/schema-BkALl7Z_.mjs +74 -0
- package/dist/schema-BkALl7Z_.mjs.map +1 -0
- package/dist/{src-DPSaLB5-.mjs → src-gpTAJuBy.mjs} +861 -287
- package/dist/src-gpTAJuBy.mjs.map +1 -0
- package/dist/{standalone-DMLk7YxP.mjs → standalone-C4i7ktpn.mjs} +48 -12
- package/dist/standalone-C4i7ktpn.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +45 -10
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-Dz8ssuMf.mjs → tools-registry-B7Y6nJsr.mjs} +39 -11
- package/dist/tools-registry-B7Y6nJsr.mjs.map +1 -0
- package/dist/version-DvQMNbEH.mjs +6 -0
- package/dist/version-DvQMNbEH.mjs.map +1 -0
- package/dist/viewer/index.html +134 -21
- package/package.json +6 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +3 -2
- package/plugin/hooks/hooks.codex.json +6 -6
- package/plugin/hooks/hooks.json +12 -12
- package/plugin/opencode/README.md +229 -0
- package/plugin/opencode/agentmemory-capture.ts +687 -0
- package/plugin/opencode/commands/recall.md +19 -0
- package/plugin/opencode/commands/remember.md +19 -0
- package/plugin/opencode/plugin.json +12 -0
- package/plugin/scripts/diagnostics.d.mts +17 -0
- package/plugin/scripts/diagnostics.d.mts.map +1 -0
- package/plugin/scripts/diagnostics.mjs.map +1 -0
- package/plugin/scripts/post-tool-use.mjs +1 -1
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +8 -0
- package/plugin/scripts/stop.mjs.map +1 -1
- package/dist/src-DPSaLB5-.mjs.map +0 -1
- package/dist/standalone-DMLk7YxP.mjs.map +0 -1
- package/dist/tools-registry-Dz8ssuMf.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,101 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { n as resolveAdapter, r as runAdapter } from "./connect-BQQXpyDS.mjs";
|
|
3
|
+
import { i as generateId } from "./schema-BkALl7Z_.mjs";
|
|
4
|
+
import { r as setBootVerbose } from "./logger-xlVlvCWX.mjs";
|
|
5
|
+
import { t as VERSION } from "./version-DvQMNbEH.mjs";
|
|
3
6
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
4
|
-
import { closeSync, constants,
|
|
7
|
+
import { closeSync, constants, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
5
8
|
import { delimiter, dirname, join } from "node:path";
|
|
6
9
|
import { fileURLToPath } from "node:url";
|
|
7
10
|
import { homedir, platform } from "node:os";
|
|
8
11
|
import * as p from "@clack/prompts";
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
12
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
11
13
|
|
|
12
|
-
//#region \0rolldown/runtime.js
|
|
13
|
-
var __defProp = Object.defineProperty;
|
|
14
|
-
var __exportAll = (all, no_symbols) => {
|
|
15
|
-
let target = {};
|
|
16
|
-
for (var name in all) {
|
|
17
|
-
__defProp(target, name, {
|
|
18
|
-
get: all[name],
|
|
19
|
-
enumerable: true
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (!no_symbols) {
|
|
23
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
24
|
-
}
|
|
25
|
-
return target;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/state/schema.ts
|
|
30
|
-
const KV = {
|
|
31
|
-
sessions: "mem:sessions",
|
|
32
|
-
observations: (sessionId) => `mem:obs:${sessionId}`,
|
|
33
|
-
memories: "mem:memories",
|
|
34
|
-
summaries: "mem:summaries",
|
|
35
|
-
config: "mem:config",
|
|
36
|
-
metrics: "mem:metrics",
|
|
37
|
-
health: "mem:health",
|
|
38
|
-
embeddings: (obsId) => `mem:emb:${obsId}`,
|
|
39
|
-
bm25Index: "mem:index:bm25",
|
|
40
|
-
relations: "mem:relations",
|
|
41
|
-
profiles: "mem:profiles",
|
|
42
|
-
claudeBridge: "mem:claude-bridge",
|
|
43
|
-
graphNodes: "mem:graph:nodes",
|
|
44
|
-
graphEdges: "mem:graph:edges",
|
|
45
|
-
semantic: "mem:semantic",
|
|
46
|
-
procedural: "mem:procedural",
|
|
47
|
-
teamShared: (teamId) => `mem:team:${teamId}:shared`,
|
|
48
|
-
teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
|
|
49
|
-
teamProfile: (teamId) => `mem:team:${teamId}:profile`,
|
|
50
|
-
audit: "mem:audit",
|
|
51
|
-
actions: "mem:actions",
|
|
52
|
-
actionEdges: "mem:action-edges",
|
|
53
|
-
leases: "mem:leases",
|
|
54
|
-
routines: "mem:routines",
|
|
55
|
-
routineRuns: "mem:routine-runs",
|
|
56
|
-
signals: "mem:signals",
|
|
57
|
-
checkpoints: "mem:checkpoints",
|
|
58
|
-
mesh: "mem:mesh",
|
|
59
|
-
sketches: "mem:sketches",
|
|
60
|
-
facets: "mem:facets",
|
|
61
|
-
sentinels: "mem:sentinels",
|
|
62
|
-
crystals: "mem:crystals",
|
|
63
|
-
lessons: "mem:lessons",
|
|
64
|
-
insights: "mem:insights",
|
|
65
|
-
graphEdgeHistory: "mem:graph:edge-history",
|
|
66
|
-
enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
|
|
67
|
-
latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
|
|
68
|
-
retentionScores: "mem:retention",
|
|
69
|
-
accessLog: "mem:access",
|
|
70
|
-
imageRefs: "mem:image-refs",
|
|
71
|
-
imageEmbeddings: "mem:image-embeddings",
|
|
72
|
-
slots: "mem:slots",
|
|
73
|
-
globalSlots: "mem:slots:global",
|
|
74
|
-
state: "mem:state",
|
|
75
|
-
commits: "mem:commits"
|
|
76
|
-
};
|
|
77
|
-
const STREAM = {
|
|
78
|
-
name: "mem-live",
|
|
79
|
-
group: (sessionId) => sessionId,
|
|
80
|
-
viewerGroup: "viewer"
|
|
81
|
-
};
|
|
82
|
-
function generateId(prefix) {
|
|
83
|
-
return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
84
|
-
}
|
|
85
|
-
function fingerprintId(prefix, content) {
|
|
86
|
-
return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
87
|
-
}
|
|
88
|
-
function jaccardSimilarity(a, b) {
|
|
89
|
-
const setA = new Set(a.split(/\s+/).filter((t) => t.length > 2));
|
|
90
|
-
const setB = new Set(b.split(/\s+/).filter((t) => t.length > 2));
|
|
91
|
-
if (setA.size === 0 && setB.size === 0) return 1;
|
|
92
|
-
if (setA.size === 0 || setB.size === 0) return 0;
|
|
93
|
-
let intersection = 0;
|
|
94
|
-
for (const word of setA) if (setB.has(word)) intersection++;
|
|
95
|
-
return intersection / (setA.size + setB.size - intersection);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
//#endregion
|
|
99
14
|
//#region src/cli/doctor-diagnostics.ts
|
|
100
15
|
/** Common placeholder values shipped in .env.example. */
|
|
101
16
|
const PLACEHOLDER_VALUES = new Set([
|
|
@@ -323,7 +238,7 @@ function envPath(home) {
|
|
|
323
238
|
function preferencesPath(home) {
|
|
324
239
|
return join(home, ".agentmemory", "preferences.json");
|
|
325
240
|
}
|
|
326
|
-
function backupsDir
|
|
241
|
+
function backupsDir(home) {
|
|
327
242
|
return join(home, ".agentmemory", "backups");
|
|
328
243
|
}
|
|
329
244
|
function dataDir(home) {
|
|
@@ -399,9 +314,9 @@ function buildRemovePlan(ctx, options) {
|
|
|
399
314
|
plan.push({
|
|
400
315
|
id: "backups",
|
|
401
316
|
description: "Delete backups/ directory (connect manifest + backups)",
|
|
402
|
-
path: backupsDir
|
|
317
|
+
path: backupsDir(home),
|
|
403
318
|
alwaysAsk: false,
|
|
404
|
-
applicable: !options.keepData && pathExists(backupsDir
|
|
319
|
+
applicable: !options.keepData && pathExists(backupsDir(home)),
|
|
405
320
|
sizeBytes: -1
|
|
406
321
|
});
|
|
407
322
|
if (connectManifest?.installed?.length) for (const entry of connectManifest.installed) plan.push({
|
|
@@ -564,544 +479,6 @@ function isFirstRun() {
|
|
|
564
479
|
return readPrefs().firstRunAt === null;
|
|
565
480
|
}
|
|
566
481
|
|
|
567
|
-
//#endregion
|
|
568
|
-
//#region src/cli/connect/util.ts
|
|
569
|
-
const AGENTMEMORY_MCP_BLOCK = {
|
|
570
|
-
command: "npx",
|
|
571
|
-
args: ["-y", "@agentmemory/mcp"],
|
|
572
|
-
env: {
|
|
573
|
-
AGENTMEMORY_URL: "${AGENTMEMORY_URL}",
|
|
574
|
-
AGENTMEMORY_SECRET: "${AGENTMEMORY_SECRET}"
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
function backupsDir() {
|
|
578
|
-
return join(homedir(), ".agentmemory", "backups");
|
|
579
|
-
}
|
|
580
|
-
function ensureBackupsDir() {
|
|
581
|
-
const dir = backupsDir();
|
|
582
|
-
mkdirSync(dir, { recursive: true });
|
|
583
|
-
return dir;
|
|
584
|
-
}
|
|
585
|
-
function timestampSlug() {
|
|
586
|
-
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
587
|
-
}
|
|
588
|
-
function backupFile(sourcePath, agent, ext = "json") {
|
|
589
|
-
ensureBackupsDir();
|
|
590
|
-
const stamp = timestampSlug();
|
|
591
|
-
const target = join(backupsDir(), `${agent}-${stamp}.${ext}`);
|
|
592
|
-
copyFileSync(sourcePath, target);
|
|
593
|
-
return target;
|
|
594
|
-
}
|
|
595
|
-
function readJsonSafe(path) {
|
|
596
|
-
if (!existsSync(path)) return null;
|
|
597
|
-
try {
|
|
598
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
599
|
-
} catch {
|
|
600
|
-
return null;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
function writeJsonAtomic(path, value) {
|
|
604
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
605
|
-
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
606
|
-
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
607
|
-
renameSync(tmp, path);
|
|
608
|
-
}
|
|
609
|
-
function logInstalled(label, target) {
|
|
610
|
-
p.log.success(`${label} → wired into ${target}`);
|
|
611
|
-
}
|
|
612
|
-
function logAlreadyWired(label, target) {
|
|
613
|
-
p.log.info(`${label} already wired in ${target} (use --force to re-install)`);
|
|
614
|
-
}
|
|
615
|
-
function logBackup(target) {
|
|
616
|
-
p.log.info(`Backup: ${target}`);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
//#endregion
|
|
620
|
-
//#region src/cli/connect/claude-code.ts
|
|
621
|
-
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
622
|
-
const CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
623
|
-
function entryMatches$1(entry) {
|
|
624
|
-
if (!entry || typeof entry !== "object") return false;
|
|
625
|
-
const e = entry;
|
|
626
|
-
if (e["command"] !== "npx") return false;
|
|
627
|
-
return (Array.isArray(e["args"]) ? e["args"] : []).includes("@agentmemory/mcp");
|
|
628
|
-
}
|
|
629
|
-
const adapter$7 = {
|
|
630
|
-
name: "claude-code",
|
|
631
|
-
displayName: "Claude Code",
|
|
632
|
-
docs: "https://github.com/rohitg00/agentmemory#claude-code-one-block-paste-it",
|
|
633
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/claude-code.md.",
|
|
634
|
-
detect() {
|
|
635
|
-
return existsSync(CLAUDE_DIR);
|
|
636
|
-
},
|
|
637
|
-
async install(opts) {
|
|
638
|
-
const existing = readJsonSafe(CLAUDE_JSON);
|
|
639
|
-
const next = existing ? { ...existing } : {};
|
|
640
|
-
const servers = { ...next.mcpServers ?? {} };
|
|
641
|
-
const alreadyHas = entryMatches$1(servers["agentmemory"]);
|
|
642
|
-
if (alreadyHas && !opts.force) {
|
|
643
|
-
logAlreadyWired("Claude Code", CLAUDE_JSON);
|
|
644
|
-
return {
|
|
645
|
-
kind: "already-wired",
|
|
646
|
-
mutatedPath: CLAUDE_JSON
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
if (opts.dryRun) {
|
|
650
|
-
p.log.info(`[dry-run] Would ${alreadyHas ? "overwrite" : "add"} mcpServers.agentmemory in ${CLAUDE_JSON}`);
|
|
651
|
-
return {
|
|
652
|
-
kind: "installed",
|
|
653
|
-
mutatedPath: CLAUDE_JSON
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
let backupPath;
|
|
657
|
-
if (existsSync(CLAUDE_JSON)) {
|
|
658
|
-
backupPath = backupFile(CLAUDE_JSON, "claude-code");
|
|
659
|
-
logBackup(backupPath);
|
|
660
|
-
} else {
|
|
661
|
-
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
662
|
-
writeFileSync(CLAUDE_JSON, "{}\n", "utf-8");
|
|
663
|
-
}
|
|
664
|
-
servers["agentmemory"] = AGENTMEMORY_MCP_BLOCK;
|
|
665
|
-
next.mcpServers = servers;
|
|
666
|
-
writeJsonAtomic(CLAUDE_JSON, next);
|
|
667
|
-
if (!entryMatches$1(readJsonSafe(CLAUDE_JSON)?.mcpServers?.["agentmemory"])) {
|
|
668
|
-
p.log.error(`Verification failed: ${CLAUDE_JSON} did not contain mcpServers.agentmemory after write.`);
|
|
669
|
-
return {
|
|
670
|
-
kind: "skipped",
|
|
671
|
-
reason: "verification-failed"
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
logInstalled("Claude Code", CLAUDE_JSON);
|
|
675
|
-
p.log.info("Restart Claude Code (or run `/mcp` inside a session) to pick up the new server.");
|
|
676
|
-
return {
|
|
677
|
-
kind: "installed",
|
|
678
|
-
mutatedPath: CLAUDE_JSON,
|
|
679
|
-
backupPath
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
//#endregion
|
|
685
|
-
//#region src/cli/connect/codex.ts
|
|
686
|
-
const CODEX_DIR = join(homedir(), ".codex");
|
|
687
|
-
const CODEX_TOML = join(CODEX_DIR, "config.toml");
|
|
688
|
-
const TOML_BLOCK = `[mcp_servers.agentmemory]
|
|
689
|
-
command = "npx"
|
|
690
|
-
args = ["-y", "@agentmemory/mcp"]
|
|
691
|
-
|
|
692
|
-
[mcp_servers.agentmemory.env]
|
|
693
|
-
AGENTMEMORY_URL = "http://localhost:3111"
|
|
694
|
-
`;
|
|
695
|
-
const SECTION_HEADER = "[mcp_servers.agentmemory]";
|
|
696
|
-
function isWiredText(toml) {
|
|
697
|
-
return toml.includes(SECTION_HEADER);
|
|
698
|
-
}
|
|
699
|
-
function stripExistingBlock(toml) {
|
|
700
|
-
const lines = toml.split(/\r?\n/);
|
|
701
|
-
const out = [];
|
|
702
|
-
let skipping = false;
|
|
703
|
-
for (const line of lines) {
|
|
704
|
-
const trimmed = line.trim();
|
|
705
|
-
if (trimmed === SECTION_HEADER || trimmed === "[mcp_servers.agentmemory.env]") {
|
|
706
|
-
skipping = true;
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
if (skipping && trimmed.startsWith("[") && trimmed !== "[mcp_servers.agentmemory.env]") skipping = false;
|
|
710
|
-
if (!skipping) out.push(line);
|
|
711
|
-
}
|
|
712
|
-
return out.join("\n").replace(/\n{3,}$/, "\n\n").trimEnd() + "\n";
|
|
713
|
-
}
|
|
714
|
-
const adapter$6 = {
|
|
715
|
-
name: "codex",
|
|
716
|
-
displayName: "Codex CLI",
|
|
717
|
-
docs: "https://github.com/rohitg00/agentmemory#codex-cli-codex-plugin-platform",
|
|
718
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/codex.md.",
|
|
719
|
-
detect() {
|
|
720
|
-
return existsSync(CODEX_DIR);
|
|
721
|
-
},
|
|
722
|
-
async install(opts) {
|
|
723
|
-
const exists = existsSync(CODEX_TOML);
|
|
724
|
-
const current = exists ? readFileSync(CODEX_TOML, "utf-8") : "";
|
|
725
|
-
const wired = isWiredText(current);
|
|
726
|
-
if (wired && !opts.force) {
|
|
727
|
-
logAlreadyWired("Codex CLI", CODEX_TOML);
|
|
728
|
-
return {
|
|
729
|
-
kind: "already-wired",
|
|
730
|
-
mutatedPath: CODEX_TOML
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
if (opts.dryRun) {
|
|
734
|
-
p.log.info(`[dry-run] Would ${wired ? "rewrite" : "append"} [mcp_servers.agentmemory] in ${CODEX_TOML}`);
|
|
735
|
-
return {
|
|
736
|
-
kind: "installed",
|
|
737
|
-
mutatedPath: CODEX_TOML
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
let backupPath;
|
|
741
|
-
if (exists) {
|
|
742
|
-
backupPath = backupFile(CODEX_TOML, "codex", "toml");
|
|
743
|
-
logBackup(backupPath);
|
|
744
|
-
} else mkdirSync(dirname(CODEX_TOML), { recursive: true });
|
|
745
|
-
const cleaned = wired ? stripExistingBlock(current) : current;
|
|
746
|
-
writeFileSync(CODEX_TOML, `${cleaned}${cleaned.length === 0 || cleaned.endsWith("\n") ? "" : "\n"}${cleaned.length > 0 ? "\n" : ""}${TOML_BLOCK}`, "utf-8");
|
|
747
|
-
if (!isWiredText(readFileSync(CODEX_TOML, "utf-8"))) {
|
|
748
|
-
p.log.error(`Verification failed: ${CODEX_TOML} did not contain ${SECTION_HEADER} after write.`);
|
|
749
|
-
return {
|
|
750
|
-
kind: "skipped",
|
|
751
|
-
reason: "verification-failed"
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
logInstalled("Codex CLI", CODEX_TOML);
|
|
755
|
-
p.log.info("Codex picks up MCP servers on next launch. For the deeper plugin install, run: codex plugin marketplace add rohitg00/agentmemory && codex plugin install agentmemory");
|
|
756
|
-
return {
|
|
757
|
-
kind: "installed",
|
|
758
|
-
mutatedPath: CODEX_TOML,
|
|
759
|
-
...backupPath !== void 0 && { backupPath }
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
//#endregion
|
|
765
|
-
//#region src/cli/connect/json-mcp-adapter.ts
|
|
766
|
-
function entryMatches(entry) {
|
|
767
|
-
if (!entry || typeof entry !== "object") return false;
|
|
768
|
-
const e = entry;
|
|
769
|
-
if (e["command"] !== "npx") return false;
|
|
770
|
-
return (Array.isArray(e["args"]) ? e["args"] : []).includes("@agentmemory/mcp");
|
|
771
|
-
}
|
|
772
|
-
function createJsonMcpAdapter(config) {
|
|
773
|
-
return {
|
|
774
|
-
name: config.name,
|
|
775
|
-
displayName: config.displayName,
|
|
776
|
-
...config.docs !== void 0 && { docs: config.docs },
|
|
777
|
-
...config.protocolNote !== void 0 && { protocolNote: config.protocolNote },
|
|
778
|
-
detect() {
|
|
779
|
-
return existsSync(config.detectDir);
|
|
780
|
-
},
|
|
781
|
-
async install(opts) {
|
|
782
|
-
const existing = readJsonSafe(config.configPath);
|
|
783
|
-
const next = existing ? { ...existing } : {};
|
|
784
|
-
const servers = { ...next.mcpServers ?? {} };
|
|
785
|
-
const alreadyHas = entryMatches(servers["agentmemory"]);
|
|
786
|
-
if (alreadyHas && !opts.force) {
|
|
787
|
-
logAlreadyWired(config.displayName, config.configPath);
|
|
788
|
-
return {
|
|
789
|
-
kind: "already-wired",
|
|
790
|
-
mutatedPath: config.configPath
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
if (opts.dryRun) {
|
|
794
|
-
p.log.info(`[dry-run] Would ${alreadyHas ? "overwrite" : "add"} mcpServers.agentmemory in ${config.configPath}`);
|
|
795
|
-
return {
|
|
796
|
-
kind: "installed",
|
|
797
|
-
mutatedPath: config.configPath
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
let backupPath;
|
|
801
|
-
if (existsSync(config.configPath)) {
|
|
802
|
-
backupPath = backupFile(config.configPath, config.name);
|
|
803
|
-
logBackup(backupPath);
|
|
804
|
-
} else mkdirSync(dirname(config.configPath), { recursive: true });
|
|
805
|
-
servers["agentmemory"] = AGENTMEMORY_MCP_BLOCK;
|
|
806
|
-
next.mcpServers = servers;
|
|
807
|
-
writeJsonAtomic(config.configPath, next);
|
|
808
|
-
if (!entryMatches(readJsonSafe(config.configPath)?.mcpServers?.["agentmemory"])) {
|
|
809
|
-
p.log.error(`Verification failed: ${config.configPath} did not contain mcpServers.agentmemory after write.`);
|
|
810
|
-
return {
|
|
811
|
-
kind: "skipped",
|
|
812
|
-
reason: "verification-failed"
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
logInstalled(config.displayName, config.configPath);
|
|
816
|
-
return {
|
|
817
|
-
kind: "installed",
|
|
818
|
-
mutatedPath: config.configPath,
|
|
819
|
-
...backupPath !== void 0 && { backupPath }
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
//#endregion
|
|
826
|
-
//#region src/cli/connect/cursor.ts
|
|
827
|
-
const adapter$5 = createJsonMcpAdapter({
|
|
828
|
-
name: "cursor",
|
|
829
|
-
displayName: "Cursor",
|
|
830
|
-
detectDir: join(homedir(), ".cursor"),
|
|
831
|
-
configPath: join(homedir(), ".cursor", "mcp.json"),
|
|
832
|
-
docs: "https://github.com/rohitg00/agentmemory#other-agents",
|
|
833
|
-
protocolNote: "→ Using MCP (the only protocol Cursor speaks). Memory bridge runs at :3111 underneath."
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
//#endregion
|
|
837
|
-
//#region src/cli/connect/gemini-cli.ts
|
|
838
|
-
const adapter$4 = createJsonMcpAdapter({
|
|
839
|
-
name: "gemini-cli",
|
|
840
|
-
displayName: "Gemini CLI",
|
|
841
|
-
detectDir: join(homedir(), ".gemini"),
|
|
842
|
-
configPath: join(homedir(), ".gemini", "settings.json"),
|
|
843
|
-
docs: "https://github.com/rohitg00/agentmemory#other-agents",
|
|
844
|
-
protocolNote: "→ Using MCP (the only protocol Gemini CLI speaks). Memory bridge runs at :3111 underneath."
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
//#endregion
|
|
848
|
-
//#region src/cli/connect/hermes.ts
|
|
849
|
-
const HERMES_DIR = join(homedir(), ".hermes");
|
|
850
|
-
const HERMES_CONFIG = join(HERMES_DIR, "config.yaml");
|
|
851
|
-
const DOCS$2 = "https://github.com/rohitg00/agentmemory/tree/main/integrations/hermes";
|
|
852
|
-
const adapter$3 = {
|
|
853
|
-
name: "hermes",
|
|
854
|
-
displayName: "Hermes Agent",
|
|
855
|
-
docs: DOCS$2,
|
|
856
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/hermes.md.",
|
|
857
|
-
detect() {
|
|
858
|
-
return existsSync(HERMES_DIR);
|
|
859
|
-
},
|
|
860
|
-
async install(_opts) {
|
|
861
|
-
p.log.warn("Hermes uses YAML config. Automated merge isn't implemented yet — manual install required.");
|
|
862
|
-
p.note([
|
|
863
|
-
`Add to ${HERMES_CONFIG}:`,
|
|
864
|
-
"",
|
|
865
|
-
" mcp_servers:",
|
|
866
|
-
" agentmemory:",
|
|
867
|
-
" command: npx",
|
|
868
|
-
" args: [\"-y\", \"@agentmemory/mcp\"]",
|
|
869
|
-
"",
|
|
870
|
-
" memory:",
|
|
871
|
-
" provider: agentmemory",
|
|
872
|
-
"",
|
|
873
|
-
`Full guide: ${DOCS$2}`
|
|
874
|
-
].join("\n"), "Hermes manual install");
|
|
875
|
-
return {
|
|
876
|
-
kind: "stub",
|
|
877
|
-
reason: "yaml-merge-not-implemented"
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
//#endregion
|
|
883
|
-
//#region src/cli/connect/openclaw.ts
|
|
884
|
-
const adapter$2 = createJsonMcpAdapter({
|
|
885
|
-
name: "openclaw",
|
|
886
|
-
displayName: "OpenClaw",
|
|
887
|
-
detectDir: join(homedir(), ".openclaw"),
|
|
888
|
-
configPath: join(homedir(), ".openclaw", "openclaw.json"),
|
|
889
|
-
docs: "https://github.com/rohitg00/agentmemory/tree/main/integrations/openclaw",
|
|
890
|
-
protocolNote: "→ Using MCP. Hooks are also available — see docs/openclaw.md."
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
//#endregion
|
|
894
|
-
//#region src/cli/connect/openhuman.ts
|
|
895
|
-
const OPENHUMAN_DIR = join(homedir(), ".openhuman");
|
|
896
|
-
const DOCS$1 = "https://github.com/tinyhumansai/openhuman";
|
|
897
|
-
const adapter$1 = {
|
|
898
|
-
name: "openhuman",
|
|
899
|
-
displayName: "OpenHuman",
|
|
900
|
-
docs: DOCS$1,
|
|
901
|
-
protocolNote: "→ Using native hooks (REST API at :3111). MCP not required.",
|
|
902
|
-
detect() {
|
|
903
|
-
return existsSync(OPENHUMAN_DIR);
|
|
904
|
-
},
|
|
905
|
-
async install(_opts) {
|
|
906
|
-
p.log.warn("OpenHuman integration is not yet automated. No `integrations/openhuman/` folder exists in the agentmemory repo today.");
|
|
907
|
-
p.note([
|
|
908
|
-
"OpenHuman is a Memory-trait host. The expected wiring is the REST",
|
|
909
|
-
"proxy at http://localhost:3111 plus an OpenHuman-side Memory trait",
|
|
910
|
-
"impl. Once integrations/openhuman/ lands in agentmemory we'll wire",
|
|
911
|
-
"this up automatically.",
|
|
912
|
-
"",
|
|
913
|
-
`Tracking: ${DOCS$1}`
|
|
914
|
-
].join("\n"), "OpenHuman manual install");
|
|
915
|
-
return {
|
|
916
|
-
kind: "stub",
|
|
917
|
-
reason: "no-integration-folder-yet"
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
//#endregion
|
|
923
|
-
//#region src/cli/connect/pi.ts
|
|
924
|
-
const PI_DIR = join(homedir(), ".pi");
|
|
925
|
-
const PI_EXT_DIR = join(PI_DIR, "agent", "extensions", "agentmemory");
|
|
926
|
-
const DOCS = "https://github.com/rohitg00/agentmemory/tree/main/integrations/pi";
|
|
927
|
-
const adapter = {
|
|
928
|
-
name: "pi",
|
|
929
|
-
displayName: "pi",
|
|
930
|
-
docs: DOCS,
|
|
931
|
-
protocolNote: "→ Using native hooks (REST API at :3111). MCP not required.",
|
|
932
|
-
detect() {
|
|
933
|
-
return existsSync(PI_DIR);
|
|
934
|
-
},
|
|
935
|
-
async install(_opts) {
|
|
936
|
-
p.log.warn("pi uses a TypeScript extension file. Automated copy + register isn't implemented yet — manual install required.");
|
|
937
|
-
p.note([
|
|
938
|
-
"Run these from the agentmemory repo root:",
|
|
939
|
-
"",
|
|
940
|
-
` mkdir -p ${PI_EXT_DIR}`,
|
|
941
|
-
` cp integrations/pi/index.ts ${PI_EXT_DIR}/index.ts`,
|
|
942
|
-
` cp integrations/pi/security.ts ${PI_EXT_DIR}/security.ts`,
|
|
943
|
-
"",
|
|
944
|
-
"Then add to ~/.pi/agent/settings.json:",
|
|
945
|
-
" { \"extensions\": [\"~/.pi/agent/extensions/agentmemory\"] }",
|
|
946
|
-
"",
|
|
947
|
-
`Full guide: ${DOCS}`
|
|
948
|
-
].join("\n"), "pi manual install");
|
|
949
|
-
return {
|
|
950
|
-
kind: "stub",
|
|
951
|
-
reason: "ts-extension-copy-not-implemented"
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
|
|
956
|
-
//#endregion
|
|
957
|
-
//#region src/cli/connect/index.ts
|
|
958
|
-
var connect_exports = /* @__PURE__ */ __exportAll({
|
|
959
|
-
ADAPTERS: () => ADAPTERS,
|
|
960
|
-
knownAgents: () => knownAgents,
|
|
961
|
-
resolveAdapter: () => resolveAdapter,
|
|
962
|
-
runAdapter: () => runAdapter,
|
|
963
|
-
runConnect: () => runConnect
|
|
964
|
-
});
|
|
965
|
-
const ADAPTERS = [
|
|
966
|
-
adapter$7,
|
|
967
|
-
adapter$6,
|
|
968
|
-
adapter$5,
|
|
969
|
-
adapter$4,
|
|
970
|
-
adapter$2,
|
|
971
|
-
adapter$3,
|
|
972
|
-
adapter,
|
|
973
|
-
adapter$1
|
|
974
|
-
];
|
|
975
|
-
function resolveAdapter(name) {
|
|
976
|
-
const lower = name.toLowerCase();
|
|
977
|
-
return ADAPTERS.find((a) => a.name === lower) ?? null;
|
|
978
|
-
}
|
|
979
|
-
function knownAgents() {
|
|
980
|
-
return ADAPTERS.map((a) => a.name);
|
|
981
|
-
}
|
|
982
|
-
function parseFlags(args) {
|
|
983
|
-
const positional = [];
|
|
984
|
-
let dryRun = false;
|
|
985
|
-
let force = false;
|
|
986
|
-
let all = false;
|
|
987
|
-
for (const a of args) if (a === "--dry-run") dryRun = true;
|
|
988
|
-
else if (a === "--force") force = true;
|
|
989
|
-
else if (a === "--all") all = true;
|
|
990
|
-
else if (!a.startsWith("-")) positional.push(a);
|
|
991
|
-
return {
|
|
992
|
-
dryRun,
|
|
993
|
-
force,
|
|
994
|
-
all,
|
|
995
|
-
positional
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
async function runAdapter(adapter, opts) {
|
|
999
|
-
if (!adapter.detect()) {
|
|
1000
|
-
p.log.warn(`${adapter.displayName}: not detected on this machine (skipping).${adapter.docs ? ` Docs: ${adapter.docs}` : ""}`);
|
|
1001
|
-
return {
|
|
1002
|
-
kind: "skipped",
|
|
1003
|
-
reason: "not-detected"
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
p.log.step(`Wiring ${adapter.displayName}…`);
|
|
1007
|
-
if (adapter.protocolNote) p.log.message(adapter.protocolNote);
|
|
1008
|
-
try {
|
|
1009
|
-
return await adapter.install(opts);
|
|
1010
|
-
} catch (err) {
|
|
1011
|
-
p.log.error(`${adapter.displayName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1012
|
-
return {
|
|
1013
|
-
kind: "skipped",
|
|
1014
|
-
reason: "exception"
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
async function runConnect(args) {
|
|
1019
|
-
if (platform() === "win32") {
|
|
1020
|
-
p.intro("agentmemory connect");
|
|
1021
|
-
p.log.warn("Windows: automated `connect` is not supported yet. See https://github.com/rohitg00/agentmemory#other-agents for manual install steps.");
|
|
1022
|
-
p.outro("Windows: manual install required — see docs");
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
const { dryRun, force, all, positional } = parseFlags(args);
|
|
1026
|
-
const opts = {
|
|
1027
|
-
dryRun,
|
|
1028
|
-
force
|
|
1029
|
-
};
|
|
1030
|
-
p.intro("agentmemory connect");
|
|
1031
|
-
if (positional.length === 0 && !all) {
|
|
1032
|
-
const detected = ADAPTERS.filter((a) => a.detect());
|
|
1033
|
-
if (detected.length === 0) {
|
|
1034
|
-
p.log.error("No supported agents detected on this machine.");
|
|
1035
|
-
p.outro(`Supported: ${knownAgents().join(", ")}`);
|
|
1036
|
-
process.exit(1);
|
|
1037
|
-
}
|
|
1038
|
-
const picked = await p.multiselect({
|
|
1039
|
-
message: "Wire agentmemory into which agents?",
|
|
1040
|
-
options: detected.map((a) => ({
|
|
1041
|
-
value: a.name,
|
|
1042
|
-
label: a.displayName
|
|
1043
|
-
})),
|
|
1044
|
-
required: true
|
|
1045
|
-
});
|
|
1046
|
-
if (p.isCancel(picked)) {
|
|
1047
|
-
p.cancel("Cancelled.");
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
const results = [];
|
|
1051
|
-
for (const name of picked) {
|
|
1052
|
-
const adapter = resolveAdapter(name);
|
|
1053
|
-
if (!adapter) continue;
|
|
1054
|
-
results.push({
|
|
1055
|
-
name,
|
|
1056
|
-
result: await runAdapter(adapter, opts)
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
summarize(results);
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
if (all) {
|
|
1063
|
-
const detected = ADAPTERS.filter((a) => a.detect());
|
|
1064
|
-
if (detected.length === 0) {
|
|
1065
|
-
p.log.error("No supported agents detected on this machine.");
|
|
1066
|
-
process.exit(1);
|
|
1067
|
-
}
|
|
1068
|
-
const results = [];
|
|
1069
|
-
for (const adapter of detected) results.push({
|
|
1070
|
-
name: adapter.name,
|
|
1071
|
-
result: await runAdapter(adapter, opts)
|
|
1072
|
-
});
|
|
1073
|
-
summarize(results);
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
const agentName = positional[0];
|
|
1077
|
-
const adapter = resolveAdapter(agentName);
|
|
1078
|
-
if (!adapter) {
|
|
1079
|
-
p.log.error(`Unknown agent: ${agentName}`);
|
|
1080
|
-
p.outro(`Supported: ${knownAgents().join(", ")}`);
|
|
1081
|
-
process.exit(1);
|
|
1082
|
-
}
|
|
1083
|
-
const result = await runAdapter(adapter, opts);
|
|
1084
|
-
summarize([{
|
|
1085
|
-
name: agentName,
|
|
1086
|
-
result
|
|
1087
|
-
}]);
|
|
1088
|
-
if (result.kind === "skipped" && result.reason !== "not-detected") process.exit(1);
|
|
1089
|
-
}
|
|
1090
|
-
function summarize(results) {
|
|
1091
|
-
const lines = results.map(({ name, result }) => {
|
|
1092
|
-
switch (result.kind) {
|
|
1093
|
-
case "installed": return ` ✓ ${name}${result.mutatedPath ? ` → ${result.mutatedPath}` : ""}`;
|
|
1094
|
-
case "already-wired": return ` ✓ ${name} (already wired)`;
|
|
1095
|
-
case "stub": return ` ⚠ ${name} (manual install required: ${result.reason})`;
|
|
1096
|
-
case "skipped": return ` ✗ ${name} (skipped: ${result.reason})`;
|
|
1097
|
-
}
|
|
1098
|
-
});
|
|
1099
|
-
p.note(lines.join("\n"), "summary");
|
|
1100
|
-
const stubs = results.filter((r) => r.result.kind === "stub");
|
|
1101
|
-
if (stubs.length > 0) p.log.info(`${stubs.length} agent(s) require manual install — see docs links above.`);
|
|
1102
|
-
p.outro("Restart any wired agent (or open a new session) to pick up agentmemory.");
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
482
|
//#endregion
|
|
1106
483
|
//#region src/cli/onboarding.ts
|
|
1107
484
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1263,7 +640,25 @@ async function seedEnvFile(provider) {
|
|
|
1263
640
|
}
|
|
1264
641
|
return target;
|
|
1265
642
|
}
|
|
643
|
+
function shouldSkipInteractiveOnboarding() {
|
|
644
|
+
const ci = process.env["CI"];
|
|
645
|
+
return process.stdin.isTTY !== true || process.stdout.isTTY !== true || ci !== void 0 && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
|
|
646
|
+
}
|
|
647
|
+
function writeDefaultOnboardingPrefs() {
|
|
648
|
+
writePrefs({
|
|
649
|
+
lastAgent: null,
|
|
650
|
+
lastAgents: [],
|
|
651
|
+
lastProvider: null,
|
|
652
|
+
skipSplash: true,
|
|
653
|
+
firstRunAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
654
|
+
});
|
|
655
|
+
return {
|
|
656
|
+
agents: [],
|
|
657
|
+
provider: null
|
|
658
|
+
};
|
|
659
|
+
}
|
|
1266
660
|
async function runOnboarding() {
|
|
661
|
+
if (shouldSkipInteractiveOnboarding()) return writeDefaultOnboardingPrefs();
|
|
1267
662
|
p.note([
|
|
1268
663
|
"Welcome to agentmemory.",
|
|
1269
664
|
"",
|
|
@@ -1394,51 +789,6 @@ async function wireSelectedAgents(agents) {
|
|
|
1394
789
|
p.note(summary.join("\n"), "wire summary");
|
|
1395
790
|
}
|
|
1396
791
|
|
|
1397
|
-
//#endregion
|
|
1398
|
-
//#region src/logger.ts
|
|
1399
|
-
function fmt(level, msg, fields) {
|
|
1400
|
-
if (!fields || Object.keys(fields).length === 0) return `[agentmemory] ${level} ${msg}`;
|
|
1401
|
-
try {
|
|
1402
|
-
return `[agentmemory] ${level} ${msg} ${JSON.stringify(fields)}`;
|
|
1403
|
-
} catch {
|
|
1404
|
-
return `[agentmemory] ${level} ${msg}`;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
function emit(level, msg, fields) {
|
|
1408
|
-
try {
|
|
1409
|
-
process.stderr.write(fmt(level, msg, fields) + "\n");
|
|
1410
|
-
} catch {}
|
|
1411
|
-
}
|
|
1412
|
-
const logger = {
|
|
1413
|
-
info(msg, fields) {
|
|
1414
|
-
emit("info", msg, fields);
|
|
1415
|
-
},
|
|
1416
|
-
warn(msg, fields) {
|
|
1417
|
-
emit("warn", msg, fields);
|
|
1418
|
-
},
|
|
1419
|
-
error(msg, fields) {
|
|
1420
|
-
emit("error", msg, fields);
|
|
1421
|
-
}
|
|
1422
|
-
};
|
|
1423
|
-
let bootVerbose = process.env["AGENTMEMORY_VERBOSE"] === "1" || process.env["AGENTMEMORY_VERBOSE"] === "true";
|
|
1424
|
-
const bootBuffer = [];
|
|
1425
|
-
function setBootVerbose(enabled) {
|
|
1426
|
-
bootVerbose = enabled;
|
|
1427
|
-
}
|
|
1428
|
-
function bootLog(msg) {
|
|
1429
|
-
if (bootVerbose) {
|
|
1430
|
-
try {
|
|
1431
|
-
process.stderr.write(`[agentmemory] ${msg}\n`);
|
|
1432
|
-
} catch {}
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
1435
|
-
if (bootBuffer.length < 500) bootBuffer.push(msg);
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
//#endregion
|
|
1439
|
-
//#region src/version.ts
|
|
1440
|
-
const VERSION = "0.9.20";
|
|
1441
|
-
|
|
1442
792
|
//#endregion
|
|
1443
793
|
//#region src/cli.ts
|
|
1444
794
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1503,7 +853,7 @@ Options:
|
|
|
1503
853
|
--help, -h Show this help
|
|
1504
854
|
--verbose, -v Show engine stderr, boot log, and diagnostic info
|
|
1505
855
|
--reset Wipe ~/.agentmemory/preferences.json and re-run onboarding
|
|
1506
|
-
--tools all|core Tool visibility (default: core =
|
|
856
|
+
--tools all|core Tool visibility (default: all = 51 tools; core = 8 essentials)
|
|
1507
857
|
--no-engine Skip auto-starting iii-engine
|
|
1508
858
|
--port <N> Override REST port (default: 3111)
|
|
1509
859
|
|
|
@@ -1543,9 +893,26 @@ function getBaseUrl() {
|
|
|
1543
893
|
if (url) return url.replace(/\/+$/, "");
|
|
1544
894
|
return `http://localhost:${getRestPort()}`;
|
|
1545
895
|
}
|
|
896
|
+
let discoveredViewerPort = null;
|
|
897
|
+
async function discoverViewerPort() {
|
|
898
|
+
if (discoveredViewerPort !== null) return;
|
|
899
|
+
try {
|
|
900
|
+
const res = await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(1e3) });
|
|
901
|
+
if (res.ok) {
|
|
902
|
+
const data = await res.json();
|
|
903
|
+
if (typeof data.viewerPort === "number") discoveredViewerPort = data.viewerPort;
|
|
904
|
+
}
|
|
905
|
+
} catch {}
|
|
906
|
+
}
|
|
1546
907
|
function getViewerUrl() {
|
|
1547
908
|
const envUrl = process.env["AGENTMEMORY_VIEWER_URL"];
|
|
1548
909
|
if (envUrl) return envUrl.replace(/\/+$/, "");
|
|
910
|
+
if (discoveredViewerPort !== null) try {
|
|
911
|
+
const u = new URL(getBaseUrl());
|
|
912
|
+
return `${u.protocol}//${u.hostname}:${discoveredViewerPort}`;
|
|
913
|
+
} catch {
|
|
914
|
+
return `http://localhost:${discoveredViewerPort}`;
|
|
915
|
+
}
|
|
1549
916
|
try {
|
|
1550
917
|
const u = new URL(getBaseUrl());
|
|
1551
918
|
const vPort = parseInt(process.env["III_VIEWER_PORT"] || "", 10) || (parseInt(u.port || "3111", 10) || 3111) + 2;
|
|
@@ -1577,7 +944,19 @@ async function isEngineRunning() {
|
|
|
1577
944
|
}
|
|
1578
945
|
async function isAgentmemoryReady() {
|
|
1579
946
|
try {
|
|
1580
|
-
|
|
947
|
+
const res = await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(2e3) });
|
|
948
|
+
if (!res.ok) return false;
|
|
949
|
+
try {
|
|
950
|
+
const data = await res.json();
|
|
951
|
+
if (typeof data.viewerPort === "number") {
|
|
952
|
+
discoveredViewerPort = data.viewerPort;
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
if (data.viewerSkipped) return true;
|
|
956
|
+
return false;
|
|
957
|
+
} catch {
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
1581
960
|
} catch {
|
|
1582
961
|
return false;
|
|
1583
962
|
}
|
|
@@ -1671,6 +1050,23 @@ function clearEnginePidfile() {
|
|
|
1671
1050
|
unlinkSync(enginePidfilePath());
|
|
1672
1051
|
} catch {}
|
|
1673
1052
|
}
|
|
1053
|
+
function workerPidfilePath() {
|
|
1054
|
+
return join(homedir(), ".agentmemory", "worker.pid");
|
|
1055
|
+
}
|
|
1056
|
+
function readWorkerPidfile() {
|
|
1057
|
+
try {
|
|
1058
|
+
const pidStr = readFileSync(workerPidfilePath(), "utf-8").trim();
|
|
1059
|
+
const pid = parseInt(pidStr, 10);
|
|
1060
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
1061
|
+
} catch {
|
|
1062
|
+
return null;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function clearWorkerPidfile() {
|
|
1066
|
+
try {
|
|
1067
|
+
unlinkSync(workerPidfilePath());
|
|
1068
|
+
} catch {}
|
|
1069
|
+
}
|
|
1674
1070
|
function writeEngineState(state) {
|
|
1675
1071
|
try {
|
|
1676
1072
|
const statePath = engineStatePath();
|
|
@@ -1752,7 +1148,7 @@ function detectIiiConsole() {
|
|
|
1752
1148
|
};
|
|
1753
1149
|
return { kind: "missing" };
|
|
1754
1150
|
}
|
|
1755
|
-
const III_CONSOLE_INSTALL_CMD = "curl -fsSL https://install.iii.dev/console/main/install.sh |
|
|
1151
|
+
const III_CONSOLE_INSTALL_CMD = "curl -fsSL https://install.iii.dev/console/main/install.sh | sh";
|
|
1756
1152
|
async function ensureIiiConsole() {
|
|
1757
1153
|
const state = detectIiiConsole();
|
|
1758
1154
|
if (state.kind === "installed") return state;
|
|
@@ -2092,7 +1488,7 @@ async function main() {
|
|
|
2092
1488
|
if (firstRun || IS_RESET) await runOnboarding();
|
|
2093
1489
|
if (skipEngine) {
|
|
2094
1490
|
if (IS_VERBOSE) p.log.info("Skipping engine check (--no-engine)");
|
|
2095
|
-
await import("./src-
|
|
1491
|
+
await import("./src-gpTAJuBy.mjs");
|
|
2096
1492
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2097
1493
|
const consoleState = await ensureIiiConsole();
|
|
2098
1494
|
await maybeOfferGlobalInstall();
|
|
@@ -2104,7 +1500,7 @@ async function main() {
|
|
|
2104
1500
|
if (IS_VERBOSE) p.log.success("iii-engine is running");
|
|
2105
1501
|
warnIfEngineVersionMismatch(whichBinary("iii") ?? fallbackIiiPaths().find((p) => existsSync(p)) ?? null);
|
|
2106
1502
|
adoptRunningEngine();
|
|
2107
|
-
await import("./src-
|
|
1503
|
+
await import("./src-gpTAJuBy.mjs");
|
|
2108
1504
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2109
1505
|
const consoleState = await ensureIiiConsole();
|
|
2110
1506
|
await maybeOfferGlobalInstall();
|
|
@@ -2153,7 +1549,7 @@ async function main() {
|
|
|
2153
1549
|
process.exit(1);
|
|
2154
1550
|
}
|
|
2155
1551
|
s.stop("iii-engine is ready");
|
|
2156
|
-
await import("./src-
|
|
1552
|
+
await import("./src-gpTAJuBy.mjs");
|
|
2157
1553
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
2158
1554
|
const consoleState = await ensureIiiConsole();
|
|
2159
1555
|
await maybeOfferGlobalInstall();
|
|
@@ -2191,6 +1587,7 @@ async function runStatus() {
|
|
|
2191
1587
|
apiFetch(base, "export"),
|
|
2192
1588
|
apiFetch(base, "config/flags")
|
|
2193
1589
|
]);
|
|
1590
|
+
if (typeof healthRes?.viewerPort === "number") discoveredViewerPort = healthRes.viewerPort;
|
|
2194
1591
|
const h = healthRes?.health;
|
|
2195
1592
|
const status = healthRes?.status || "unknown";
|
|
2196
1593
|
const version = healthRes?.version || "?";
|
|
@@ -2310,6 +1707,7 @@ function buildDoctorEffects() {
|
|
|
2310
1707
|
iiiBinaryVersion: (binPath) => iiiBinVersion(binPath),
|
|
2311
1708
|
viewerReachable: async (timeoutMs = 2e3) => {
|
|
2312
1709
|
try {
|
|
1710
|
+
await discoverViewerPort();
|
|
2313
1711
|
return (await fetch(getViewerUrl(), { signal: AbortSignal.timeout(timeoutMs) })).ok;
|
|
2314
1712
|
} catch {
|
|
2315
1713
|
return false;
|
|
@@ -2859,14 +2257,14 @@ async function runUpgrade() {
|
|
|
2859
2257
|
};
|
|
2860
2258
|
if (hasPackageJson) if (!!pnpmBin && hasPnpmLock && pnpmBin) {
|
|
2861
2259
|
requireSuccess(runCommand(pnpmBin, ["install"], { label: "Refreshing dependencies (pnpm install)" }), "pnpm install");
|
|
2862
|
-
runCommand(pnpmBin, ["up", "iii-sdk@
|
|
2863
|
-
label: "
|
|
2260
|
+
runCommand(pnpmBin, ["up", "iii-sdk@0.11.2"], {
|
|
2261
|
+
label: "Pinning iii-sdk@0.11.2",
|
|
2864
2262
|
optional: true
|
|
2865
2263
|
});
|
|
2866
2264
|
} else if (npmBin) {
|
|
2867
2265
|
requireSuccess(runCommand(npmBin, ["install"], { label: "Refreshing dependencies (npm install)" }), "npm install");
|
|
2868
|
-
runCommand(npmBin, ["install", "iii-sdk@
|
|
2869
|
-
label: "
|
|
2266
|
+
runCommand(npmBin, ["install", "iii-sdk@0.11.2"], {
|
|
2267
|
+
label: "Pinning iii-sdk@0.11.2",
|
|
2870
2268
|
optional: true
|
|
2871
2269
|
});
|
|
2872
2270
|
} else p.log.warn("No package manager found (pnpm/npm). Skipping JS dependency upgrade.");
|
|
@@ -2974,6 +2372,7 @@ async function stopDockerEngine(composeFile, port) {
|
|
|
2974
2372
|
], { label: `docker compose -f ${composeFile} down` });
|
|
2975
2373
|
clearEnginePidfile();
|
|
2976
2374
|
clearEngineState();
|
|
2375
|
+
clearWorkerPidfile();
|
|
2977
2376
|
if (!ok) {
|
|
2978
2377
|
p.log.error(`docker compose down failed. The engine may still be running on :${port}. Inspect with:\n docker compose -f ${composeFile} ps`);
|
|
2979
2378
|
process.exit(1);
|
|
@@ -2991,6 +2390,7 @@ async function runStop() {
|
|
|
2991
2390
|
p.log.info(`No engine responding on port ${port}.`);
|
|
2992
2391
|
clearEnginePidfile();
|
|
2993
2392
|
clearEngineState();
|
|
2393
|
+
clearWorkerPidfile();
|
|
2994
2394
|
p.outro("Nothing to stop.");
|
|
2995
2395
|
return;
|
|
2996
2396
|
}
|
|
@@ -2999,17 +2399,35 @@ async function runStop() {
|
|
|
2999
2399
|
}
|
|
3000
2400
|
const portPids = findEnginePidsByPort(port);
|
|
3001
2401
|
const pidfilePid = readEnginePidfile();
|
|
2402
|
+
const workerPid = readWorkerPidfile();
|
|
3002
2403
|
if (!running) {
|
|
3003
|
-
if (portPids.length === 0 && pidfilePid === null) {
|
|
2404
|
+
if (portPids.length === 0 && pidfilePid === null && workerPid === null) {
|
|
3004
2405
|
clearEnginePidfile();
|
|
3005
2406
|
clearEngineState();
|
|
2407
|
+
clearWorkerPidfile();
|
|
3006
2408
|
p.outro("Nothing to stop.");
|
|
3007
2409
|
return;
|
|
3008
2410
|
}
|
|
2411
|
+
if (workerPid !== null && portPids.length === 0 && pidfilePid === null) {
|
|
2412
|
+
const s = p.spinner();
|
|
2413
|
+
s.start(`Stopping orphaned agentmemory worker (pid ${workerPid})...`);
|
|
2414
|
+
const ok = await signalAndWait(workerPid, "SIGTERM", 3e3);
|
|
2415
|
+
s.stop(ok ? `Stopped worker pid ${workerPid}` : `Failed to stop worker pid ${workerPid}`);
|
|
2416
|
+
clearEnginePidfile();
|
|
2417
|
+
clearEngineState();
|
|
2418
|
+
clearWorkerPidfile();
|
|
2419
|
+
if (!ok) {
|
|
2420
|
+
p.log.error(`Worker pid ${workerPid} survived SIGKILL. Investigate with \`ps\`.`);
|
|
2421
|
+
process.exit(1);
|
|
2422
|
+
}
|
|
2423
|
+
p.outro("Stopped orphaned worker. Memories persisted to disk.");
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
3009
2426
|
const survivors = new Set(portPids);
|
|
3010
2427
|
if (pidfilePid) survivors.add(pidfilePid);
|
|
2428
|
+
if (workerPid) survivors.add(workerPid);
|
|
3011
2429
|
p.log.warn(`Engine not responding on :${port}, but ${survivors.size} process(es) still hold the port or pidfile: ${[...survivors].join(", ")}`);
|
|
3012
|
-
p.log.info(`Preserving ~/.agentmemory/iii.pid. Investigate before manual cleanup:\n ps -p ${[...survivors].join(",")} -o pid,ppid,comm,etime\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port}`);
|
|
2430
|
+
p.log.info(`Preserving ~/.agentmemory/iii.pid + worker.pid. Investigate before manual cleanup:\n ps -p ${[...survivors].join(",")} -o pid,ppid,comm,etime\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port}`);
|
|
3013
2431
|
process.exit(1);
|
|
3014
2432
|
}
|
|
3015
2433
|
if (!state) {
|
|
@@ -3023,7 +2441,9 @@ async function runStop() {
|
|
|
3023
2441
|
const candidates = /* @__PURE__ */ new Set();
|
|
3024
2442
|
if (pidfilePid) candidates.add(pidfilePid);
|
|
3025
2443
|
for (const pid of portPids) candidates.add(pid);
|
|
3026
|
-
|
|
2444
|
+
const workerCandidates = /* @__PURE__ */ new Set();
|
|
2445
|
+
if (workerPid) workerCandidates.add(workerPid);
|
|
2446
|
+
if (candidates.size === 0 && workerCandidates.size === 0) {
|
|
3027
2447
|
p.log.error(`Could not locate engine process. Try:\n ${IS_WINDOWS ? "netstat -ano | findstr :" + port : "lsof -i :" + port + " -t | xargs kill -9"}`);
|
|
3028
2448
|
process.exit(1);
|
|
3029
2449
|
}
|
|
@@ -3035,19 +2455,28 @@ async function runStop() {
|
|
|
3035
2455
|
s.stop(ok ? `Stopped pid ${pid}` : `Failed to stop pid ${pid}`);
|
|
3036
2456
|
if (!ok) allStopped = false;
|
|
3037
2457
|
}
|
|
2458
|
+
for (const pid of workerCandidates) {
|
|
2459
|
+
if (candidates.has(pid)) continue;
|
|
2460
|
+
const s = p.spinner();
|
|
2461
|
+
s.start(`Stopping agentmemory worker (pid ${pid})...`);
|
|
2462
|
+
const ok = await signalAndWait(pid, "SIGTERM", 3e3);
|
|
2463
|
+
s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
|
|
2464
|
+
if (!ok) allStopped = false;
|
|
2465
|
+
}
|
|
3038
2466
|
clearEnginePidfile();
|
|
3039
2467
|
clearEngineState();
|
|
2468
|
+
clearWorkerPidfile();
|
|
3040
2469
|
if (!allStopped) {
|
|
3041
|
-
p.log.error("One or more
|
|
2470
|
+
p.log.error("One or more processes survived SIGKILL. Investigate with `ps`.");
|
|
3042
2471
|
process.exit(1);
|
|
3043
2472
|
}
|
|
3044
2473
|
p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
|
|
3045
2474
|
}
|
|
3046
2475
|
async function runMcp() {
|
|
3047
|
-
await import("./standalone-
|
|
2476
|
+
await import("./standalone-C4i7ktpn.mjs");
|
|
3048
2477
|
}
|
|
3049
2478
|
async function runConnectCmd() {
|
|
3050
|
-
const { runConnect } = await
|
|
2479
|
+
const { runConnect } = await import("./connect-BQQXpyDS.mjs").then((n) => n.t);
|
|
3051
2480
|
await runConnect(args.slice(1));
|
|
3052
2481
|
}
|
|
3053
2482
|
async function runImportJsonl() {
|
|
@@ -3280,5 +2709,5 @@ async function runRemove() {
|
|
|
3280
2709
|
});
|
|
3281
2710
|
|
|
3282
2711
|
//#endregion
|
|
3283
|
-
export {
|
|
2712
|
+
export { discoverViewerPort };
|
|
3284
2713
|
//# sourceMappingURL=cli.mjs.map
|