@gamaze/hicortex 0.3.6 → 0.3.8
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/init.js +78 -8
- package/dist/mcp-server.js +55 -14
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -106,6 +106,11 @@ async function detect() {
|
|
|
106
106
|
// ---------------------------------------------------------------------------
|
|
107
107
|
function registerCcMcp(serverUrl) {
|
|
108
108
|
try {
|
|
109
|
+
// Remove existing entry first (idempotent — ignore if not found)
|
|
110
|
+
try {
|
|
111
|
+
(0, node_child_process_1.execSync)("claude mcp remove hicortex 2>/dev/null", { encoding: "utf-8", stdio: "pipe" });
|
|
112
|
+
}
|
|
113
|
+
catch { /* not found */ }
|
|
109
114
|
// Use claude CLI to register — it knows the correct config format and location
|
|
110
115
|
(0, node_child_process_1.execSync)(`claude mcp add hicortex --transport sse ${serverUrl}/sse`, { encoding: "utf-8", stdio: "pipe" });
|
|
111
116
|
console.log(` ✓ Registered MCP server via claude CLI`);
|
|
@@ -223,21 +228,84 @@ Tell them: "Get a license key at https://hicortex.gamaze.com/ — after purchase
|
|
|
223
228
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(CC_COMMANDS_DIR, "hicortex-activate.md"), activateContent);
|
|
224
229
|
console.log(` ✓ Installed /learn and /hicortex-activate commands in ${CC_COMMANDS_DIR}`);
|
|
225
230
|
}
|
|
226
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Detect LLM API key from current shell environment and persist to
|
|
233
|
+
* ~/.hicortex/config.json so the daemon can use it (launchd/systemd
|
|
234
|
+
* don't inherit shell env vars).
|
|
235
|
+
*/
|
|
236
|
+
function persistLlmConfig() {
|
|
237
|
+
const configPath = (0, node_path_1.join)(HICORTEX_HOME, "config.json");
|
|
238
|
+
// Read existing config (may have licenseKey)
|
|
239
|
+
let config = {};
|
|
227
240
|
try {
|
|
228
|
-
|
|
229
|
-
const pkg = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, "utf-8"));
|
|
230
|
-
return pkg.version ?? "latest";
|
|
241
|
+
config = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
|
|
231
242
|
}
|
|
232
|
-
catch {
|
|
233
|
-
|
|
243
|
+
catch { /* new file */ }
|
|
244
|
+
// Don't overwrite if LLM config already persisted
|
|
245
|
+
if (config.llmApiKey && config.llmBaseUrl) {
|
|
246
|
+
console.log(` ✓ LLM config already in ${configPath}`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Detect from environment (same priority as resolveLlmConfigForCC)
|
|
250
|
+
const hcKey = process.env.HICORTEX_LLM_API_KEY;
|
|
251
|
+
const hcUrl = process.env.HICORTEX_LLM_BASE_URL;
|
|
252
|
+
const hcModel = process.env.HICORTEX_LLM_MODEL;
|
|
253
|
+
if (hcKey && hcUrl) {
|
|
254
|
+
config.llmApiKey = hcKey;
|
|
255
|
+
config.llmBaseUrl = hcUrl;
|
|
256
|
+
if (hcModel)
|
|
257
|
+
config.llmModel = hcModel;
|
|
258
|
+
}
|
|
259
|
+
else if (process.env.ANTHROPIC_API_KEY) {
|
|
260
|
+
config.llmApiKey = process.env.ANTHROPIC_API_KEY;
|
|
261
|
+
config.llmBaseUrl = process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com";
|
|
262
|
+
config.llmProvider = "anthropic";
|
|
263
|
+
}
|
|
264
|
+
else if (process.env.OPENAI_API_KEY) {
|
|
265
|
+
config.llmApiKey = process.env.OPENAI_API_KEY;
|
|
266
|
+
config.llmBaseUrl = process.env.OPENAI_BASE_URL ?? "https://api.openai.com";
|
|
267
|
+
config.llmProvider = "openai";
|
|
268
|
+
}
|
|
269
|
+
else if (process.env.GOOGLE_API_KEY) {
|
|
270
|
+
config.llmApiKey = process.env.GOOGLE_API_KEY;
|
|
271
|
+
config.llmBaseUrl = "https://generativelanguage.googleapis.com/v1beta";
|
|
272
|
+
config.llmProvider = "google";
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
console.log(" ⚠ No LLM API key found in environment. Server will use Ollama fallback.");
|
|
276
|
+
console.log(" Set ANTHROPIC_API_KEY and re-run init, or edit ~/.hicortex/config.json");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
(0, node_fs_1.mkdirSync)(HICORTEX_HOME, { recursive: true });
|
|
280
|
+
(0, node_fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
281
|
+
console.log(` ✓ LLM config saved to ${configPath}`);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Determine the npm package specifier for the daemon.
|
|
285
|
+
* Uses tag-based resolution so restarts pick up new versions automatically.
|
|
286
|
+
*
|
|
287
|
+
* Checks if the current version matches the npm `latest` tag.
|
|
288
|
+
* If not (e.g. running from @next), uses @gamaze/hicortex@next.
|
|
289
|
+
* If it does match latest, uses bare @gamaze/hicortex.
|
|
290
|
+
*/
|
|
291
|
+
function getPackageSpec() {
|
|
292
|
+
try {
|
|
293
|
+
const currentVersion = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8")).version;
|
|
294
|
+
const latestVersion = (0, node_child_process_1.execSync)("npm view @gamaze/hicortex version 2>/dev/null", {
|
|
295
|
+
encoding: "utf-8",
|
|
296
|
+
timeout: 5000,
|
|
297
|
+
}).trim();
|
|
298
|
+
if (currentVersion !== latestVersion) {
|
|
299
|
+
return "@gamaze/hicortex@next";
|
|
300
|
+
}
|
|
234
301
|
}
|
|
302
|
+
catch { /* can't check — default to bare */ }
|
|
303
|
+
return "@gamaze/hicortex";
|
|
235
304
|
}
|
|
236
305
|
function installDaemon() {
|
|
237
306
|
const os = (0, node_os_1.platform)();
|
|
238
307
|
const npxPath = findNpxPath();
|
|
239
|
-
const
|
|
240
|
-
const packageSpec = `@gamaze/hicortex@${version}`;
|
|
308
|
+
const packageSpec = getPackageSpec();
|
|
241
309
|
if (os === "darwin") {
|
|
242
310
|
return installLaunchd(npxPath, packageSpec);
|
|
243
311
|
}
|
|
@@ -412,6 +480,8 @@ async function runInit() {
|
|
|
412
480
|
}
|
|
413
481
|
console.log();
|
|
414
482
|
// Phase 3: Execute
|
|
483
|
+
// Persist LLM config from current environment for the daemon
|
|
484
|
+
persistLlmConfig();
|
|
415
485
|
// Install daemon if needed
|
|
416
486
|
if (!d.localServer && !d.remoteServer) {
|
|
417
487
|
installDaemon();
|
package/dist/mcp-server.js
CHANGED
|
@@ -179,13 +179,18 @@ async function startServer(options = {}) {
|
|
|
179
179
|
console.log(`[hicortex] Initializing database at ${dbPath}`);
|
|
180
180
|
db = (0, db_js_1.initDb)(dbPath);
|
|
181
181
|
stateDir = dbPath.replace(/\/hicortex\.db$/, "");
|
|
182
|
-
// LLM config
|
|
183
|
-
const
|
|
182
|
+
// LLM config: read from config.json first (persisted by init), then env vars
|
|
183
|
+
const savedConfig = readConfigFile(stateDir);
|
|
184
|
+
const llmConfig = (0, llm_js_1.resolveLlmConfigForCC)({
|
|
185
|
+
llmBaseUrl: savedConfig?.llmBaseUrl,
|
|
186
|
+
llmApiKey: savedConfig?.llmApiKey,
|
|
187
|
+
llmModel: savedConfig?.llmModel,
|
|
188
|
+
});
|
|
184
189
|
llm = new llm_js_1.LlmClient(llmConfig);
|
|
185
190
|
console.log(`[hicortex] LLM: ${llmConfig.provider}/${llmConfig.model} (reflect: ${llmConfig.reflectModel})`);
|
|
186
191
|
// License: read from options, config file, or env var
|
|
187
192
|
const licenseKey = options.licenseKey
|
|
188
|
-
??
|
|
193
|
+
?? savedConfig?.licenseKey
|
|
189
194
|
?? process.env.HICORTEX_LICENSE_KEY;
|
|
190
195
|
(0, license_js_1.validateLicense)(licenseKey, stateDir).catch((err) => console.log(`[hicortex] License validation failed: ${err}`));
|
|
191
196
|
if (licenseKey) {
|
|
@@ -196,16 +201,16 @@ async function startServer(options = {}) {
|
|
|
196
201
|
cancelConsolidation = (0, consolidate_js_1.scheduleConsolidation)(db, llm, embedder_js_1.embed, consolidateHour);
|
|
197
202
|
// Seed lesson on first run
|
|
198
203
|
await (0, seed_lesson_js_1.injectSeedLesson)(db);
|
|
204
|
+
// Self-heal: fix pinned version in daemon config
|
|
205
|
+
fixDaemonVersionPin();
|
|
199
206
|
// Stats
|
|
200
207
|
const stats = (0, db_js_1.getStats)(db, dbPath);
|
|
201
208
|
console.log(`[hicortex] Ready: ${stats.memories} memories, ${stats.links} links, ` +
|
|
202
209
|
`${Math.round(stats.db_size_bytes / 1024)} KB`);
|
|
203
|
-
// Create MCP server
|
|
204
|
-
const mcpServer = createMcpServer();
|
|
205
210
|
// Express app
|
|
206
211
|
const app = (0, express_1.default)();
|
|
207
212
|
app.use(express_1.default.json());
|
|
208
|
-
// SSE transport management
|
|
213
|
+
// SSE transport management — each connection gets its own McpServer instance
|
|
209
214
|
const transports = new Map();
|
|
210
215
|
// Health endpoint
|
|
211
216
|
app.get("/health", (_req, res) => {
|
|
@@ -219,9 +224,10 @@ async function startServer(options = {}) {
|
|
|
219
224
|
llm: `${llmConfig.provider}/${llmConfig.model}`,
|
|
220
225
|
});
|
|
221
226
|
});
|
|
222
|
-
// SSE endpoint —
|
|
227
|
+
// SSE endpoint — each connection gets its own McpServer + transport
|
|
223
228
|
app.get("/sse", async (req, res) => {
|
|
224
229
|
const transport = new sse_js_1.SSEServerTransport("/messages", res);
|
|
230
|
+
const mcpServer = createMcpServer();
|
|
225
231
|
transports.set(transport.sessionId, transport);
|
|
226
232
|
transport.onclose = () => {
|
|
227
233
|
transports.delete(transport.sessionId);
|
|
@@ -282,20 +288,55 @@ async function startServer(options = {}) {
|
|
|
282
288
|
// Helpers
|
|
283
289
|
// ---------------------------------------------------------------------------
|
|
284
290
|
/**
|
|
285
|
-
* Read
|
|
286
|
-
* Written by /hicortex-activate CC command.
|
|
291
|
+
* Read ~/.hicortex/config.json (persisted by init with LLM and license config).
|
|
287
292
|
*/
|
|
288
|
-
function
|
|
293
|
+
function readConfigFile(stateDir) {
|
|
289
294
|
try {
|
|
290
295
|
const { readFileSync } = require("node:fs");
|
|
291
296
|
const { join } = require("node:path");
|
|
292
297
|
const configPath = join(stateDir, "config.json");
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
298
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Self-heal: if the daemon plist/systemd unit has a pinned version
|
|
306
|
+
* (e.g. @gamaze/hicortex@0.3.4), rewrite it to use the bare package
|
|
307
|
+
* name so future restarts pick up the latest version automatically.
|
|
308
|
+
*/
|
|
309
|
+
function fixDaemonVersionPin() {
|
|
310
|
+
try {
|
|
311
|
+
const os = require("node:os");
|
|
312
|
+
const fs = require("node:fs");
|
|
313
|
+
const path = require("node:path");
|
|
314
|
+
if (os.platform() === "darwin") {
|
|
315
|
+
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.gamaze.hicortex.plist");
|
|
316
|
+
if (!fs.existsSync(plistPath))
|
|
317
|
+
return;
|
|
318
|
+
const content = fs.readFileSync(plistPath, "utf-8");
|
|
319
|
+
// Match @gamaze/hicortex@X.Y.Z (pinned to specific version)
|
|
320
|
+
if (/@gamaze\/hicortex@\d+\.\d+\.\d+/.test(content)) {
|
|
321
|
+
const fixed = content.replace(/@gamaze\/hicortex@\d+\.\d+\.\d+/, "@gamaze/hicortex");
|
|
322
|
+
fs.writeFileSync(plistPath, fixed);
|
|
323
|
+
console.log("[hicortex] Fixed daemon config: removed pinned version (will use latest on next restart)");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (os.platform() === "linux") {
|
|
327
|
+
const servicePath = path.join(os.homedir(), ".config", "systemd", "user", "hicortex.service");
|
|
328
|
+
if (!fs.existsSync(servicePath))
|
|
329
|
+
return;
|
|
330
|
+
const content = fs.readFileSync(servicePath, "utf-8");
|
|
331
|
+
if (/@gamaze\/hicortex@\d+\.\d+\.\d+/.test(content)) {
|
|
332
|
+
const fixed = content.replace(/@gamaze\/hicortex@\d+\.\d+\.\d+/, "@gamaze/hicortex");
|
|
333
|
+
fs.writeFileSync(servicePath, fixed);
|
|
334
|
+
console.log("[hicortex] Fixed daemon config: removed pinned version");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
296
337
|
}
|
|
297
338
|
catch {
|
|
298
|
-
|
|
339
|
+
// Non-fatal
|
|
299
340
|
}
|
|
300
341
|
}
|
|
301
342
|
function formatResults(results) {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "hicortex",
|
|
3
3
|
"name": "Hicortex — Long-term Memory That Learns",
|
|
4
4
|
"description": "Your agents remember past decisions, avoid repeated mistakes, and get smarter every day. Nightly reflection generates actionable lessons that automatically update agent behavior.",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.8",
|
|
6
6
|
"kind": "lifecycle",
|
|
7
7
|
"skills": ["./skills/hicortex-memory", "./skills/hicortex-learn", "./skills/hicortex-activate"],
|
|
8
8
|
"configSchema": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Human-like memory for self-improving AI agents. Automatic capturing, nightly reflection, and cross-agent learning. Works with Claude Code and OpenClaw.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|