@gamaze/hicortex 0.3.13 → 0.3.15
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 +32 -8
- package/dist/llm.d.ts +1 -0
- package/dist/llm.js +10 -48
- package/dist/mcp-server.js +43 -27
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -116,23 +116,45 @@ function registerCcMcp(serverUrl) {
|
|
|
116
116
|
console.log(` ✓ Registered MCP server via claude CLI`);
|
|
117
117
|
}
|
|
118
118
|
catch (err) {
|
|
119
|
-
// Fallback: write directly to
|
|
119
|
+
// Fallback: write directly to ~/.claude.json (where CC reads MCP servers)
|
|
120
120
|
const msg = err instanceof Error ? err.message : String(err);
|
|
121
|
-
console.log(` ⚠ claude CLI registration failed (${msg}), writing
|
|
122
|
-
|
|
121
|
+
console.log(` ⚠ claude CLI registration failed (${msg}), writing ~/.claude.json directly`);
|
|
122
|
+
const claudeJsonPath = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude.json");
|
|
123
|
+
let config = {};
|
|
123
124
|
try {
|
|
124
|
-
|
|
125
|
+
config = JSON.parse((0, node_fs_1.readFileSync)(claudeJsonPath, "utf-8"));
|
|
125
126
|
}
|
|
126
127
|
catch { /* create new */ }
|
|
127
|
-
if (!
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
if (!config.mcpServers)
|
|
129
|
+
config.mcpServers = {};
|
|
130
|
+
config.mcpServers.hicortex = {
|
|
130
131
|
type: "sse",
|
|
131
132
|
url: `${serverUrl}/sse`,
|
|
132
133
|
};
|
|
134
|
+
(0, node_fs_1.writeFileSync)(claudeJsonPath, JSON.stringify(config, null, 2));
|
|
135
|
+
console.log(` ✓ Registered MCP server in ${claudeJsonPath}`);
|
|
136
|
+
}
|
|
137
|
+
// Add MCP tool permissions to settings.json so users don't get prompted
|
|
138
|
+
allowHicortexTools();
|
|
139
|
+
}
|
|
140
|
+
function allowHicortexTools() {
|
|
141
|
+
let settings = {};
|
|
142
|
+
try {
|
|
143
|
+
settings = JSON.parse((0, node_fs_1.readFileSync)(CC_SETTINGS, "utf-8"));
|
|
144
|
+
}
|
|
145
|
+
catch { /* create new */ }
|
|
146
|
+
if (!settings.permissions)
|
|
147
|
+
settings.permissions = {};
|
|
148
|
+
const perms = settings.permissions;
|
|
149
|
+
if (!perms.allow)
|
|
150
|
+
perms.allow = [];
|
|
151
|
+
const allow = perms.allow;
|
|
152
|
+
const rule = "mcp__hicortex__*";
|
|
153
|
+
if (!allow.includes(rule)) {
|
|
154
|
+
allow.push(rule);
|
|
133
155
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(CC_SETTINGS), { recursive: true });
|
|
134
156
|
(0, node_fs_1.writeFileSync)(CC_SETTINGS, JSON.stringify(settings, null, 2));
|
|
135
|
-
console.log(` ✓
|
|
157
|
+
console.log(` ✓ Added Hicortex tool permissions to ${CC_SETTINGS}`);
|
|
136
158
|
}
|
|
137
159
|
}
|
|
138
160
|
function installCcCommands() {
|
|
@@ -587,6 +609,8 @@ async function runInit() {
|
|
|
587
609
|
if (!d.ccMcpRegistered) {
|
|
588
610
|
registerCcMcp(serverUrl);
|
|
589
611
|
}
|
|
612
|
+
// Ensure tool permissions are set (also needed for users upgrading from older versions)
|
|
613
|
+
allowHicortexTools();
|
|
590
614
|
// Install CC commands
|
|
591
615
|
installCcCommands();
|
|
592
616
|
// Inject CLAUDE.md (only if server has a DB we can read)
|
package/dist/llm.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export declare class LlmClient {
|
|
|
85
85
|
completeDistill(prompt: string, maxTokens?: number): Promise<string>;
|
|
86
86
|
/**
|
|
87
87
|
* Complete with overridden baseUrl/apiKey/provider (used for reflect tier with separate endpoint).
|
|
88
|
+
* Creates a temporary LlmClient to avoid mutating shared config under concurrent calls.
|
|
88
89
|
*/
|
|
89
90
|
private completeWithOverride;
|
|
90
91
|
private complete;
|
package/dist/llm.js
CHANGED
|
@@ -199,34 +199,6 @@ function readOcProviderBaseUrl(config, provider) {
|
|
|
199
199
|
return providerConfig.baseUrl;
|
|
200
200
|
return undefined;
|
|
201
201
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Read provider base URL from per-agent models.json files.
|
|
204
|
-
* Scans all agent dirs for a matching provider entry.
|
|
205
|
-
*/
|
|
206
|
-
function readAgentModelsBaseUrl(provider) {
|
|
207
|
-
try {
|
|
208
|
-
const { readdirSync } = require("node:fs");
|
|
209
|
-
const agentsDir = (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "agents");
|
|
210
|
-
const agents = readdirSync(agentsDir);
|
|
211
|
-
for (const agentId of agents) {
|
|
212
|
-
try {
|
|
213
|
-
const modelsPath = (0, node_path_1.join)(agentsDir, agentId, "agent", "models.json");
|
|
214
|
-
const raw = (0, node_fs_1.readFileSync)(modelsPath, "utf-8");
|
|
215
|
-
const models = JSON.parse(raw);
|
|
216
|
-
const providerConfig = models?.providers?.[provider];
|
|
217
|
-
if (providerConfig?.baseUrl)
|
|
218
|
-
return providerConfig.baseUrl;
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
// Skip agents without models.json
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
// No agents dir
|
|
227
|
-
}
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
202
|
/**
|
|
231
203
|
* Read API key from OC's per-agent auth-profiles.json.
|
|
232
204
|
* Scans all agent dirs for a matching provider profile.
|
|
@@ -337,9 +309,6 @@ const PROVIDER_BASE_URLS = {
|
|
|
337
309
|
function getDefaultUrlForProvider(provider) {
|
|
338
310
|
return PROVIDER_BASE_URLS[provider.toLowerCase()] ?? "https://api.openai.com/v1";
|
|
339
311
|
}
|
|
340
|
-
function getEnvKeyForZai() {
|
|
341
|
-
return process.env.ZAI_API_KEY ?? process.env.LLM_API_KEY;
|
|
342
|
-
}
|
|
343
312
|
/**
|
|
344
313
|
* Find the claude CLI binary. Returns the full path or null.
|
|
345
314
|
*/
|
|
@@ -438,24 +407,16 @@ class LlmClient {
|
|
|
438
407
|
}
|
|
439
408
|
/**
|
|
440
409
|
* Complete with overridden baseUrl/apiKey/provider (used for reflect tier with separate endpoint).
|
|
410
|
+
* Creates a temporary LlmClient to avoid mutating shared config under concurrent calls.
|
|
441
411
|
*/
|
|
442
412
|
async completeWithOverride(baseUrl, apiKey, provider, model, prompt, maxTokens, timeoutMs) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this.config.provider = provider;
|
|
451
|
-
try {
|
|
452
|
-
return await this.complete(model, prompt, maxTokens, timeoutMs);
|
|
453
|
-
}
|
|
454
|
-
finally {
|
|
455
|
-
this.config.baseUrl = saved.baseUrl;
|
|
456
|
-
this.config.apiKey = saved.apiKey;
|
|
457
|
-
this.config.provider = saved.provider;
|
|
458
|
-
}
|
|
413
|
+
const tempClient = new LlmClient({
|
|
414
|
+
...this.config,
|
|
415
|
+
baseUrl,
|
|
416
|
+
apiKey,
|
|
417
|
+
provider,
|
|
418
|
+
});
|
|
419
|
+
return tempClient.complete(model, prompt, maxTokens, timeoutMs);
|
|
459
420
|
}
|
|
460
421
|
async complete(model, prompt, maxTokens, timeoutMs) {
|
|
461
422
|
if (this.isRateLimited) {
|
|
@@ -530,7 +491,8 @@ class LlmClient {
|
|
|
530
491
|
*/
|
|
531
492
|
async completeAnthropic(model, prompt, maxTokens, timeoutMs) {
|
|
532
493
|
const baseUrl = this.config.baseUrl.replace(/\/$/, "");
|
|
533
|
-
const
|
|
494
|
+
const hasVersion = /\/v\d+\/?$/.test(baseUrl);
|
|
495
|
+
const url = hasVersion ? `${baseUrl}/messages` : `${baseUrl}/v1/messages`;
|
|
534
496
|
const resp = await fetch(url, {
|
|
535
497
|
method: "POST",
|
|
536
498
|
headers: {
|
package/dist/mcp-server.js
CHANGED
|
@@ -178,7 +178,7 @@ async function startServer(options = {}) {
|
|
|
178
178
|
const dbPath = (0, db_js_1.resolveDbPath)(options.dbPath);
|
|
179
179
|
console.log(`[hicortex] Initializing database at ${dbPath}`);
|
|
180
180
|
db = (0, db_js_1.initDb)(dbPath);
|
|
181
|
-
stateDir =
|
|
181
|
+
stateDir = require("node:path").dirname(dbPath);
|
|
182
182
|
// LLM config: check config.json first, then env vars, then claude CLI
|
|
183
183
|
const savedConfig = readConfigFile(stateDir);
|
|
184
184
|
let llmConfig;
|
|
@@ -189,13 +189,18 @@ async function startServer(options = {}) {
|
|
|
189
189
|
}
|
|
190
190
|
else {
|
|
191
191
|
console.warn("[hicortex] claude-cli configured but claude binary not found, falling back");
|
|
192
|
-
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)(
|
|
192
|
+
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)({
|
|
193
|
+
llmBaseUrl: savedConfig?.llmBaseUrl,
|
|
194
|
+
llmApiKey: savedConfig?.llmApiKey,
|
|
195
|
+
llmModel: savedConfig?.llmModel,
|
|
196
|
+
reflectModel: savedConfig?.reflectModel,
|
|
197
|
+
});
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
|
-
else if (savedConfig?.llmBackend === "ollama"
|
|
196
|
-
// Ollama: no API key needed,
|
|
200
|
+
else if (savedConfig?.llmBackend === "ollama") {
|
|
201
|
+
// Ollama: no API key needed, default to localhost:11434
|
|
197
202
|
llmConfig = {
|
|
198
|
-
baseUrl: savedConfig.llmBaseUrl,
|
|
203
|
+
baseUrl: savedConfig.llmBaseUrl ?? "http://localhost:11434",
|
|
199
204
|
apiKey: "",
|
|
200
205
|
model: savedConfig.llmModel ?? "qwen3.5:4b",
|
|
201
206
|
reflectModel: savedConfig.reflectModel ?? savedConfig.llmModel ?? "qwen3.5:4b",
|
|
@@ -252,25 +257,7 @@ async function startServer(options = {}) {
|
|
|
252
257
|
// Express app
|
|
253
258
|
const app = (0, express_1.default)();
|
|
254
259
|
app.use(express_1.default.json());
|
|
255
|
-
//
|
|
256
|
-
if (authToken) {
|
|
257
|
-
console.log(`[hicortex] Bearer token auth enabled`);
|
|
258
|
-
app.use((req, res, next) => {
|
|
259
|
-
// Always allow health endpoint
|
|
260
|
-
if (req.path === "/health")
|
|
261
|
-
return next();
|
|
262
|
-
// Allow localhost without auth
|
|
263
|
-
const ip = req.ip ?? req.socket.remoteAddress ?? "";
|
|
264
|
-
if (ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1")
|
|
265
|
-
return next();
|
|
266
|
-
// Check bearer token
|
|
267
|
-
const auth = req.headers.authorization;
|
|
268
|
-
if (auth === `Bearer ${authToken}`)
|
|
269
|
-
return next();
|
|
270
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
// CORS: allow Claude Desktop (https://claude.ai) and other browser-based MCP clients
|
|
260
|
+
// CORS: must be before auth so preflight OPTIONS requests get proper headers
|
|
274
261
|
app.use((req, res, next) => {
|
|
275
262
|
const origin = req.headers.origin;
|
|
276
263
|
if (origin) {
|
|
@@ -278,6 +265,7 @@ async function startServer(options = {}) {
|
|
|
278
265
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
279
266
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization");
|
|
280
267
|
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
268
|
+
res.setHeader("Vary", "Origin");
|
|
281
269
|
}
|
|
282
270
|
if (req.method === "OPTIONS") {
|
|
283
271
|
res.status(204).end();
|
|
@@ -285,6 +273,21 @@ async function startServer(options = {}) {
|
|
|
285
273
|
}
|
|
286
274
|
next();
|
|
287
275
|
});
|
|
276
|
+
// Optional bearer token auth (skip for /health, OPTIONS, and localhost)
|
|
277
|
+
if (authToken) {
|
|
278
|
+
console.log(`[hicortex] Bearer token auth enabled`);
|
|
279
|
+
app.use((req, res, next) => {
|
|
280
|
+
if (req.path === "/health")
|
|
281
|
+
return next();
|
|
282
|
+
const ip = req.ip ?? req.socket.remoteAddress ?? "";
|
|
283
|
+
if (ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1")
|
|
284
|
+
return next();
|
|
285
|
+
const auth = req.headers.authorization;
|
|
286
|
+
if (auth === `Bearer ${authToken}`)
|
|
287
|
+
return next();
|
|
288
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
289
|
+
});
|
|
290
|
+
}
|
|
288
291
|
// SSE transport management — each connection gets its own McpServer instance
|
|
289
292
|
const transports = new Map();
|
|
290
293
|
// Health endpoint
|
|
@@ -307,7 +310,14 @@ async function startServer(options = {}) {
|
|
|
307
310
|
transport.onclose = () => {
|
|
308
311
|
transports.delete(transport.sessionId);
|
|
309
312
|
};
|
|
310
|
-
|
|
313
|
+
try {
|
|
314
|
+
await mcpServer.connect(transport);
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
transports.delete(transport.sessionId);
|
|
318
|
+
if (!res.headersSent)
|
|
319
|
+
res.status(500).json({ error: "MCP connect failed" });
|
|
320
|
+
}
|
|
311
321
|
});
|
|
312
322
|
// Message endpoint — client POSTs MCP messages here
|
|
313
323
|
app.post("/messages", async (req, res) => {
|
|
@@ -317,8 +327,14 @@ async function startServer(options = {}) {
|
|
|
317
327
|
return;
|
|
318
328
|
}
|
|
319
329
|
const transport = transports.get(sessionId);
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
try {
|
|
331
|
+
// Pass parsed body since express.json() already consumed the stream
|
|
332
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
if (!res.headersSent)
|
|
336
|
+
res.status(500).json({ error: "Message handling failed" });
|
|
337
|
+
}
|
|
322
338
|
});
|
|
323
339
|
// Start listening
|
|
324
340
|
const server = app.listen(port, host, () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
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": {
|