memorix 1.9.4 → 1.9.5
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/CHANGELOG.md +33 -1
- package/README.md +6 -5
- package/README.zh-CN.md +6 -5
- package/dist/cli/index.js +890 -325
- package/dist/index.d.ts +3 -1
- package/dist/index.js +251 -104
- package/dist/maintenance-runner.js +144 -113
- package/dist/media-audio-runner.js +57 -26
- package/dist/media-video-runner.js +57 -26
- package/dist/memcode-runtime/CHANGELOG.md +33 -1
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +1012 -842
- package/dist/types.d.ts +1 -1
- package/dist/vector-backfill-runner.js +57 -26
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
- package/docs/API_REFERENCE.md +3 -1
- package/docs/CONFIGURATION.md +15 -1
- package/docs/DOCKER.md +23 -3
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +26 -1
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +4 -0
- package/docs/SETUP.md +55 -0
- package/package.json +2 -2
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/command-guide.ts +2 -2
- package/src/cli/commands/agent-integrations.ts +57 -0
- package/src/cli/commands/background.ts +11 -2
- package/src/cli/commands/serve-http.ts +193 -42
- package/src/cli/commands/setup.ts +77 -10
- package/src/cli/commands/sync-store.ts +47 -12
- package/src/cli/commands/sync.ts +26 -0
- package/src/dashboard/server.ts +10 -12
- package/src/integrations/registry.ts +1 -1
- package/src/memory/graph.ts +10 -8
- package/src/memory/session.ts +1 -1
- package/src/sdk.ts +24 -0
- package/src/server/mcp-binding-store.ts +14 -3
- package/src/server.ts +2 -2
- package/src/store/file-lock.ts +28 -3
- package/src/store/graph-store.ts +178 -46
- package/src/store/persistence.ts +14 -9
- package/src/store/sqlite-db.ts +6 -3
- package/src/store/sqlite-store.ts +32 -27
- package/src/sync/engine.ts +9 -7
- package/src/sync/namespace.ts +23 -0
- package/src/sync/policy.ts +19 -4
- package/src/sync/store-port.ts +43 -13
- package/src/sync/types.ts +2 -0
- package/src/types.ts +1 -1
- package/src/workspace/mcp-adapters/grok.ts +32 -0
package/dist/cli/index.js
CHANGED
|
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3232
3232
|
|
|
3233
3233
|
// src/cli/version.ts
|
|
3234
3234
|
function getCliVersion() {
|
|
3235
|
-
return true ? "1.9.
|
|
3235
|
+
return true ? "1.9.5" : pkg.version;
|
|
3236
3236
|
}
|
|
3237
3237
|
var init_version = __esm({
|
|
3238
3238
|
"src/cli/version.ts"() {
|
|
@@ -9473,6 +9473,14 @@ END;
|
|
|
9473
9473
|
});
|
|
9474
9474
|
|
|
9475
9475
|
// src/store/sqlite-db.ts
|
|
9476
|
+
var sqlite_db_exports = {};
|
|
9477
|
+
__export(sqlite_db_exports, {
|
|
9478
|
+
closeAllDatabases: () => closeAllDatabases,
|
|
9479
|
+
closeDatabase: () => closeDatabase,
|
|
9480
|
+
getDatabase: () => getDatabase,
|
|
9481
|
+
isSqliteAvailable: () => isSqliteAvailable,
|
|
9482
|
+
loadBetterSqlite3: () => loadBetterSqlite3
|
|
9483
|
+
});
|
|
9476
9484
|
import "module";
|
|
9477
9485
|
import path3 from "path";
|
|
9478
9486
|
import fs from "fs";
|
|
@@ -9589,6 +9597,17 @@ function getDatabase(dataDir) {
|
|
|
9589
9597
|
throw error40;
|
|
9590
9598
|
}
|
|
9591
9599
|
}
|
|
9600
|
+
function closeDatabase(dataDir) {
|
|
9601
|
+
const normalized = path3.resolve(dataDir);
|
|
9602
|
+
const db2 = _dbCache.get(normalized);
|
|
9603
|
+
if (db2) {
|
|
9604
|
+
try {
|
|
9605
|
+
db2.close();
|
|
9606
|
+
} catch {
|
|
9607
|
+
}
|
|
9608
|
+
_dbCache.delete(normalized);
|
|
9609
|
+
}
|
|
9610
|
+
}
|
|
9592
9611
|
function closeAllDatabases() {
|
|
9593
9612
|
for (const [key, db2] of _dbCache) {
|
|
9594
9613
|
try {
|
|
@@ -9598,6 +9617,14 @@ function closeAllDatabases() {
|
|
|
9598
9617
|
_dbCache.delete(key);
|
|
9599
9618
|
}
|
|
9600
9619
|
}
|
|
9620
|
+
function isSqliteAvailable() {
|
|
9621
|
+
try {
|
|
9622
|
+
loadBetterSqlite3();
|
|
9623
|
+
return true;
|
|
9624
|
+
} catch {
|
|
9625
|
+
return false;
|
|
9626
|
+
}
|
|
9627
|
+
}
|
|
9601
9628
|
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
9602
9629
|
var init_sqlite_db = __esm({
|
|
9603
9630
|
"src/store/sqlite-db.ts"() {
|
|
@@ -9770,18 +9797,21 @@ CREATE TABLE IF NOT EXISTS chat_transcript (
|
|
|
9770
9797
|
`;
|
|
9771
9798
|
CREATE_GRAPH_ENTITIES_TABLE = `
|
|
9772
9799
|
CREATE TABLE IF NOT EXISTS graph_entities (
|
|
9773
|
-
|
|
9800
|
+
projectId TEXT NOT NULL,
|
|
9801
|
+
name TEXT NOT NULL,
|
|
9774
9802
|
entityType TEXT NOT NULL DEFAULT '',
|
|
9775
|
-
observations TEXT NOT NULL DEFAULT '[]'
|
|
9803
|
+
observations TEXT NOT NULL DEFAULT '[]',
|
|
9804
|
+
PRIMARY KEY (projectId, name)
|
|
9776
9805
|
);
|
|
9777
9806
|
`;
|
|
9778
9807
|
CREATE_GRAPH_RELATIONS_TABLE = `
|
|
9779
9808
|
CREATE TABLE IF NOT EXISTS graph_relations (
|
|
9780
9809
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9810
|
+
projectId TEXT NOT NULL,
|
|
9781
9811
|
from_entity TEXT NOT NULL,
|
|
9782
9812
|
to_entity TEXT NOT NULL,
|
|
9783
9813
|
relationType TEXT NOT NULL DEFAULT '',
|
|
9784
|
-
UNIQUE(from_entity, to_entity, relationType)
|
|
9814
|
+
UNIQUE(projectId, from_entity, to_entity, relationType)
|
|
9785
9815
|
);
|
|
9786
9816
|
`;
|
|
9787
9817
|
CREATE_CODE_FILES_TABLE = `
|
|
@@ -10649,6 +10679,21 @@ var init_sqlite_reliability = __esm({
|
|
|
10649
10679
|
import { promises as fs2 } from "fs";
|
|
10650
10680
|
import { randomUUID } from "crypto";
|
|
10651
10681
|
import path4 from "path";
|
|
10682
|
+
function canReclaimStaleLock(raw) {
|
|
10683
|
+
try {
|
|
10684
|
+
const parsed = JSON.parse(raw);
|
|
10685
|
+
const pid = Number(parsed.pid);
|
|
10686
|
+
if (!Number.isSafeInteger(pid) || pid < 1) return true;
|
|
10687
|
+
try {
|
|
10688
|
+
process.kill(pid, 0);
|
|
10689
|
+
return false;
|
|
10690
|
+
} catch (error40) {
|
|
10691
|
+
return error40.code !== "EPERM";
|
|
10692
|
+
}
|
|
10693
|
+
} catch {
|
|
10694
|
+
return true;
|
|
10695
|
+
}
|
|
10696
|
+
}
|
|
10652
10697
|
async function acquireLock(lockPath) {
|
|
10653
10698
|
for (let i4 = 0; i4 < MAX_RETRIES; i4++) {
|
|
10654
10699
|
try {
|
|
@@ -10662,9 +10707,12 @@ async function acquireLock(lockPath) {
|
|
|
10662
10707
|
try {
|
|
10663
10708
|
const stat6 = await fs2.stat(lockPath);
|
|
10664
10709
|
if (Date.now() - stat6.mtimeMs > LOCK_STALE_MS) {
|
|
10665
|
-
await fs2.
|
|
10666
|
-
|
|
10667
|
-
|
|
10710
|
+
const raw2 = await fs2.readFile(lockPath, "utf8").catch(() => "");
|
|
10711
|
+
if (canReclaimStaleLock(raw2)) {
|
|
10712
|
+
await fs2.unlink(lockPath).catch(() => {
|
|
10713
|
+
});
|
|
10714
|
+
continue;
|
|
10715
|
+
}
|
|
10668
10716
|
}
|
|
10669
10717
|
} catch {
|
|
10670
10718
|
continue;
|
|
@@ -10675,6 +10723,10 @@ async function acquireLock(lockPath) {
|
|
|
10675
10723
|
}
|
|
10676
10724
|
}
|
|
10677
10725
|
}
|
|
10726
|
+
const raw = await fs2.readFile(lockPath, "utf8").catch(() => "");
|
|
10727
|
+
if (!canReclaimStaleLock(raw)) {
|
|
10728
|
+
throw new Error(`Failed to acquire lock: ${lockPath} (owner is still running)`);
|
|
10729
|
+
}
|
|
10678
10730
|
await fs2.unlink(lockPath).catch(() => {
|
|
10679
10731
|
});
|
|
10680
10732
|
try {
|
|
@@ -10913,6 +10965,11 @@ async function listProjectDirs(baseDir) {
|
|
|
10913
10965
|
}
|
|
10914
10966
|
}
|
|
10915
10967
|
async function migrateSubdirsToFlat(baseDir) {
|
|
10968
|
+
const base = baseDir ?? resolveDefaultDataDir();
|
|
10969
|
+
await fs4.mkdir(base, { recursive: true });
|
|
10970
|
+
return withFileLock(base, () => migrateSubdirsToFlatUnlocked(base));
|
|
10971
|
+
}
|
|
10972
|
+
async function migrateSubdirsToFlatUnlocked(baseDir) {
|
|
10916
10973
|
const base = baseDir ?? resolveDefaultDataDir();
|
|
10917
10974
|
await fs4.mkdir(base, { recursive: true });
|
|
10918
10975
|
let entries;
|
|
@@ -10969,7 +11026,7 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
10969
11026
|
for (const { obs } of subdirData) {
|
|
10970
11027
|
for (const o4 of obs) {
|
|
10971
11028
|
const isDuplicate = allObs.some(
|
|
10972
|
-
(existing) => existing.title === o4.title && existing.createdAt === o4.createdAt
|
|
11029
|
+
(existing) => existing.title === o4.title && existing.createdAt === o4.createdAt && existing.projectId === o4.projectId
|
|
10973
11030
|
);
|
|
10974
11031
|
if (!isDuplicate) {
|
|
10975
11032
|
allObs.push(o4);
|
|
@@ -11002,18 +11059,14 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
11002
11059
|
mergedRelations.push(rel);
|
|
11003
11060
|
}
|
|
11004
11061
|
}
|
|
11005
|
-
await
|
|
11006
|
-
await
|
|
11007
|
-
path6.join(base, "counter.json"),
|
|
11008
|
-
JSON.stringify({ nextId: allObs.length + 1 }),
|
|
11009
|
-
"utf-8"
|
|
11010
|
-
);
|
|
11062
|
+
await atomicWriteFile(path6.join(base, "observations.json"), JSON.stringify(allObs, null, 2));
|
|
11063
|
+
await atomicWriteFile(path6.join(base, "counter.json"), JSON.stringify({ nextId: allObs.length + 1 }));
|
|
11011
11064
|
const graphLines = [
|
|
11012
11065
|
...[...entityMap.values()].map((e5) => JSON.stringify({ type: "entity", name: e5.name, entityType: e5.entityType, observations: e5.observations })),
|
|
11013
11066
|
...mergedRelations.map((r6) => JSON.stringify({ type: "relation", from: r6.from, to: r6.to, relationType: r6.relationType }))
|
|
11014
11067
|
];
|
|
11015
11068
|
if (graphLines.length > 0) {
|
|
11016
|
-
await
|
|
11069
|
+
await atomicWriteFile(path6.join(base, "graph.jsonl"), graphLines.join("\n"));
|
|
11017
11070
|
}
|
|
11018
11071
|
let allSessions = [];
|
|
11019
11072
|
try {
|
|
@@ -11031,7 +11084,7 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
11031
11084
|
}
|
|
11032
11085
|
}
|
|
11033
11086
|
if (allSessions.length > 0) {
|
|
11034
|
-
await
|
|
11087
|
+
await atomicWriteFile(path6.join(base, "sessions.json"), JSON.stringify(allSessions, null, 2));
|
|
11035
11088
|
}
|
|
11036
11089
|
const backupDir = path6.join(base, ".migrated-subdirs");
|
|
11037
11090
|
await fs4.mkdir(backupDir, { recursive: true });
|
|
@@ -11067,6 +11120,7 @@ var init_persistence = __esm({
|
|
|
11067
11120
|
"src/store/persistence.ts"() {
|
|
11068
11121
|
"use strict";
|
|
11069
11122
|
init_esm_shims();
|
|
11123
|
+
init_file_lock();
|
|
11070
11124
|
init_persistence_json();
|
|
11071
11125
|
}
|
|
11072
11126
|
});
|
|
@@ -25338,6 +25392,7 @@ var init_sqlite_store = __esm({
|
|
|
25338
25392
|
init_sqlite_db();
|
|
25339
25393
|
init_sqlite_reliability();
|
|
25340
25394
|
init_sqlite_fts();
|
|
25395
|
+
init_file_lock();
|
|
25341
25396
|
SqliteBackend = class {
|
|
25342
25397
|
db = null;
|
|
25343
25398
|
dataDir = "";
|
|
@@ -25501,38 +25556,43 @@ var init_sqlite_store = __esm({
|
|
|
25501
25556
|
}
|
|
25502
25557
|
// ── Migration ────────────────────────────────────────────────────
|
|
25503
25558
|
async migrateFromJsonIfNeeded() {
|
|
25504
|
-
const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
|
|
25505
|
-
if (count3.cnt > 0) return;
|
|
25506
25559
|
const jsonPath = path9.join(this.dataDir, "observations.json");
|
|
25507
25560
|
if (!fs6.existsSync(jsonPath)) return;
|
|
25508
|
-
|
|
25561
|
+
await withFileLock(this.dataDir, async () => {
|
|
25562
|
+
const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
|
|
25563
|
+
if (count3.cnt > 0) return;
|
|
25509
25564
|
const raw = fs6.readFileSync(jsonPath, "utf-8");
|
|
25510
|
-
const
|
|
25565
|
+
const parsed = JSON.parse(raw);
|
|
25566
|
+
if (!Array.isArray(parsed)) {
|
|
25567
|
+
throw new Error("[memorix] observations.json migration refused: expected a JSON array");
|
|
25568
|
+
}
|
|
25569
|
+
const observations2 = parsed;
|
|
25511
25570
|
if (observations2.length === 0) return;
|
|
25571
|
+
if (observations2.some((obs) => !obs || !Number.isSafeInteger(obs.id) || obs.id < 1 || typeof obs.projectId !== "string")) {
|
|
25572
|
+
throw new Error("[memorix] observations.json migration refused: invalid observation record");
|
|
25573
|
+
}
|
|
25512
25574
|
console.error(`[memorix] Migrating ${observations2.length} observations from JSON to SQLite...`);
|
|
25513
25575
|
const insertMany = this.db.transaction((obsList) => {
|
|
25514
25576
|
for (const obs of obsList) {
|
|
25515
25577
|
this.stmtInsert.run(obsToRow(obs));
|
|
25516
25578
|
}
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
if (fs6.existsSync(counterPath)) {
|
|
25521
|
-
try {
|
|
25579
|
+
const counterPath = path9.join(this.dataDir, "counter.json");
|
|
25580
|
+
let counterNextId;
|
|
25581
|
+
if (fs6.existsSync(counterPath)) {
|
|
25522
25582
|
const counterData = JSON.parse(fs6.readFileSync(counterPath, "utf-8"));
|
|
25523
|
-
const
|
|
25524
|
-
|
|
25525
|
-
|
|
25526
|
-
|
|
25583
|
+
const candidate2 = counterData.nextId ?? counterData.next_id;
|
|
25584
|
+
if (candidate2 !== void 0 && (!Number.isSafeInteger(candidate2) || Number(candidate2) < 1)) {
|
|
25585
|
+
throw new Error("[memorix] counter.json migration refused: nextId must be a positive integer");
|
|
25586
|
+
}
|
|
25587
|
+
counterNextId = candidate2 === void 0 ? void 0 : Number(candidate2);
|
|
25527
25588
|
}
|
|
25528
|
-
|
|
25529
|
-
this.rawSaveIdCounter(Math.max(
|
|
25530
|
-
|
|
25531
|
-
|
|
25589
|
+
const maxId = Math.max(...obsList.map((obs) => obs.id));
|
|
25590
|
+
this.rawSaveIdCounter(Math.max(counterNextId ?? 1, maxId + 1));
|
|
25591
|
+
this.bumpGeneration();
|
|
25592
|
+
});
|
|
25593
|
+
insertMany(observations2);
|
|
25532
25594
|
console.error(`[memorix] Migration complete. ${observations2.length} observations now in SQLite.`);
|
|
25533
|
-
}
|
|
25534
|
-
console.error(`[memorix] JSON\u2192SQLite migration failed (non-fatal, data preserved in JSON): ${err}`);
|
|
25535
|
-
}
|
|
25595
|
+
});
|
|
25536
25596
|
}
|
|
25537
25597
|
// ── Public read ──────────────────────────────────────────────────
|
|
25538
25598
|
async loadAll() {
|
|
@@ -86686,12 +86746,12 @@ var require_jwa = __commonJS({
|
|
|
86686
86746
|
};
|
|
86687
86747
|
}
|
|
86688
86748
|
var bufferEqual;
|
|
86689
|
-
var
|
|
86749
|
+
var timingSafeEqual3 = "timingSafeEqual" in crypto2 ? function timingSafeEqual4(a4, b4) {
|
|
86690
86750
|
if (a4.byteLength !== b4.byteLength) {
|
|
86691
86751
|
return false;
|
|
86692
86752
|
}
|
|
86693
86753
|
return crypto2.timingSafeEqual(a4, b4);
|
|
86694
|
-
} : function
|
|
86754
|
+
} : function timingSafeEqual4(a4, b4) {
|
|
86695
86755
|
if (!bufferEqual) {
|
|
86696
86756
|
bufferEqual = require_buffer_equal_constant_time();
|
|
86697
86757
|
}
|
|
@@ -86700,7 +86760,7 @@ var require_jwa = __commonJS({
|
|
|
86700
86760
|
function createHmacVerifier(bits) {
|
|
86701
86761
|
return function verify2(thing, signature, secret) {
|
|
86702
86762
|
var computedSig = createHmacSigner(bits)(thing, secret);
|
|
86703
|
-
return
|
|
86763
|
+
return timingSafeEqual3(Buffer5.from(signature), Buffer5.from(computedSig));
|
|
86704
86764
|
};
|
|
86705
86765
|
}
|
|
86706
86766
|
function createKeySigner(bits) {
|
|
@@ -194277,6 +194337,32 @@ var init_codebuddy = __esm({
|
|
|
194277
194337
|
}
|
|
194278
194338
|
});
|
|
194279
194339
|
|
|
194340
|
+
// src/workspace/mcp-adapters/grok.ts
|
|
194341
|
+
import { homedir as homedir25 } from "os";
|
|
194342
|
+
import { join as join22 } from "path";
|
|
194343
|
+
var GrokMCPAdapter;
|
|
194344
|
+
var init_grok = __esm({
|
|
194345
|
+
"src/workspace/mcp-adapters/grok.ts"() {
|
|
194346
|
+
"use strict";
|
|
194347
|
+
init_esm_shims();
|
|
194348
|
+
init_codex();
|
|
194349
|
+
GrokMCPAdapter = class {
|
|
194350
|
+
source = "grok";
|
|
194351
|
+
toml = new CodexMCPAdapter();
|
|
194352
|
+
parse(content) {
|
|
194353
|
+
return this.toml.parse(content);
|
|
194354
|
+
}
|
|
194355
|
+
generate(servers) {
|
|
194356
|
+
return this.toml.generate(servers);
|
|
194357
|
+
}
|
|
194358
|
+
getConfigPath(_projectRoot) {
|
|
194359
|
+
const grokHome = process.env.GROK_HOME?.trim() || join22(homedir25(), ".grok");
|
|
194360
|
+
return join22(grokHome, "config.toml");
|
|
194361
|
+
}
|
|
194362
|
+
};
|
|
194363
|
+
}
|
|
194364
|
+
});
|
|
194365
|
+
|
|
194280
194366
|
// src/integrations/registry.ts
|
|
194281
194367
|
function getSetupIntegrationRows() {
|
|
194282
194368
|
return MEMORIX_INTEGRATIONS.filter((item) => item.status === "ready" && item.agent !== "memcode" && item.agent !== "any-mcp").map((item) => ({
|
|
@@ -194334,7 +194420,7 @@ var init_registry2 = __esm({
|
|
|
194334
194420
|
status: "ready",
|
|
194335
194421
|
install: "memorix setup --agent grok --global",
|
|
194336
194422
|
surfaces: ["hooks", "AGENTS.md"],
|
|
194337
|
-
note: "Writes ~/.grok/hooks/memorix.json and leaves Grok MCP host-owned
|
|
194423
|
+
note: "Writes ~/.grok/hooks/memorix.json and leaves Grok MCP host-owned by default. Pass --mcp http to write [mcp_servers.memorix] in Grok config.toml."
|
|
194338
194424
|
},
|
|
194339
194425
|
{
|
|
194340
194426
|
agent: "cursor",
|
|
@@ -194739,9 +194825,9 @@ __export(audit_exports, {
|
|
|
194739
194825
|
});
|
|
194740
194826
|
import * as fs18 from "fs/promises";
|
|
194741
194827
|
import * as path30 from "path";
|
|
194742
|
-
import { homedir as
|
|
194828
|
+
import { homedir as homedir26 } from "os";
|
|
194743
194829
|
function getAuditFilePath() {
|
|
194744
|
-
return process.env.MEMORIX_AUDIT_FILE || path30.join(
|
|
194830
|
+
return process.env.MEMORIX_AUDIT_FILE || path30.join(homedir26(), ".memorix", "audit.json");
|
|
194745
194831
|
}
|
|
194746
194832
|
async function loadAudit() {
|
|
194747
194833
|
try {
|
|
@@ -196195,6 +196281,7 @@ var init_installers = __esm({
|
|
|
196195
196281
|
// src/cli/commands/setup.ts
|
|
196196
196282
|
var setup_exports = {};
|
|
196197
196283
|
__export(setup_exports, {
|
|
196284
|
+
MEMORIX_HTTP_MCP_URL: () => MEMORIX_HTTP_MCP_URL,
|
|
196198
196285
|
buildMemorixServer: () => buildMemorixServer,
|
|
196199
196286
|
buildSetupPlan: () => buildSetupPlan,
|
|
196200
196287
|
default: () => setup_default,
|
|
@@ -196214,19 +196301,20 @@ __export(setup_exports, {
|
|
|
196214
196301
|
migrateLegacyCodexIntegration: () => migrateLegacyCodexIntegration,
|
|
196215
196302
|
parseCodexPluginState: () => parseCodexPluginState,
|
|
196216
196303
|
removeLegacyCodexMemorixMcpConfig: () => removeLegacyCodexMemorixMcpConfig,
|
|
196304
|
+
rewriteBundledMemorixMcpToHttp: () => rewriteBundledMemorixMcpToHttp,
|
|
196217
196305
|
tryInstallCodexPlugin: () => tryInstallCodexPlugin
|
|
196218
196306
|
});
|
|
196219
196307
|
import { spawnSync } from "child_process";
|
|
196220
196308
|
import { existsSync as existsSync17 } from "fs";
|
|
196221
196309
|
import { copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6, readdir as readdir3, rm as rm2, writeFile as writeFile7 } from "fs/promises";
|
|
196222
|
-
import { homedir as
|
|
196310
|
+
import { homedir as homedir28 } from "os";
|
|
196223
196311
|
import path32 from "path";
|
|
196224
196312
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
196225
196313
|
function getSetupAgentTargets() {
|
|
196226
196314
|
return [...SUPPORTED_SETUP_AGENTS];
|
|
196227
196315
|
}
|
|
196228
196316
|
function buildSetupPlan(options) {
|
|
196229
|
-
const mcp = options.agent === "grok" ? "none" : options.mcp ?? "stdio";
|
|
196317
|
+
const mcp = options.agent === "grok" ? options.mcp === "http" ? "http" : "none" : options.mcp ?? "stdio";
|
|
196230
196318
|
const actions = [];
|
|
196231
196319
|
const isPackageOwnedAgent = options.agent !== "all" && PACKAGE_OWNED_INTEGRATION_AGENTS.has(options.agent);
|
|
196232
196320
|
const includeHooks = options.hooks ?? true;
|
|
@@ -196382,7 +196470,8 @@ function getMcpAdapter(agent) {
|
|
|
196382
196470
|
opencode: new OpenCodeMCPAdapter(),
|
|
196383
196471
|
trae: new TraeMCPAdapter(),
|
|
196384
196472
|
dsh: new DshMCPAdapter(),
|
|
196385
|
-
workbuddy: new WorkbuddyMCPAdapter()
|
|
196473
|
+
workbuddy: new WorkbuddyMCPAdapter(),
|
|
196474
|
+
grok: new GrokMCPAdapter()
|
|
196386
196475
|
};
|
|
196387
196476
|
return adapters[agent];
|
|
196388
196477
|
}
|
|
@@ -196392,7 +196481,7 @@ function buildMemorixServer(mcp) {
|
|
|
196392
196481
|
name: "memorix",
|
|
196393
196482
|
command: "",
|
|
196394
196483
|
args: [],
|
|
196395
|
-
url:
|
|
196484
|
+
url: MEMORIX_HTTP_MCP_URL
|
|
196396
196485
|
};
|
|
196397
196486
|
}
|
|
196398
196487
|
return {
|
|
@@ -196403,6 +196492,36 @@ function buildMemorixServer(mcp) {
|
|
|
196403
196492
|
args: ["serve", "--mode", "lite"]
|
|
196404
196493
|
};
|
|
196405
196494
|
}
|
|
196495
|
+
function toHttpMemorixServer(existing) {
|
|
196496
|
+
const next = { url: MEMORIX_HTTP_MCP_URL };
|
|
196497
|
+
if (existing.alwaysLoad === true) next.alwaysLoad = true;
|
|
196498
|
+
if (typeof existing.type === "string") next.type = "http";
|
|
196499
|
+
return next;
|
|
196500
|
+
}
|
|
196501
|
+
async function rewriteBundledMemorixMcpToHttp(root) {
|
|
196502
|
+
const candidates = [".mcp.json", "mcp_config.json", "gemini-extension.json"].map((name) => path32.join(root, name));
|
|
196503
|
+
const rewritten = [];
|
|
196504
|
+
for (const filePath of candidates) {
|
|
196505
|
+
if (!existsSync17(filePath)) continue;
|
|
196506
|
+
let doc;
|
|
196507
|
+
try {
|
|
196508
|
+
doc = JSON.parse(await readFile6(filePath, "utf-8"));
|
|
196509
|
+
} catch {
|
|
196510
|
+
continue;
|
|
196511
|
+
}
|
|
196512
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) continue;
|
|
196513
|
+
const record2 = doc;
|
|
196514
|
+
const servers = record2.mcpServers;
|
|
196515
|
+
if (!servers || typeof servers !== "object" || Array.isArray(servers)) continue;
|
|
196516
|
+
const serverMap = servers;
|
|
196517
|
+
const existing = serverMap.memorix;
|
|
196518
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) continue;
|
|
196519
|
+
serverMap.memorix = toHttpMemorixServer(existing);
|
|
196520
|
+
await writeFile7(filePath, JSON.stringify(record2, null, 2) + "\n", "utf-8");
|
|
196521
|
+
rewritten.push(filePath);
|
|
196522
|
+
}
|
|
196523
|
+
return rewritten;
|
|
196524
|
+
}
|
|
196406
196525
|
function mergeJsonMcpConfig(existingContent, generatedContent) {
|
|
196407
196526
|
let generated;
|
|
196408
196527
|
try {
|
|
@@ -196504,7 +196623,7 @@ function isOwnedLegacyCodexMcp(content) {
|
|
|
196504
196623
|
const server = new CodexMCPAdapter().parse(content).find((entry) => entry.name === "memorix");
|
|
196505
196624
|
return Boolean(server && isLegacyCodexMemorixNodeServer(server));
|
|
196506
196625
|
}
|
|
196507
|
-
async function removeLegacyCodexMemorixMcpConfig(configPath = path32.join(
|
|
196626
|
+
async function removeLegacyCodexMemorixMcpConfig(configPath = path32.join(homedir28(), ".codex", "config.toml")) {
|
|
196508
196627
|
let existingContent;
|
|
196509
196628
|
try {
|
|
196510
196629
|
existingContent = await readFile6(configPath, "utf-8");
|
|
@@ -196561,7 +196680,7 @@ async function removeOwnedLegacyCodexPluginFolder(homeDir) {
|
|
|
196561
196680
|
}
|
|
196562
196681
|
}
|
|
196563
196682
|
async function migrateLegacyCodexIntegration(options = {}) {
|
|
196564
|
-
const homeDir = options.homeDir ??
|
|
196683
|
+
const homeDir = options.homeDir ?? homedir28();
|
|
196565
196684
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
196566
196685
|
const configPath = path32.join(homeDir, ".codex", "config.toml");
|
|
196567
196686
|
let mcp = "missing";
|
|
@@ -196671,7 +196790,7 @@ async function installMcpConfig(options) {
|
|
|
196671
196790
|
existingContent = null;
|
|
196672
196791
|
}
|
|
196673
196792
|
const generated = adapter.generate([server]);
|
|
196674
|
-
const content = options.agent === "codex" ? mergeTomlMcpConfig(existingContent, generated) : options.agent === "hermes" ? mergeYamlMcpConfig(existingContent, generated) : options.agent === "dsh" ? mergeDshPatchConfig(existingContent, generated) : mergeJsonMcpConfig(existingContent, generated);
|
|
196793
|
+
const content = options.agent === "codex" || options.agent === "grok" ? mergeTomlMcpConfig(existingContent, generated) : options.agent === "hermes" ? mergeYamlMcpConfig(existingContent, generated) : options.agent === "dsh" ? mergeDshPatchConfig(existingContent, generated) : mergeJsonMcpConfig(existingContent, generated);
|
|
196675
196794
|
await mkdir6(path32.dirname(configPath), { recursive: true });
|
|
196676
196795
|
await writeFile7(configPath, content, "utf-8");
|
|
196677
196796
|
return { agent: options.agent, configPath, transport: options.mcp };
|
|
@@ -196762,7 +196881,7 @@ async function stripCodeBuddyHookCapture(pluginPath) {
|
|
|
196762
196881
|
await writeFile7(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
196763
196882
|
}
|
|
196764
196883
|
async function installCodeBuddyPluginPackage(options = {}) {
|
|
196765
|
-
const home = options.homeDir ??
|
|
196884
|
+
const home = options.homeDir ?? homedir28();
|
|
196766
196885
|
const packageRoot = findPackageRoot();
|
|
196767
196886
|
const source = path32.join(packageRoot, "plugins", "codebuddy", "memorix");
|
|
196768
196887
|
if (!existsSync17(source)) throw new Error(`CodeBuddy plugin template not found: ${source}`);
|
|
@@ -196782,7 +196901,7 @@ async function installCodeBuddyPluginPackage(options = {}) {
|
|
|
196782
196901
|
};
|
|
196783
196902
|
}
|
|
196784
196903
|
async function installPluginPackage(options) {
|
|
196785
|
-
const home = options.homeDir ??
|
|
196904
|
+
const home = options.homeDir ?? homedir28();
|
|
196786
196905
|
const packageRoot = findPackageRoot();
|
|
196787
196906
|
const source = path32.join(packageRoot, "plugins", options.agent, "memorix");
|
|
196788
196907
|
const includeHooks = options.includeHooks ?? true;
|
|
@@ -196831,7 +196950,7 @@ async function installPluginPackage(options) {
|
|
|
196831
196950
|
};
|
|
196832
196951
|
}
|
|
196833
196952
|
async function installGeminiExtensionPackage(options = {}) {
|
|
196834
|
-
const home = options.homeDir ??
|
|
196953
|
+
const home = options.homeDir ?? homedir28();
|
|
196835
196954
|
const packageRoot = findPackageRoot();
|
|
196836
196955
|
const source = path32.join(packageRoot, "plugins", "gemini", "memorix");
|
|
196837
196956
|
if (!existsSync17(source)) {
|
|
@@ -196851,7 +196970,7 @@ async function installAntigravityPluginPackage(options = {}) {
|
|
|
196851
196970
|
if (!existsSync17(source)) {
|
|
196852
196971
|
throw new Error(`Antigravity plugin template not found: ${source}`);
|
|
196853
196972
|
}
|
|
196854
|
-
const pluginPath = options.global === false ? path32.join(options.projectRoot ?? process.cwd(), ".agents", "plugins", "memorix") : path32.join(options.homeDir ??
|
|
196973
|
+
const pluginPath = options.global === false ? path32.join(options.projectRoot ?? process.cwd(), ".agents", "plugins", "memorix") : path32.join(options.homeDir ?? homedir28(), ".gemini", "config", "plugins", "memorix");
|
|
196855
196974
|
await copyDir(source, pluginPath);
|
|
196856
196975
|
if (options.includeHooks === false) {
|
|
196857
196976
|
await rm2(path32.join(pluginPath, "hooks.json"), { force: true });
|
|
@@ -196869,7 +196988,7 @@ async function installPiPackage(options = {}) {
|
|
|
196869
196988
|
if (!existsSync17(source)) {
|
|
196870
196989
|
throw new Error(`Pi package template not found: ${source}`);
|
|
196871
196990
|
}
|
|
196872
|
-
const defaultRoot = options.global ? path32.join(options.homeDir ??
|
|
196991
|
+
const defaultRoot = options.global ? path32.join(options.homeDir ?? homedir28(), ".pi", "agent", "packages") : path32.join(process.cwd(), ".pi", "packages");
|
|
196873
196992
|
const packagePath = options.packageDir ?? path32.join(defaultRoot, "memorix");
|
|
196874
196993
|
await copyDir(source, packagePath);
|
|
196875
196994
|
await writeOfficialSkills(path32.join(packagePath, "skills"));
|
|
@@ -196881,7 +197000,7 @@ async function installPiPackage(options = {}) {
|
|
|
196881
197000
|
};
|
|
196882
197001
|
}
|
|
196883
197002
|
async function installOpenClawBundlePackage(options = {}) {
|
|
196884
|
-
const home = options.homeDir ??
|
|
197003
|
+
const home = options.homeDir ?? homedir28();
|
|
196885
197004
|
const packageRoot = findPackageRoot();
|
|
196886
197005
|
const source = path32.join(packageRoot, "plugins", "openclaw", "memorix");
|
|
196887
197006
|
if (!existsSync17(source)) {
|
|
@@ -196930,7 +197049,7 @@ async function installOmpPackage(options = {}) {
|
|
|
196930
197049
|
if (!existsSync17(source)) {
|
|
196931
197050
|
throw new Error(`Oh-my-Pi package template not found: ${source}`);
|
|
196932
197051
|
}
|
|
196933
|
-
const defaultRoot = options.global ? path32.join(options.homeDir ??
|
|
197052
|
+
const defaultRoot = options.global ? path32.join(options.homeDir ?? homedir28(), ".omp", "agent", "packages") : path32.join(process.cwd(), ".omp", "packages");
|
|
196934
197053
|
const packagePath = options.packageDir ?? path32.join(defaultRoot, "memorix");
|
|
196935
197054
|
await copyDir(source, packagePath);
|
|
196936
197055
|
await writeOfficialSkills(path32.join(packagePath, "skills"));
|
|
@@ -197204,7 +197323,7 @@ ${result.stdout || ""}`.toLowerCase();
|
|
|
197204
197323
|
}
|
|
197205
197324
|
async function installAgentSetup(agent, plan, global2) {
|
|
197206
197325
|
const { installAgentGuidance: installAgentGuidance2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
|
|
197207
|
-
const targetRoot = global2 ?
|
|
197326
|
+
const targetRoot = global2 ? homedir28() : process.cwd();
|
|
197208
197327
|
const hasPluginPackage = plan.actions.includes("plugin-package") && PLUGIN_PACKAGE_AGENTS.has(agent);
|
|
197209
197328
|
const hasExtensionPackage = plan.actions.includes("extension-package") && agent === "gemini-cli";
|
|
197210
197329
|
const hasPiPackage = plan.actions.includes("pi-package") && agent === "pi";
|
|
@@ -197213,7 +197332,8 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197213
197332
|
const hasHermesPlugin = plan.actions.includes("hermes-plugin") && agent === "hermes";
|
|
197214
197333
|
const hasOmpPackage = plan.actions.includes("omp-package") && agent === "omp";
|
|
197215
197334
|
const hasCodeBuddyPlugin = plan.actions.includes("codebuddy-plugin") && agent === "codebuddy";
|
|
197216
|
-
const wantsMcpConfig = plan.mcp !== "none" && agent !== "pi" && agent
|
|
197335
|
+
const wantsMcpConfig = plan.mcp !== "none" && agent !== "pi" && !(agent === "codex" && !global2) && !(agent === "grok" && plan.mcp !== "http");
|
|
197336
|
+
const bundledMcpRoots = [];
|
|
197217
197337
|
if (PLUGIN_PACKAGE_AGENTS.has(agent) && plan.includePlugin && !global2) {
|
|
197218
197338
|
v3.info(`${agent}: project setup leaves user-level plugins untouched. Run \`memorix setup --agent ${agent} --global\` to install its plugin, skills, and lifecycle hooks.`);
|
|
197219
197339
|
}
|
|
@@ -197222,6 +197342,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197222
197342
|
agent,
|
|
197223
197343
|
includeHooks: plan.includeHooks
|
|
197224
197344
|
});
|
|
197345
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197225
197346
|
v3.success(`${agent}: plugin package -> ${result.pluginPath}`);
|
|
197226
197347
|
if (result.marketplacePath) {
|
|
197227
197348
|
v3.info(`${agent}: marketplace -> ${result.marketplacePath}`);
|
|
@@ -197252,6 +197373,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197252
197373
|
}
|
|
197253
197374
|
if (hasCodeBuddyPlugin) {
|
|
197254
197375
|
const result = await installCodeBuddyPluginPackage({ includeHooks: plan.includeHooks });
|
|
197376
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197255
197377
|
v3.success(`${agent}: plugin -> ${result.pluginPath}`);
|
|
197256
197378
|
v3.info(`${agent}: marketplace -> ${result.marketplacePath}`);
|
|
197257
197379
|
const install = tryInstallCodeBuddyPlugin(result.marketplaceRoot);
|
|
@@ -197262,6 +197384,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197262
197384
|
}
|
|
197263
197385
|
if (hasExtensionPackage) {
|
|
197264
197386
|
const result = await installGeminiExtensionPackage();
|
|
197387
|
+
bundledMcpRoots.push(result.extensionPath);
|
|
197265
197388
|
v3.success(`${agent}: extension package -> ${result.extensionPath}`);
|
|
197266
197389
|
v3.info(result.installHint);
|
|
197267
197390
|
}
|
|
@@ -197275,6 +197398,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197275
197398
|
}
|
|
197276
197399
|
if (hasOpenClawBundle) {
|
|
197277
197400
|
const result = await installOpenClawBundlePackage({ includeHooks: plan.includeHooks });
|
|
197401
|
+
bundledMcpRoots.push(result.bundlePath);
|
|
197278
197402
|
v3.success(`${agent}: compatible bundle -> ${result.bundlePath}`);
|
|
197279
197403
|
if (plan.includeHooks) {
|
|
197280
197404
|
const install = tryInstallOpenClawBundle(result.bundlePath);
|
|
@@ -197287,6 +197411,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197287
197411
|
}
|
|
197288
197412
|
if (hasAntigravityPlugin) {
|
|
197289
197413
|
const result = await installAntigravityPluginPackage({ global: global2, projectRoot: targetRoot, includeHooks: plan.includeHooks });
|
|
197414
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197290
197415
|
v3.success(`${agent}: plugin -> ${result.pluginPath}`);
|
|
197291
197416
|
if (!plan.includeHooks) {
|
|
197292
197417
|
v3.info(`${agent}: hooks.json skipped because --noHooks was selected`);
|
|
@@ -197317,9 +197442,17 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197317
197442
|
v3.info(result.installHint);
|
|
197318
197443
|
}
|
|
197319
197444
|
if (wantsMcpConfig) {
|
|
197320
|
-
|
|
197445
|
+
const mcpOwnedByPackage = hasPluginPackage || hasCodeBuddyPlugin || hasExtensionPackage || hasOpenClawBundle || hasAntigravityPlugin;
|
|
197446
|
+
if (mcpOwnedByPackage && plan.mcp === "stdio") {
|
|
197321
197447
|
const packageLabel = hasExtensionPackage ? "extension package" : hasCodeBuddyPlugin ? "CodeBuddy plugin" : hasOpenClawBundle ? "OpenClaw bundle" : hasAntigravityPlugin ? "Antigravity plugin" : "plugin package";
|
|
197322
197448
|
v3.info(`${agent}: stdio MCP is bundled in the ${packageLabel}`);
|
|
197449
|
+
} else if (mcpOwnedByPackage && plan.mcp === "http") {
|
|
197450
|
+
for (const root of bundledMcpRoots) {
|
|
197451
|
+
const rewritten = await rewriteBundledMemorixMcpToHttp(root);
|
|
197452
|
+
for (const filePath of rewritten) {
|
|
197453
|
+
v3.success(`${agent}: plugin MCP -> HTTP ${filePath}`);
|
|
197454
|
+
}
|
|
197455
|
+
}
|
|
197323
197456
|
} else {
|
|
197324
197457
|
const mcp = plan.mcp === "http" ? "http" : "stdio";
|
|
197325
197458
|
const result = await installMcpConfig({ agent, projectRoot: targetRoot, global: global2, mcp });
|
|
@@ -197373,7 +197506,11 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197373
197506
|
if (agent === "pi") {
|
|
197374
197507
|
v3.info("pi: Pi has no MCP config lane in the current CLI; use the installed package extension and skill.");
|
|
197375
197508
|
} else if (agent === "grok") {
|
|
197376
|
-
|
|
197509
|
+
if (plan.mcp === "http") {
|
|
197510
|
+
v3.info("grok: wrote [mcp_servers.memorix] HTTP URL in Grok config.toml. Start the control plane with `memorix background start`.");
|
|
197511
|
+
} else {
|
|
197512
|
+
v3.info("grok: lifecycle hooks write ~/.grok/hooks/memorix.json. MCP stays host-owned unless you pass `--mcp http`.");
|
|
197513
|
+
}
|
|
197377
197514
|
if (!global2) v3.info("grok: project hooks require `/hooks-trust` or launching Grok with `--trust`.");
|
|
197378
197515
|
} else if (plan.mcp === "stdio") {
|
|
197379
197516
|
v3.info(`${agent}: MCP server command is \`memorix serve\``);
|
|
@@ -197400,7 +197537,7 @@ function printSetupIntegrationRows() {
|
|
|
197400
197537
|
].join("\n");
|
|
197401
197538
|
ye2(table, "Memorix integrations");
|
|
197402
197539
|
}
|
|
197403
|
-
var import_yaml5, PLUGIN_PACKAGE_AGENTS, EXTENSION_PACKAGE_AGENTS, PI_PACKAGE_AGENTS, OPENCLAW_BUNDLE_AGENTS, ANTIGRAVITY_PLUGIN_AGENTS, HERMES_PLUGIN_AGENTS, OMP_PACKAGE_AGENTS, CODEBUDDY_PLUGIN_AGENTS, PACKAGE_OWNED_INTEGRATION_AGENTS, SUPPORTED_SETUP_AGENTS, getSetupIntegrationRows2, setup_default;
|
|
197540
|
+
var import_yaml5, PLUGIN_PACKAGE_AGENTS, EXTENSION_PACKAGE_AGENTS, PI_PACKAGE_AGENTS, OPENCLAW_BUNDLE_AGENTS, ANTIGRAVITY_PLUGIN_AGENTS, HERMES_PLUGIN_AGENTS, OMP_PACKAGE_AGENTS, CODEBUDDY_PLUGIN_AGENTS, PACKAGE_OWNED_INTEGRATION_AGENTS, SUPPORTED_SETUP_AGENTS, getSetupIntegrationRows2, MEMORIX_HTTP_MCP_URL, setup_default;
|
|
197404
197541
|
var init_setup = __esm({
|
|
197405
197542
|
"src/cli/commands/setup.ts"() {
|
|
197406
197543
|
"use strict";
|
|
@@ -197424,6 +197561,7 @@ var init_setup = __esm({
|
|
|
197424
197561
|
init_dsh();
|
|
197425
197562
|
init_workbuddy();
|
|
197426
197563
|
init_codebuddy();
|
|
197564
|
+
init_grok();
|
|
197427
197565
|
init_registry2();
|
|
197428
197566
|
init_official_skills();
|
|
197429
197567
|
init_version();
|
|
@@ -197457,6 +197595,7 @@ var init_setup = __esm({
|
|
|
197457
197595
|
"grok"
|
|
197458
197596
|
];
|
|
197459
197597
|
getSetupIntegrationRows2 = getSetupIntegrationRows;
|
|
197598
|
+
MEMORIX_HTTP_MCP_URL = "http://localhost:3211/mcp";
|
|
197460
197599
|
setup_default = defineCommand({
|
|
197461
197600
|
meta: {
|
|
197462
197601
|
name: "setup",
|
|
@@ -197532,7 +197671,7 @@ var init_setup = __esm({
|
|
|
197532
197671
|
{ value: "codex", label: "Codex", hint: "plugin package + marketplace + MCP" },
|
|
197533
197672
|
{ value: "copilot", label: "GitHub Copilot CLI", hint: "plugin package + hooks + MCP" },
|
|
197534
197673
|
{ value: "cursor", label: "Cursor", hint: "MCP + rules + skills" },
|
|
197535
|
-
{ value: "grok", label: "Grok Build", hint: "hooks file; MCP
|
|
197674
|
+
{ value: "grok", label: "Grok Build", hint: "hooks file; MCP host-owned unless --mcp http" },
|
|
197536
197675
|
{ value: "gemini-cli", label: "Gemini CLI", hint: "extension + MCP" },
|
|
197537
197676
|
{ value: "openclaw", label: "OpenClaw", hint: "bundle + hook pack + skills + MCP" },
|
|
197538
197677
|
{ value: "hermes", label: "Hermes Agent", hint: "plugin + hooks + commands + skills + MCP" },
|
|
@@ -197598,7 +197737,7 @@ var config_path_exports = {};
|
|
|
197598
197737
|
__export(config_path_exports, {
|
|
197599
197738
|
default: () => config_path_default
|
|
197600
197739
|
});
|
|
197601
|
-
import { homedir as
|
|
197740
|
+
import { homedir as homedir29 } from "os";
|
|
197602
197741
|
var config_path_default;
|
|
197603
197742
|
var init_config_path = __esm({
|
|
197604
197743
|
"src/cli/commands/config-path.ts"() {
|
|
@@ -197616,7 +197755,7 @@ var init_config_path = __esm({
|
|
|
197616
197755
|
run: async () => {
|
|
197617
197756
|
const project = detectProject(process.cwd());
|
|
197618
197757
|
ye2([
|
|
197619
|
-
`Global: ${getGlobalConfigTomlPath(
|
|
197758
|
+
`Global: ${getGlobalConfigTomlPath(homedir29())}`,
|
|
197620
197759
|
`Project: ${project ? getProjectConfigTomlPath(project.rootPath) : "not inside a git project"}`
|
|
197621
197760
|
].join("\n"), "Memorix config");
|
|
197622
197761
|
}
|
|
@@ -197692,7 +197831,7 @@ __export(config_migrate_exports, {
|
|
|
197692
197831
|
serializeResolvedConfigToToml: () => serializeResolvedConfigToToml
|
|
197693
197832
|
});
|
|
197694
197833
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
197695
|
-
import { homedir as
|
|
197834
|
+
import { homedir as homedir30 } from "os";
|
|
197696
197835
|
import { dirname as dirname4 } from "path";
|
|
197697
197836
|
function serializeResolvedConfigToToml(config2, options = {}) {
|
|
197698
197837
|
const includeSecrets = options.includeSecrets === true;
|
|
@@ -197791,7 +197930,7 @@ var init_config_migrate = __esm({
|
|
|
197791
197930
|
v3.error("Not inside a git project. Use `memorix config migrate --global` for global config.");
|
|
197792
197931
|
return;
|
|
197793
197932
|
}
|
|
197794
|
-
const target = isGlobal ? getGlobalConfigTomlPath(
|
|
197933
|
+
const target = isGlobal ? getGlobalConfigTomlPath(homedir30()) : getProjectConfigTomlPath(project.rootPath);
|
|
197795
197934
|
const resolved = getResolvedConfig({ projectRoot: project?.rootPath ?? null });
|
|
197796
197935
|
const content = serializeResolvedConfigToToml(resolved, { includeSecrets: isGlobal });
|
|
197797
197936
|
mkdirSync4(dirname4(target), { recursive: true });
|
|
@@ -197803,8 +197942,8 @@ var init_config_migrate = __esm({
|
|
|
197803
197942
|
});
|
|
197804
197943
|
|
|
197805
197944
|
// src/cli/commands/integrate-shared.ts
|
|
197806
|
-
import { homedir as
|
|
197807
|
-
function getIntegrationTargetRoot(useGlobalDefaults, cwd, homeDir =
|
|
197945
|
+
import { homedir as homedir31 } from "os";
|
|
197946
|
+
function getIntegrationTargetRoot(useGlobalDefaults, cwd, homeDir = homedir31()) {
|
|
197808
197947
|
return useGlobalDefaults ? homeDir : cwd;
|
|
197809
197948
|
}
|
|
197810
197949
|
function getIntegrationScopeLabel(useGlobalDefaults) {
|
|
@@ -199066,7 +199205,7 @@ __export(external_provider_exports, {
|
|
|
199066
199205
|
});
|
|
199067
199206
|
import { spawn as spawn5 } from "child_process";
|
|
199068
199207
|
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
199069
|
-
import { delimiter, dirname as dirname5, extname, isAbsolute as isAbsolute4, join as
|
|
199208
|
+
import { delimiter, dirname as dirname5, extname, isAbsolute as isAbsolute4, join as join25, relative as relative4, resolve as resolve6, sep as sep2 } from "path";
|
|
199070
199209
|
function boundedTimeout(value) {
|
|
199071
199210
|
if (!Number.isFinite(value)) return DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS;
|
|
199072
199211
|
return Math.max(100, Math.min(Math.floor(value), MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS));
|
|
@@ -199095,10 +199234,10 @@ function resolveCommandCandidates(command) {
|
|
|
199095
199234
|
}
|
|
199096
199235
|
const names = extname(requested) ? [requested] : process.platform === "win32" ? [requested + ".exe", requested + ".cmd", requested + ".js", requested] : [requested];
|
|
199097
199236
|
const directories = [
|
|
199098
|
-
...process.platform === "win32" && process.env.LOCALAPPDATA ? [
|
|
199237
|
+
...process.platform === "win32" && process.env.LOCALAPPDATA ? [join25(process.env.LOCALAPPDATA, "codegraph", "current", "bin")] : [],
|
|
199099
199238
|
...(process.env.PATH ?? "").split(delimiter).filter(Boolean)
|
|
199100
199239
|
];
|
|
199101
|
-
return [...new Set(directories.flatMap((directory) => names.map((name) =>
|
|
199240
|
+
return [...new Set(directories.flatMap((directory) => names.map((name) => join25(directory, name))))];
|
|
199102
199241
|
}
|
|
199103
199242
|
function resolveLaunch(command) {
|
|
199104
199243
|
for (const candidate2 of resolveCommandCandidates(command)) {
|
|
@@ -199106,8 +199245,8 @@ function resolveLaunch(command) {
|
|
|
199106
199245
|
const extension3 = extname(candidate2).toLowerCase();
|
|
199107
199246
|
if (extension3 === ".cmd") {
|
|
199108
199247
|
const bundleRoot = resolve6(dirname5(candidate2), "..");
|
|
199109
|
-
const node =
|
|
199110
|
-
const entry =
|
|
199248
|
+
const node = join25(bundleRoot, "node.exe");
|
|
199249
|
+
const entry = join25(bundleRoot, "lib", "dist", "bin", "codegraph.js");
|
|
199111
199250
|
if (existsSync19(node) && existsSync19(entry)) {
|
|
199112
199251
|
return { executable: node, prefixArgs: ["--liftoff-only", entry] };
|
|
199113
199252
|
}
|
|
@@ -199265,7 +199404,7 @@ async function inspectExternalCodeGraph(input) {
|
|
|
199265
199404
|
const health2 = { state: "disabled", reason: "External CodeGraph is disabled by configuration." };
|
|
199266
199405
|
return { health: health2, quality: providerQuality({ mode, health: health2 }) };
|
|
199267
199406
|
}
|
|
199268
|
-
if (!existsSync19(
|
|
199407
|
+
if (!existsSync19(join25(input.projectRoot, ".codegraph"))) {
|
|
199269
199408
|
const health2 = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
|
|
199270
199409
|
return { health: health2, quality: providerQuality({ mode, health: health2 }) };
|
|
199271
199410
|
}
|
|
@@ -199300,7 +199439,7 @@ async function runExternalCodeGraphLifecycle(input) {
|
|
|
199300
199439
|
quality: providerQuality({ mode, health })
|
|
199301
199440
|
};
|
|
199302
199441
|
}
|
|
199303
|
-
const indexPath =
|
|
199442
|
+
const indexPath = join25(input.projectRoot, ".codegraph");
|
|
199304
199443
|
if (input.action === "sync" && !existsSync19(indexPath)) {
|
|
199305
199444
|
const health = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
|
|
199306
199445
|
return {
|
|
@@ -200126,6 +200265,7 @@ var init_project_context = __esm({
|
|
|
200126
200265
|
// src/store/graph-store.ts
|
|
200127
200266
|
var graph_store_exports = {};
|
|
200128
200267
|
__export(graph_store_exports, {
|
|
200268
|
+
DEFAULT_GRAPH_PROJECT_ID: () => DEFAULT_GRAPH_PROJECT_ID,
|
|
200129
200269
|
GraphSqliteStore: () => GraphSqliteStore,
|
|
200130
200270
|
getGraphStore: () => getGraphStore,
|
|
200131
200271
|
initGraphStore: () => initGraphStore,
|
|
@@ -200155,37 +200295,106 @@ function entityToRow(entity) {
|
|
|
200155
200295
|
observations: JSON.stringify(entity.observations ?? [])
|
|
200156
200296
|
};
|
|
200157
200297
|
}
|
|
200158
|
-
|
|
200159
|
-
|
|
200160
|
-
|
|
200161
|
-
|
|
200162
|
-
|
|
200163
|
-
|
|
200298
|
+
function ensureScopedGraphSchema(db2) {
|
|
200299
|
+
const entityColumns = db2.prepare("PRAGMA table_info(graph_entities)").all();
|
|
200300
|
+
if (!entityColumns.some((column) => column.name === "projectId")) {
|
|
200301
|
+
const migrate = db2.transaction(() => {
|
|
200302
|
+
db2.exec("ALTER TABLE graph_entities RENAME TO graph_entities_legacy_unscoped");
|
|
200303
|
+
db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
|
|
200304
|
+
db2.exec(`INSERT INTO graph_entities (projectId, name, entityType, observations)
|
|
200305
|
+
SELECT '${LEGACY_GRAPH_PROJECT_ID}', name, entityType, observations
|
|
200306
|
+
FROM graph_entities_legacy_unscoped`);
|
|
200307
|
+
db2.exec("DROP TABLE graph_entities_legacy_unscoped");
|
|
200308
|
+
});
|
|
200309
|
+
migrate();
|
|
200310
|
+
} else {
|
|
200311
|
+
db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
|
|
200312
|
+
}
|
|
200313
|
+
const relationColumns = db2.prepare("PRAGMA table_info(graph_relations)").all();
|
|
200314
|
+
if (!relationColumns.some((column) => column.name === "projectId")) {
|
|
200315
|
+
const migrate = db2.transaction(() => {
|
|
200316
|
+
db2.exec("ALTER TABLE graph_relations RENAME TO graph_relations_legacy_unscoped");
|
|
200317
|
+
db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
|
|
200318
|
+
db2.exec(`INSERT INTO graph_relations (projectId, from_entity, to_entity, relationType)
|
|
200319
|
+
SELECT '${LEGACY_GRAPH_PROJECT_ID}', from_entity, to_entity, relationType
|
|
200320
|
+
FROM graph_relations_legacy_unscoped`);
|
|
200321
|
+
db2.exec("DROP TABLE graph_relations_legacy_unscoped");
|
|
200322
|
+
});
|
|
200323
|
+
migrate();
|
|
200324
|
+
} else {
|
|
200325
|
+
db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
|
|
200326
|
+
}
|
|
200327
|
+
}
|
|
200328
|
+
function graphStoreKey(dataDir, projectId) {
|
|
200329
|
+
return `${path34.resolve(dataDir)}\0${projectId}`;
|
|
200164
200330
|
}
|
|
200165
|
-
function
|
|
200166
|
-
|
|
200167
|
-
|
|
200331
|
+
async function initGraphStore(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
|
|
200332
|
+
const key = graphStoreKey(dataDir, projectId);
|
|
200333
|
+
const existing = _graphStores.get(key);
|
|
200334
|
+
if (existing) {
|
|
200335
|
+
_graphStores.delete(key);
|
|
200336
|
+
_graphStores.set(key, existing);
|
|
200337
|
+
_activeGraphKey = key;
|
|
200338
|
+
return existing;
|
|
200339
|
+
}
|
|
200340
|
+
const store2 = new GraphSqliteStore();
|
|
200341
|
+
await store2.init(dataDir, projectId);
|
|
200342
|
+
_graphStores.set(key, store2);
|
|
200343
|
+
while (_graphStores.size > MAX_GRAPH_STORE_CACHE) {
|
|
200344
|
+
const oldest = _graphStores.keys().next().value;
|
|
200345
|
+
if (!oldest) break;
|
|
200346
|
+
_graphStores.delete(oldest);
|
|
200347
|
+
}
|
|
200348
|
+
_activeGraphKey = key;
|
|
200349
|
+
return store2;
|
|
200350
|
+
}
|
|
200351
|
+
function getGraphStore(dataDir, projectId) {
|
|
200352
|
+
const key = dataDir && projectId ? graphStoreKey(dataDir, projectId) : _activeGraphKey;
|
|
200353
|
+
const store2 = key ? _graphStores.get(key) : void 0;
|
|
200354
|
+
if (!store2) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
|
|
200355
|
+
return store2;
|
|
200168
200356
|
}
|
|
200169
200357
|
function resetGraphStore() {
|
|
200170
|
-
|
|
200358
|
+
for (const store2 of _graphStores.values()) {
|
|
200171
200359
|
try {
|
|
200172
|
-
|
|
200360
|
+
store2.close();
|
|
200173
200361
|
} catch {
|
|
200174
200362
|
}
|
|
200175
200363
|
}
|
|
200176
|
-
|
|
200177
|
-
|
|
200364
|
+
_graphStores.clear();
|
|
200365
|
+
_activeGraphKey = null;
|
|
200178
200366
|
}
|
|
200179
|
-
var GraphSqliteStore,
|
|
200367
|
+
var DEFAULT_GRAPH_PROJECT_ID, LEGACY_GRAPH_PROJECT_ID, MAX_GRAPH_STORE_CACHE, CREATE_SCOPED_GRAPH_ENTITIES, CREATE_SCOPED_GRAPH_RELATIONS, GraphSqliteStore, _graphStores, _activeGraphKey;
|
|
200180
200368
|
var init_graph_store = __esm({
|
|
200181
200369
|
"src/store/graph-store.ts"() {
|
|
200182
200370
|
"use strict";
|
|
200183
200371
|
init_esm_shims();
|
|
200184
200372
|
init_sqlite_db();
|
|
200185
200373
|
init_persistence();
|
|
200374
|
+
DEFAULT_GRAPH_PROJECT_ID = "__default__";
|
|
200375
|
+
LEGACY_GRAPH_PROJECT_ID = "__legacy__";
|
|
200376
|
+
MAX_GRAPH_STORE_CACHE = 32;
|
|
200377
|
+
CREATE_SCOPED_GRAPH_ENTITIES = `
|
|
200378
|
+
CREATE TABLE IF NOT EXISTS graph_entities (
|
|
200379
|
+
projectId TEXT NOT NULL,
|
|
200380
|
+
name TEXT NOT NULL,
|
|
200381
|
+
entityType TEXT NOT NULL DEFAULT '',
|
|
200382
|
+
observations TEXT NOT NULL DEFAULT '[]',
|
|
200383
|
+
PRIMARY KEY (projectId, name)
|
|
200384
|
+
);`;
|
|
200385
|
+
CREATE_SCOPED_GRAPH_RELATIONS = `
|
|
200386
|
+
CREATE TABLE IF NOT EXISTS graph_relations (
|
|
200387
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
200388
|
+
projectId TEXT NOT NULL,
|
|
200389
|
+
from_entity TEXT NOT NULL,
|
|
200390
|
+
to_entity TEXT NOT NULL,
|
|
200391
|
+
relationType TEXT NOT NULL DEFAULT '',
|
|
200392
|
+
UNIQUE(projectId, from_entity, to_entity, relationType)
|
|
200393
|
+
);`;
|
|
200186
200394
|
GraphSqliteStore = class {
|
|
200187
200395
|
db = null;
|
|
200188
200396
|
dataDir = "";
|
|
200397
|
+
projectId = DEFAULT_GRAPH_PROJECT_ID;
|
|
200189
200398
|
stmtInsertEntity = null;
|
|
200190
200399
|
stmtUpdateEntityObs = null;
|
|
200191
200400
|
stmtDeleteEntity = null;
|
|
@@ -200195,45 +200404,89 @@ var init_graph_store = __esm({
|
|
|
200195
200404
|
stmtDeleteRelation = null;
|
|
200196
200405
|
stmtDeleteRelationsByEntity = null;
|
|
200197
200406
|
stmtSelectAllRelations = null;
|
|
200198
|
-
async init(dataDir) {
|
|
200407
|
+
async init(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
|
|
200199
200408
|
this.dataDir = dataDir;
|
|
200409
|
+
this.projectId = projectId;
|
|
200200
200410
|
this.db = getDatabase(dataDir);
|
|
200411
|
+
ensureScopedGraphSchema(this.db);
|
|
200201
200412
|
this.stmtInsertEntity = this.db.prepare(
|
|
200202
|
-
`INSERT OR REPLACE INTO graph_entities (name, entityType, observations) VALUES (@name, @entityType, @observations)`
|
|
200413
|
+
`INSERT OR REPLACE INTO graph_entities (projectId, name, entityType, observations) VALUES (@projectId, @name, @entityType, @observations)`
|
|
200203
200414
|
);
|
|
200204
200415
|
this.stmtUpdateEntityObs = this.db.prepare(
|
|
200205
|
-
`UPDATE graph_entities SET observations = @observations WHERE name = @name`
|
|
200416
|
+
`UPDATE graph_entities SET observations = @observations WHERE projectId = @projectId AND name = @name`
|
|
200206
200417
|
);
|
|
200207
|
-
this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE name = ?`);
|
|
200208
|
-
this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities`);
|
|
200209
|
-
this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE name = ?`);
|
|
200418
|
+
this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE projectId = ? AND name = ?`);
|
|
200419
|
+
this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? ORDER BY name`);
|
|
200420
|
+
this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? AND name = ?`);
|
|
200210
200421
|
this.stmtInsertRelation = this.db.prepare(
|
|
200211
|
-
`INSERT OR IGNORE INTO graph_relations (from_entity, to_entity, relationType) VALUES (@from, @to, @relationType)`
|
|
200422
|
+
`INSERT OR IGNORE INTO graph_relations (projectId, from_entity, to_entity, relationType) VALUES (@projectId, @from, @to, @relationType)`
|
|
200212
200423
|
);
|
|
200213
200424
|
this.stmtDeleteRelation = this.db.prepare(
|
|
200214
|
-
`DELETE FROM graph_relations WHERE from_entity = ? AND to_entity = ? AND relationType = ?`
|
|
200425
|
+
`DELETE FROM graph_relations WHERE projectId = ? AND from_entity = ? AND to_entity = ? AND relationType = ?`
|
|
200215
200426
|
);
|
|
200216
200427
|
this.stmtDeleteRelationsByEntity = this.db.prepare(
|
|
200217
|
-
`DELETE FROM graph_relations WHERE from_entity = ? OR to_entity =
|
|
200428
|
+
`DELETE FROM graph_relations WHERE projectId = ? AND (from_entity = ? OR to_entity = ?)`
|
|
200218
200429
|
);
|
|
200219
|
-
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations`);
|
|
200430
|
+
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations WHERE projectId = ? ORDER BY id`);
|
|
200220
200431
|
await this.migrateFromJsonlIfNeeded();
|
|
200221
200432
|
}
|
|
200222
200433
|
async migrateFromJsonlIfNeeded() {
|
|
200223
|
-
const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities
|
|
200434
|
+
const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities WHERE projectId = ?`).get(this.projectId);
|
|
200435
|
+
const totalCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities`).get();
|
|
200224
200436
|
if (count3.cnt > 0) return;
|
|
200437
|
+
if (totalCount.cnt > 0) return;
|
|
200225
200438
|
const jsonlPath = path34.join(this.dataDir, "graph.jsonl");
|
|
200226
200439
|
if (!fs20.existsSync(jsonlPath)) return;
|
|
200227
200440
|
try {
|
|
200228
200441
|
const data = await loadGraphJsonl(this.dataDir);
|
|
200229
200442
|
if (data.entities.length === 0 && data.relations.length === 0) return;
|
|
200230
200443
|
console.error(`[memorix] Migrating graph from JSONL to SQLite (${data.entities.length} entities, ${data.relations.length} relations)...`);
|
|
200444
|
+
const observations2 = this.db.prepare("SELECT id, projectId FROM observations").all();
|
|
200445
|
+
const projectByObservationId = new Map(observations2.map((row) => [row.id, row.projectId]));
|
|
200446
|
+
const projectsByEntity = /* @__PURE__ */ new Map();
|
|
200447
|
+
for (const entity of data.entities) {
|
|
200448
|
+
const projects = /* @__PURE__ */ new Set();
|
|
200449
|
+
for (const content of entity.observations ?? []) {
|
|
200450
|
+
const match2 = /^\[#(\d+)\]/.exec(content);
|
|
200451
|
+
const projectId = match2 ? projectByObservationId.get(Number(match2[1])) : void 0;
|
|
200452
|
+
if (projectId) projects.add(projectId);
|
|
200453
|
+
}
|
|
200454
|
+
projectsByEntity.set(entity.name, projects);
|
|
200455
|
+
}
|
|
200456
|
+
let changed = true;
|
|
200457
|
+
while (changed) {
|
|
200458
|
+
changed = false;
|
|
200459
|
+
for (const relation of data.relations) {
|
|
200460
|
+
const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
|
|
200461
|
+
const to2 = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
|
|
200462
|
+
if (from.size > 0 && to2.size === 0) {
|
|
200463
|
+
projectsByEntity.set(relation.to, new Set(from));
|
|
200464
|
+
changed = true;
|
|
200465
|
+
} else if (to2.size > 0 && from.size === 0) {
|
|
200466
|
+
projectsByEntity.set(relation.from, new Set(to2));
|
|
200467
|
+
changed = true;
|
|
200468
|
+
}
|
|
200469
|
+
}
|
|
200470
|
+
}
|
|
200471
|
+
const entityRows = data.entities.flatMap((entity) => {
|
|
200472
|
+
const projects = projectsByEntity.get(entity.name) ?? /* @__PURE__ */ new Set();
|
|
200473
|
+
const owners = projects.size > 0 ? [...projects] : [this.projectId];
|
|
200474
|
+
return owners.map((projectId) => ({ ...entityToRow(entity), projectId }));
|
|
200475
|
+
});
|
|
200476
|
+
const projectSet = new Set(entityRows.map((row) => String(row.projectId)));
|
|
200477
|
+
const relationRows = data.relations.flatMap((relation) => {
|
|
200478
|
+
const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
|
|
200479
|
+
const to2 = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
|
|
200480
|
+
const shared = [...from].filter((projectId) => to2.has(projectId));
|
|
200481
|
+
const owners = shared.length > 0 ? shared : from.size === 0 && to2.size === 0 ? [this.projectId] : [];
|
|
200482
|
+
return owners.filter((projectId) => projectSet.has(projectId)).map((projectId) => ({ projectId, from: relation.from, to: relation.to, relationType: relation.relationType }));
|
|
200483
|
+
});
|
|
200231
200484
|
const insertAll = this.db.transaction(() => {
|
|
200232
|
-
for (const entity of
|
|
200233
|
-
this.stmtInsertEntity.run(
|
|
200485
|
+
for (const entity of entityRows) {
|
|
200486
|
+
this.stmtInsertEntity.run(entity);
|
|
200234
200487
|
}
|
|
200235
|
-
for (const
|
|
200236
|
-
this.stmtInsertRelation.run(
|
|
200488
|
+
for (const relation of relationRows) {
|
|
200489
|
+
this.stmtInsertRelation.run(relation);
|
|
200237
200490
|
}
|
|
200238
200491
|
});
|
|
200239
200492
|
insertAll();
|
|
@@ -200243,10 +200496,10 @@ var init_graph_store = __esm({
|
|
|
200243
200496
|
}
|
|
200244
200497
|
}
|
|
200245
200498
|
loadEntities() {
|
|
200246
|
-
return this.stmtSelectAllEntities.all().map(rowToEntity);
|
|
200499
|
+
return this.stmtSelectAllEntities.all(this.projectId).map(rowToEntity);
|
|
200247
200500
|
}
|
|
200248
200501
|
loadRelations() {
|
|
200249
|
-
return this.stmtSelectAllRelations.all().map((row) => ({
|
|
200502
|
+
return this.stmtSelectAllRelations.all(this.projectId).map((row) => ({
|
|
200250
200503
|
from: row.from_entity,
|
|
200251
200504
|
to: row.to_entity,
|
|
200252
200505
|
relationType: row.relationType
|
|
@@ -200255,7 +200508,7 @@ var init_graph_store = __esm({
|
|
|
200255
200508
|
insertEntities(entities) {
|
|
200256
200509
|
const insertAll = this.db.transaction(() => {
|
|
200257
200510
|
for (const entity of entities) {
|
|
200258
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
200511
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
200259
200512
|
}
|
|
200260
200513
|
});
|
|
200261
200514
|
insertAll();
|
|
@@ -200263,7 +200516,7 @@ var init_graph_store = __esm({
|
|
|
200263
200516
|
insertRelations(relations) {
|
|
200264
200517
|
const insertAll = this.db.transaction(() => {
|
|
200265
200518
|
for (const rel of relations) {
|
|
200266
|
-
this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
200519
|
+
this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
200267
200520
|
}
|
|
200268
200521
|
});
|
|
200269
200522
|
insertAll();
|
|
@@ -200271,8 +200524,8 @@ var init_graph_store = __esm({
|
|
|
200271
200524
|
deleteEntities(names) {
|
|
200272
200525
|
const deleteAll = this.db.transaction(() => {
|
|
200273
200526
|
for (const name of names) {
|
|
200274
|
-
this.stmtDeleteRelationsByEntity.run(name, name);
|
|
200275
|
-
this.stmtDeleteEntity.run(name);
|
|
200527
|
+
this.stmtDeleteRelationsByEntity.run(this.projectId, name, name);
|
|
200528
|
+
this.stmtDeleteEntity.run(this.projectId, name);
|
|
200276
200529
|
}
|
|
200277
200530
|
});
|
|
200278
200531
|
deleteAll();
|
|
@@ -200280,7 +200533,7 @@ var init_graph_store = __esm({
|
|
|
200280
200533
|
deleteRelations(relations) {
|
|
200281
200534
|
const deleteAll = this.db.transaction(() => {
|
|
200282
200535
|
for (const rel of relations) {
|
|
200283
|
-
this.stmtDeleteRelation.run(rel.from, rel.to, rel.relationType);
|
|
200536
|
+
this.stmtDeleteRelation.run(this.projectId, rel.from, rel.to, rel.relationType);
|
|
200284
200537
|
}
|
|
200285
200538
|
});
|
|
200286
200539
|
deleteAll();
|
|
@@ -200288,13 +200541,13 @@ var init_graph_store = __esm({
|
|
|
200288
200541
|
addObservations(updates) {
|
|
200289
200542
|
const updateAll = this.db.transaction(() => {
|
|
200290
200543
|
for (const u5 of updates) {
|
|
200291
|
-
const row = this.stmtSelectEntityByName.get(u5.entityName);
|
|
200544
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, u5.entityName);
|
|
200292
200545
|
if (!row) continue;
|
|
200293
200546
|
const existing = safeJsonParse3(row.observations, []);
|
|
200294
200547
|
const newObs = u5.contents.filter((c6) => !existing.includes(c6));
|
|
200295
200548
|
if (newObs.length > 0) {
|
|
200296
200549
|
existing.push(...newObs);
|
|
200297
|
-
this.stmtUpdateEntityObs.run({ name: u5.entityName, observations: JSON.stringify(existing) });
|
|
200550
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: u5.entityName, observations: JSON.stringify(existing) });
|
|
200298
200551
|
}
|
|
200299
200552
|
}
|
|
200300
200553
|
});
|
|
@@ -200303,11 +200556,11 @@ var init_graph_store = __esm({
|
|
|
200303
200556
|
deleteObservations(deletions) {
|
|
200304
200557
|
const deleteAll = this.db.transaction(() => {
|
|
200305
200558
|
for (const d4 of deletions) {
|
|
200306
|
-
const row = this.stmtSelectEntityByName.get(d4.entityName);
|
|
200559
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, d4.entityName);
|
|
200307
200560
|
if (!row) continue;
|
|
200308
200561
|
const existing = safeJsonParse3(row.observations, []);
|
|
200309
200562
|
const filtered = existing.filter((o4) => !d4.observations.includes(o4));
|
|
200310
|
-
this.stmtUpdateEntityObs.run({ name: d4.entityName, observations: JSON.stringify(filtered) });
|
|
200563
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: d4.entityName, observations: JSON.stringify(filtered) });
|
|
200311
200564
|
}
|
|
200312
200565
|
});
|
|
200313
200566
|
deleteAll();
|
|
@@ -200318,13 +200571,13 @@ var init_graph_store = __esm({
|
|
|
200318
200571
|
*/
|
|
200319
200572
|
replaceAll(entities, relations) {
|
|
200320
200573
|
const replaceAll = this.db.transaction(() => {
|
|
200321
|
-
this.db.prepare("DELETE FROM graph_relations").run();
|
|
200322
|
-
this.db.prepare("DELETE FROM graph_entities").run();
|
|
200574
|
+
this.db.prepare("DELETE FROM graph_relations WHERE projectId = ?").run(this.projectId);
|
|
200575
|
+
this.db.prepare("DELETE FROM graph_entities WHERE projectId = ?").run(this.projectId);
|
|
200323
200576
|
for (const entity of entities) {
|
|
200324
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
200577
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
200325
200578
|
}
|
|
200326
200579
|
for (const rel of relations) {
|
|
200327
|
-
this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
200580
|
+
this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
200328
200581
|
}
|
|
200329
200582
|
});
|
|
200330
200583
|
replaceAll();
|
|
@@ -200332,8 +200585,8 @@ var init_graph_store = __esm({
|
|
|
200332
200585
|
close() {
|
|
200333
200586
|
}
|
|
200334
200587
|
};
|
|
200335
|
-
|
|
200336
|
-
|
|
200588
|
+
_graphStores = /* @__PURE__ */ new Map();
|
|
200589
|
+
_activeGraphKey = null;
|
|
200337
200590
|
}
|
|
200338
200591
|
});
|
|
200339
200592
|
|
|
@@ -200348,11 +200601,14 @@ var init_graph = __esm({
|
|
|
200348
200601
|
entities = [];
|
|
200349
200602
|
relations = [];
|
|
200350
200603
|
projectDir;
|
|
200604
|
+
projectId;
|
|
200605
|
+
store = null;
|
|
200351
200606
|
initialized = false;
|
|
200352
200607
|
/** Index: lowercase entity name → Entity for O(1) lookups */
|
|
200353
200608
|
entityIndex = /* @__PURE__ */ new Map();
|
|
200354
|
-
constructor(projectDir2) {
|
|
200609
|
+
constructor(projectDir2, projectId = "__default__") {
|
|
200355
200610
|
this.projectDir = projectDir2;
|
|
200611
|
+
this.projectId = projectId;
|
|
200356
200612
|
}
|
|
200357
200613
|
/** Rebuild the entity name index */
|
|
200358
200614
|
rebuildIndex() {
|
|
@@ -200364,10 +200620,9 @@ var init_graph = __esm({
|
|
|
200364
200620
|
/** Load graph from SQLite on first access */
|
|
200365
200621
|
async init() {
|
|
200366
200622
|
if (this.initialized) return;
|
|
200367
|
-
await initGraphStore(this.projectDir);
|
|
200368
|
-
|
|
200369
|
-
this.
|
|
200370
|
-
this.relations = store2.loadRelations();
|
|
200623
|
+
this.store = await initGraphStore(this.projectDir, this.projectId);
|
|
200624
|
+
this.entities = this.store.loadEntities();
|
|
200625
|
+
this.relations = this.store.loadRelations();
|
|
200371
200626
|
this.rebuildIndex();
|
|
200372
200627
|
this.initialized = true;
|
|
200373
200628
|
}
|
|
@@ -200381,8 +200636,8 @@ var init_graph = __esm({
|
|
|
200381
200636
|
}
|
|
200382
200637
|
/** Persist current state to SQLite */
|
|
200383
200638
|
async save() {
|
|
200384
|
-
|
|
200385
|
-
|
|
200639
|
+
if (!this.store) throw new Error("[memorix] KnowledgeGraphManager not initialized");
|
|
200640
|
+
this.store.replaceAll(this.entities, this.relations);
|
|
200386
200641
|
}
|
|
200387
200642
|
/** Create new entities (skip duplicates by name) */
|
|
200388
200643
|
async createEntities(entities) {
|
|
@@ -200758,7 +201013,7 @@ async function getSessionContext(projectDir2, projectId, limit2 = 3, reader2) {
|
|
|
200758
201013
|
let graphNeighbors = [];
|
|
200759
201014
|
if (!reader2 && activeEntities.length > 0) {
|
|
200760
201015
|
try {
|
|
200761
|
-
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
201016
|
+
const graphMgr = new KnowledgeGraphManager(projectDir2, projectId);
|
|
200762
201017
|
await graphMgr.init();
|
|
200763
201018
|
const { relations } = await graphMgr.readGraph();
|
|
200764
201019
|
const activeSet = new Set(activeEntities.map((n3) => n3.toLowerCase()));
|
|
@@ -205266,8 +205521,8 @@ __export(engine_exports2, {
|
|
|
205266
205521
|
SkillsEngine: () => SkillsEngine
|
|
205267
205522
|
});
|
|
205268
205523
|
import { existsSync as existsSync21, readFileSync as readFileSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "fs";
|
|
205269
|
-
import { join as
|
|
205270
|
-
import { homedir as
|
|
205524
|
+
import { join as join26 } from "path";
|
|
205525
|
+
import { homedir as homedir32 } from "os";
|
|
205271
205526
|
var SKILLS_DIRS, SKILL_WORTHY_TYPES, MIN_OBS_FOR_SKILL, MIN_SCORE_FOR_SKILL, LOW_SIGNAL_TITLE_PATTERNS, LOW_SIGNAL_ENTITY_PATTERNS, COMMAND_TRACE_PATTERNS2, SkillsEngine;
|
|
205272
205527
|
var init_engine4 = __esm({
|
|
205273
205528
|
"src/skills/engine.ts"() {
|
|
@@ -205338,12 +205593,12 @@ var init_engine4 = __esm({
|
|
|
205338
205593
|
listSkills() {
|
|
205339
205594
|
const skills = [];
|
|
205340
205595
|
const seen = /* @__PURE__ */ new Set();
|
|
205341
|
-
const home =
|
|
205596
|
+
const home = homedir32();
|
|
205342
205597
|
for (const [agent, dirs] of Object.entries(SKILLS_DIRS)) {
|
|
205343
205598
|
for (const dir of dirs) {
|
|
205344
|
-
const paths = [
|
|
205599
|
+
const paths = [join26(this.projectRoot, dir)];
|
|
205345
205600
|
if (!this.skipGlobal) {
|
|
205346
|
-
paths.push(
|
|
205601
|
+
paths.push(join26(home, dir));
|
|
205347
205602
|
}
|
|
205348
205603
|
for (const skillsRoot of paths) {
|
|
205349
205604
|
if (!existsSync21(skillsRoot)) continue;
|
|
@@ -205353,7 +205608,7 @@ var init_engine4 = __esm({
|
|
|
205353
205608
|
if (!entry.isDirectory()) continue;
|
|
205354
205609
|
const name = entry.name;
|
|
205355
205610
|
if (seen.has(name)) continue;
|
|
205356
|
-
const skillMd =
|
|
205611
|
+
const skillMd = join26(skillsRoot, name, "SKILL.md");
|
|
205357
205612
|
if (!existsSync21(skillMd)) continue;
|
|
205358
205613
|
try {
|
|
205359
205614
|
const content = readFileSync13(skillMd, "utf-8");
|
|
@@ -205361,7 +205616,7 @@ var init_engine4 = __esm({
|
|
|
205361
205616
|
skills.push({
|
|
205362
205617
|
name,
|
|
205363
205618
|
description,
|
|
205364
|
-
sourcePath:
|
|
205619
|
+
sourcePath: join26(skillsRoot, name),
|
|
205365
205620
|
sourceAgent: agent,
|
|
205366
205621
|
content,
|
|
205367
205622
|
generated: false
|
|
@@ -205404,11 +205659,11 @@ var init_engine4 = __esm({
|
|
|
205404
205659
|
writeSkill(skill2, target) {
|
|
205405
205660
|
const dirs = SKILLS_DIRS[target];
|
|
205406
205661
|
if (!dirs || dirs.length === 0) return null;
|
|
205407
|
-
const targetDir =
|
|
205662
|
+
const targetDir = join26(this.projectRoot, dirs[0], skill2.name);
|
|
205408
205663
|
try {
|
|
205409
205664
|
mkdirSync5(targetDir, { recursive: true });
|
|
205410
|
-
writeFileSync5(
|
|
205411
|
-
return
|
|
205665
|
+
writeFileSync5(join26(targetDir, "SKILL.md"), skill2.content, "utf-8");
|
|
205666
|
+
return join26(dirs[0], skill2.name, "SKILL.md");
|
|
205412
205667
|
} catch {
|
|
205413
205668
|
return null;
|
|
205414
205669
|
}
|
|
@@ -206577,7 +206832,7 @@ var init_task_graph = __esm({
|
|
|
206577
206832
|
// src/orchestrate/context-collector.ts
|
|
206578
206833
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
206579
206834
|
import { readFileSync as readFileSync14, existsSync as existsSync22 } from "fs";
|
|
206580
|
-
import { join as
|
|
206835
|
+
import { join as join27 } from "path";
|
|
206581
206836
|
function normalizeEntryLimit(value, fallback) {
|
|
206582
206837
|
if (!Number.isSafeInteger(value) || value < 1) return fallback;
|
|
206583
206838
|
return Math.min(value, 500);
|
|
@@ -206652,7 +206907,7 @@ function collectFileTree(projectDir2, maxEntries) {
|
|
|
206652
206907
|
}
|
|
206653
206908
|
function collectDependencies(projectDir2) {
|
|
206654
206909
|
try {
|
|
206655
|
-
const pkgPath =
|
|
206910
|
+
const pkgPath = join27(projectDir2, "package.json");
|
|
206656
206911
|
if (!existsSync22(pkgPath)) return "";
|
|
206657
206912
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
206658
206913
|
const parts = [];
|
|
@@ -207989,7 +208244,7 @@ var init_tool_profile = __esm({
|
|
|
207989
208244
|
function getMcpServerInfo() {
|
|
207990
208245
|
return {
|
|
207991
208246
|
name: "memorix",
|
|
207992
|
-
version: true ? "1.9.
|
|
208247
|
+
version: true ? "1.9.5" : "1.0.1"
|
|
207993
208248
|
};
|
|
207994
208249
|
}
|
|
207995
208250
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -221795,8 +222050,8 @@ __export(engine_exports3, {
|
|
|
221795
222050
|
WorkspaceSyncEngine: () => WorkspaceSyncEngine
|
|
221796
222051
|
});
|
|
221797
222052
|
import { readFileSync as readFileSync16, readdirSync as readdirSync4, existsSync as existsSync25, cpSync, mkdirSync as mkdirSync7 } from "fs";
|
|
221798
|
-
import { join as
|
|
221799
|
-
import { homedir as
|
|
222053
|
+
import { join as join29 } from "path";
|
|
222054
|
+
import { homedir as homedir33 } from "os";
|
|
221800
222055
|
var WorkspaceSyncEngine;
|
|
221801
222056
|
var init_engine5 = __esm({
|
|
221802
222057
|
"src/workspace/engine.ts"() {
|
|
@@ -221868,9 +222123,9 @@ var init_engine5 = __esm({
|
|
|
221868
222123
|
const globalPath = adapter.getConfigPath();
|
|
221869
222124
|
const pathsToCheck = [configPath, globalPath];
|
|
221870
222125
|
if (target === "antigravity") {
|
|
221871
|
-
pathsToCheck.push(
|
|
221872
|
-
pathsToCheck.push(
|
|
221873
|
-
pathsToCheck.push(
|
|
222126
|
+
pathsToCheck.push(join29(this.projectRoot, ".gemini", "settings.json"));
|
|
222127
|
+
pathsToCheck.push(join29(homedir33(), ".gemini", "settings.json"));
|
|
222128
|
+
pathsToCheck.push(join29(homedir33(), ".gemini", "antigravity", "mcp_config.json"));
|
|
221874
222129
|
}
|
|
221875
222130
|
const merged = /* @__PURE__ */ new Map();
|
|
221876
222131
|
for (const path50 of pathsToCheck) {
|
|
@@ -221987,7 +222242,7 @@ var init_engine5 = __esm({
|
|
|
221987
222242
|
getTargetSkillsDir(target) {
|
|
221988
222243
|
const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
|
|
221989
222244
|
if (!dirs || dirs.length === 0) return null;
|
|
221990
|
-
return
|
|
222245
|
+
return join29(this.projectRoot, dirs[0]);
|
|
221991
222246
|
}
|
|
221992
222247
|
/**
|
|
221993
222248
|
* Scan all agent skills directories and collect unique skills.
|
|
@@ -221996,12 +222251,12 @@ var init_engine5 = __esm({
|
|
|
221996
222251
|
const skills = [];
|
|
221997
222252
|
const conflicts = [];
|
|
221998
222253
|
const seen = /* @__PURE__ */ new Map();
|
|
221999
|
-
const home =
|
|
222254
|
+
const home = homedir33();
|
|
222000
222255
|
for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
|
|
222001
222256
|
for (const dir of dirs) {
|
|
222002
222257
|
const paths = [
|
|
222003
|
-
|
|
222004
|
-
|
|
222258
|
+
join29(this.projectRoot, dir),
|
|
222259
|
+
join29(home, dir)
|
|
222005
222260
|
];
|
|
222006
222261
|
for (const skillsRoot of paths) {
|
|
222007
222262
|
if (!existsSync25(skillsRoot)) continue;
|
|
@@ -222009,7 +222264,7 @@ var init_engine5 = __esm({
|
|
|
222009
222264
|
const entries = readdirSync4(skillsRoot, { withFileTypes: true });
|
|
222010
222265
|
for (const entry of entries) {
|
|
222011
222266
|
if (!entry.isDirectory()) continue;
|
|
222012
|
-
const skillMd =
|
|
222267
|
+
const skillMd = join29(skillsRoot, entry.name, "SKILL.md");
|
|
222013
222268
|
if (!existsSync25(skillMd)) continue;
|
|
222014
222269
|
let description = "";
|
|
222015
222270
|
try {
|
|
@@ -222021,7 +222276,7 @@ var init_engine5 = __esm({
|
|
|
222021
222276
|
const newEntry = {
|
|
222022
222277
|
name: entry.name,
|
|
222023
222278
|
description,
|
|
222024
|
-
sourcePath:
|
|
222279
|
+
sourcePath: join29(skillsRoot, entry.name),
|
|
222025
222280
|
sourceAgent: agent
|
|
222026
222281
|
};
|
|
222027
222282
|
const existing = seen.get(entry.name);
|
|
@@ -222058,7 +222313,7 @@ var init_engine5 = __esm({
|
|
|
222058
222313
|
}
|
|
222059
222314
|
for (const skill2 of skills) {
|
|
222060
222315
|
if (skill2.sourceAgent === target) continue;
|
|
222061
|
-
const dest =
|
|
222316
|
+
const dest = join29(targetDir, skill2.name);
|
|
222062
222317
|
if (existsSync25(dest)) {
|
|
222063
222318
|
skipped.push(`${skill2.name} (already exists in ${target})`);
|
|
222064
222319
|
continue;
|
|
@@ -222074,13 +222329,13 @@ var init_engine5 = __esm({
|
|
|
222074
222329
|
}
|
|
222075
222330
|
scanWorkflows() {
|
|
222076
222331
|
const workflows = [];
|
|
222077
|
-
const wfDir =
|
|
222332
|
+
const wfDir = join29(this.projectRoot, ".windsurf", "workflows");
|
|
222078
222333
|
if (!existsSync25(wfDir)) return workflows;
|
|
222079
222334
|
try {
|
|
222080
222335
|
const files = readdirSync4(wfDir).filter((f7) => f7.endsWith(".md"));
|
|
222081
222336
|
for (const file2 of files) {
|
|
222082
222337
|
try {
|
|
222083
|
-
const content = readFileSync16(
|
|
222338
|
+
const content = readFileSync16(join29(wfDir, file2), "utf-8");
|
|
222084
222339
|
workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file2, content));
|
|
222085
222340
|
} catch {
|
|
222086
222341
|
}
|
|
@@ -222355,7 +222610,7 @@ __export(behavior_exports, {
|
|
|
222355
222610
|
resetBehaviorConfigCache: () => resetBehaviorConfigCache
|
|
222356
222611
|
});
|
|
222357
222612
|
import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
|
|
222358
|
-
import { homedir as
|
|
222613
|
+
import { homedir as homedir34 } from "os";
|
|
222359
222614
|
function isSessionInject(value) {
|
|
222360
222615
|
return value === "full" || value === "minimal" || value === "silent";
|
|
222361
222616
|
}
|
|
@@ -222373,7 +222628,7 @@ function legacyBehavior(homeDir) {
|
|
|
222373
222628
|
}
|
|
222374
222629
|
}
|
|
222375
222630
|
function getBehaviorConfig(options = {}) {
|
|
222376
|
-
const homeDir = options.homeDir ??
|
|
222631
|
+
const homeDir = options.homeDir ?? homedir34();
|
|
222377
222632
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
222378
222633
|
const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
222379
222634
|
const cached2 = cache4.get(cacheKey2);
|
|
@@ -223611,8 +223866,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223611
223866
|
break;
|
|
223612
223867
|
}
|
|
223613
223868
|
case "/graph": {
|
|
223614
|
-
await initGraphStore(effectiveDataDir);
|
|
223615
|
-
const gStore = getGraphStore();
|
|
223869
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
223616
223870
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223617
223871
|
const graphObs = filterDashboardObservations(
|
|
223618
223872
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
@@ -223656,8 +223910,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223656
223910
|
break;
|
|
223657
223911
|
}
|
|
223658
223912
|
case "/stats": {
|
|
223659
|
-
await initGraphStore(effectiveDataDir);
|
|
223660
|
-
const graph = { entities:
|
|
223913
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
223914
|
+
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223661
223915
|
const observations2 = filterDashboardObservations(
|
|
223662
223916
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
223663
223917
|
effectiveProjectId
|
|
@@ -223850,15 +224104,15 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223850
224104
|
case "/knowledge-graph": {
|
|
223851
224105
|
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
223852
224106
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
223853
|
-
const { initGraphStore: initGraphStore2
|
|
224107
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
223854
224108
|
await initMiniSkillStore2(effectiveDataDir);
|
|
223855
|
-
await initGraphStore2(effectiveDataDir);
|
|
224109
|
+
const gStore = await initGraphStore2(effectiveDataDir, effectiveProjectId);
|
|
223856
224110
|
const allObs = filterDashboardObservations(
|
|
223857
224111
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
223858
224112
|
effectiveProjectId
|
|
223859
224113
|
);
|
|
223860
224114
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
223861
|
-
const fullGraph = { entities:
|
|
224115
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223862
224116
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
223863
224117
|
const graph = generateKnowledgeGraph2({
|
|
223864
224118
|
projectId: effectiveProjectId,
|
|
@@ -223873,7 +224127,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223873
224127
|
case "/config": {
|
|
223874
224128
|
const os10 = await import("os");
|
|
223875
224129
|
const { existsSync: existsSync36 } = await import("fs");
|
|
223876
|
-
const { join:
|
|
224130
|
+
const { join: join36 } = await import("path");
|
|
223877
224131
|
const configProjectRoot = effectiveProjectRoot;
|
|
223878
224132
|
const resolved = getResolvedConfig({ projectRoot: configProjectRoot });
|
|
223879
224133
|
const files = {
|
|
@@ -223888,13 +224142,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223888
224142
|
try {
|
|
223889
224143
|
const home = os10.homedir();
|
|
223890
224144
|
const paths = {
|
|
223891
|
-
"project memorix.yml": configProjectRoot ?
|
|
223892
|
-
"user memorix.yml":
|
|
223893
|
-
"project memorix.toml": configProjectRoot ?
|
|
223894
|
-
"user config.toml":
|
|
223895
|
-
"project .env": configProjectRoot ?
|
|
223896
|
-
"user .env":
|
|
223897
|
-
"legacy config.json":
|
|
224145
|
+
"project memorix.yml": configProjectRoot ? join36(configProjectRoot, "memorix.yml") : null,
|
|
224146
|
+
"user memorix.yml": join36(home, ".memorix", "memorix.yml"),
|
|
224147
|
+
"project memorix.toml": configProjectRoot ? join36(configProjectRoot, "memorix.toml") : null,
|
|
224148
|
+
"user config.toml": join36(home, ".memorix", "config.toml"),
|
|
224149
|
+
"project .env": configProjectRoot ? join36(configProjectRoot, ".env") : null,
|
|
224150
|
+
"user .env": join36(home, ".memorix", ".env"),
|
|
224151
|
+
"legacy config.json": join36(home, ".memorix", "config.json")
|
|
223898
224152
|
};
|
|
223899
224153
|
for (const [key, fpath] of Object.entries(paths)) {
|
|
223900
224154
|
if (fpath === null) {
|
|
@@ -224055,8 +224309,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
224055
224309
|
} else {
|
|
224056
224310
|
await obsStore.remove(obsId);
|
|
224057
224311
|
try {
|
|
224058
|
-
await initGraphStore(effectiveDataDir);
|
|
224059
|
-
const gStore = getGraphStore();
|
|
224312
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
224060
224313
|
const prefix = `[#${obsId}] `;
|
|
224061
224314
|
const deletions = [];
|
|
224062
224315
|
for (const entity of gStore.loadEntities()) {
|
|
@@ -224071,8 +224324,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
224071
224324
|
break;
|
|
224072
224325
|
}
|
|
224073
224326
|
if (apiPath === "/export") {
|
|
224074
|
-
await initGraphStore(effectiveDataDir);
|
|
224075
|
-
const fullGraph = { entities:
|
|
224327
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
224328
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
224076
224329
|
const observations2 = filterDashboardObservations(
|
|
224077
224330
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
224078
224331
|
effectiveProjectId
|
|
@@ -264596,7 +264849,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
264596
264849
|
const store2 = getObservationStore();
|
|
264597
264850
|
console.error(`[memorix] ObservationStore backend: ${store2.getBackendName()}, generation: ${store2.getGeneration()}`);
|
|
264598
264851
|
}
|
|
264599
|
-
let graphManager = new KnowledgeGraphManager(projectDir2);
|
|
264852
|
+
let graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
264600
264853
|
if (!deferProjectRuntimeInit) {
|
|
264601
264854
|
await graphManager.init();
|
|
264602
264855
|
await initObservations(projectDir2);
|
|
@@ -264608,7 +264861,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
264608
264861
|
await initObservationStore(projectDir2);
|
|
264609
264862
|
await initMiniSkillStore(projectDir2);
|
|
264610
264863
|
await initSessionStore(projectDir2);
|
|
264611
|
-
graphManager = new KnowledgeGraphManager(projectDir2);
|
|
264864
|
+
graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
264612
264865
|
await graphManager.init();
|
|
264613
264866
|
await initObservations(projectDir2);
|
|
264614
264867
|
const indexed = await prepareSearchIndex();
|
|
@@ -266820,10 +267073,10 @@ ${Math.max(0, managedIds.size - result.resolved.length)} visible writable observ
|
|
|
266820
267073
|
);
|
|
266821
267074
|
let enableKG = isToolInProfile("create_entities", toolProfile);
|
|
266822
267075
|
try {
|
|
266823
|
-
const { homedir:
|
|
266824
|
-
const { join:
|
|
267076
|
+
const { homedir: homedir38 } = await import("os");
|
|
267077
|
+
const { join: join36 } = await import("path");
|
|
266825
267078
|
const { readFile: readFile11 } = await import("fs/promises");
|
|
266826
|
-
const raw = await readFile11(
|
|
267079
|
+
const raw = await readFile11(join36(homedir38(), ".memorix", "settings.json"), "utf-8");
|
|
266827
267080
|
const s4 = JSON.parse(raw);
|
|
266828
267081
|
if (s4.knowledgeGraph === true) enableKG = true;
|
|
266829
267082
|
} catch {
|
|
@@ -269398,8 +269651,8 @@ __export(update_checker_exports, {
|
|
|
269398
269651
|
});
|
|
269399
269652
|
import { execFile as execFile3 } from "child_process";
|
|
269400
269653
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir7 } from "fs/promises";
|
|
269401
|
-
import { join as
|
|
269402
|
-
import { homedir as
|
|
269654
|
+
import { join as join30 } from "path";
|
|
269655
|
+
import { homedir as homedir35 } from "os";
|
|
269403
269656
|
function getCurrentVersion() {
|
|
269404
269657
|
try {
|
|
269405
269658
|
return getCliVersion();
|
|
@@ -269578,8 +269831,8 @@ var init_update_checker = __esm({
|
|
|
269578
269831
|
MIN_AUTO_UPDATE_TIMEOUT_MS = 5e3;
|
|
269579
269832
|
MAX_AUTO_UPDATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
269580
269833
|
REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
269581
|
-
CACHE_DIR2 =
|
|
269582
|
-
CACHE_FILE2 =
|
|
269834
|
+
CACHE_DIR2 = join30(homedir35(), ".memorix");
|
|
269835
|
+
CACHE_FILE2 = join30(CACHE_DIR2, "update-check.json");
|
|
269583
269836
|
_testing = {
|
|
269584
269837
|
CACHE_FILE: CACHE_FILE2,
|
|
269585
269838
|
CHECK_INTERVAL_MS,
|
|
@@ -269637,7 +269890,7 @@ var init_serve = __esm({
|
|
|
269637
269890
|
const { serveStdio, StdioServerTransport } = await import("@modelcontextprotocol/server/stdio");
|
|
269638
269891
|
const { createModernMcpBridge: createModernMcpBridge2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
|
|
269639
269892
|
const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
269640
|
-
const { homedir:
|
|
269893
|
+
const { homedir: homedir38 } = await import("os");
|
|
269641
269894
|
const { writeLastProjectRoot: writeLastProjectRoot2 } = await Promise.resolve().then(() => (init_launch_root(), launch_root_exports));
|
|
269642
269895
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
269643
269896
|
let stdioHandle;
|
|
@@ -269666,7 +269919,7 @@ var init_serve = __esm({
|
|
|
269666
269919
|
try {
|
|
269667
269920
|
safeCwd = process.cwd();
|
|
269668
269921
|
} catch {
|
|
269669
|
-
safeCwd =
|
|
269922
|
+
safeCwd = homedir38();
|
|
269670
269923
|
}
|
|
269671
269924
|
const resolution = resolveServeProject2(
|
|
269672
269925
|
{
|
|
@@ -269674,7 +269927,7 @@ var init_serve = __esm({
|
|
|
269674
269927
|
envProjectRoot: process.env.MEMORIX_PROJECT_ROOT,
|
|
269675
269928
|
initCwd: process.env.INIT_CWD,
|
|
269676
269929
|
processCwd: safeCwd,
|
|
269677
|
-
homeDir:
|
|
269930
|
+
homeDir: homedir38()
|
|
269678
269931
|
},
|
|
269679
269932
|
{ detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 }
|
|
269680
269933
|
);
|
|
@@ -271895,6 +272148,7 @@ var init_streamableHttp = __esm({
|
|
|
271895
272148
|
// src/server/mcp-binding-store.ts
|
|
271896
272149
|
var mcp_binding_store_exports = {};
|
|
271897
272150
|
__export(mcp_binding_store_exports, {
|
|
272151
|
+
DEFAULT_MCP_BINDING_TTL_MS: () => DEFAULT_MCP_BINDING_TTL_MS,
|
|
271898
272152
|
McpBindingStore: () => McpBindingStore
|
|
271899
272153
|
});
|
|
271900
272154
|
import { randomUUID as randomUUID19 } from "crypto";
|
|
@@ -271909,12 +272163,13 @@ function rowToBinding(row) {
|
|
|
271909
272163
|
...row.expires_at ? { expiresAt: String(row.expires_at) } : {}
|
|
271910
272164
|
};
|
|
271911
272165
|
}
|
|
271912
|
-
var McpBindingStore;
|
|
272166
|
+
var DEFAULT_MCP_BINDING_TTL_MS, McpBindingStore;
|
|
271913
272167
|
var init_mcp_binding_store = __esm({
|
|
271914
272168
|
"src/server/mcp-binding-store.ts"() {
|
|
271915
272169
|
"use strict";
|
|
271916
272170
|
init_esm_shims();
|
|
271917
272171
|
init_sqlite_db();
|
|
272172
|
+
DEFAULT_MCP_BINDING_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
271918
272173
|
McpBindingStore = class {
|
|
271919
272174
|
db = null;
|
|
271920
272175
|
async init(dataDir) {
|
|
@@ -271926,6 +272181,7 @@ var init_mcp_binding_store = __esm({
|
|
|
271926
272181
|
}
|
|
271927
272182
|
create(input) {
|
|
271928
272183
|
const now3 = /* @__PURE__ */ new Date();
|
|
272184
|
+
const ttlMs = input.ttlMs ?? DEFAULT_MCP_BINDING_TTL_MS;
|
|
271929
272185
|
const binding = {
|
|
271930
272186
|
handleId: `mxh_${randomUUID19().replace(/-/g, "")}`,
|
|
271931
272187
|
projectId: input.projectId,
|
|
@@ -271933,7 +272189,7 @@ var init_mcp_binding_store = __esm({
|
|
|
271933
272189
|
dataDir: input.dataDir,
|
|
271934
272190
|
createdAt: now3.toISOString(),
|
|
271935
272191
|
lastUsedAt: now3.toISOString(),
|
|
271936
|
-
...
|
|
272192
|
+
...ttlMs > 0 ? { expiresAt: new Date(now3.getTime() + ttlMs).toISOString() } : {}
|
|
271937
272193
|
};
|
|
271938
272194
|
this.requireDb().prepare(`
|
|
271939
272195
|
INSERT INTO mcp_bindings (handle_id, project_id, project_root, data_dir, created_at, last_used_at, expires_at)
|
|
@@ -271955,8 +272211,15 @@ var init_mcp_binding_store = __esm({
|
|
|
271955
272211
|
const binding = this.get(handleId);
|
|
271956
272212
|
if (!binding) return void 0;
|
|
271957
272213
|
const lastUsedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
271958
|
-
|
|
271959
|
-
|
|
272214
|
+
const expiresAt = new Date(Date.now() + DEFAULT_MCP_BINDING_TTL_MS).toISOString();
|
|
272215
|
+
this.requireDb().prepare(`UPDATE mcp_bindings SET last_used_at = ?, expires_at = ? WHERE handle_id = ?`).run(lastUsedAt, expiresAt, handleId);
|
|
272216
|
+
return { ...binding, lastUsedAt, expiresAt };
|
|
272217
|
+
}
|
|
272218
|
+
cleanupExpired(now3 = Date.now()) {
|
|
272219
|
+
const result = this.requireDb().prepare(
|
|
272220
|
+
`DELETE FROM mcp_bindings WHERE expires_at IS NOT NULL AND expires_at <= ?`
|
|
272221
|
+
).run(new Date(now3).toISOString());
|
|
272222
|
+
return Number(result.changes ?? 0);
|
|
271960
272223
|
}
|
|
271961
272224
|
/** Reuse the most recent valid extension handle for a verified project root. */
|
|
271962
272225
|
findByProjectRoot(projectRoot) {
|
|
@@ -272046,16 +272309,25 @@ var init_control_plane_maintenance = __esm({
|
|
|
272046
272309
|
// src/cli/commands/serve-http.ts
|
|
272047
272310
|
var serve_http_exports = {};
|
|
272048
272311
|
__export(serve_http_exports, {
|
|
272312
|
+
DEFAULT_HTTP_BODY_LIMIT_BYTES: () => DEFAULT_HTTP_BODY_LIMIT_BYTES,
|
|
272049
272313
|
DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
|
|
272050
272314
|
EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
|
|
272315
|
+
HttpPayloadTooLargeError: () => HttpPayloadTooLargeError,
|
|
272316
|
+
MAX_HTTP_BODY_LIMIT_BYTES: () => MAX_HTTP_BODY_LIMIT_BYTES,
|
|
272051
272317
|
MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
|
|
272318
|
+
MIN_HTTP_BODY_LIMIT_BYTES: () => MIN_HTTP_BODY_LIMIT_BYTES,
|
|
272052
272319
|
_testing: () => _testing2,
|
|
272053
272320
|
default: () => serve_http_default,
|
|
272321
|
+
isLoopbackHost: () => isLoopbackHost,
|
|
272054
272322
|
isServerDiscoverRequest: () => isServerDiscoverRequest,
|
|
272323
|
+
parseHttpBodyLimit: () => parseHttpBodyLimit,
|
|
272324
|
+
parseJsonBody: () => parseJsonBody,
|
|
272055
272325
|
parseSessionTimeoutMs: () => parseSessionTimeoutMs,
|
|
272056
|
-
resolveServeHttpDefaults: () => resolveServeHttpDefaults
|
|
272326
|
+
resolveServeHttpDefaults: () => resolveServeHttpDefaults,
|
|
272327
|
+
validateHttpBindSecurity: () => validateHttpBindSecurity
|
|
272057
272328
|
});
|
|
272058
272329
|
import { getHeapStatistics } from "v8";
|
|
272330
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
272059
272331
|
function isServerDiscoverRequest(body) {
|
|
272060
272332
|
return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
|
|
272061
272333
|
}
|
|
@@ -272066,6 +272338,78 @@ function parseSessionTimeoutMs(raw) {
|
|
|
272066
272338
|
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
272067
272339
|
return Math.floor(parsed);
|
|
272068
272340
|
}
|
|
272341
|
+
function parseHttpBodyLimit(raw) {
|
|
272342
|
+
const value = raw?.trim();
|
|
272343
|
+
if (!value) return DEFAULT_HTTP_BODY_LIMIT_BYTES;
|
|
272344
|
+
const parsed = Number(value);
|
|
272345
|
+
if (!Number.isSafeInteger(parsed) || parsed < MIN_HTTP_BODY_LIMIT_BYTES) {
|
|
272346
|
+
return DEFAULT_HTTP_BODY_LIMIT_BYTES;
|
|
272347
|
+
}
|
|
272348
|
+
return Math.min(parsed, MAX_HTTP_BODY_LIMIT_BYTES);
|
|
272349
|
+
}
|
|
272350
|
+
function isLoopbackHost(host) {
|
|
272351
|
+
const normalized = host.trim().toLowerCase();
|
|
272352
|
+
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
|
|
272353
|
+
}
|
|
272354
|
+
function validateHttpBindSecurity(host, env3 = process.env) {
|
|
272355
|
+
const hasToken = Boolean(env3.MEMORIX_HTTP_AUTH_TOKEN?.trim());
|
|
272356
|
+
const explicitlyUnauthenticated = env3.MEMORIX_HTTP_ALLOW_UNAUTHENTICATED_BIND === "1";
|
|
272357
|
+
if (!isLoopbackHost(host) && !hasToken && !explicitlyUnauthenticated) {
|
|
272358
|
+
throw new Error(
|
|
272359
|
+
`[memorix] Refusing unauthenticated non-loopback HTTP bind on ${host}. Set MEMORIX_HTTP_AUTH_TOKEN or explicitly set MEMORIX_HTTP_ALLOW_UNAUTHENTICATED_BIND=1.`
|
|
272360
|
+
);
|
|
272361
|
+
}
|
|
272362
|
+
return { requiresAuth: hasToken, explicitlyUnauthenticated };
|
|
272363
|
+
}
|
|
272364
|
+
function isAuthorizedHttpRequest(req, token) {
|
|
272365
|
+
const header = req.headers.authorization;
|
|
272366
|
+
if (!header) return false;
|
|
272367
|
+
const match2 = /^Bearer\s+(.+)$/iu.exec(header);
|
|
272368
|
+
if (!match2) return false;
|
|
272369
|
+
const provided = Buffer.from(match2[1], "utf8");
|
|
272370
|
+
const expected = Buffer.from(token, "utf8");
|
|
272371
|
+
return provided.length === expected.length && timingSafeEqual2(provided, expected);
|
|
272372
|
+
}
|
|
272373
|
+
function parseJsonBody(req, limitBytes) {
|
|
272374
|
+
return new Promise((resolve9, reject) => {
|
|
272375
|
+
const contentLength = Number(req.headers["content-length"]);
|
|
272376
|
+
if (Number.isSafeInteger(contentLength) && contentLength > limitBytes) {
|
|
272377
|
+
req.resume();
|
|
272378
|
+
reject(new HttpPayloadTooLargeError(limitBytes));
|
|
272379
|
+
return;
|
|
272380
|
+
}
|
|
272381
|
+
const chunks = [];
|
|
272382
|
+
let size = 0;
|
|
272383
|
+
let settled = false;
|
|
272384
|
+
const fail2 = (error40) => {
|
|
272385
|
+
if (settled) return;
|
|
272386
|
+
settled = true;
|
|
272387
|
+
reject(error40);
|
|
272388
|
+
};
|
|
272389
|
+
req.on("data", (chunk) => {
|
|
272390
|
+
if (settled) return;
|
|
272391
|
+
size += chunk.length;
|
|
272392
|
+
if (size > limitBytes) {
|
|
272393
|
+
req.removeAllListeners("data");
|
|
272394
|
+
req.resume();
|
|
272395
|
+
fail2(new HttpPayloadTooLargeError(limitBytes));
|
|
272396
|
+
return;
|
|
272397
|
+
}
|
|
272398
|
+
chunks.push(chunk);
|
|
272399
|
+
});
|
|
272400
|
+
req.on("end", () => {
|
|
272401
|
+
if (settled) return;
|
|
272402
|
+
settled = true;
|
|
272403
|
+
try {
|
|
272404
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
272405
|
+
resolve9(body ? JSON.parse(body) : void 0);
|
|
272406
|
+
} catch (error40) {
|
|
272407
|
+
reject(error40);
|
|
272408
|
+
}
|
|
272409
|
+
});
|
|
272410
|
+
req.on("error", fail2);
|
|
272411
|
+
});
|
|
272412
|
+
}
|
|
272069
272413
|
function resolveServeHttpDefaults(argsPort, configured) {
|
|
272070
272414
|
const configuredPort = typeof configured.port === "number" && Number.isFinite(configured.port) ? configured.port : void 0;
|
|
272071
272415
|
const port = parseTcpPortOrReport(argsPort, configuredPort ?? 3211);
|
|
@@ -272075,7 +272419,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
|
|
|
272075
272419
|
const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
|
|
272076
272420
|
return { port, transportNotice };
|
|
272077
272421
|
}
|
|
272078
|
-
var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, MCP_SDK_COMPAT_PROTOCOL_VERSION, serve_http_default, _testing2;
|
|
272422
|
+
var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, DEFAULT_HTTP_BODY_LIMIT_BYTES, MAX_HTTP_BODY_LIMIT_BYTES, MIN_HTTP_BODY_LIMIT_BYTES, MCP_SDK_COMPAT_PROTOCOL_VERSION, HttpPayloadTooLargeError, serve_http_default, _testing2;
|
|
272079
272423
|
var init_serve_http = __esm({
|
|
272080
272424
|
"src/cli/commands/serve-http.ts"() {
|
|
272081
272425
|
"use strict";
|
|
@@ -272091,7 +272435,17 @@ var init_serve_http = __esm({
|
|
|
272091
272435
|
init_mcp_root_path();
|
|
272092
272436
|
DEFAULT_SESSION_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
|
|
272093
272437
|
EXPIRED_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
272438
|
+
DEFAULT_HTTP_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
272439
|
+
MAX_HTTP_BODY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
272440
|
+
MIN_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
272094
272441
|
MCP_SDK_COMPAT_PROTOCOL_VERSION = "2025-11-25";
|
|
272442
|
+
HttpPayloadTooLargeError = class extends Error {
|
|
272443
|
+
statusCode = 413;
|
|
272444
|
+
constructor(limitBytes) {
|
|
272445
|
+
super(`Request body exceeds the ${limitBytes} byte limit.`);
|
|
272446
|
+
this.name = "HttpPayloadTooLargeError";
|
|
272447
|
+
}
|
|
272448
|
+
};
|
|
272095
272449
|
serve_http_default = defineCommand({
|
|
272096
272450
|
meta: {
|
|
272097
272451
|
name: "serve-http",
|
|
@@ -272128,7 +272482,7 @@ var init_serve_http = __esm({
|
|
|
272128
272482
|
const { createProjectBindingController: createProjectBindingController2 } = await Promise.resolve().then(() => (init_request_context(), request_context_exports));
|
|
272129
272483
|
const { findGitInSubdirs: findGitInSubdirs2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
272130
272484
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync13 } = await import("fs");
|
|
272131
|
-
const { homedir:
|
|
272485
|
+
const { homedir: homedir38 } = await import("os");
|
|
272132
272486
|
const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
|
|
272133
272487
|
const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
|
|
272134
272488
|
const { createModernMcpBridge: createModernMcpBridge2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
|
|
@@ -272144,12 +272498,20 @@ var init_serve_http = __esm({
|
|
|
272144
272498
|
if (resolved.transportNotice) console.error(resolved.transportNotice);
|
|
272145
272499
|
const port = resolved.port;
|
|
272146
272500
|
const host = args.host || "127.0.0.1";
|
|
272501
|
+
const bindSecurity = validateHttpBindSecurity(host);
|
|
272502
|
+
const httpAuthToken = process.env.MEMORIX_HTTP_AUTH_TOKEN?.trim() || "";
|
|
272503
|
+
if (bindSecurity.explicitlyUnauthenticated && !isLoopbackHost(host)) {
|
|
272504
|
+
console.error(`[memorix] WARNING: HTTP control plane is unauthenticated on ${host}; keep it behind a private network or authenticated proxy.`);
|
|
272505
|
+
}
|
|
272506
|
+
if (bindSecurity.requiresAuth) {
|
|
272507
|
+
console.error("[memorix] HTTP control plane authentication is enabled (Bearer token).");
|
|
272508
|
+
}
|
|
272147
272509
|
const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "team" });
|
|
272148
272510
|
let safeCwd;
|
|
272149
272511
|
try {
|
|
272150
272512
|
safeCwd = process.cwd();
|
|
272151
272513
|
} catch {
|
|
272152
|
-
safeCwd =
|
|
272514
|
+
safeCwd = homedir38();
|
|
272153
272515
|
}
|
|
272154
272516
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
272155
272517
|
const { writeLastProjectRoot: writeLastProjectRoot2 } = await Promise.resolve().then(() => (init_launch_root(), launch_root_exports));
|
|
@@ -272160,7 +272522,7 @@ var init_serve_http = __esm({
|
|
|
272160
272522
|
cwdArg: args.cwd,
|
|
272161
272523
|
envProjectRoot: process.env.MEMORIX_PROJECT_ROOT,
|
|
272162
272524
|
processCwd: safeCwd,
|
|
272163
|
-
homeDir:
|
|
272525
|
+
homeDir: homedir38()
|
|
272164
272526
|
},
|
|
272165
272527
|
{ detectProject: detectorMod.detectProject, findGitInSubdirs: detectorMod.findGitInSubdirs, isSystemDirectory: detectorMod.isSystemDirectory }
|
|
272166
272528
|
);
|
|
@@ -272174,6 +272536,7 @@ var init_serve_http = __esm({
|
|
|
272174
272536
|
console.error(`[memorix] HTTP transport starting on ${host}:${port}`);
|
|
272175
272537
|
console.error(`[memorix] Project root: ${projectRoot}`);
|
|
272176
272538
|
const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
|
|
272539
|
+
const httpBodyLimitBytes = parseHttpBodyLimit(process.env.MEMORIX_HTTP_MAX_BODY_BYTES);
|
|
272177
272540
|
console.error(
|
|
272178
272541
|
sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
|
|
272179
272542
|
);
|
|
@@ -272244,21 +272607,6 @@ var init_serve_http = __esm({
|
|
|
272244
272607
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
272245
272608
|
res.end(message);
|
|
272246
272609
|
}
|
|
272247
|
-
function parseBody(req) {
|
|
272248
|
-
return new Promise((resolve9, reject) => {
|
|
272249
|
-
const chunks = [];
|
|
272250
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
272251
|
-
req.on("end", () => {
|
|
272252
|
-
try {
|
|
272253
|
-
const body = Buffer.concat(chunks).toString("utf-8");
|
|
272254
|
-
resolve9(body ? JSON.parse(body) : void 0);
|
|
272255
|
-
} catch (err) {
|
|
272256
|
-
reject(err);
|
|
272257
|
-
}
|
|
272258
|
-
});
|
|
272259
|
-
req.on("error", reject);
|
|
272260
|
-
});
|
|
272261
|
-
}
|
|
272262
272610
|
function patchMcpResponse(res) {
|
|
272263
272611
|
const origWriteHead = res.writeHead;
|
|
272264
272612
|
const origWrite = res.write;
|
|
@@ -272348,7 +272696,7 @@ var init_serve_http = __esm({
|
|
|
272348
272696
|
res.setHeader("Vary", "Origin");
|
|
272349
272697
|
}
|
|
272350
272698
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
272351
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Mcp-Session-Id, Mcp-Project-Handle, Mcp-Project-Root, Mcp-Stateless, Last-Event-Id");
|
|
272699
|
+
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Project-Handle, Mcp-Project-Root, Mcp-Stateless, Last-Event-Id");
|
|
272352
272700
|
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id, Mcp-Project-Handle, Mcp-Protocol-Version, Mcp-Method, Mcp-Name");
|
|
272353
272701
|
res.setHeader("Mcp-Protocol-Version", MCP_SDK_COMPAT_PROTOCOL_VERSION);
|
|
272354
272702
|
res.setHeader("Mcp-Method", req.method || "UNKNOWN");
|
|
@@ -272356,7 +272704,7 @@ var init_serve_http = __esm({
|
|
|
272356
272704
|
}
|
|
272357
272705
|
async function handlePost(req, res) {
|
|
272358
272706
|
const sessionId = req.headers["mcp-session-id"];
|
|
272359
|
-
const body = await
|
|
272707
|
+
const body = await parseJsonBody(req, httpBodyLimitBytes);
|
|
272360
272708
|
const params = body && typeof body === "object" && !Array.isArray(body) ? body.params : void 0;
|
|
272361
272709
|
const claimlessDiscovery = !sessionId && isServerDiscoverRequest(body) && !req.headers["mcp-protocol-version"] && !req.headers["mcp-method"] && req.headers["mcp-stateless"] !== "true" && (params === void 0 || typeof params === "object" && params !== null && !Array.isArray(params) && !Object.prototype.hasOwnProperty.call(params, "_meta"));
|
|
272362
272710
|
if (claimlessDiscovery) {
|
|
@@ -272660,7 +273008,7 @@ var init_serve_http = __esm({
|
|
|
272660
273008
|
sendJson2({ error: "Maintenance actions require POST." }, 405);
|
|
272661
273009
|
return;
|
|
272662
273010
|
}
|
|
272663
|
-
const bodyValue = await
|
|
273011
|
+
const bodyValue = await parseJsonBody(req, Math.min(httpBodyLimitBytes, 1024 * 1024));
|
|
272664
273012
|
const body = bodyValue && typeof bodyValue === "object" && !Array.isArray(bodyValue) ? bodyValue : {};
|
|
272665
273013
|
const { projectId: maintenanceProjectId, dataDir: maintenanceDataDir } = await resolveRequestProject(url2);
|
|
272666
273014
|
const maintenanceStore = await getDashboardObservationStore(maintenanceDataDir);
|
|
@@ -272795,9 +273143,9 @@ var init_serve_http = __esm({
|
|
|
272795
273143
|
}
|
|
272796
273144
|
if (apiPath === "/stats") {
|
|
272797
273145
|
const { projectId: statsProjectId, dataDir: statsDataDir } = await resolveRequestProject(url2);
|
|
272798
|
-
const { initGraphStore: initGraphStore2
|
|
272799
|
-
await initGraphStore2(statsDataDir);
|
|
272800
|
-
const graph = { entities:
|
|
273146
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273147
|
+
const gStore = await initGraphStore2(statsDataDir, statsProjectId);
|
|
273148
|
+
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
272801
273149
|
const observations2 = await loadDashboardProjectObservations(statsDataDir, statsProjectId, "active");
|
|
272802
273150
|
const feedback = await loadProjectFeedback(statsDataDir, statsProjectId);
|
|
272803
273151
|
const statsStore = await getDashboardObservationStore(statsDataDir);
|
|
@@ -272959,9 +273307,9 @@ var init_serve_http = __esm({
|
|
|
272959
273307
|
}
|
|
272960
273308
|
if (apiPath === "/graph") {
|
|
272961
273309
|
const { projectId: graphProjectId, dataDir: graphDataDir } = await resolveRequestProject(url2);
|
|
272962
|
-
const { initGraphStore: initGraphStore2
|
|
272963
|
-
await initGraphStore2(graphDataDir);
|
|
272964
|
-
const fullGraph = { entities:
|
|
273310
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273311
|
+
const gStore = await initGraphStore2(graphDataDir, graphProjectId);
|
|
273312
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
272965
273313
|
const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, "active");
|
|
272966
273314
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
272967
273315
|
sendJson2({ entities: scoped.entities, relations: scoped.relations });
|
|
@@ -273034,12 +273382,12 @@ var init_serve_http = __esm({
|
|
|
273034
273382
|
const { projectId: kgProjectId, dataDir: kgDataDir } = await resolveRequestProject(url2);
|
|
273035
273383
|
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
273036
273384
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
273037
|
-
const { initGraphStore: initGraphStore2
|
|
273385
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273038
273386
|
await initMiniSkillStore2(kgDataDir);
|
|
273039
|
-
await initGraphStore2(kgDataDir);
|
|
273387
|
+
const gStore = await initGraphStore2(kgDataDir, kgProjectId);
|
|
273040
273388
|
const allObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, "active");
|
|
273041
273389
|
const skills = await getMiniSkillStore2().loadByProject(kgProjectId);
|
|
273042
|
-
const fullGraph = { entities:
|
|
273390
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
273043
273391
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
273044
273392
|
const graph = generateKnowledgeGraph2({
|
|
273045
273393
|
projectId: kgProjectId,
|
|
@@ -273055,7 +273403,7 @@ var init_serve_http = __esm({
|
|
|
273055
273403
|
const { projectId: configProjectId } = await resolveRequestProject(url2);
|
|
273056
273404
|
const os10 = await import("os");
|
|
273057
273405
|
const { existsSync: existsSync36 } = await import("fs");
|
|
273058
|
-
const { join:
|
|
273406
|
+
const { join: join36 } = await import("path");
|
|
273059
273407
|
const { loadYamlConfig: loadYamlConfig2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
|
|
273060
273408
|
const { loadFileConfig: loadFileConfig2, loadDotenv: loadDotenv2, getLoadedEnvFiles: getLoadedEnvFiles2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
273061
273409
|
const isStartupProject = configProjectId === defaultProject.id;
|
|
@@ -273069,11 +273417,11 @@ var init_serve_http = __esm({
|
|
|
273069
273417
|
const home = os10.homedir();
|
|
273070
273418
|
if (effectiveRoot) {
|
|
273071
273419
|
const paths = {
|
|
273072
|
-
"project memorix.yml":
|
|
273073
|
-
"user memorix.yml":
|
|
273074
|
-
"project .env":
|
|
273075
|
-
"user .env":
|
|
273076
|
-
"legacy config.json":
|
|
273420
|
+
"project memorix.yml": join36(effectiveRoot, "memorix.yml"),
|
|
273421
|
+
"user memorix.yml": join36(home, ".memorix", "memorix.yml"),
|
|
273422
|
+
"project .env": join36(effectiveRoot, ".env"),
|
|
273423
|
+
"user .env": join36(home, ".memorix", ".env"),
|
|
273424
|
+
"legacy config.json": join36(home, ".memorix", "config.json")
|
|
273077
273425
|
};
|
|
273078
273426
|
for (const [key, fpath] of Object.entries(paths)) {
|
|
273079
273427
|
files[key] = { exists: existsSync36(fpath), path: fpath };
|
|
@@ -273082,9 +273430,9 @@ var init_serve_http = __esm({
|
|
|
273082
273430
|
files["project memorix.yml"] = { exists: false, path: "unavailable", unavailable: true };
|
|
273083
273431
|
files["project .env"] = { exists: false, path: "unavailable", unavailable: true };
|
|
273084
273432
|
const userPaths = {
|
|
273085
|
-
"user memorix.yml":
|
|
273086
|
-
"user .env":
|
|
273087
|
-
"legacy config.json":
|
|
273433
|
+
"user memorix.yml": join36(home, ".memorix", "memorix.yml"),
|
|
273434
|
+
"user .env": join36(home, ".memorix", ".env"),
|
|
273435
|
+
"legacy config.json": join36(home, ".memorix", "config.json")
|
|
273088
273436
|
};
|
|
273089
273437
|
for (const [key, fpath] of Object.entries(userPaths)) {
|
|
273090
273438
|
files[key] = { exists: existsSync36(fpath), path: fpath };
|
|
@@ -273300,9 +273648,8 @@ var init_serve_http = __esm({
|
|
|
273300
273648
|
} else {
|
|
273301
273649
|
await store2.remove(obsId);
|
|
273302
273650
|
try {
|
|
273303
|
-
const { initGraphStore: initGraphStore2
|
|
273304
|
-
await initGraphStore2(delDataDir);
|
|
273305
|
-
const gStore = getGraphStore2();
|
|
273651
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273652
|
+
const gStore = await initGraphStore2(delDataDir, delProjectId);
|
|
273306
273653
|
const prefix = `[#${obsId}] `;
|
|
273307
273654
|
const deletions = [];
|
|
273308
273655
|
for (const entity of gStore.loadEntities()) {
|
|
@@ -273318,9 +273665,9 @@ var init_serve_http = __esm({
|
|
|
273318
273665
|
}
|
|
273319
273666
|
if (apiPath === "/export") {
|
|
273320
273667
|
const { projectId: expProjectId, projectName: expProjectName, dataDir: expDataDir } = await resolveRequestProject(url2);
|
|
273321
|
-
const { initGraphStore: initGraphStore2
|
|
273322
|
-
await initGraphStore2(expDataDir);
|
|
273323
|
-
const fullGraph = { entities:
|
|
273668
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273669
|
+
const gStore = await initGraphStore2(expDataDir, expProjectId);
|
|
273670
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
273324
273671
|
const observations2 = await loadDashboardProjectObservations(expDataDir, expProjectId, "active");
|
|
273325
273672
|
const expStore = await getDashboardObservationStore(expDataDir);
|
|
273326
273673
|
const nextId2 = await expStore.loadIdCounter();
|
|
@@ -273358,7 +273705,8 @@ var init_serve_http = __esm({
|
|
|
273358
273705
|
}, 503);
|
|
273359
273706
|
return;
|
|
273360
273707
|
}
|
|
273361
|
-
|
|
273708
|
+
const status = err instanceof HttpPayloadTooLargeError ? err.statusCode : 500;
|
|
273709
|
+
sendJson2({ error: err instanceof Error ? err.message : "Unknown error" }, status);
|
|
273362
273710
|
}
|
|
273363
273711
|
}
|
|
273364
273712
|
let httpReadyAt = null;
|
|
@@ -273370,6 +273718,14 @@ var init_serve_http = __esm({
|
|
|
273370
273718
|
return;
|
|
273371
273719
|
}
|
|
273372
273720
|
const url2 = new URL(req.url || "/", `http://localhost:${port}`);
|
|
273721
|
+
if (bindSecurity.requiresAuth && url2.pathname !== "/health" && !isAuthorizedHttpRequest(req, httpAuthToken)) {
|
|
273722
|
+
res.writeHead(401, {
|
|
273723
|
+
"Content-Type": "application/json",
|
|
273724
|
+
"WWW-Authenticate": "Bearer"
|
|
273725
|
+
});
|
|
273726
|
+
res.end(JSON.stringify({ error: "Authentication required" }));
|
|
273727
|
+
return;
|
|
273728
|
+
}
|
|
273373
273729
|
if (url2.pathname === "/health") {
|
|
273374
273730
|
const { getEmbeddingRuntimeHealth: getEmbeddingRuntimeHealth2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
273375
273731
|
const memory = process.memoryUsage();
|
|
@@ -273440,10 +273796,15 @@ var init_serve_http = __esm({
|
|
|
273440
273796
|
} catch (err) {
|
|
273441
273797
|
console.error("[memorix] HTTP handler error:", err);
|
|
273442
273798
|
if (!res.headersSent) {
|
|
273443
|
-
|
|
273799
|
+
const status = err instanceof HttpPayloadTooLargeError ? err.statusCode : 500;
|
|
273800
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
273801
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
273444
273802
|
res.end(JSON.stringify({
|
|
273445
273803
|
jsonrpc: "2.0",
|
|
273446
|
-
error: {
|
|
273804
|
+
error: {
|
|
273805
|
+
code: status === 413 ? -32013 : -32603,
|
|
273806
|
+
message: status === 413 ? message : "Internal server error"
|
|
273807
|
+
},
|
|
273447
273808
|
id: null
|
|
273448
273809
|
}));
|
|
273449
273810
|
}
|
|
@@ -273458,31 +273819,62 @@ var init_serve_http = __esm({
|
|
|
273458
273819
|
});
|
|
273459
273820
|
httpServer.keepAliveTimeout = 6e4;
|
|
273460
273821
|
httpServer.headersTimeout = 65e3;
|
|
273461
|
-
|
|
273462
|
-
|
|
273463
|
-
|
|
273464
|
-
|
|
273465
|
-
|
|
273466
|
-
|
|
273467
|
-
|
|
273822
|
+
const listenPromise = new Promise((resolve9, reject) => {
|
|
273823
|
+
const onError = (error40) => {
|
|
273824
|
+
if (typeof httpServer.off === "function") httpServer.off("listening", onListening);
|
|
273825
|
+
reject(error40);
|
|
273826
|
+
};
|
|
273827
|
+
const onListening = () => {
|
|
273828
|
+
if (typeof httpServer.off === "function") httpServer.off("error", onError);
|
|
273829
|
+
httpReadyAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
273830
|
+
try {
|
|
273831
|
+
const memorixDir = (process.env.HOME || process.env.USERPROFILE || "").replace(/\\/g, "/") + "/.memorix";
|
|
273832
|
+
mkdirSync13(memorixDir, { recursive: true });
|
|
273833
|
+
writeFileSync13(memorixDir + "/background.ready", JSON.stringify({ pid: process.pid, port, readyAt: Date.now() }));
|
|
273834
|
+
} catch {
|
|
273835
|
+
}
|
|
273836
|
+
console.error(`[memorix] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
273837
|
+
console.error(`[memorix] Mode: Control Plane (HTTP MCP + multi-session live dashboard)`);
|
|
273838
|
+
console.error(`[memorix] MCP Server listening on http://${host}:${port}/mcp`);
|
|
273839
|
+
console.error(`[memorix] Dashboard: http://${host}:${port}/`);
|
|
273840
|
+
console.error(`[memorix] Coordination: http://${host}:${port}/api/team`);
|
|
273841
|
+
console.error(`[memorix] Port: ${port}`);
|
|
273842
|
+
console.error(`[memorix] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
273843
|
+
console.error(`[memorix] Sessions at startup: ${sessions.size} (live count available at /api/team)`);
|
|
273844
|
+
console.error(`[memorix] Agents can connect via: { "transport": "http", "url": "http://localhost:${port}/mcp" }`);
|
|
273845
|
+
Promise.resolve().then(() => (init_update_checker(), update_checker_exports)).then((m6) => m6.checkForUpdates()).catch(() => {
|
|
273846
|
+
});
|
|
273847
|
+
resolve9();
|
|
273848
|
+
};
|
|
273849
|
+
if (typeof httpServer.once === "function") {
|
|
273850
|
+
httpServer.once("error", onError);
|
|
273851
|
+
httpServer.once("listening", onListening);
|
|
273852
|
+
httpServer.listen(port, host);
|
|
273853
|
+
} else {
|
|
273854
|
+
httpServer.listen(port, host, onListening);
|
|
273468
273855
|
}
|
|
273469
|
-
console.error(`[memorix] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
273470
|
-
console.error(`[memorix] Mode: Control Plane (HTTP MCP + multi-session live dashboard)`);
|
|
273471
|
-
console.error(`[memorix] MCP Server listening on http://${host}:${port}/mcp`);
|
|
273472
|
-
console.error(`[memorix] Dashboard: http://${host}:${port}/`);
|
|
273473
|
-
console.error(`[memorix] Coordination: http://${host}:${port}/api/team`);
|
|
273474
|
-
console.error(`[memorix] Port: ${port}`);
|
|
273475
|
-
console.error(`[memorix] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
273476
|
-
console.error(`[memorix] Sessions at startup: ${sessions.size} (live count available at /api/team)`);
|
|
273477
|
-
console.error(`[memorix] Agents can connect via: { "transport": "http", "url": "http://localhost:${port}/mcp" }`);
|
|
273478
|
-
Promise.resolve().then(() => (init_update_checker(), update_checker_exports)).then((m6) => m6.checkForUpdates()).catch(() => {
|
|
273479
|
-
});
|
|
273480
273856
|
});
|
|
273857
|
+
try {
|
|
273858
|
+
await listenPromise;
|
|
273859
|
+
} catch (error40) {
|
|
273860
|
+
console.error(`[memorix] HTTP server failed to listen on ${host}:${port}: ${error40 instanceof Error ? error40.message : String(error40)}`);
|
|
273861
|
+
for (const store2 of dashboardObservationStores.values()) {
|
|
273862
|
+
try {
|
|
273863
|
+
store2.close();
|
|
273864
|
+
} catch {
|
|
273865
|
+
}
|
|
273866
|
+
}
|
|
273867
|
+
dashboardObservationStores.clear();
|
|
273868
|
+
const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
273869
|
+
closeAllDatabases2();
|
|
273870
|
+
throw error40;
|
|
273871
|
+
}
|
|
273481
273872
|
const SESSION_TIMEOUT_MS = sessionTimeoutMs;
|
|
273482
273873
|
const gcInterval = setInterval(() => {
|
|
273483
273874
|
maybeProbeSummary();
|
|
273484
273875
|
const now3 = Date.now();
|
|
273485
273876
|
forgetExpiredSessions(now3);
|
|
273877
|
+
statelessBindingStore.cleanupExpired(now3);
|
|
273486
273878
|
if (SESSION_TIMEOUT_MS === 0) return;
|
|
273487
273879
|
for (const [sid, state] of sessions) {
|
|
273488
273880
|
const lastActive = sessionLastActivity.get(sid) ?? 0;
|
|
@@ -273497,7 +273889,10 @@ var init_serve_http = __esm({
|
|
|
273497
273889
|
}
|
|
273498
273890
|
}, 6e4);
|
|
273499
273891
|
gcInterval.unref();
|
|
273892
|
+
let shutdownStarted = false;
|
|
273500
273893
|
const shutdown = async () => {
|
|
273894
|
+
if (shutdownStarted) return;
|
|
273895
|
+
shutdownStarted = true;
|
|
273501
273896
|
if (suppressedProbeCount > 0) {
|
|
273502
273897
|
console.error(`[memorix] ${suppressedProbeCount} probe connection(s) suppressed during this session`);
|
|
273503
273898
|
}
|
|
@@ -273531,7 +273926,17 @@ var init_serve_http = __esm({
|
|
|
273531
273926
|
console.error(`[memorix] Error closing session ${sid}:`, err);
|
|
273532
273927
|
}
|
|
273533
273928
|
}
|
|
273534
|
-
|
|
273929
|
+
clearInterval(gcInterval);
|
|
273930
|
+
clearInterval(heartbeatInterval);
|
|
273931
|
+
await new Promise((resolve9) => {
|
|
273932
|
+
if (!httpServer.listening) {
|
|
273933
|
+
resolve9();
|
|
273934
|
+
return;
|
|
273935
|
+
}
|
|
273936
|
+
httpServer.close(() => resolve9());
|
|
273937
|
+
});
|
|
273938
|
+
const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
273939
|
+
closeAllDatabases2();
|
|
273535
273940
|
process.exit(0);
|
|
273536
273941
|
};
|
|
273537
273942
|
process.on("SIGINT", shutdown);
|
|
@@ -273570,7 +273975,13 @@ var init_serve_http = __esm({
|
|
|
273570
273975
|
});
|
|
273571
273976
|
_testing2 = {
|
|
273572
273977
|
DEFAULT_SESSION_TIMEOUT_MS,
|
|
273978
|
+
DEFAULT_HTTP_BODY_LIMIT_BYTES,
|
|
273979
|
+
MAX_HTTP_BODY_LIMIT_BYTES,
|
|
273980
|
+
MIN_HTTP_BODY_LIMIT_BYTES,
|
|
273573
273981
|
parseSessionTimeoutMs,
|
|
273982
|
+
parseHttpBodyLimit,
|
|
273983
|
+
isLoopbackHost,
|
|
273984
|
+
validateHttpBindSecurity,
|
|
273574
273985
|
parseTcpPortOrReport
|
|
273575
273986
|
};
|
|
273576
273987
|
}
|
|
@@ -274021,8 +274432,10 @@ var init_journal = __esm({
|
|
|
274021
274432
|
});
|
|
274022
274433
|
|
|
274023
274434
|
// src/sync/policy.ts
|
|
274024
|
-
function syncEligibility(observation,
|
|
274025
|
-
if (observation.projectId !==
|
|
274435
|
+
function syncEligibility(observation, scopeId, scope = "project") {
|
|
274436
|
+
if (scope === "project" && observation.projectId !== scopeId) {
|
|
274437
|
+
return { eligible: false, reason: "other-project" };
|
|
274438
|
+
}
|
|
274026
274439
|
if ((observation.visibility ?? "project") !== "project") return { eligible: false, reason: "non-project-visibility" };
|
|
274027
274440
|
if ((observation.sharedWithAgentIds?.length ?? 0) > 0) {
|
|
274028
274441
|
return { eligible: false, reason: "agent-targeted" };
|
|
@@ -274033,11 +274446,11 @@ function syncEligibility(observation, projectId) {
|
|
|
274033
274446
|
if (observation.valueCategory === "ephemeral") return { eligible: false, reason: "ephemeral-value" };
|
|
274034
274447
|
return { eligible: true };
|
|
274035
274448
|
}
|
|
274036
|
-
function eligibleObservations(observations2,
|
|
274449
|
+
function eligibleObservations(observations2, scopeId, scope = "project") {
|
|
274037
274450
|
const eligible = [];
|
|
274038
274451
|
let excluded = 0;
|
|
274039
274452
|
for (const observation of observations2) {
|
|
274040
|
-
if (syncEligibility(observation,
|
|
274453
|
+
if (syncEligibility(observation, scopeId, scope).eligible) eligible.push(observation);
|
|
274041
274454
|
else excluded++;
|
|
274042
274455
|
}
|
|
274043
274456
|
return { eligible, excluded };
|
|
@@ -274054,13 +274467,26 @@ import { createHash as createHash22 } from "crypto";
|
|
|
274054
274467
|
function syncNamespace(projectId) {
|
|
274055
274468
|
return `project-${createHash22("sha256").update(projectId).digest("hex").slice(0, 32)}`;
|
|
274056
274469
|
}
|
|
274470
|
+
function userSyncNamespace(env3 = process.env) {
|
|
274471
|
+
const override = env3.MEMORIX_SYNC_USER_NAMESPACE?.trim();
|
|
274472
|
+
if (!override) {
|
|
274473
|
+
throw new Error("[memorix] MEMORIX_SYNC_USER_NAMESPACE is required for --scope user; use one stable per-user namespace across devices");
|
|
274474
|
+
}
|
|
274475
|
+
if (!isSafeSyncDeviceId(override)) {
|
|
274476
|
+
throw new Error("[memorix] MEMORIX_SYNC_USER_NAMESPACE must match [A-Za-z0-9][A-Za-z0-9._-]{0,127}");
|
|
274477
|
+
}
|
|
274478
|
+
return `${USER_SYNC_NAMESPACE}${override}`;
|
|
274479
|
+
}
|
|
274057
274480
|
function isSafeSyncDeviceId(value) {
|
|
274058
274481
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);
|
|
274059
274482
|
}
|
|
274483
|
+
var USER_SYNC_SCOPE_ID, USER_SYNC_NAMESPACE;
|
|
274060
274484
|
var init_namespace = __esm({
|
|
274061
274485
|
"src/sync/namespace.ts"() {
|
|
274062
274486
|
"use strict";
|
|
274063
274487
|
init_esm_shims();
|
|
274488
|
+
USER_SYNC_SCOPE_ID = "__user__";
|
|
274489
|
+
USER_SYNC_NAMESPACE = "user-";
|
|
274064
274490
|
}
|
|
274065
274491
|
});
|
|
274066
274492
|
|
|
@@ -274071,6 +274497,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274071
274497
|
remote: remote.kind,
|
|
274072
274498
|
namespace: remote.namespace,
|
|
274073
274499
|
projectId: store2.projectId(),
|
|
274500
|
+
scope: store2.scope(),
|
|
274074
274501
|
deviceId: opts.deviceId,
|
|
274075
274502
|
pushed: 0,
|
|
274076
274503
|
pulledBatches: 0,
|
|
@@ -274098,7 +274525,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274098
274525
|
report.pending = pending.length;
|
|
274099
274526
|
if (!opts.dryRun) {
|
|
274100
274527
|
for (const pendingBatch of pending) {
|
|
274101
|
-
validateBatches([pendingBatch.batch], store2.projectId(), remote.namespace);
|
|
274528
|
+
validateBatches([pendingBatch.batch], store2.projectId(), remote.namespace, store2.scope());
|
|
274102
274529
|
if (pendingBatch.batch.deviceId !== opts.deviceId) {
|
|
274103
274530
|
throw new Error("[memorix] sync outbox rejected: batch belongs to another device");
|
|
274104
274531
|
}
|
|
@@ -274110,7 +274537,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274110
274537
|
report.pending = 0;
|
|
274111
274538
|
}
|
|
274112
274539
|
const current = await store2.loadAll();
|
|
274113
|
-
const filtered = eligibleObservations(current, store2.projectId());
|
|
274540
|
+
const filtered = eligibleObservations(current, store2.projectId(), store2.scope());
|
|
274114
274541
|
report.eligible = filtered.eligible.length;
|
|
274115
274542
|
report.excluded = filtered.excluded;
|
|
274116
274543
|
const state = await store2.loadState();
|
|
@@ -274133,7 +274560,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274133
274560
|
producedAt: nowIso(),
|
|
274134
274561
|
entries: chunk.map((c6) => c6.entry)
|
|
274135
274562
|
};
|
|
274136
|
-
if (!opts.dryRun) validateBatches([batch], store2.projectId(), remote.namespace);
|
|
274563
|
+
if (!opts.dryRun) validateBatches([batch], store2.projectId(), remote.namespace, store2.scope());
|
|
274137
274564
|
report.pushed += chunk.length;
|
|
274138
274565
|
if (!opts.dryRun) {
|
|
274139
274566
|
const states = chunk.map((c6) => ({ ...c6.nextState, shippedSeq: sequence }));
|
|
@@ -274154,7 +274581,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274154
274581
|
while (hasMore) {
|
|
274155
274582
|
const page = await remote.pull(pullSince, PULL_PAGE_SIZE, pageToken);
|
|
274156
274583
|
report.pulledBatches += page.batches.length;
|
|
274157
|
-
validateBatches(page.batches, store2.projectId(), remote.namespace);
|
|
274584
|
+
validateBatches(page.batches, store2.projectId(), remote.namespace, store2.scope());
|
|
274158
274585
|
for (const batch of page.batches) {
|
|
274159
274586
|
if (batch.deviceId === opts.deviceId) {
|
|
274160
274587
|
cursor.applied[batch.deviceId] = Math.max(cursor.applied[batch.deviceId] ?? 0, batch.sequence);
|
|
@@ -274224,7 +274651,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274224
274651
|
await remote.close();
|
|
274225
274652
|
}
|
|
274226
274653
|
}
|
|
274227
|
-
function validateBatches(batches, projectId, namespace) {
|
|
274654
|
+
function validateBatches(batches, projectId, namespace, scope = "project") {
|
|
274228
274655
|
for (const batch of batches) {
|
|
274229
274656
|
if (batch.formatVersion !== 3 || batch.projectId !== projectId || batch.namespace !== namespace) {
|
|
274230
274657
|
throw new Error("[memorix] sync batch rejected: wrong format, project, or remote namespace");
|
|
@@ -274236,7 +274663,7 @@ function validateBatches(batches, projectId, namespace) {
|
|
|
274236
274663
|
if (!entry.syncKey || entry.kind !== "upsert" && entry.kind !== "tombstone" || !entry.version || !Number.isSafeInteger(entry.version.revision) || entry.version.revision < 1 || !entry.version.writer || typeof entry.contentHash !== "string" || entry.kind === "tombstone" && (entry.row !== void 0 || entry.contentHash !== "") || entry.kind === "upsert" && (!/^[a-f0-9]{64}$/.test(entry.contentHash) || entry.row === void 0)) {
|
|
274237
274664
|
throw new Error("[memorix] sync batch rejected: malformed change entry");
|
|
274238
274665
|
}
|
|
274239
|
-
if (entry.kind === "upsert" && (!Number.isSafeInteger(entry.row.id) || entry.row.id < 1 || !eligibleObservations([entry.row], projectId).eligible.length)) {
|
|
274666
|
+
if (entry.kind === "upsert" && (!Number.isSafeInteger(entry.row.id) || entry.row.id < 1 || !eligibleObservations([entry.row], projectId, scope).eligible.length)) {
|
|
274240
274667
|
throw new Error("[memorix] sync batch rejected: ineligible observation payload");
|
|
274241
274668
|
}
|
|
274242
274669
|
const incomingHash = entry.row ? contentHash(entry.row) : "";
|
|
@@ -274308,9 +274735,11 @@ var init_engine6 = __esm({
|
|
|
274308
274735
|
// src/sync/store-port.ts
|
|
274309
274736
|
import os7 from "os";
|
|
274310
274737
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
274311
|
-
function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
274738
|
+
function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
|
|
274312
274739
|
const db2 = getDatabase(dataDir);
|
|
274313
|
-
|
|
274740
|
+
const scope = options.scope ?? "project";
|
|
274741
|
+
const scopeId = scope === "user" ? USER_SYNC_SCOPE_ID : projectId;
|
|
274742
|
+
ensureSyncSchema(db2, scope === "user" ? LEGACY_PROJECT_ID : projectId);
|
|
274314
274743
|
const getMeta = db2.prepare("SELECT value FROM sync_meta WHERE key = ?");
|
|
274315
274744
|
const setMeta = db2.prepare("INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)");
|
|
274316
274745
|
const selState = db2.prepare("SELECT projectId, syncKey, obsId, revision, writer, kind, contentHash, shippedSeq FROM sync_row_state WHERE projectId = ?");
|
|
@@ -274350,7 +274779,7 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274350
274779
|
SET deviceId = ?, batchJson = ?
|
|
274351
274780
|
WHERE projectId = ? AND namespace = ? AND deviceId = ? AND sequence = ?`
|
|
274352
274781
|
);
|
|
274353
|
-
const namespace = syncNamespace(projectId);
|
|
274782
|
+
const namespace = scope === "user" ? userSyncNamespace() : syncNamespace(projectId);
|
|
274354
274783
|
const deviceFingerprint = process.env.MEMORIX_SYNC_DEVICE_FINGERPRINT?.trim() || `${process.platform}|${os7.hostname()}|${os7.homedir()}`;
|
|
274355
274784
|
function ensureDevice() {
|
|
274356
274785
|
const row = getMeta.get(DEVICE_KEY);
|
|
@@ -274378,7 +274807,8 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274378
274807
|
return `cursor:${namespace}`;
|
|
274379
274808
|
}
|
|
274380
274809
|
return {
|
|
274381
|
-
projectId: () =>
|
|
274810
|
+
projectId: () => scopeId,
|
|
274811
|
+
scope: () => scope,
|
|
274382
274812
|
namespace: () => namespace,
|
|
274383
274813
|
deviceId: ensureDevice,
|
|
274384
274814
|
assertCanPublish,
|
|
@@ -274418,14 +274848,14 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274418
274848
|
setMeta.run(cursorKey(), JSON.stringify(cursor));
|
|
274419
274849
|
},
|
|
274420
274850
|
async loadPendingBatches() {
|
|
274421
|
-
return selPending.all(
|
|
274851
|
+
return selPending.all(scopeId, namespace, ensureDevice()).map((row) => ({
|
|
274422
274852
|
batch: JSON.parse(row.batchJson),
|
|
274423
274853
|
states: JSON.parse(row.statesJson)
|
|
274424
274854
|
}));
|
|
274425
274855
|
},
|
|
274426
274856
|
async enqueueBatch(pending) {
|
|
274427
274857
|
insertPending.run({
|
|
274428
|
-
projectId,
|
|
274858
|
+
projectId: scopeId,
|
|
274429
274859
|
namespace,
|
|
274430
274860
|
deviceId: pending.batch.deviceId,
|
|
274431
274861
|
sequence: pending.batch.sequence,
|
|
@@ -274435,13 +274865,13 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274435
274865
|
});
|
|
274436
274866
|
},
|
|
274437
274867
|
async markBatchShipped(sequence) {
|
|
274438
|
-
deletePending.run(
|
|
274868
|
+
deletePending.run(scopeId, namespace, ensureDevice(), sequence);
|
|
274439
274869
|
},
|
|
274440
274870
|
async loadAll() {
|
|
274441
|
-
return store2.loadByProject(projectId);
|
|
274871
|
+
return scope === "user" ? store2.loadAll() : store2.loadByProject(projectId);
|
|
274442
274872
|
},
|
|
274443
274873
|
async loadState() {
|
|
274444
|
-
const rows = selState.all(
|
|
274874
|
+
const rows = selState.all(scopeId);
|
|
274445
274875
|
const map2 = /* @__PURE__ */ new Map();
|
|
274446
274876
|
for (const r6 of rows) map2.set(r6.syncKey, r6);
|
|
274447
274877
|
return map2;
|
|
@@ -274455,8 +274885,10 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274455
274885
|
async allocateId() {
|
|
274456
274886
|
return store2.atomic(async (tx) => {
|
|
274457
274887
|
const nextId2 = await tx.loadIdCounter();
|
|
274458
|
-
|
|
274459
|
-
|
|
274888
|
+
const maxId = Number(db2.prepare("SELECT COALESCE(MAX(id), 0) AS maxId FROM observations").get()?.maxId ?? 0);
|
|
274889
|
+
const allocated = Math.max(nextId2, maxId + 1);
|
|
274890
|
+
await tx.saveIdCounter(allocated + 1);
|
|
274891
|
+
return allocated;
|
|
274460
274892
|
});
|
|
274461
274893
|
},
|
|
274462
274894
|
async applyInsert(row, state) {
|
|
@@ -274486,7 +274918,11 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274486
274918
|
async applyRemove(id, state) {
|
|
274487
274919
|
await store2.atomic(async (tx) => {
|
|
274488
274920
|
const existing = await tx.getById(id);
|
|
274489
|
-
|
|
274921
|
+
const expectedProjectId = scope === "user" ? sourceProjectIdFromSyncKey(state.syncKey) : state.projectId;
|
|
274922
|
+
if (!expectedProjectId) {
|
|
274923
|
+
throw new Error("[memorix] sync refused to remove an observation without a project-scoped sync key");
|
|
274924
|
+
}
|
|
274925
|
+
if (existing && existing.projectId !== expectedProjectId) {
|
|
274490
274926
|
throw new Error("[memorix] sync refused to remove an observation from another project");
|
|
274491
274927
|
}
|
|
274492
274928
|
await tx.remove(id);
|
|
@@ -274515,6 +274951,15 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274515
274951
|
}
|
|
274516
274952
|
};
|
|
274517
274953
|
}
|
|
274954
|
+
function sourceProjectIdFromSyncKey(syncKey) {
|
|
274955
|
+
const match2 = /^p:([^:]+):/.exec(syncKey);
|
|
274956
|
+
if (!match2) return void 0;
|
|
274957
|
+
try {
|
|
274958
|
+
return decodeURIComponent(match2[1]);
|
|
274959
|
+
} catch {
|
|
274960
|
+
return void 0;
|
|
274961
|
+
}
|
|
274962
|
+
}
|
|
274518
274963
|
function ensureSyncSchema(db2, projectId) {
|
|
274519
274964
|
db2.exec(CREATE_SYNC_TABLES);
|
|
274520
274965
|
const columns = db2.prepare("PRAGMA table_info(sync_row_state)").all();
|
|
@@ -275425,9 +275870,30 @@ var sync_store_exports = {};
|
|
|
275425
275870
|
__export(sync_store_exports, {
|
|
275426
275871
|
default: () => sync_store_default
|
|
275427
275872
|
});
|
|
275873
|
+
function parseScope(raw) {
|
|
275874
|
+
const value = String(raw || "project").toLowerCase();
|
|
275875
|
+
if (value === "project" || value === "user") return value;
|
|
275876
|
+
throw new Error("--scope must be one of: project, user");
|
|
275877
|
+
}
|
|
275878
|
+
async function openSyncStore(scope) {
|
|
275879
|
+
if (scope === "user") {
|
|
275880
|
+
const dataDir2 = await getProjectDataDir(USER_SYNC_SCOPE_ID);
|
|
275881
|
+
await initObservationStore(dataDir2);
|
|
275882
|
+
return {
|
|
275883
|
+
projectId: USER_SYNC_SCOPE_ID,
|
|
275884
|
+
syncStore: createSqliteSyncStore(dataDir2, getObservationStore(), USER_SYNC_SCOPE_ID, { scope: "user" })
|
|
275885
|
+
};
|
|
275886
|
+
}
|
|
275887
|
+
const { project, dataDir } = await getCliProjectContext();
|
|
275888
|
+
await initObservationStore(dataDir);
|
|
275889
|
+
return {
|
|
275890
|
+
projectId: project.id,
|
|
275891
|
+
syncStore: createSqliteSyncStore(dataDir, getObservationStore(), project.id)
|
|
275892
|
+
};
|
|
275893
|
+
}
|
|
275428
275894
|
function renderReport(report) {
|
|
275429
275895
|
const lines = [
|
|
275430
|
-
`Store sync (${report.remote}) \u2014 device ${report.deviceId}${report.dryRun ? " [dry-run]" : ""}`,
|
|
275896
|
+
`Store sync (${report.remote}, scope ${report.scope}) \u2014 device ${report.deviceId}${report.dryRun ? " [dry-run]" : ""}`,
|
|
275431
275897
|
`- Pushed changes: ${report.pushed}`,
|
|
275432
275898
|
`- Pulled batches: ${report.pulledBatches}`,
|
|
275433
275899
|
`- Applied upserts: ${report.applied}`,
|
|
@@ -275453,9 +275919,11 @@ var init_sync_store = __esm({
|
|
|
275453
275919
|
init_dist2();
|
|
275454
275920
|
init_operator_shared();
|
|
275455
275921
|
init_obs_store();
|
|
275922
|
+
init_persistence();
|
|
275456
275923
|
init_engine6();
|
|
275457
275924
|
init_store_port();
|
|
275458
275925
|
init_remote_factory();
|
|
275926
|
+
init_namespace();
|
|
275459
275927
|
sync_store_default = defineCommand({
|
|
275460
275928
|
meta: {
|
|
275461
275929
|
name: "sync-store",
|
|
@@ -275467,23 +275935,34 @@ var init_sync_store = __esm({
|
|
|
275467
275935
|
"push-only": { type: "boolean", description: "Only push local changes", default: false },
|
|
275468
275936
|
"pull-only": { type: "boolean", description: "Only pull remote changes", default: false },
|
|
275469
275937
|
through: { type: "string", description: "Compaction cutoff: device=sequence,device=sequence" },
|
|
275470
|
-
yes: { type: "boolean", description: "Confirm destructive remote compaction", default: false }
|
|
275938
|
+
yes: { type: "boolean", description: "Confirm destructive remote compaction", default: false },
|
|
275939
|
+
scope: {
|
|
275940
|
+
type: "string",
|
|
275941
|
+
description: "project (default: current Git project) or user (all local projects)",
|
|
275942
|
+
default: "project"
|
|
275943
|
+
}
|
|
275471
275944
|
},
|
|
275472
275945
|
run: async ({ args }) => {
|
|
275473
275946
|
const asJson = Boolean(args.json);
|
|
275474
275947
|
const action = args._?.[0] || "status";
|
|
275948
|
+
let scope;
|
|
275949
|
+
try {
|
|
275950
|
+
scope = parseScope(args.scope);
|
|
275951
|
+
} catch (err) {
|
|
275952
|
+
emitError(err.message, asJson);
|
|
275953
|
+
process.exitCode = 2;
|
|
275954
|
+
return;
|
|
275955
|
+
}
|
|
275475
275956
|
if (action === "device") {
|
|
275476
|
-
const { project: project2, dataDir: dataDir2 } = await getCliProjectContext();
|
|
275477
|
-
await initObservationStore(dataDir2);
|
|
275478
|
-
const syncStore2 = createSqliteSyncStore(dataDir2, getObservationStore(), project2.id);
|
|
275479
275957
|
const subaction = args._?.[1] || "";
|
|
275480
275958
|
if (subaction !== "rotate") {
|
|
275481
275959
|
emitError('expected "memorix sync store device rotate"', asJson);
|
|
275482
275960
|
process.exitCode = 2;
|
|
275483
275961
|
return;
|
|
275484
275962
|
}
|
|
275963
|
+
const { projectId: projectId2, syncStore: syncStore2 } = await openSyncStore("user");
|
|
275485
275964
|
const deviceId = syncStore2.rotateDevice();
|
|
275486
|
-
emitResult({ project:
|
|
275965
|
+
emitResult({ project: projectId2, scope: "user", deviceId, rotated: true }, `Sync device identity rotated: ${deviceId}`, asJson);
|
|
275487
275966
|
return;
|
|
275488
275967
|
}
|
|
275489
275968
|
if (action !== "push" && action !== "pull" && action !== "status" && action !== "compact") {
|
|
@@ -275507,10 +275986,7 @@ var init_sync_store = __esm({
|
|
|
275507
275986
|
);
|
|
275508
275987
|
return;
|
|
275509
275988
|
}
|
|
275510
|
-
const {
|
|
275511
|
-
await initObservationStore(dataDir);
|
|
275512
|
-
const obsStore = getObservationStore();
|
|
275513
|
-
const syncStore = createSqliteSyncStore(dataDir, obsStore, project.id);
|
|
275989
|
+
const { projectId, syncStore } = await openSyncStore(scope);
|
|
275514
275990
|
if (action === "compact") {
|
|
275515
275991
|
if (!args.yes && !args.dry) {
|
|
275516
275992
|
emitError("remote compaction deletes relay events; repeat with --yes after specifying --through device=sequence", asJson);
|
|
@@ -275545,7 +276021,7 @@ var init_sync_store = __esm({
|
|
|
275545
276021
|
try {
|
|
275546
276022
|
await remote2.init({ create: false });
|
|
275547
276023
|
const result = await remote2.compact(through, { dryRun: Boolean(args.dry) });
|
|
275548
|
-
emitResult({ project:
|
|
276024
|
+
emitResult({ project: projectId, scope, through, ...result, dryRun: Boolean(args.dry) }, `Compaction candidates: ${result.candidates}; deleted: ${result.deleted}`, asJson);
|
|
275549
276025
|
} catch (err) {
|
|
275550
276026
|
emitError(err.message, asJson);
|
|
275551
276027
|
process.exitCode = 1;
|
|
@@ -275570,7 +276046,7 @@ var init_sync_store = __esm({
|
|
|
275570
276046
|
push: true,
|
|
275571
276047
|
pull: true
|
|
275572
276048
|
});
|
|
275573
|
-
emitResult({ project:
|
|
276049
|
+
emitResult({ project: projectId, ...report }, renderReport(report), asJson);
|
|
275574
276050
|
} catch (err) {
|
|
275575
276051
|
emitError(err.message, asJson);
|
|
275576
276052
|
process.exitCode = 1;
|
|
@@ -275594,7 +276070,7 @@ var init_sync_store = __esm({
|
|
|
275594
276070
|
push: doPush,
|
|
275595
276071
|
pull: doPull
|
|
275596
276072
|
});
|
|
275597
|
-
emitResult({ project:
|
|
276073
|
+
emitResult({ project: projectId, ...report }, renderReport(report), asJson);
|
|
275598
276074
|
} catch (err) {
|
|
275599
276075
|
emitError(err.message, asJson);
|
|
275600
276076
|
process.exitCode = 1;
|
|
@@ -275656,6 +276132,32 @@ var init_sync = __esm({
|
|
|
275656
276132
|
type: "boolean",
|
|
275657
276133
|
description: "Emit machine-readable JSON output",
|
|
275658
276134
|
default: false
|
|
276135
|
+
},
|
|
276136
|
+
scope: {
|
|
276137
|
+
type: "string",
|
|
276138
|
+
description: "Store sync scope: project (default) or user",
|
|
276139
|
+
required: false,
|
|
276140
|
+
default: "project"
|
|
276141
|
+
},
|
|
276142
|
+
"push-only": {
|
|
276143
|
+
type: "boolean",
|
|
276144
|
+
description: "Store sync: only push local changes",
|
|
276145
|
+
required: false
|
|
276146
|
+
},
|
|
276147
|
+
"pull-only": {
|
|
276148
|
+
type: "boolean",
|
|
276149
|
+
description: "Store sync: only pull remote changes",
|
|
276150
|
+
required: false
|
|
276151
|
+
},
|
|
276152
|
+
through: {
|
|
276153
|
+
type: "string",
|
|
276154
|
+
description: "Store sync compaction cutoff: device=sequence[,device=sequence]",
|
|
276155
|
+
required: false
|
|
276156
|
+
},
|
|
276157
|
+
yes: {
|
|
276158
|
+
type: "boolean",
|
|
276159
|
+
description: "Store sync: confirm destructive remote compaction",
|
|
276160
|
+
required: false
|
|
275659
276161
|
}
|
|
275660
276162
|
},
|
|
275661
276163
|
run: async ({ args }) => {
|
|
@@ -280273,8 +280775,20 @@ async function doStartUnlocked(port) {
|
|
|
280273
280775
|
if (healthy) {
|
|
280274
280776
|
process.stderr.write("[OK] Control plane is running and healthy.\n");
|
|
280275
280777
|
} else {
|
|
280276
|
-
process.stderr.write("[
|
|
280277
|
-
|
|
280778
|
+
process.stderr.write("[ERROR] Health check timed out; refusing to leave an unverified background state.\n");
|
|
280779
|
+
if (isProcessRunning(pid)) {
|
|
280780
|
+
killProcess(pid);
|
|
280781
|
+
for (let i4 = 0; i4 < 10 && isProcessRunning(pid); i4++) {
|
|
280782
|
+
await new Promise((r6) => setTimeout(r6, 250));
|
|
280783
|
+
}
|
|
280784
|
+
}
|
|
280785
|
+
clearState();
|
|
280786
|
+
try {
|
|
280787
|
+
fs26.unlinkSync(getMemorixDir() + "/background.ready");
|
|
280788
|
+
} catch {
|
|
280789
|
+
}
|
|
280790
|
+
process.exitCode = 1;
|
|
280791
|
+
return;
|
|
280278
280792
|
}
|
|
280279
280793
|
const footer = [
|
|
280280
280794
|
"",
|
|
@@ -280662,7 +281176,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
280662
281176
|
import { existsSync as existsSync31, realpathSync as realpathSync2 } from "fs";
|
|
280663
281177
|
import { mkdir as mkdir9, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
280664
281178
|
import { basename as basename5, dirname as dirname8, resolve as resolve7 } from "path";
|
|
280665
|
-
import { homedir as
|
|
281179
|
+
import { homedir as homedir36 } from "os";
|
|
280666
281180
|
function requestedAgents(agent) {
|
|
280667
281181
|
const all = getSetupAgentTargets();
|
|
280668
281182
|
if (!agent || agent === "all") return all;
|
|
@@ -280740,16 +281254,16 @@ function asRecordArray(value) {
|
|
|
280740
281254
|
return Array.isArray(value) ? value.map(asRecord3).filter((entry) => entry !== null) : [];
|
|
280741
281255
|
}
|
|
280742
281256
|
function codexPluginPath() {
|
|
280743
|
-
return `${
|
|
281257
|
+
return `${homedir36()}/plugins/${CODEX_PLUGIN_NAME}`;
|
|
280744
281258
|
}
|
|
280745
281259
|
function codexPluginMcpPath() {
|
|
280746
281260
|
return `${codexPluginPath()}/.mcp.json`;
|
|
280747
281261
|
}
|
|
280748
281262
|
function codexMarketplacePath() {
|
|
280749
|
-
return `${
|
|
281263
|
+
return `${homedir36()}/.agents/plugins/marketplace.json`;
|
|
280750
281264
|
}
|
|
280751
281265
|
function codexConfigPath() {
|
|
280752
|
-
return `${
|
|
281266
|
+
return `${homedir36()}/.codex/config.toml`;
|
|
280753
281267
|
}
|
|
280754
281268
|
function normalizeMarketplacePath(value) {
|
|
280755
281269
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -281064,7 +281578,7 @@ function canonicalProjectPath(projectRoot) {
|
|
|
281064
281578
|
return canonical.replace(/\\/g, "/");
|
|
281065
281579
|
}
|
|
281066
281580
|
function getClaudeLocalConfigPath() {
|
|
281067
|
-
return `${
|
|
281581
|
+
return `${homedir36()}/.claude.json`;
|
|
281068
281582
|
}
|
|
281069
281583
|
function coerceMcpServer(name, value) {
|
|
281070
281584
|
const entry = asRecord3(value);
|
|
@@ -281226,7 +281740,58 @@ async function inspectCodexPluginMcp() {
|
|
|
281226
281740
|
};
|
|
281227
281741
|
}
|
|
281228
281742
|
}
|
|
281743
|
+
async function inspectOptionalGrokMcp(scope) {
|
|
281744
|
+
if (scope === "project" || scope === "local") {
|
|
281745
|
+
return { status: "skipped", issues: ["mcp-host-owned"], checks: [] };
|
|
281746
|
+
}
|
|
281747
|
+
const adapter = getMcpAdapter("grok");
|
|
281748
|
+
const configPath = adapter.getConfigPath();
|
|
281749
|
+
if (!existsSync31(configPath)) {
|
|
281750
|
+
return {
|
|
281751
|
+
status: "skipped",
|
|
281752
|
+
issues: ["mcp-host-owned"],
|
|
281753
|
+
checks: [{
|
|
281754
|
+
scope: "global",
|
|
281755
|
+
path: configPath,
|
|
281756
|
+
exists: false,
|
|
281757
|
+
status: "skipped",
|
|
281758
|
+
issues: ["mcp-host-owned"]
|
|
281759
|
+
}]
|
|
281760
|
+
};
|
|
281761
|
+
}
|
|
281762
|
+
const content = await readFile10(configPath, "utf-8");
|
|
281763
|
+
const server = adapter.parse(content).find((entry) => entry.name === "memorix");
|
|
281764
|
+
if (!server) {
|
|
281765
|
+
return {
|
|
281766
|
+
status: "skipped",
|
|
281767
|
+
issues: ["mcp-host-owned"],
|
|
281768
|
+
checks: [{
|
|
281769
|
+
scope: "global",
|
|
281770
|
+
path: configPath,
|
|
281771
|
+
exists: true,
|
|
281772
|
+
status: "skipped",
|
|
281773
|
+
issues: ["mcp-host-owned"]
|
|
281774
|
+
}]
|
|
281775
|
+
};
|
|
281776
|
+
}
|
|
281777
|
+
const issues = [];
|
|
281778
|
+
if (!server.url && looksLikeStaleMemorixCommand(server)) issues.push("stale-command-path");
|
|
281779
|
+
if (!server.url && !isRecommendedStdioServer(server)) issues.push("nonstandard-mcp-command");
|
|
281780
|
+
return {
|
|
281781
|
+
status: issues.length > 0 ? "repairable" : "ok",
|
|
281782
|
+
issues,
|
|
281783
|
+
checks: [{
|
|
281784
|
+
scope: "global",
|
|
281785
|
+
path: configPath,
|
|
281786
|
+
exists: true,
|
|
281787
|
+
status: issues.length > 0 ? "repairable" : "ok",
|
|
281788
|
+
issues,
|
|
281789
|
+
server: sanitizeServer(server)
|
|
281790
|
+
}]
|
|
281791
|
+
};
|
|
281792
|
+
}
|
|
281229
281793
|
async function inspectMcp(agent, projectRoot, scope) {
|
|
281794
|
+
if (agent === "grok") return inspectOptionalGrokMcp(scope);
|
|
281230
281795
|
if (!isMcpConfigAgent(agent)) {
|
|
281231
281796
|
return { status: "skipped", issues: ["mcp-managed-by-package"], checks: [] };
|
|
281232
281797
|
}
|
|
@@ -281323,7 +281888,7 @@ async function inspectGuidance(agent, projectRoot, scope) {
|
|
|
281323
281888
|
}
|
|
281324
281889
|
const checks = [];
|
|
281325
281890
|
for (const targetScope of requestedGuidanceScopes(scope)) {
|
|
281326
|
-
const root = targetScope === "project" ? projectRoot :
|
|
281891
|
+
const root = targetScope === "project" ? projectRoot : homedir36();
|
|
281327
281892
|
const rulesPath = getAgentRulesPath(agent, root, targetScope === "global");
|
|
281328
281893
|
if (!existsSync31(rulesPath)) {
|
|
281329
281894
|
checks.push({
|
|
@@ -281604,8 +282169,8 @@ var init_doctor = __esm({
|
|
|
281604
282169
|
return;
|
|
281605
282170
|
}
|
|
281606
282171
|
const { existsSync: existsSync36, readFileSync: readFileSync25 } = await import("fs");
|
|
281607
|
-
const { join:
|
|
281608
|
-
const { homedir:
|
|
282172
|
+
const { join: join36, basename: basename7 } = await import("path");
|
|
282173
|
+
const { homedir: homedir38 } = await import("os");
|
|
281609
282174
|
const { execSync: execSync5 } = await import("child_process");
|
|
281610
282175
|
const report = {};
|
|
281611
282176
|
const issues = [];
|
|
@@ -281667,8 +282232,8 @@ var init_doctor = __esm({
|
|
|
281667
282232
|
}
|
|
281668
282233
|
lines.push("");
|
|
281669
282234
|
lines.push("\u250C\u2500 Runtime Mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
281670
|
-
const memorixDir =
|
|
281671
|
-
const bgStatePath =
|
|
282235
|
+
const memorixDir = join36(homedir38(), ".memorix");
|
|
282236
|
+
const bgStatePath = join36(memorixDir, "background.json");
|
|
281672
282237
|
let bgRunning = false;
|
|
281673
282238
|
let bgPort = 0;
|
|
281674
282239
|
let bgPid = 0;
|
|
@@ -281788,7 +282353,7 @@ var init_doctor = __esm({
|
|
|
281788
282353
|
}
|
|
281789
282354
|
}
|
|
281790
282355
|
try {
|
|
281791
|
-
const cachePath =
|
|
282356
|
+
const cachePath = join36(memorixDir, "embedding-cache.json");
|
|
281792
282357
|
if (existsSync36(cachePath)) {
|
|
281793
282358
|
const cache5 = JSON.parse(readFileSync25(cachePath, "utf-8"));
|
|
281794
282359
|
const cacheCount = Object.keys(cache5).length;
|
|
@@ -282042,7 +282607,7 @@ var init_doctor = __esm({
|
|
|
282042
282607
|
lines.push("\u250C\u2500 Conflict Check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
282043
282608
|
let conflictsFound = false;
|
|
282044
282609
|
if (dataDir) {
|
|
282045
|
-
const lockFile =
|
|
282610
|
+
const lockFile = join36(dataDir, ".memorix.lock");
|
|
282046
282611
|
if (existsSync36(lockFile)) {
|
|
282047
282612
|
try {
|
|
282048
282613
|
const lockData = JSON.parse(readFileSync25(lockFile, "utf-8"));
|
|
@@ -284254,7 +284819,7 @@ var init_pipeline_trace = __esm({
|
|
|
284254
284819
|
// src/orchestrate/worktree.ts
|
|
284255
284820
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
284256
284821
|
import { existsSync as existsSync33, rmSync as rmSync2 } from "fs";
|
|
284257
|
-
import { join as
|
|
284822
|
+
import { join as join34, basename as basename6 } from "path";
|
|
284258
284823
|
function runGit4(cwd, args, timeout = 5e3) {
|
|
284259
284824
|
return String(execFileSync5("git", args, {
|
|
284260
284825
|
cwd,
|
|
@@ -284293,7 +284858,7 @@ function isManagedWorktreePath(worktreePath, worktreeBase) {
|
|
|
284293
284858
|
function createWorktree(projectDir2, taskId, pipelineId) {
|
|
284294
284859
|
const shortId2 = taskId.slice(0, 8);
|
|
284295
284860
|
const shortPipeline = pipelineId.slice(0, 8);
|
|
284296
|
-
const worktreeBase =
|
|
284861
|
+
const worktreeBase = join34(projectDir2, WORKTREE_DIR, `task-${shortId2}`);
|
|
284297
284862
|
const branchBase = `pipeline/${shortPipeline}/task-${shortId2}`;
|
|
284298
284863
|
for (let attempt = 1; attempt <= 100; attempt++) {
|
|
284299
284864
|
const suffix = attempt === 1 ? "" : `-${attempt}`;
|
|
@@ -284383,7 +284948,7 @@ function listWorktrees(projectDir2) {
|
|
|
284383
284948
|
}
|
|
284384
284949
|
}
|
|
284385
284950
|
if (current) worktrees.push(current);
|
|
284386
|
-
const worktreeBase =
|
|
284951
|
+
const worktreeBase = join34(projectDir2, WORKTREE_DIR);
|
|
284387
284952
|
return worktrees.filter((w4) => isManagedWorktreePath(w4.path, worktreeBase));
|
|
284388
284953
|
} catch {
|
|
284389
284954
|
return [];
|
|
@@ -284805,16 +285370,16 @@ var init_cost_tracker = __esm({
|
|
|
284805
285370
|
|
|
284806
285371
|
// src/orchestrate/evidence.ts
|
|
284807
285372
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
284808
|
-
import { join as
|
|
285373
|
+
import { join as join35 } from "path";
|
|
284809
285374
|
function writeTaskEvidence(projectDir2, pipelineId, evidence) {
|
|
284810
285375
|
try {
|
|
284811
|
-
const dir =
|
|
285376
|
+
const dir = join35(projectDir2, ".pipeline", pipelineId, "evidence", evidence.taskId.slice(0, 12));
|
|
284812
285377
|
mkdirSync11(dir, { recursive: true });
|
|
284813
285378
|
if (evidence.prompt) {
|
|
284814
|
-
safeWrite(
|
|
285379
|
+
safeWrite(join35(dir, "prompt.md"), evidence.prompt);
|
|
284815
285380
|
}
|
|
284816
285381
|
if (evidence.tailOutput) {
|
|
284817
|
-
safeWrite(
|
|
285382
|
+
safeWrite(join35(dir, "output.txt"), evidence.tailOutput);
|
|
284818
285383
|
}
|
|
284819
285384
|
if (evidence.gateResults) {
|
|
284820
285385
|
for (const g4 of evidence.gateResults) {
|
|
@@ -284823,7 +285388,7 @@ Status: ${g4.passed ? "PASS" : "FAIL"}
|
|
|
284823
285388
|
Duration: ${(g4.durationMs / 1e3).toFixed(1)}s
|
|
284824
285389
|
|
|
284825
285390
|
${g4.output}`;
|
|
284826
|
-
safeWrite(
|
|
285391
|
+
safeWrite(join35(dir, `${g4.gate}.txt`), content);
|
|
284827
285392
|
}
|
|
284828
285393
|
}
|
|
284829
285394
|
const resultJson = {
|
|
@@ -284837,7 +285402,7 @@ ${g4.output}`;
|
|
|
284837
285402
|
tokenUsage: evidence.tokenUsage,
|
|
284838
285403
|
costUSD: evidence.costUSD
|
|
284839
285404
|
};
|
|
284840
|
-
safeWrite(
|
|
285405
|
+
safeWrite(join35(dir, "result.json"), JSON.stringify(resultJson, null, 2));
|
|
284841
285406
|
return dir;
|
|
284842
285407
|
} catch {
|
|
284843
285408
|
return null;
|
|
@@ -284845,7 +285410,7 @@ ${g4.output}`;
|
|
|
284845
285410
|
}
|
|
284846
285411
|
function writePipelineSummary(projectDir2, summary) {
|
|
284847
285412
|
try {
|
|
284848
|
-
const dir =
|
|
285413
|
+
const dir = join35(projectDir2, ".pipeline", summary.pipelineId);
|
|
284849
285414
|
mkdirSync11(dir, { recursive: true });
|
|
284850
285415
|
const elapsed = (summary.elapsedMs / 1e3).toFixed(0);
|
|
284851
285416
|
const lines = [
|
|
@@ -284866,8 +285431,8 @@ function writePipelineSummary(projectDir2, summary) {
|
|
|
284866
285431
|
lines.push(`- [${status}] ${t4.taskDescription.slice(0, 80)} \u2014 ${t4.agentName}, ${dur}s${fixes}`);
|
|
284867
285432
|
}
|
|
284868
285433
|
const content = lines.join("\n") + "\n";
|
|
284869
|
-
safeWrite(
|
|
284870
|
-
return
|
|
285434
|
+
safeWrite(join35(dir, "summary.md"), content);
|
|
285435
|
+
return join35(dir, "summary.md");
|
|
284871
285436
|
} catch {
|
|
284872
285437
|
return null;
|
|
284873
285438
|
}
|
|
@@ -291246,11 +291811,11 @@ var GUIDES = {
|
|
|
291246
291811
|
"memorix sync rules --action status",
|
|
291247
291812
|
"memorix sync workspace --action scan",
|
|
291248
291813
|
"memorix sync store status --json",
|
|
291249
|
-
"memorix sync store push|pull [--dry] [--json]",
|
|
291814
|
+
"memorix sync store push|pull [--dry] [--json] [--scope project|user]",
|
|
291250
291815
|
"memorix sync store compact --dry --through device=sequence",
|
|
291251
291816
|
"memorix sync store device rotate"
|
|
291252
291817
|
],
|
|
291253
|
-
notes: ["Store sync is opt-in
|
|
291818
|
+
notes: ["Store sync is opt-in and never copies the live SQLite database. Default scope is the current Git project; `--scope user` relays every local project."]
|
|
291254
291819
|
},
|
|
291255
291820
|
ingest: {
|
|
291256
291821
|
summary: "Ingest Git and image evidence into project memory.",
|