@mingxy/cerebro 1.8.2 → 1.9.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/dist/client.d.ts +3 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -2
- package/dist/client.js.map +1 -1
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +21 -5
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -25
- package/dist/index.js.map +1 -1
- package/dist/tools.d.ts +2 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +8 -3
- package/dist/tools.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +20 -4
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +384 -380
- package/src/hooks.ts +457 -452
- package/src/index.ts +151 -151
package/src/index.ts
CHANGED
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { OmemClient } from "./client.js";
|
|
7
|
-
import { autoRecallHook, compactingHook, keywordDetectionHook, sessionIdleHook } from "./hooks.js";
|
|
8
|
-
import { getUserTag, getProjectTag } from "./tags.js";
|
|
9
|
-
import { buildTools } from "./tools.js";
|
|
10
|
-
import { logInfo, logError } from "./logger.js";
|
|
11
|
-
import { loadPluginConfig } from "./config.js";
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
|
|
16
|
-
let pluginVersion = "unknown";
|
|
17
|
-
try {
|
|
18
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
19
|
-
if (pkg?.version && typeof pkg.version === "string") {
|
|
20
|
-
pluginVersion = pkg.version;
|
|
21
|
-
}
|
|
22
|
-
} catch {}
|
|
23
|
-
|
|
24
|
-
// Per-session auto-store toggle: sessionId → enabled (default: true = auto-store on)
|
|
25
|
-
const autoStoreSessions = new Map<string, boolean>();
|
|
26
|
-
|
|
27
|
-
function getStateFilePath(sessionId: string): string {
|
|
28
|
-
return join(tmpdir(), `cerebro_autostore_${sessionId}.json`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isAutoStoreEnabled(sessionId: string | undefined): boolean {
|
|
32
|
-
if (!sessionId) return true;
|
|
33
|
-
return autoStoreSessions.get(sessionId) ?? true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function setAutoStoreEnabled(sessionId: string, enabled: boolean): void {
|
|
37
|
-
autoStoreSessions.set(sessionId, enabled);
|
|
38
|
-
try {
|
|
39
|
-
writeFileSync(getStateFilePath(sessionId), JSON.stringify({ enabled }));
|
|
40
|
-
} catch {}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
(globalThis as any).__cerebro_autoStoreMap = autoStoreSessions;
|
|
44
|
-
|
|
45
|
-
function showToast(tui: any, title: string, message?: string, variant: string = "info", duration: number = 5000) {
|
|
46
|
-
if (!tui) return;
|
|
47
|
-
setTimeout(() => {
|
|
48
|
-
try {
|
|
49
|
-
const body: any = { variant, duration };
|
|
50
|
-
if (message) {
|
|
51
|
-
body.title = title;
|
|
52
|
-
body.message = message;
|
|
53
|
-
} else {
|
|
54
|
-
body.message = title;
|
|
55
|
-
}
|
|
56
|
-
tui.showToast({ body });
|
|
57
|
-
} catch (err) {
|
|
58
|
-
console.error("[cerebro] showToast failed:", err);
|
|
59
|
-
}
|
|
60
|
-
}, 3000);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const OmemPlugin: Plugin = async (input) => {
|
|
64
|
-
const { directory, client } = input;
|
|
65
|
-
// Proxy: dynamically resolve client.tui on each access so toast works
|
|
66
|
-
// even if client.tui isn't ready yet at plugin init time
|
|
67
|
-
const tui = new Proxy({} as any, {
|
|
68
|
-
get(_, prop) {
|
|
69
|
-
return (client as any)?.tui?.[prop];
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Load overrides from opencode.json plugin_config
|
|
74
|
-
let overrides: Record<string, unknown> = {};
|
|
75
|
-
try {
|
|
76
|
-
const ocCfg = JSON.parse(readFileSync(join(directory, "opencode.json"), "utf-8"));
|
|
77
|
-
const pc = ocCfg?.plugin_config?.["@mingxy/omem"] || ocCfg?.plugin_config?.["@ourmem/opencode"];
|
|
78
|
-
if (pc) overrides = pc;
|
|
79
|
-
} catch {}
|
|
80
|
-
|
|
81
|
-
const config = loadPluginConfig(overrides as any);
|
|
82
|
-
|
|
83
|
-
const omemClient = new OmemClient(config.apiUrl, config.apiKey, config);
|
|
84
|
-
|
|
85
|
-
// 启动时检测连接状态
|
|
86
|
-
try {
|
|
87
|
-
await omemClient.getStats();
|
|
88
|
-
showToast(tui, "🧠 Cerebro · Connected", `Version v${pluginVersion}`, "success", 6000);
|
|
89
|
-
logInfo(`Connected to ${config.apiUrl}`);
|
|
90
|
-
} catch (err) {
|
|
91
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
92
|
-
logError(`Connection failed: ${errMsg}`);
|
|
93
|
-
if (errMsg.includes("[omem]")) {
|
|
94
|
-
const cleanMsg = errMsg.replace(/^\[omem\]\s*/, "");
|
|
95
|
-
showToast(
|
|
96
|
-
tui,
|
|
97
|
-
`🧠 Cerebro v${pluginVersion} · Server Error`,
|
|
98
|
-
cleanMsg.substring(0, 150),
|
|
99
|
-
"error",
|
|
100
|
-
8000
|
|
101
|
-
);
|
|
102
|
-
} else {
|
|
103
|
-
showToast(
|
|
104
|
-
tui,
|
|
105
|
-
`🧠 Cerebro v${pluginVersion} · Connection Failed`,
|
|
106
|
-
`Unable to reach ${config.apiUrl}`,
|
|
107
|
-
"error",
|
|
108
|
-
8000
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const email = process.env.GIT_AUTHOR_EMAIL || process.env.USER || "unknown";
|
|
114
|
-
const cwd = directory || process.cwd();
|
|
115
|
-
const containerTags = [getUserTag(email), getProjectTag(cwd)];
|
|
116
|
-
const agentId = process.env.OMEM_AGENT_ID || "opencode";
|
|
117
|
-
|
|
118
|
-
let currentSessionId: string | undefined;
|
|
119
|
-
|
|
120
|
-
const recallHook = autoRecallHook(omemClient, containerTags, tui, config);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
config: async (cfg: any) => {
|
|
124
|
-
cfg.command ??= {};
|
|
125
|
-
cfg.command["memory-toggle"] = {
|
|
126
|
-
template: "Use the memory_toggle tool with state='$ARGUMENTS' to toggle Cerebro auto-store for this session. You MUST call the memory_toggle tool, do not just acknowledge.",
|
|
127
|
-
description: "Toggle Cerebro auto-store ON or OFF for current session",
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
"experimental.chat.system.transform": async (input: any, output: any) => {
|
|
131
|
-
if (input.sessionID) currentSessionId = input.sessionID;
|
|
132
|
-
return recallHook(input, output);
|
|
133
|
-
},
|
|
134
|
-
"chat.message": keywordDetectionHook(omemClient, containerTags, config.autoCaptureThreshold, tui, config.ingestMode),
|
|
135
|
-
"experimental.session.compacting": compactingHook(omemClient, containerTags, tui, config.ingestMode, isAutoStoreEnabled),
|
|
136
|
-
tool: buildTools(omemClient, containerTags, { agentId, getSessionId: () => currentSessionId }),
|
|
137
|
-
event: sessionIdleHook(omemClient, containerTags, tui, client, config.ingestMode, config.autoCaptureThreshold, () => currentSessionId, isAutoStoreEnabled),
|
|
138
|
-
"shell.env": async (_input: any, output: any) => {
|
|
139
|
-
if (directory) {
|
|
140
|
-
output.env.OMEM_PROJECT_DIR = directory;
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export { OmemPlugin };
|
|
147
|
-
|
|
148
|
-
export default {
|
|
149
|
-
id: "ourmem",
|
|
150
|
-
server: OmemPlugin,
|
|
151
|
-
};
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { OmemClient } from "./client.js";
|
|
7
|
+
import { autoRecallHook, compactingHook, keywordDetectionHook, sessionIdleHook } from "./hooks.js";
|
|
8
|
+
import { getUserTag, getProjectTag } from "./tags.js";
|
|
9
|
+
import { buildTools } from "./tools.js";
|
|
10
|
+
import { logInfo, logError } from "./logger.js";
|
|
11
|
+
import { loadPluginConfig } from "./config.js";
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
let pluginVersion = "unknown";
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
19
|
+
if (pkg?.version && typeof pkg.version === "string") {
|
|
20
|
+
pluginVersion = pkg.version;
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
|
|
24
|
+
// Per-session auto-store toggle: sessionId → enabled (default: true = auto-store on)
|
|
25
|
+
const autoStoreSessions = new Map<string, boolean>();
|
|
26
|
+
|
|
27
|
+
function getStateFilePath(sessionId: string): string {
|
|
28
|
+
return join(tmpdir(), `cerebro_autostore_${sessionId}.json`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isAutoStoreEnabled(sessionId: string | undefined): boolean {
|
|
32
|
+
if (!sessionId) return true;
|
|
33
|
+
return autoStoreSessions.get(sessionId) ?? true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setAutoStoreEnabled(sessionId: string, enabled: boolean): void {
|
|
37
|
+
autoStoreSessions.set(sessionId, enabled);
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(getStateFilePath(sessionId), JSON.stringify({ enabled }));
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
(globalThis as any).__cerebro_autoStoreMap = autoStoreSessions;
|
|
44
|
+
|
|
45
|
+
function showToast(tui: any, title: string, message?: string, variant: string = "info", duration: number = 5000) {
|
|
46
|
+
if (!tui) return;
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
try {
|
|
49
|
+
const body: any = { variant, duration };
|
|
50
|
+
if (message) {
|
|
51
|
+
body.title = title;
|
|
52
|
+
body.message = message;
|
|
53
|
+
} else {
|
|
54
|
+
body.message = title;
|
|
55
|
+
}
|
|
56
|
+
tui.showToast({ body });
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error("[cerebro] showToast failed:", err);
|
|
59
|
+
}
|
|
60
|
+
}, 3000);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const OmemPlugin: Plugin = async (input) => {
|
|
64
|
+
const { directory, client } = input;
|
|
65
|
+
// Proxy: dynamically resolve client.tui on each access so toast works
|
|
66
|
+
// even if client.tui isn't ready yet at plugin init time
|
|
67
|
+
const tui = new Proxy({} as any, {
|
|
68
|
+
get(_, prop) {
|
|
69
|
+
return (client as any)?.tui?.[prop];
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Load overrides from opencode.json plugin_config
|
|
74
|
+
let overrides: Record<string, unknown> = {};
|
|
75
|
+
try {
|
|
76
|
+
const ocCfg = JSON.parse(readFileSync(join(directory, "opencode.json"), "utf-8"));
|
|
77
|
+
const pc = ocCfg?.plugin_config?.["@mingxy/omem"] || ocCfg?.plugin_config?.["@ourmem/opencode"];
|
|
78
|
+
if (pc) overrides = pc;
|
|
79
|
+
} catch {}
|
|
80
|
+
|
|
81
|
+
const config = loadPluginConfig(overrides as any);
|
|
82
|
+
|
|
83
|
+
const omemClient = new OmemClient(config.apiUrl, config.apiKey, config);
|
|
84
|
+
|
|
85
|
+
// 启动时检测连接状态
|
|
86
|
+
try {
|
|
87
|
+
await omemClient.getStats();
|
|
88
|
+
showToast(tui, "🧠 Cerebro · Connected", `Version v${pluginVersion}`, "success", 6000);
|
|
89
|
+
logInfo(`Connected to ${config.apiUrl}`);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
92
|
+
logError(`Connection failed: ${errMsg}`);
|
|
93
|
+
if (errMsg.includes("[omem]")) {
|
|
94
|
+
const cleanMsg = errMsg.replace(/^\[omem\]\s*/, "");
|
|
95
|
+
showToast(
|
|
96
|
+
tui,
|
|
97
|
+
`🧠 Cerebro v${pluginVersion} · Server Error`,
|
|
98
|
+
cleanMsg.substring(0, 150),
|
|
99
|
+
"error",
|
|
100
|
+
8000
|
|
101
|
+
);
|
|
102
|
+
} else {
|
|
103
|
+
showToast(
|
|
104
|
+
tui,
|
|
105
|
+
`🧠 Cerebro v${pluginVersion} · Connection Failed`,
|
|
106
|
+
`Unable to reach ${config.apiUrl}`,
|
|
107
|
+
"error",
|
|
108
|
+
8000
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const email = process.env.GIT_AUTHOR_EMAIL || process.env.USER || "unknown";
|
|
114
|
+
const cwd = directory || process.cwd();
|
|
115
|
+
const containerTags = [getUserTag(email), getProjectTag(cwd)];
|
|
116
|
+
const agentId = process.env.OMEM_AGENT_ID || "opencode";
|
|
117
|
+
|
|
118
|
+
let currentSessionId: string | undefined;
|
|
119
|
+
|
|
120
|
+
const recallHook = autoRecallHook(omemClient, containerTags, tui, config);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
config: async (cfg: any) => {
|
|
124
|
+
cfg.command ??= {};
|
|
125
|
+
cfg.command["memory-toggle"] = {
|
|
126
|
+
template: "Use the memory_toggle tool with state='$ARGUMENTS' to toggle Cerebro auto-store for this session. You MUST call the memory_toggle tool, do not just acknowledge.",
|
|
127
|
+
description: "Toggle Cerebro auto-store ON or OFF for current session",
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
"experimental.chat.system.transform": async (input: any, output: any) => {
|
|
131
|
+
if (input.sessionID) currentSessionId = input.sessionID;
|
|
132
|
+
return recallHook(input, output);
|
|
133
|
+
},
|
|
134
|
+
"chat.message": keywordDetectionHook(omemClient, containerTags, config.autoCaptureThreshold, tui, config.ingestMode),
|
|
135
|
+
"experimental.session.compacting": compactingHook(omemClient, containerTags, tui, config.ingestMode, isAutoStoreEnabled, () => currentSessionId),
|
|
136
|
+
tool: buildTools(omemClient, containerTags, { agentId, getSessionId: () => currentSessionId }),
|
|
137
|
+
event: sessionIdleHook(omemClient, containerTags, tui, client, config.ingestMode, config.autoCaptureThreshold, () => currentSessionId, isAutoStoreEnabled, agentId),
|
|
138
|
+
"shell.env": async (_input: any, output: any) => {
|
|
139
|
+
if (directory) {
|
|
140
|
+
output.env.OMEM_PROJECT_DIR = directory;
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export { OmemPlugin };
|
|
147
|
+
|
|
148
|
+
export default {
|
|
149
|
+
id: "ourmem",
|
|
150
|
+
server: OmemPlugin,
|
|
151
|
+
};
|