memorix 1.9.3 → 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 +49 -1
- package/README.md +7 -6
- package/README.zh-CN.md +7 -6
- package/dist/cli/index.js +930 -330
- package/dist/index.d.ts +3 -1
- package/dist/index.js +289 -109
- package/dist/maintenance-runner.js +179 -117
- package/dist/media-audio-runner.js +92 -30
- package/dist/media-video-runner.js +92 -30
- package/dist/memcode-runtime/CHANGELOG.md +49 -1
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +1050 -847
- package/dist/types.d.ts +1 -1
- package/dist/vector-backfill-runner.js +92 -30
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
- package/docs/API_REFERENCE.md +3 -1
- package/docs/CONFIGURATION.md +15 -1
- package/docs/DEVELOPMENT.md +1 -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/README.md +1 -1
- 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/media.ts +2 -0
- 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/media/minimax.ts +26 -2
- package/src/media/video-jobs.ts +9 -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 +5 -3
- 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) {
|
|
@@ -177667,7 +177727,20 @@ function normalizeMiniMaxVideoRequest(input) {
|
|
|
177667
177727
|
if (ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1") {
|
|
177668
177728
|
throw new Error("MiniMax video ratio must be adaptive, 16:9, 9:16, or 1:1");
|
|
177669
177729
|
}
|
|
177670
|
-
|
|
177730
|
+
let firstFrameImageUrl;
|
|
177731
|
+
if (input.firstFrameImageUrl !== void 0) {
|
|
177732
|
+
firstFrameImageUrl = input.firstFrameImageUrl.trim();
|
|
177733
|
+
let parsed;
|
|
177734
|
+
try {
|
|
177735
|
+
parsed = new URL(firstFrameImageUrl);
|
|
177736
|
+
} catch {
|
|
177737
|
+
throw new Error("MiniMax first-frame image must be a public HTTPS URL");
|
|
177738
|
+
}
|
|
177739
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password || sanitizeCredentials(firstFrameImageUrl) !== firstFrameImageUrl) {
|
|
177740
|
+
throw new Error("MiniMax first-frame image must be an HTTPS URL without credentials");
|
|
177741
|
+
}
|
|
177742
|
+
}
|
|
177743
|
+
return { region, model, prompt: prompt2, resolution, duration: duration3, ratio, ...firstFrameImageUrl ? { firstFrameImageUrl } : {} };
|
|
177671
177744
|
}
|
|
177672
177745
|
function videoRequestConfig(input) {
|
|
177673
177746
|
const normalized = normalizeMiniMaxVideoRequest(input);
|
|
@@ -177744,7 +177817,14 @@ async function createMiniMaxVideoTask(input, dependencies = {}) {
|
|
|
177744
177817
|
headers: videoHeaders(config2.apiKey),
|
|
177745
177818
|
body: JSON.stringify({
|
|
177746
177819
|
model: config2.model,
|
|
177747
|
-
content: [
|
|
177820
|
+
content: [
|
|
177821
|
+
{ type: "text", text: config2.prompt },
|
|
177822
|
+
...config2.firstFrameImageUrl ? [{
|
|
177823
|
+
type: "image_url",
|
|
177824
|
+
image_url: { url: config2.firstFrameImageUrl },
|
|
177825
|
+
role: "first_frame"
|
|
177826
|
+
}] : []
|
|
177827
|
+
],
|
|
177748
177828
|
resolution: config2.resolution,
|
|
177749
177829
|
duration: config2.duration,
|
|
177750
177830
|
ratio: config2.ratio
|
|
@@ -177992,10 +178072,19 @@ function parseStoredRequest(value) {
|
|
|
177992
178072
|
const duration3 = value.duration;
|
|
177993
178073
|
const ratio = value.ratio;
|
|
177994
178074
|
const maxBytes = value.maxBytes;
|
|
177995
|
-
if (provider2 !== "minimax" && provider2 !== "minimax-cn" || region !== "global" && region !== "cn" || model !== "MiniMax-H3" || typeof prompt2 !== "string" || resolution !== "2K" || duration3 !== 5 && duration3 !== 10 || ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1" || typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 1) {
|
|
178075
|
+
if (provider2 !== "minimax" && provider2 !== "minimax-cn" || region !== "global" && region !== "cn" || model !== "MiniMax-H3" || typeof prompt2 !== "string" || value.firstFrameImageUrl !== void 0 && typeof value.firstFrameImageUrl !== "string" || resolution !== "2K" || duration3 !== 5 && duration3 !== 10 || ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1" || typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 1) {
|
|
177996
178076
|
throw new Error("Media video job has an invalid request payload");
|
|
177997
178077
|
}
|
|
177998
|
-
|
|
178078
|
+
const normalized = normalizeMiniMaxVideoRequest({
|
|
178079
|
+
region,
|
|
178080
|
+
model,
|
|
178081
|
+
prompt: prompt2,
|
|
178082
|
+
resolution,
|
|
178083
|
+
duration: duration3,
|
|
178084
|
+
ratio,
|
|
178085
|
+
firstFrameImageUrl: value.firstFrameImageUrl
|
|
178086
|
+
});
|
|
178087
|
+
return { provider: provider2, ...normalized, maxBytes };
|
|
177999
178088
|
}
|
|
178000
178089
|
function mediaJobIdFromMaintenance(job) {
|
|
178001
178090
|
const mediaJobId = job.payload.mediaJobId;
|
|
@@ -178007,6 +178096,7 @@ function mediaJobIdFromMaintenance(job) {
|
|
|
178007
178096
|
function toVideoRequest(request) {
|
|
178008
178097
|
return {
|
|
178009
178098
|
prompt: request.prompt,
|
|
178099
|
+
firstFrameImageUrl: request.firstFrameImageUrl,
|
|
178010
178100
|
model: request.model,
|
|
178011
178101
|
region: request.region,
|
|
178012
178102
|
resolution: request.resolution,
|
|
@@ -178098,6 +178188,7 @@ function queueMiniMaxVideoGeneration(input) {
|
|
|
178098
178188
|
region: normalized.region,
|
|
178099
178189
|
model: normalized.model,
|
|
178100
178190
|
prompt: normalized.prompt,
|
|
178191
|
+
...normalized.firstFrameImageUrl ? { firstFrameImageUrl: normalized.firstFrameImageUrl } : {},
|
|
178101
178192
|
resolution: normalized.resolution,
|
|
178102
178193
|
duration: normalized.duration,
|
|
178103
178194
|
ratio: normalized.ratio,
|
|
@@ -194246,6 +194337,32 @@ var init_codebuddy = __esm({
|
|
|
194246
194337
|
}
|
|
194247
194338
|
});
|
|
194248
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
|
+
|
|
194249
194366
|
// src/integrations/registry.ts
|
|
194250
194367
|
function getSetupIntegrationRows() {
|
|
194251
194368
|
return MEMORIX_INTEGRATIONS.filter((item) => item.status === "ready" && item.agent !== "memcode" && item.agent !== "any-mcp").map((item) => ({
|
|
@@ -194303,7 +194420,7 @@ var init_registry2 = __esm({
|
|
|
194303
194420
|
status: "ready",
|
|
194304
194421
|
install: "memorix setup --agent grok --global",
|
|
194305
194422
|
surfaces: ["hooks", "AGENTS.md"],
|
|
194306
|
-
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."
|
|
194307
194424
|
},
|
|
194308
194425
|
{
|
|
194309
194426
|
agent: "cursor",
|
|
@@ -194708,9 +194825,9 @@ __export(audit_exports, {
|
|
|
194708
194825
|
});
|
|
194709
194826
|
import * as fs18 from "fs/promises";
|
|
194710
194827
|
import * as path30 from "path";
|
|
194711
|
-
import { homedir as
|
|
194828
|
+
import { homedir as homedir26 } from "os";
|
|
194712
194829
|
function getAuditFilePath() {
|
|
194713
|
-
return process.env.MEMORIX_AUDIT_FILE || path30.join(
|
|
194830
|
+
return process.env.MEMORIX_AUDIT_FILE || path30.join(homedir26(), ".memorix", "audit.json");
|
|
194714
194831
|
}
|
|
194715
194832
|
async function loadAudit() {
|
|
194716
194833
|
try {
|
|
@@ -196164,6 +196281,7 @@ var init_installers = __esm({
|
|
|
196164
196281
|
// src/cli/commands/setup.ts
|
|
196165
196282
|
var setup_exports = {};
|
|
196166
196283
|
__export(setup_exports, {
|
|
196284
|
+
MEMORIX_HTTP_MCP_URL: () => MEMORIX_HTTP_MCP_URL,
|
|
196167
196285
|
buildMemorixServer: () => buildMemorixServer,
|
|
196168
196286
|
buildSetupPlan: () => buildSetupPlan,
|
|
196169
196287
|
default: () => setup_default,
|
|
@@ -196183,19 +196301,20 @@ __export(setup_exports, {
|
|
|
196183
196301
|
migrateLegacyCodexIntegration: () => migrateLegacyCodexIntegration,
|
|
196184
196302
|
parseCodexPluginState: () => parseCodexPluginState,
|
|
196185
196303
|
removeLegacyCodexMemorixMcpConfig: () => removeLegacyCodexMemorixMcpConfig,
|
|
196304
|
+
rewriteBundledMemorixMcpToHttp: () => rewriteBundledMemorixMcpToHttp,
|
|
196186
196305
|
tryInstallCodexPlugin: () => tryInstallCodexPlugin
|
|
196187
196306
|
});
|
|
196188
196307
|
import { spawnSync } from "child_process";
|
|
196189
196308
|
import { existsSync as existsSync17 } from "fs";
|
|
196190
196309
|
import { copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6, readdir as readdir3, rm as rm2, writeFile as writeFile7 } from "fs/promises";
|
|
196191
|
-
import { homedir as
|
|
196310
|
+
import { homedir as homedir28 } from "os";
|
|
196192
196311
|
import path32 from "path";
|
|
196193
196312
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
196194
196313
|
function getSetupAgentTargets() {
|
|
196195
196314
|
return [...SUPPORTED_SETUP_AGENTS];
|
|
196196
196315
|
}
|
|
196197
196316
|
function buildSetupPlan(options) {
|
|
196198
|
-
const mcp = options.agent === "grok" ? "none" : options.mcp ?? "stdio";
|
|
196317
|
+
const mcp = options.agent === "grok" ? options.mcp === "http" ? "http" : "none" : options.mcp ?? "stdio";
|
|
196199
196318
|
const actions = [];
|
|
196200
196319
|
const isPackageOwnedAgent = options.agent !== "all" && PACKAGE_OWNED_INTEGRATION_AGENTS.has(options.agent);
|
|
196201
196320
|
const includeHooks = options.hooks ?? true;
|
|
@@ -196351,7 +196470,8 @@ function getMcpAdapter(agent) {
|
|
|
196351
196470
|
opencode: new OpenCodeMCPAdapter(),
|
|
196352
196471
|
trae: new TraeMCPAdapter(),
|
|
196353
196472
|
dsh: new DshMCPAdapter(),
|
|
196354
|
-
workbuddy: new WorkbuddyMCPAdapter()
|
|
196473
|
+
workbuddy: new WorkbuddyMCPAdapter(),
|
|
196474
|
+
grok: new GrokMCPAdapter()
|
|
196355
196475
|
};
|
|
196356
196476
|
return adapters[agent];
|
|
196357
196477
|
}
|
|
@@ -196361,7 +196481,7 @@ function buildMemorixServer(mcp) {
|
|
|
196361
196481
|
name: "memorix",
|
|
196362
196482
|
command: "",
|
|
196363
196483
|
args: [],
|
|
196364
|
-
url:
|
|
196484
|
+
url: MEMORIX_HTTP_MCP_URL
|
|
196365
196485
|
};
|
|
196366
196486
|
}
|
|
196367
196487
|
return {
|
|
@@ -196372,6 +196492,36 @@ function buildMemorixServer(mcp) {
|
|
|
196372
196492
|
args: ["serve", "--mode", "lite"]
|
|
196373
196493
|
};
|
|
196374
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
|
+
}
|
|
196375
196525
|
function mergeJsonMcpConfig(existingContent, generatedContent) {
|
|
196376
196526
|
let generated;
|
|
196377
196527
|
try {
|
|
@@ -196473,7 +196623,7 @@ function isOwnedLegacyCodexMcp(content) {
|
|
|
196473
196623
|
const server = new CodexMCPAdapter().parse(content).find((entry) => entry.name === "memorix");
|
|
196474
196624
|
return Boolean(server && isLegacyCodexMemorixNodeServer(server));
|
|
196475
196625
|
}
|
|
196476
|
-
async function removeLegacyCodexMemorixMcpConfig(configPath = path32.join(
|
|
196626
|
+
async function removeLegacyCodexMemorixMcpConfig(configPath = path32.join(homedir28(), ".codex", "config.toml")) {
|
|
196477
196627
|
let existingContent;
|
|
196478
196628
|
try {
|
|
196479
196629
|
existingContent = await readFile6(configPath, "utf-8");
|
|
@@ -196530,7 +196680,7 @@ async function removeOwnedLegacyCodexPluginFolder(homeDir) {
|
|
|
196530
196680
|
}
|
|
196531
196681
|
}
|
|
196532
196682
|
async function migrateLegacyCodexIntegration(options = {}) {
|
|
196533
|
-
const homeDir = options.homeDir ??
|
|
196683
|
+
const homeDir = options.homeDir ?? homedir28();
|
|
196534
196684
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
196535
196685
|
const configPath = path32.join(homeDir, ".codex", "config.toml");
|
|
196536
196686
|
let mcp = "missing";
|
|
@@ -196640,7 +196790,7 @@ async function installMcpConfig(options) {
|
|
|
196640
196790
|
existingContent = null;
|
|
196641
196791
|
}
|
|
196642
196792
|
const generated = adapter.generate([server]);
|
|
196643
|
-
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);
|
|
196644
196794
|
await mkdir6(path32.dirname(configPath), { recursive: true });
|
|
196645
196795
|
await writeFile7(configPath, content, "utf-8");
|
|
196646
196796
|
return { agent: options.agent, configPath, transport: options.mcp };
|
|
@@ -196731,7 +196881,7 @@ async function stripCodeBuddyHookCapture(pluginPath) {
|
|
|
196731
196881
|
await writeFile7(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
196732
196882
|
}
|
|
196733
196883
|
async function installCodeBuddyPluginPackage(options = {}) {
|
|
196734
|
-
const home = options.homeDir ??
|
|
196884
|
+
const home = options.homeDir ?? homedir28();
|
|
196735
196885
|
const packageRoot = findPackageRoot();
|
|
196736
196886
|
const source = path32.join(packageRoot, "plugins", "codebuddy", "memorix");
|
|
196737
196887
|
if (!existsSync17(source)) throw new Error(`CodeBuddy plugin template not found: ${source}`);
|
|
@@ -196751,7 +196901,7 @@ async function installCodeBuddyPluginPackage(options = {}) {
|
|
|
196751
196901
|
};
|
|
196752
196902
|
}
|
|
196753
196903
|
async function installPluginPackage(options) {
|
|
196754
|
-
const home = options.homeDir ??
|
|
196904
|
+
const home = options.homeDir ?? homedir28();
|
|
196755
196905
|
const packageRoot = findPackageRoot();
|
|
196756
196906
|
const source = path32.join(packageRoot, "plugins", options.agent, "memorix");
|
|
196757
196907
|
const includeHooks = options.includeHooks ?? true;
|
|
@@ -196800,7 +196950,7 @@ async function installPluginPackage(options) {
|
|
|
196800
196950
|
};
|
|
196801
196951
|
}
|
|
196802
196952
|
async function installGeminiExtensionPackage(options = {}) {
|
|
196803
|
-
const home = options.homeDir ??
|
|
196953
|
+
const home = options.homeDir ?? homedir28();
|
|
196804
196954
|
const packageRoot = findPackageRoot();
|
|
196805
196955
|
const source = path32.join(packageRoot, "plugins", "gemini", "memorix");
|
|
196806
196956
|
if (!existsSync17(source)) {
|
|
@@ -196820,7 +196970,7 @@ async function installAntigravityPluginPackage(options = {}) {
|
|
|
196820
196970
|
if (!existsSync17(source)) {
|
|
196821
196971
|
throw new Error(`Antigravity plugin template not found: ${source}`);
|
|
196822
196972
|
}
|
|
196823
|
-
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");
|
|
196824
196974
|
await copyDir(source, pluginPath);
|
|
196825
196975
|
if (options.includeHooks === false) {
|
|
196826
196976
|
await rm2(path32.join(pluginPath, "hooks.json"), { force: true });
|
|
@@ -196838,7 +196988,7 @@ async function installPiPackage(options = {}) {
|
|
|
196838
196988
|
if (!existsSync17(source)) {
|
|
196839
196989
|
throw new Error(`Pi package template not found: ${source}`);
|
|
196840
196990
|
}
|
|
196841
|
-
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");
|
|
196842
196992
|
const packagePath = options.packageDir ?? path32.join(defaultRoot, "memorix");
|
|
196843
196993
|
await copyDir(source, packagePath);
|
|
196844
196994
|
await writeOfficialSkills(path32.join(packagePath, "skills"));
|
|
@@ -196850,7 +197000,7 @@ async function installPiPackage(options = {}) {
|
|
|
196850
197000
|
};
|
|
196851
197001
|
}
|
|
196852
197002
|
async function installOpenClawBundlePackage(options = {}) {
|
|
196853
|
-
const home = options.homeDir ??
|
|
197003
|
+
const home = options.homeDir ?? homedir28();
|
|
196854
197004
|
const packageRoot = findPackageRoot();
|
|
196855
197005
|
const source = path32.join(packageRoot, "plugins", "openclaw", "memorix");
|
|
196856
197006
|
if (!existsSync17(source)) {
|
|
@@ -196899,7 +197049,7 @@ async function installOmpPackage(options = {}) {
|
|
|
196899
197049
|
if (!existsSync17(source)) {
|
|
196900
197050
|
throw new Error(`Oh-my-Pi package template not found: ${source}`);
|
|
196901
197051
|
}
|
|
196902
|
-
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");
|
|
196903
197053
|
const packagePath = options.packageDir ?? path32.join(defaultRoot, "memorix");
|
|
196904
197054
|
await copyDir(source, packagePath);
|
|
196905
197055
|
await writeOfficialSkills(path32.join(packagePath, "skills"));
|
|
@@ -197173,7 +197323,7 @@ ${result.stdout || ""}`.toLowerCase();
|
|
|
197173
197323
|
}
|
|
197174
197324
|
async function installAgentSetup(agent, plan, global2) {
|
|
197175
197325
|
const { installAgentGuidance: installAgentGuidance2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
|
|
197176
|
-
const targetRoot = global2 ?
|
|
197326
|
+
const targetRoot = global2 ? homedir28() : process.cwd();
|
|
197177
197327
|
const hasPluginPackage = plan.actions.includes("plugin-package") && PLUGIN_PACKAGE_AGENTS.has(agent);
|
|
197178
197328
|
const hasExtensionPackage = plan.actions.includes("extension-package") && agent === "gemini-cli";
|
|
197179
197329
|
const hasPiPackage = plan.actions.includes("pi-package") && agent === "pi";
|
|
@@ -197182,7 +197332,8 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197182
197332
|
const hasHermesPlugin = plan.actions.includes("hermes-plugin") && agent === "hermes";
|
|
197183
197333
|
const hasOmpPackage = plan.actions.includes("omp-package") && agent === "omp";
|
|
197184
197334
|
const hasCodeBuddyPlugin = plan.actions.includes("codebuddy-plugin") && agent === "codebuddy";
|
|
197185
|
-
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 = [];
|
|
197186
197337
|
if (PLUGIN_PACKAGE_AGENTS.has(agent) && plan.includePlugin && !global2) {
|
|
197187
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.`);
|
|
197188
197339
|
}
|
|
@@ -197191,6 +197342,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197191
197342
|
agent,
|
|
197192
197343
|
includeHooks: plan.includeHooks
|
|
197193
197344
|
});
|
|
197345
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197194
197346
|
v3.success(`${agent}: plugin package -> ${result.pluginPath}`);
|
|
197195
197347
|
if (result.marketplacePath) {
|
|
197196
197348
|
v3.info(`${agent}: marketplace -> ${result.marketplacePath}`);
|
|
@@ -197221,6 +197373,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197221
197373
|
}
|
|
197222
197374
|
if (hasCodeBuddyPlugin) {
|
|
197223
197375
|
const result = await installCodeBuddyPluginPackage({ includeHooks: plan.includeHooks });
|
|
197376
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197224
197377
|
v3.success(`${agent}: plugin -> ${result.pluginPath}`);
|
|
197225
197378
|
v3.info(`${agent}: marketplace -> ${result.marketplacePath}`);
|
|
197226
197379
|
const install = tryInstallCodeBuddyPlugin(result.marketplaceRoot);
|
|
@@ -197231,6 +197384,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197231
197384
|
}
|
|
197232
197385
|
if (hasExtensionPackage) {
|
|
197233
197386
|
const result = await installGeminiExtensionPackage();
|
|
197387
|
+
bundledMcpRoots.push(result.extensionPath);
|
|
197234
197388
|
v3.success(`${agent}: extension package -> ${result.extensionPath}`);
|
|
197235
197389
|
v3.info(result.installHint);
|
|
197236
197390
|
}
|
|
@@ -197244,6 +197398,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197244
197398
|
}
|
|
197245
197399
|
if (hasOpenClawBundle) {
|
|
197246
197400
|
const result = await installOpenClawBundlePackage({ includeHooks: plan.includeHooks });
|
|
197401
|
+
bundledMcpRoots.push(result.bundlePath);
|
|
197247
197402
|
v3.success(`${agent}: compatible bundle -> ${result.bundlePath}`);
|
|
197248
197403
|
if (plan.includeHooks) {
|
|
197249
197404
|
const install = tryInstallOpenClawBundle(result.bundlePath);
|
|
@@ -197256,6 +197411,7 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197256
197411
|
}
|
|
197257
197412
|
if (hasAntigravityPlugin) {
|
|
197258
197413
|
const result = await installAntigravityPluginPackage({ global: global2, projectRoot: targetRoot, includeHooks: plan.includeHooks });
|
|
197414
|
+
bundledMcpRoots.push(result.pluginPath);
|
|
197259
197415
|
v3.success(`${agent}: plugin -> ${result.pluginPath}`);
|
|
197260
197416
|
if (!plan.includeHooks) {
|
|
197261
197417
|
v3.info(`${agent}: hooks.json skipped because --noHooks was selected`);
|
|
@@ -197286,9 +197442,17 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197286
197442
|
v3.info(result.installHint);
|
|
197287
197443
|
}
|
|
197288
197444
|
if (wantsMcpConfig) {
|
|
197289
|
-
|
|
197445
|
+
const mcpOwnedByPackage = hasPluginPackage || hasCodeBuddyPlugin || hasExtensionPackage || hasOpenClawBundle || hasAntigravityPlugin;
|
|
197446
|
+
if (mcpOwnedByPackage && plan.mcp === "stdio") {
|
|
197290
197447
|
const packageLabel = hasExtensionPackage ? "extension package" : hasCodeBuddyPlugin ? "CodeBuddy plugin" : hasOpenClawBundle ? "OpenClaw bundle" : hasAntigravityPlugin ? "Antigravity plugin" : "plugin package";
|
|
197291
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
|
+
}
|
|
197292
197456
|
} else {
|
|
197293
197457
|
const mcp = plan.mcp === "http" ? "http" : "stdio";
|
|
197294
197458
|
const result = await installMcpConfig({ agent, projectRoot: targetRoot, global: global2, mcp });
|
|
@@ -197342,7 +197506,11 @@ async function installAgentSetup(agent, plan, global2) {
|
|
|
197342
197506
|
if (agent === "pi") {
|
|
197343
197507
|
v3.info("pi: Pi has no MCP config lane in the current CLI; use the installed package extension and skill.");
|
|
197344
197508
|
} else if (agent === "grok") {
|
|
197345
|
-
|
|
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
|
+
}
|
|
197346
197514
|
if (!global2) v3.info("grok: project hooks require `/hooks-trust` or launching Grok with `--trust`.");
|
|
197347
197515
|
} else if (plan.mcp === "stdio") {
|
|
197348
197516
|
v3.info(`${agent}: MCP server command is \`memorix serve\``);
|
|
@@ -197369,7 +197537,7 @@ function printSetupIntegrationRows() {
|
|
|
197369
197537
|
].join("\n");
|
|
197370
197538
|
ye2(table, "Memorix integrations");
|
|
197371
197539
|
}
|
|
197372
|
-
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;
|
|
197373
197541
|
var init_setup = __esm({
|
|
197374
197542
|
"src/cli/commands/setup.ts"() {
|
|
197375
197543
|
"use strict";
|
|
@@ -197393,6 +197561,7 @@ var init_setup = __esm({
|
|
|
197393
197561
|
init_dsh();
|
|
197394
197562
|
init_workbuddy();
|
|
197395
197563
|
init_codebuddy();
|
|
197564
|
+
init_grok();
|
|
197396
197565
|
init_registry2();
|
|
197397
197566
|
init_official_skills();
|
|
197398
197567
|
init_version();
|
|
@@ -197426,6 +197595,7 @@ var init_setup = __esm({
|
|
|
197426
197595
|
"grok"
|
|
197427
197596
|
];
|
|
197428
197597
|
getSetupIntegrationRows2 = getSetupIntegrationRows;
|
|
197598
|
+
MEMORIX_HTTP_MCP_URL = "http://localhost:3211/mcp";
|
|
197429
197599
|
setup_default = defineCommand({
|
|
197430
197600
|
meta: {
|
|
197431
197601
|
name: "setup",
|
|
@@ -197501,7 +197671,7 @@ var init_setup = __esm({
|
|
|
197501
197671
|
{ value: "codex", label: "Codex", hint: "plugin package + marketplace + MCP" },
|
|
197502
197672
|
{ value: "copilot", label: "GitHub Copilot CLI", hint: "plugin package + hooks + MCP" },
|
|
197503
197673
|
{ value: "cursor", label: "Cursor", hint: "MCP + rules + skills" },
|
|
197504
|
-
{ value: "grok", label: "Grok Build", hint: "hooks file; MCP
|
|
197674
|
+
{ value: "grok", label: "Grok Build", hint: "hooks file; MCP host-owned unless --mcp http" },
|
|
197505
197675
|
{ value: "gemini-cli", label: "Gemini CLI", hint: "extension + MCP" },
|
|
197506
197676
|
{ value: "openclaw", label: "OpenClaw", hint: "bundle + hook pack + skills + MCP" },
|
|
197507
197677
|
{ value: "hermes", label: "Hermes Agent", hint: "plugin + hooks + commands + skills + MCP" },
|
|
@@ -197567,7 +197737,7 @@ var config_path_exports = {};
|
|
|
197567
197737
|
__export(config_path_exports, {
|
|
197568
197738
|
default: () => config_path_default
|
|
197569
197739
|
});
|
|
197570
|
-
import { homedir as
|
|
197740
|
+
import { homedir as homedir29 } from "os";
|
|
197571
197741
|
var config_path_default;
|
|
197572
197742
|
var init_config_path = __esm({
|
|
197573
197743
|
"src/cli/commands/config-path.ts"() {
|
|
@@ -197585,7 +197755,7 @@ var init_config_path = __esm({
|
|
|
197585
197755
|
run: async () => {
|
|
197586
197756
|
const project = detectProject(process.cwd());
|
|
197587
197757
|
ye2([
|
|
197588
|
-
`Global: ${getGlobalConfigTomlPath(
|
|
197758
|
+
`Global: ${getGlobalConfigTomlPath(homedir29())}`,
|
|
197589
197759
|
`Project: ${project ? getProjectConfigTomlPath(project.rootPath) : "not inside a git project"}`
|
|
197590
197760
|
].join("\n"), "Memorix config");
|
|
197591
197761
|
}
|
|
@@ -197661,7 +197831,7 @@ __export(config_migrate_exports, {
|
|
|
197661
197831
|
serializeResolvedConfigToToml: () => serializeResolvedConfigToToml
|
|
197662
197832
|
});
|
|
197663
197833
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
197664
|
-
import { homedir as
|
|
197834
|
+
import { homedir as homedir30 } from "os";
|
|
197665
197835
|
import { dirname as dirname4 } from "path";
|
|
197666
197836
|
function serializeResolvedConfigToToml(config2, options = {}) {
|
|
197667
197837
|
const includeSecrets = options.includeSecrets === true;
|
|
@@ -197760,7 +197930,7 @@ var init_config_migrate = __esm({
|
|
|
197760
197930
|
v3.error("Not inside a git project. Use `memorix config migrate --global` for global config.");
|
|
197761
197931
|
return;
|
|
197762
197932
|
}
|
|
197763
|
-
const target = isGlobal ? getGlobalConfigTomlPath(
|
|
197933
|
+
const target = isGlobal ? getGlobalConfigTomlPath(homedir30()) : getProjectConfigTomlPath(project.rootPath);
|
|
197764
197934
|
const resolved = getResolvedConfig({ projectRoot: project?.rootPath ?? null });
|
|
197765
197935
|
const content = serializeResolvedConfigToToml(resolved, { includeSecrets: isGlobal });
|
|
197766
197936
|
mkdirSync4(dirname4(target), { recursive: true });
|
|
@@ -197772,8 +197942,8 @@ var init_config_migrate = __esm({
|
|
|
197772
197942
|
});
|
|
197773
197943
|
|
|
197774
197944
|
// src/cli/commands/integrate-shared.ts
|
|
197775
|
-
import { homedir as
|
|
197776
|
-
function getIntegrationTargetRoot(useGlobalDefaults, cwd, homeDir =
|
|
197945
|
+
import { homedir as homedir31 } from "os";
|
|
197946
|
+
function getIntegrationTargetRoot(useGlobalDefaults, cwd, homeDir = homedir31()) {
|
|
197777
197947
|
return useGlobalDefaults ? homeDir : cwd;
|
|
197778
197948
|
}
|
|
197779
197949
|
function getIntegrationScopeLabel(useGlobalDefaults) {
|
|
@@ -199035,7 +199205,7 @@ __export(external_provider_exports, {
|
|
|
199035
199205
|
});
|
|
199036
199206
|
import { spawn as spawn5 } from "child_process";
|
|
199037
199207
|
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
199038
|
-
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";
|
|
199039
199209
|
function boundedTimeout(value) {
|
|
199040
199210
|
if (!Number.isFinite(value)) return DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS;
|
|
199041
199211
|
return Math.max(100, Math.min(Math.floor(value), MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS));
|
|
@@ -199064,10 +199234,10 @@ function resolveCommandCandidates(command) {
|
|
|
199064
199234
|
}
|
|
199065
199235
|
const names = extname(requested) ? [requested] : process.platform === "win32" ? [requested + ".exe", requested + ".cmd", requested + ".js", requested] : [requested];
|
|
199066
199236
|
const directories = [
|
|
199067
|
-
...process.platform === "win32" && process.env.LOCALAPPDATA ? [
|
|
199237
|
+
...process.platform === "win32" && process.env.LOCALAPPDATA ? [join25(process.env.LOCALAPPDATA, "codegraph", "current", "bin")] : [],
|
|
199068
199238
|
...(process.env.PATH ?? "").split(delimiter).filter(Boolean)
|
|
199069
199239
|
];
|
|
199070
|
-
return [...new Set(directories.flatMap((directory) => names.map((name) =>
|
|
199240
|
+
return [...new Set(directories.flatMap((directory) => names.map((name) => join25(directory, name))))];
|
|
199071
199241
|
}
|
|
199072
199242
|
function resolveLaunch(command) {
|
|
199073
199243
|
for (const candidate2 of resolveCommandCandidates(command)) {
|
|
@@ -199075,8 +199245,8 @@ function resolveLaunch(command) {
|
|
|
199075
199245
|
const extension3 = extname(candidate2).toLowerCase();
|
|
199076
199246
|
if (extension3 === ".cmd") {
|
|
199077
199247
|
const bundleRoot = resolve6(dirname5(candidate2), "..");
|
|
199078
|
-
const node =
|
|
199079
|
-
const entry =
|
|
199248
|
+
const node = join25(bundleRoot, "node.exe");
|
|
199249
|
+
const entry = join25(bundleRoot, "lib", "dist", "bin", "codegraph.js");
|
|
199080
199250
|
if (existsSync19(node) && existsSync19(entry)) {
|
|
199081
199251
|
return { executable: node, prefixArgs: ["--liftoff-only", entry] };
|
|
199082
199252
|
}
|
|
@@ -199234,7 +199404,7 @@ async function inspectExternalCodeGraph(input) {
|
|
|
199234
199404
|
const health2 = { state: "disabled", reason: "External CodeGraph is disabled by configuration." };
|
|
199235
199405
|
return { health: health2, quality: providerQuality({ mode, health: health2 }) };
|
|
199236
199406
|
}
|
|
199237
|
-
if (!existsSync19(
|
|
199407
|
+
if (!existsSync19(join25(input.projectRoot, ".codegraph"))) {
|
|
199238
199408
|
const health2 = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
|
|
199239
199409
|
return { health: health2, quality: providerQuality({ mode, health: health2 }) };
|
|
199240
199410
|
}
|
|
@@ -199269,7 +199439,7 @@ async function runExternalCodeGraphLifecycle(input) {
|
|
|
199269
199439
|
quality: providerQuality({ mode, health })
|
|
199270
199440
|
};
|
|
199271
199441
|
}
|
|
199272
|
-
const indexPath =
|
|
199442
|
+
const indexPath = join25(input.projectRoot, ".codegraph");
|
|
199273
199443
|
if (input.action === "sync" && !existsSync19(indexPath)) {
|
|
199274
199444
|
const health = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
|
|
199275
199445
|
return {
|
|
@@ -200095,6 +200265,7 @@ var init_project_context = __esm({
|
|
|
200095
200265
|
// src/store/graph-store.ts
|
|
200096
200266
|
var graph_store_exports = {};
|
|
200097
200267
|
__export(graph_store_exports, {
|
|
200268
|
+
DEFAULT_GRAPH_PROJECT_ID: () => DEFAULT_GRAPH_PROJECT_ID,
|
|
200098
200269
|
GraphSqliteStore: () => GraphSqliteStore,
|
|
200099
200270
|
getGraphStore: () => getGraphStore,
|
|
200100
200271
|
initGraphStore: () => initGraphStore,
|
|
@@ -200124,37 +200295,106 @@ function entityToRow(entity) {
|
|
|
200124
200295
|
observations: JSON.stringify(entity.observations ?? [])
|
|
200125
200296
|
};
|
|
200126
200297
|
}
|
|
200127
|
-
|
|
200128
|
-
|
|
200129
|
-
|
|
200130
|
-
|
|
200131
|
-
|
|
200132
|
-
|
|
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}`;
|
|
200133
200330
|
}
|
|
200134
|
-
function
|
|
200135
|
-
|
|
200136
|
-
|
|
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;
|
|
200137
200356
|
}
|
|
200138
200357
|
function resetGraphStore() {
|
|
200139
|
-
|
|
200358
|
+
for (const store2 of _graphStores.values()) {
|
|
200140
200359
|
try {
|
|
200141
|
-
|
|
200360
|
+
store2.close();
|
|
200142
200361
|
} catch {
|
|
200143
200362
|
}
|
|
200144
200363
|
}
|
|
200145
|
-
|
|
200146
|
-
|
|
200364
|
+
_graphStores.clear();
|
|
200365
|
+
_activeGraphKey = null;
|
|
200147
200366
|
}
|
|
200148
|
-
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;
|
|
200149
200368
|
var init_graph_store = __esm({
|
|
200150
200369
|
"src/store/graph-store.ts"() {
|
|
200151
200370
|
"use strict";
|
|
200152
200371
|
init_esm_shims();
|
|
200153
200372
|
init_sqlite_db();
|
|
200154
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
|
+
);`;
|
|
200155
200394
|
GraphSqliteStore = class {
|
|
200156
200395
|
db = null;
|
|
200157
200396
|
dataDir = "";
|
|
200397
|
+
projectId = DEFAULT_GRAPH_PROJECT_ID;
|
|
200158
200398
|
stmtInsertEntity = null;
|
|
200159
200399
|
stmtUpdateEntityObs = null;
|
|
200160
200400
|
stmtDeleteEntity = null;
|
|
@@ -200164,45 +200404,89 @@ var init_graph_store = __esm({
|
|
|
200164
200404
|
stmtDeleteRelation = null;
|
|
200165
200405
|
stmtDeleteRelationsByEntity = null;
|
|
200166
200406
|
stmtSelectAllRelations = null;
|
|
200167
|
-
async init(dataDir) {
|
|
200407
|
+
async init(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
|
|
200168
200408
|
this.dataDir = dataDir;
|
|
200409
|
+
this.projectId = projectId;
|
|
200169
200410
|
this.db = getDatabase(dataDir);
|
|
200411
|
+
ensureScopedGraphSchema(this.db);
|
|
200170
200412
|
this.stmtInsertEntity = this.db.prepare(
|
|
200171
|
-
`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)`
|
|
200172
200414
|
);
|
|
200173
200415
|
this.stmtUpdateEntityObs = this.db.prepare(
|
|
200174
|
-
`UPDATE graph_entities SET observations = @observations WHERE name = @name`
|
|
200416
|
+
`UPDATE graph_entities SET observations = @observations WHERE projectId = @projectId AND name = @name`
|
|
200175
200417
|
);
|
|
200176
|
-
this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE name = ?`);
|
|
200177
|
-
this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities`);
|
|
200178
|
-
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 = ?`);
|
|
200179
200421
|
this.stmtInsertRelation = this.db.prepare(
|
|
200180
|
-
`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)`
|
|
200181
200423
|
);
|
|
200182
200424
|
this.stmtDeleteRelation = this.db.prepare(
|
|
200183
|
-
`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 = ?`
|
|
200184
200426
|
);
|
|
200185
200427
|
this.stmtDeleteRelationsByEntity = this.db.prepare(
|
|
200186
|
-
`DELETE FROM graph_relations WHERE from_entity = ? OR to_entity =
|
|
200428
|
+
`DELETE FROM graph_relations WHERE projectId = ? AND (from_entity = ? OR to_entity = ?)`
|
|
200187
200429
|
);
|
|
200188
|
-
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations`);
|
|
200430
|
+
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations WHERE projectId = ? ORDER BY id`);
|
|
200189
200431
|
await this.migrateFromJsonlIfNeeded();
|
|
200190
200432
|
}
|
|
200191
200433
|
async migrateFromJsonlIfNeeded() {
|
|
200192
|
-
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();
|
|
200193
200436
|
if (count3.cnt > 0) return;
|
|
200437
|
+
if (totalCount.cnt > 0) return;
|
|
200194
200438
|
const jsonlPath = path34.join(this.dataDir, "graph.jsonl");
|
|
200195
200439
|
if (!fs20.existsSync(jsonlPath)) return;
|
|
200196
200440
|
try {
|
|
200197
200441
|
const data = await loadGraphJsonl(this.dataDir);
|
|
200198
200442
|
if (data.entities.length === 0 && data.relations.length === 0) return;
|
|
200199
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
|
+
});
|
|
200200
200484
|
const insertAll = this.db.transaction(() => {
|
|
200201
|
-
for (const entity of
|
|
200202
|
-
this.stmtInsertEntity.run(
|
|
200485
|
+
for (const entity of entityRows) {
|
|
200486
|
+
this.stmtInsertEntity.run(entity);
|
|
200203
200487
|
}
|
|
200204
|
-
for (const
|
|
200205
|
-
this.stmtInsertRelation.run(
|
|
200488
|
+
for (const relation of relationRows) {
|
|
200489
|
+
this.stmtInsertRelation.run(relation);
|
|
200206
200490
|
}
|
|
200207
200491
|
});
|
|
200208
200492
|
insertAll();
|
|
@@ -200212,10 +200496,10 @@ var init_graph_store = __esm({
|
|
|
200212
200496
|
}
|
|
200213
200497
|
}
|
|
200214
200498
|
loadEntities() {
|
|
200215
|
-
return this.stmtSelectAllEntities.all().map(rowToEntity);
|
|
200499
|
+
return this.stmtSelectAllEntities.all(this.projectId).map(rowToEntity);
|
|
200216
200500
|
}
|
|
200217
200501
|
loadRelations() {
|
|
200218
|
-
return this.stmtSelectAllRelations.all().map((row) => ({
|
|
200502
|
+
return this.stmtSelectAllRelations.all(this.projectId).map((row) => ({
|
|
200219
200503
|
from: row.from_entity,
|
|
200220
200504
|
to: row.to_entity,
|
|
200221
200505
|
relationType: row.relationType
|
|
@@ -200224,7 +200508,7 @@ var init_graph_store = __esm({
|
|
|
200224
200508
|
insertEntities(entities) {
|
|
200225
200509
|
const insertAll = this.db.transaction(() => {
|
|
200226
200510
|
for (const entity of entities) {
|
|
200227
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
200511
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
200228
200512
|
}
|
|
200229
200513
|
});
|
|
200230
200514
|
insertAll();
|
|
@@ -200232,7 +200516,7 @@ var init_graph_store = __esm({
|
|
|
200232
200516
|
insertRelations(relations) {
|
|
200233
200517
|
const insertAll = this.db.transaction(() => {
|
|
200234
200518
|
for (const rel of relations) {
|
|
200235
|
-
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 });
|
|
200236
200520
|
}
|
|
200237
200521
|
});
|
|
200238
200522
|
insertAll();
|
|
@@ -200240,8 +200524,8 @@ var init_graph_store = __esm({
|
|
|
200240
200524
|
deleteEntities(names) {
|
|
200241
200525
|
const deleteAll = this.db.transaction(() => {
|
|
200242
200526
|
for (const name of names) {
|
|
200243
|
-
this.stmtDeleteRelationsByEntity.run(name, name);
|
|
200244
|
-
this.stmtDeleteEntity.run(name);
|
|
200527
|
+
this.stmtDeleteRelationsByEntity.run(this.projectId, name, name);
|
|
200528
|
+
this.stmtDeleteEntity.run(this.projectId, name);
|
|
200245
200529
|
}
|
|
200246
200530
|
});
|
|
200247
200531
|
deleteAll();
|
|
@@ -200249,7 +200533,7 @@ var init_graph_store = __esm({
|
|
|
200249
200533
|
deleteRelations(relations) {
|
|
200250
200534
|
const deleteAll = this.db.transaction(() => {
|
|
200251
200535
|
for (const rel of relations) {
|
|
200252
|
-
this.stmtDeleteRelation.run(rel.from, rel.to, rel.relationType);
|
|
200536
|
+
this.stmtDeleteRelation.run(this.projectId, rel.from, rel.to, rel.relationType);
|
|
200253
200537
|
}
|
|
200254
200538
|
});
|
|
200255
200539
|
deleteAll();
|
|
@@ -200257,13 +200541,13 @@ var init_graph_store = __esm({
|
|
|
200257
200541
|
addObservations(updates) {
|
|
200258
200542
|
const updateAll = this.db.transaction(() => {
|
|
200259
200543
|
for (const u5 of updates) {
|
|
200260
|
-
const row = this.stmtSelectEntityByName.get(u5.entityName);
|
|
200544
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, u5.entityName);
|
|
200261
200545
|
if (!row) continue;
|
|
200262
200546
|
const existing = safeJsonParse3(row.observations, []);
|
|
200263
200547
|
const newObs = u5.contents.filter((c6) => !existing.includes(c6));
|
|
200264
200548
|
if (newObs.length > 0) {
|
|
200265
200549
|
existing.push(...newObs);
|
|
200266
|
-
this.stmtUpdateEntityObs.run({ name: u5.entityName, observations: JSON.stringify(existing) });
|
|
200550
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: u5.entityName, observations: JSON.stringify(existing) });
|
|
200267
200551
|
}
|
|
200268
200552
|
}
|
|
200269
200553
|
});
|
|
@@ -200272,11 +200556,11 @@ var init_graph_store = __esm({
|
|
|
200272
200556
|
deleteObservations(deletions) {
|
|
200273
200557
|
const deleteAll = this.db.transaction(() => {
|
|
200274
200558
|
for (const d4 of deletions) {
|
|
200275
|
-
const row = this.stmtSelectEntityByName.get(d4.entityName);
|
|
200559
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, d4.entityName);
|
|
200276
200560
|
if (!row) continue;
|
|
200277
200561
|
const existing = safeJsonParse3(row.observations, []);
|
|
200278
200562
|
const filtered = existing.filter((o4) => !d4.observations.includes(o4));
|
|
200279
|
-
this.stmtUpdateEntityObs.run({ name: d4.entityName, observations: JSON.stringify(filtered) });
|
|
200563
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: d4.entityName, observations: JSON.stringify(filtered) });
|
|
200280
200564
|
}
|
|
200281
200565
|
});
|
|
200282
200566
|
deleteAll();
|
|
@@ -200287,13 +200571,13 @@ var init_graph_store = __esm({
|
|
|
200287
200571
|
*/
|
|
200288
200572
|
replaceAll(entities, relations) {
|
|
200289
200573
|
const replaceAll = this.db.transaction(() => {
|
|
200290
|
-
this.db.prepare("DELETE FROM graph_relations").run();
|
|
200291
|
-
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);
|
|
200292
200576
|
for (const entity of entities) {
|
|
200293
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
200577
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
200294
200578
|
}
|
|
200295
200579
|
for (const rel of relations) {
|
|
200296
|
-
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 });
|
|
200297
200581
|
}
|
|
200298
200582
|
});
|
|
200299
200583
|
replaceAll();
|
|
@@ -200301,8 +200585,8 @@ var init_graph_store = __esm({
|
|
|
200301
200585
|
close() {
|
|
200302
200586
|
}
|
|
200303
200587
|
};
|
|
200304
|
-
|
|
200305
|
-
|
|
200588
|
+
_graphStores = /* @__PURE__ */ new Map();
|
|
200589
|
+
_activeGraphKey = null;
|
|
200306
200590
|
}
|
|
200307
200591
|
});
|
|
200308
200592
|
|
|
@@ -200317,11 +200601,14 @@ var init_graph = __esm({
|
|
|
200317
200601
|
entities = [];
|
|
200318
200602
|
relations = [];
|
|
200319
200603
|
projectDir;
|
|
200604
|
+
projectId;
|
|
200605
|
+
store = null;
|
|
200320
200606
|
initialized = false;
|
|
200321
200607
|
/** Index: lowercase entity name → Entity for O(1) lookups */
|
|
200322
200608
|
entityIndex = /* @__PURE__ */ new Map();
|
|
200323
|
-
constructor(projectDir2) {
|
|
200609
|
+
constructor(projectDir2, projectId = "__default__") {
|
|
200324
200610
|
this.projectDir = projectDir2;
|
|
200611
|
+
this.projectId = projectId;
|
|
200325
200612
|
}
|
|
200326
200613
|
/** Rebuild the entity name index */
|
|
200327
200614
|
rebuildIndex() {
|
|
@@ -200333,10 +200620,9 @@ var init_graph = __esm({
|
|
|
200333
200620
|
/** Load graph from SQLite on first access */
|
|
200334
200621
|
async init() {
|
|
200335
200622
|
if (this.initialized) return;
|
|
200336
|
-
await initGraphStore(this.projectDir);
|
|
200337
|
-
|
|
200338
|
-
this.
|
|
200339
|
-
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();
|
|
200340
200626
|
this.rebuildIndex();
|
|
200341
200627
|
this.initialized = true;
|
|
200342
200628
|
}
|
|
@@ -200350,8 +200636,8 @@ var init_graph = __esm({
|
|
|
200350
200636
|
}
|
|
200351
200637
|
/** Persist current state to SQLite */
|
|
200352
200638
|
async save() {
|
|
200353
|
-
|
|
200354
|
-
|
|
200639
|
+
if (!this.store) throw new Error("[memorix] KnowledgeGraphManager not initialized");
|
|
200640
|
+
this.store.replaceAll(this.entities, this.relations);
|
|
200355
200641
|
}
|
|
200356
200642
|
/** Create new entities (skip duplicates by name) */
|
|
200357
200643
|
async createEntities(entities) {
|
|
@@ -200727,7 +201013,7 @@ async function getSessionContext(projectDir2, projectId, limit2 = 3, reader2) {
|
|
|
200727
201013
|
let graphNeighbors = [];
|
|
200728
201014
|
if (!reader2 && activeEntities.length > 0) {
|
|
200729
201015
|
try {
|
|
200730
|
-
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
201016
|
+
const graphMgr = new KnowledgeGraphManager(projectDir2, projectId);
|
|
200731
201017
|
await graphMgr.init();
|
|
200732
201018
|
const { relations } = await graphMgr.readGraph();
|
|
200733
201019
|
const activeSet = new Set(activeEntities.map((n3) => n3.toLowerCase()));
|
|
@@ -205235,8 +205521,8 @@ __export(engine_exports2, {
|
|
|
205235
205521
|
SkillsEngine: () => SkillsEngine
|
|
205236
205522
|
});
|
|
205237
205523
|
import { existsSync as existsSync21, readFileSync as readFileSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "fs";
|
|
205238
|
-
import { join as
|
|
205239
|
-
import { homedir as
|
|
205524
|
+
import { join as join26 } from "path";
|
|
205525
|
+
import { homedir as homedir32 } from "os";
|
|
205240
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;
|
|
205241
205527
|
var init_engine4 = __esm({
|
|
205242
205528
|
"src/skills/engine.ts"() {
|
|
@@ -205307,12 +205593,12 @@ var init_engine4 = __esm({
|
|
|
205307
205593
|
listSkills() {
|
|
205308
205594
|
const skills = [];
|
|
205309
205595
|
const seen = /* @__PURE__ */ new Set();
|
|
205310
|
-
const home =
|
|
205596
|
+
const home = homedir32();
|
|
205311
205597
|
for (const [agent, dirs] of Object.entries(SKILLS_DIRS)) {
|
|
205312
205598
|
for (const dir of dirs) {
|
|
205313
|
-
const paths = [
|
|
205599
|
+
const paths = [join26(this.projectRoot, dir)];
|
|
205314
205600
|
if (!this.skipGlobal) {
|
|
205315
|
-
paths.push(
|
|
205601
|
+
paths.push(join26(home, dir));
|
|
205316
205602
|
}
|
|
205317
205603
|
for (const skillsRoot of paths) {
|
|
205318
205604
|
if (!existsSync21(skillsRoot)) continue;
|
|
@@ -205322,7 +205608,7 @@ var init_engine4 = __esm({
|
|
|
205322
205608
|
if (!entry.isDirectory()) continue;
|
|
205323
205609
|
const name = entry.name;
|
|
205324
205610
|
if (seen.has(name)) continue;
|
|
205325
|
-
const skillMd =
|
|
205611
|
+
const skillMd = join26(skillsRoot, name, "SKILL.md");
|
|
205326
205612
|
if (!existsSync21(skillMd)) continue;
|
|
205327
205613
|
try {
|
|
205328
205614
|
const content = readFileSync13(skillMd, "utf-8");
|
|
@@ -205330,7 +205616,7 @@ var init_engine4 = __esm({
|
|
|
205330
205616
|
skills.push({
|
|
205331
205617
|
name,
|
|
205332
205618
|
description,
|
|
205333
|
-
sourcePath:
|
|
205619
|
+
sourcePath: join26(skillsRoot, name),
|
|
205334
205620
|
sourceAgent: agent,
|
|
205335
205621
|
content,
|
|
205336
205622
|
generated: false
|
|
@@ -205373,11 +205659,11 @@ var init_engine4 = __esm({
|
|
|
205373
205659
|
writeSkill(skill2, target) {
|
|
205374
205660
|
const dirs = SKILLS_DIRS[target];
|
|
205375
205661
|
if (!dirs || dirs.length === 0) return null;
|
|
205376
|
-
const targetDir =
|
|
205662
|
+
const targetDir = join26(this.projectRoot, dirs[0], skill2.name);
|
|
205377
205663
|
try {
|
|
205378
205664
|
mkdirSync5(targetDir, { recursive: true });
|
|
205379
|
-
writeFileSync5(
|
|
205380
|
-
return
|
|
205665
|
+
writeFileSync5(join26(targetDir, "SKILL.md"), skill2.content, "utf-8");
|
|
205666
|
+
return join26(dirs[0], skill2.name, "SKILL.md");
|
|
205381
205667
|
} catch {
|
|
205382
205668
|
return null;
|
|
205383
205669
|
}
|
|
@@ -206546,7 +206832,7 @@ var init_task_graph = __esm({
|
|
|
206546
206832
|
// src/orchestrate/context-collector.ts
|
|
206547
206833
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
206548
206834
|
import { readFileSync as readFileSync14, existsSync as existsSync22 } from "fs";
|
|
206549
|
-
import { join as
|
|
206835
|
+
import { join as join27 } from "path";
|
|
206550
206836
|
function normalizeEntryLimit(value, fallback) {
|
|
206551
206837
|
if (!Number.isSafeInteger(value) || value < 1) return fallback;
|
|
206552
206838
|
return Math.min(value, 500);
|
|
@@ -206621,7 +206907,7 @@ function collectFileTree(projectDir2, maxEntries) {
|
|
|
206621
206907
|
}
|
|
206622
206908
|
function collectDependencies(projectDir2) {
|
|
206623
206909
|
try {
|
|
206624
|
-
const pkgPath =
|
|
206910
|
+
const pkgPath = join27(projectDir2, "package.json");
|
|
206625
206911
|
if (!existsSync22(pkgPath)) return "";
|
|
206626
206912
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
206627
206913
|
const parts = [];
|
|
@@ -207958,7 +208244,7 @@ var init_tool_profile = __esm({
|
|
|
207958
208244
|
function getMcpServerInfo() {
|
|
207959
208245
|
return {
|
|
207960
208246
|
name: "memorix",
|
|
207961
|
-
version: true ? "1.9.
|
|
208247
|
+
version: true ? "1.9.5" : "1.0.1"
|
|
207962
208248
|
};
|
|
207963
208249
|
}
|
|
207964
208250
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -221764,8 +222050,8 @@ __export(engine_exports3, {
|
|
|
221764
222050
|
WorkspaceSyncEngine: () => WorkspaceSyncEngine
|
|
221765
222051
|
});
|
|
221766
222052
|
import { readFileSync as readFileSync16, readdirSync as readdirSync4, existsSync as existsSync25, cpSync, mkdirSync as mkdirSync7 } from "fs";
|
|
221767
|
-
import { join as
|
|
221768
|
-
import { homedir as
|
|
222053
|
+
import { join as join29 } from "path";
|
|
222054
|
+
import { homedir as homedir33 } from "os";
|
|
221769
222055
|
var WorkspaceSyncEngine;
|
|
221770
222056
|
var init_engine5 = __esm({
|
|
221771
222057
|
"src/workspace/engine.ts"() {
|
|
@@ -221837,9 +222123,9 @@ var init_engine5 = __esm({
|
|
|
221837
222123
|
const globalPath = adapter.getConfigPath();
|
|
221838
222124
|
const pathsToCheck = [configPath, globalPath];
|
|
221839
222125
|
if (target === "antigravity") {
|
|
221840
|
-
pathsToCheck.push(
|
|
221841
|
-
pathsToCheck.push(
|
|
221842
|
-
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"));
|
|
221843
222129
|
}
|
|
221844
222130
|
const merged = /* @__PURE__ */ new Map();
|
|
221845
222131
|
for (const path50 of pathsToCheck) {
|
|
@@ -221956,7 +222242,7 @@ var init_engine5 = __esm({
|
|
|
221956
222242
|
getTargetSkillsDir(target) {
|
|
221957
222243
|
const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
|
|
221958
222244
|
if (!dirs || dirs.length === 0) return null;
|
|
221959
|
-
return
|
|
222245
|
+
return join29(this.projectRoot, dirs[0]);
|
|
221960
222246
|
}
|
|
221961
222247
|
/**
|
|
221962
222248
|
* Scan all agent skills directories and collect unique skills.
|
|
@@ -221965,12 +222251,12 @@ var init_engine5 = __esm({
|
|
|
221965
222251
|
const skills = [];
|
|
221966
222252
|
const conflicts = [];
|
|
221967
222253
|
const seen = /* @__PURE__ */ new Map();
|
|
221968
|
-
const home =
|
|
222254
|
+
const home = homedir33();
|
|
221969
222255
|
for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
|
|
221970
222256
|
for (const dir of dirs) {
|
|
221971
222257
|
const paths = [
|
|
221972
|
-
|
|
221973
|
-
|
|
222258
|
+
join29(this.projectRoot, dir),
|
|
222259
|
+
join29(home, dir)
|
|
221974
222260
|
];
|
|
221975
222261
|
for (const skillsRoot of paths) {
|
|
221976
222262
|
if (!existsSync25(skillsRoot)) continue;
|
|
@@ -221978,7 +222264,7 @@ var init_engine5 = __esm({
|
|
|
221978
222264
|
const entries = readdirSync4(skillsRoot, { withFileTypes: true });
|
|
221979
222265
|
for (const entry of entries) {
|
|
221980
222266
|
if (!entry.isDirectory()) continue;
|
|
221981
|
-
const skillMd =
|
|
222267
|
+
const skillMd = join29(skillsRoot, entry.name, "SKILL.md");
|
|
221982
222268
|
if (!existsSync25(skillMd)) continue;
|
|
221983
222269
|
let description = "";
|
|
221984
222270
|
try {
|
|
@@ -221990,7 +222276,7 @@ var init_engine5 = __esm({
|
|
|
221990
222276
|
const newEntry = {
|
|
221991
222277
|
name: entry.name,
|
|
221992
222278
|
description,
|
|
221993
|
-
sourcePath:
|
|
222279
|
+
sourcePath: join29(skillsRoot, entry.name),
|
|
221994
222280
|
sourceAgent: agent
|
|
221995
222281
|
};
|
|
221996
222282
|
const existing = seen.get(entry.name);
|
|
@@ -222027,7 +222313,7 @@ var init_engine5 = __esm({
|
|
|
222027
222313
|
}
|
|
222028
222314
|
for (const skill2 of skills) {
|
|
222029
222315
|
if (skill2.sourceAgent === target) continue;
|
|
222030
|
-
const dest =
|
|
222316
|
+
const dest = join29(targetDir, skill2.name);
|
|
222031
222317
|
if (existsSync25(dest)) {
|
|
222032
222318
|
skipped.push(`${skill2.name} (already exists in ${target})`);
|
|
222033
222319
|
continue;
|
|
@@ -222043,13 +222329,13 @@ var init_engine5 = __esm({
|
|
|
222043
222329
|
}
|
|
222044
222330
|
scanWorkflows() {
|
|
222045
222331
|
const workflows = [];
|
|
222046
|
-
const wfDir =
|
|
222332
|
+
const wfDir = join29(this.projectRoot, ".windsurf", "workflows");
|
|
222047
222333
|
if (!existsSync25(wfDir)) return workflows;
|
|
222048
222334
|
try {
|
|
222049
222335
|
const files = readdirSync4(wfDir).filter((f7) => f7.endsWith(".md"));
|
|
222050
222336
|
for (const file2 of files) {
|
|
222051
222337
|
try {
|
|
222052
|
-
const content = readFileSync16(
|
|
222338
|
+
const content = readFileSync16(join29(wfDir, file2), "utf-8");
|
|
222053
222339
|
workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file2, content));
|
|
222054
222340
|
} catch {
|
|
222055
222341
|
}
|
|
@@ -222324,7 +222610,7 @@ __export(behavior_exports, {
|
|
|
222324
222610
|
resetBehaviorConfigCache: () => resetBehaviorConfigCache
|
|
222325
222611
|
});
|
|
222326
222612
|
import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
|
|
222327
|
-
import { homedir as
|
|
222613
|
+
import { homedir as homedir34 } from "os";
|
|
222328
222614
|
function isSessionInject(value) {
|
|
222329
222615
|
return value === "full" || value === "minimal" || value === "silent";
|
|
222330
222616
|
}
|
|
@@ -222342,7 +222628,7 @@ function legacyBehavior(homeDir) {
|
|
|
222342
222628
|
}
|
|
222343
222629
|
}
|
|
222344
222630
|
function getBehaviorConfig(options = {}) {
|
|
222345
|
-
const homeDir = options.homeDir ??
|
|
222631
|
+
const homeDir = options.homeDir ?? homedir34();
|
|
222346
222632
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
222347
222633
|
const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
222348
222634
|
const cached2 = cache4.get(cacheKey2);
|
|
@@ -223580,8 +223866,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223580
223866
|
break;
|
|
223581
223867
|
}
|
|
223582
223868
|
case "/graph": {
|
|
223583
|
-
await initGraphStore(effectiveDataDir);
|
|
223584
|
-
const gStore = getGraphStore();
|
|
223869
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
223585
223870
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223586
223871
|
const graphObs = filterDashboardObservations(
|
|
223587
223872
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
@@ -223625,8 +223910,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223625
223910
|
break;
|
|
223626
223911
|
}
|
|
223627
223912
|
case "/stats": {
|
|
223628
|
-
await initGraphStore(effectiveDataDir);
|
|
223629
|
-
const graph = { entities:
|
|
223913
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
223914
|
+
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223630
223915
|
const observations2 = filterDashboardObservations(
|
|
223631
223916
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
223632
223917
|
effectiveProjectId
|
|
@@ -223819,15 +224104,15 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223819
224104
|
case "/knowledge-graph": {
|
|
223820
224105
|
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
223821
224106
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
223822
|
-
const { initGraphStore: initGraphStore2
|
|
224107
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
223823
224108
|
await initMiniSkillStore2(effectiveDataDir);
|
|
223824
|
-
await initGraphStore2(effectiveDataDir);
|
|
224109
|
+
const gStore = await initGraphStore2(effectiveDataDir, effectiveProjectId);
|
|
223825
224110
|
const allObs = filterDashboardObservations(
|
|
223826
224111
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
223827
224112
|
effectiveProjectId
|
|
223828
224113
|
);
|
|
223829
224114
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
223830
|
-
const fullGraph = { entities:
|
|
224115
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
223831
224116
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
223832
224117
|
const graph = generateKnowledgeGraph2({
|
|
223833
224118
|
projectId: effectiveProjectId,
|
|
@@ -223842,7 +224127,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223842
224127
|
case "/config": {
|
|
223843
224128
|
const os10 = await import("os");
|
|
223844
224129
|
const { existsSync: existsSync36 } = await import("fs");
|
|
223845
|
-
const { join:
|
|
224130
|
+
const { join: join36 } = await import("path");
|
|
223846
224131
|
const configProjectRoot = effectiveProjectRoot;
|
|
223847
224132
|
const resolved = getResolvedConfig({ projectRoot: configProjectRoot });
|
|
223848
224133
|
const files = {
|
|
@@ -223857,13 +224142,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
223857
224142
|
try {
|
|
223858
224143
|
const home = os10.homedir();
|
|
223859
224144
|
const paths = {
|
|
223860
|
-
"project memorix.yml": configProjectRoot ?
|
|
223861
|
-
"user memorix.yml":
|
|
223862
|
-
"project memorix.toml": configProjectRoot ?
|
|
223863
|
-
"user config.toml":
|
|
223864
|
-
"project .env": configProjectRoot ?
|
|
223865
|
-
"user .env":
|
|
223866
|
-
"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")
|
|
223867
224152
|
};
|
|
223868
224153
|
for (const [key, fpath] of Object.entries(paths)) {
|
|
223869
224154
|
if (fpath === null) {
|
|
@@ -224024,8 +224309,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
224024
224309
|
} else {
|
|
224025
224310
|
await obsStore.remove(obsId);
|
|
224026
224311
|
try {
|
|
224027
|
-
await initGraphStore(effectiveDataDir);
|
|
224028
|
-
const gStore = getGraphStore();
|
|
224312
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
224029
224313
|
const prefix = `[#${obsId}] `;
|
|
224030
224314
|
const deletions = [];
|
|
224031
224315
|
for (const entity of gStore.loadEntities()) {
|
|
@@ -224040,8 +224324,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
224040
224324
|
break;
|
|
224041
224325
|
}
|
|
224042
224326
|
if (apiPath === "/export") {
|
|
224043
|
-
await initGraphStore(effectiveDataDir);
|
|
224044
|
-
const fullGraph = { entities:
|
|
224327
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
224328
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
224045
224329
|
const observations2 = filterDashboardObservations(
|
|
224046
224330
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
224047
224331
|
effectiveProjectId
|
|
@@ -264565,7 +264849,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
264565
264849
|
const store2 = getObservationStore();
|
|
264566
264850
|
console.error(`[memorix] ObservationStore backend: ${store2.getBackendName()}, generation: ${store2.getGeneration()}`);
|
|
264567
264851
|
}
|
|
264568
|
-
let graphManager = new KnowledgeGraphManager(projectDir2);
|
|
264852
|
+
let graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
264569
264853
|
if (!deferProjectRuntimeInit) {
|
|
264570
264854
|
await graphManager.init();
|
|
264571
264855
|
await initObservations(projectDir2);
|
|
@@ -264577,7 +264861,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
264577
264861
|
await initObservationStore(projectDir2);
|
|
264578
264862
|
await initMiniSkillStore(projectDir2);
|
|
264579
264863
|
await initSessionStore(projectDir2);
|
|
264580
|
-
graphManager = new KnowledgeGraphManager(projectDir2);
|
|
264864
|
+
graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
264581
264865
|
await graphManager.init();
|
|
264582
264866
|
await initObservations(projectDir2);
|
|
264583
264867
|
const indexed = await prepareSearchIndex();
|
|
@@ -266789,10 +267073,10 @@ ${Math.max(0, managedIds.size - result.resolved.length)} visible writable observ
|
|
|
266789
267073
|
);
|
|
266790
267074
|
let enableKG = isToolInProfile("create_entities", toolProfile);
|
|
266791
267075
|
try {
|
|
266792
|
-
const { homedir:
|
|
266793
|
-
const { join:
|
|
267076
|
+
const { homedir: homedir38 } = await import("os");
|
|
267077
|
+
const { join: join36 } = await import("path");
|
|
266794
267078
|
const { readFile: readFile11 } = await import("fs/promises");
|
|
266795
|
-
const raw = await readFile11(
|
|
267079
|
+
const raw = await readFile11(join36(homedir38(), ".memorix", "settings.json"), "utf-8");
|
|
266796
267080
|
const s4 = JSON.parse(raw);
|
|
266797
267081
|
if (s4.knowledgeGraph === true) enableKG = true;
|
|
266798
267082
|
} catch {
|
|
@@ -268636,6 +268920,7 @@ ${lines.join("\n")}` }] };
|
|
|
268636
268920
|
} : {},
|
|
268637
268921
|
...mediaGenerationEnabled ? {
|
|
268638
268922
|
image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
268923
|
+
firstFrameImageUrl: external_exports2.string().optional().describe("Public HTTPS first-frame image URL for MiniMax video generation; stored with the queued job."),
|
|
268639
268924
|
region: external_exports2.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
268640
268925
|
n: external_exports2.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
268641
268926
|
ratio: external_exports2.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
|
|
@@ -268645,7 +268930,7 @@ ${lines.join("\n")}` }] };
|
|
|
268645
268930
|
} : {}
|
|
268646
268931
|
}
|
|
268647
268932
|
},
|
|
268648
|
-
async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, image, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
|
|
268933
|
+
async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, image, firstFrameImageUrl, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
|
|
268649
268934
|
const unresolved = requireResolvedProject("manage controlled media");
|
|
268650
268935
|
if (unresolved) return unresolved;
|
|
268651
268936
|
const safeError4 = (error40) => sanitizeCredentials(
|
|
@@ -268815,6 +269100,7 @@ ${lines.join("\n")}` }] };
|
|
|
268815
269100
|
dataDir: projectDir2,
|
|
268816
269101
|
projectId: project.id,
|
|
268817
269102
|
prompt: prompt2,
|
|
269103
|
+
firstFrameImageUrl,
|
|
268818
269104
|
...model === "MiniMax-H3" ? { model } : {},
|
|
268819
269105
|
...region ? { region } : {},
|
|
268820
269106
|
...videoRatio ? { ratio: videoRatio } : {},
|
|
@@ -269365,8 +269651,8 @@ __export(update_checker_exports, {
|
|
|
269365
269651
|
});
|
|
269366
269652
|
import { execFile as execFile3 } from "child_process";
|
|
269367
269653
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir7 } from "fs/promises";
|
|
269368
|
-
import { join as
|
|
269369
|
-
import { homedir as
|
|
269654
|
+
import { join as join30 } from "path";
|
|
269655
|
+
import { homedir as homedir35 } from "os";
|
|
269370
269656
|
function getCurrentVersion() {
|
|
269371
269657
|
try {
|
|
269372
269658
|
return getCliVersion();
|
|
@@ -269545,8 +269831,8 @@ var init_update_checker = __esm({
|
|
|
269545
269831
|
MIN_AUTO_UPDATE_TIMEOUT_MS = 5e3;
|
|
269546
269832
|
MAX_AUTO_UPDATE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
269547
269833
|
REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
269548
|
-
CACHE_DIR2 =
|
|
269549
|
-
CACHE_FILE2 =
|
|
269834
|
+
CACHE_DIR2 = join30(homedir35(), ".memorix");
|
|
269835
|
+
CACHE_FILE2 = join30(CACHE_DIR2, "update-check.json");
|
|
269550
269836
|
_testing = {
|
|
269551
269837
|
CACHE_FILE: CACHE_FILE2,
|
|
269552
269838
|
CHECK_INTERVAL_MS,
|
|
@@ -269604,7 +269890,7 @@ var init_serve = __esm({
|
|
|
269604
269890
|
const { serveStdio, StdioServerTransport } = await import("@modelcontextprotocol/server/stdio");
|
|
269605
269891
|
const { createModernMcpBridge: createModernMcpBridge2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
|
|
269606
269892
|
const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
269607
|
-
const { homedir:
|
|
269893
|
+
const { homedir: homedir38 } = await import("os");
|
|
269608
269894
|
const { writeLastProjectRoot: writeLastProjectRoot2 } = await Promise.resolve().then(() => (init_launch_root(), launch_root_exports));
|
|
269609
269895
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
269610
269896
|
let stdioHandle;
|
|
@@ -269633,7 +269919,7 @@ var init_serve = __esm({
|
|
|
269633
269919
|
try {
|
|
269634
269920
|
safeCwd = process.cwd();
|
|
269635
269921
|
} catch {
|
|
269636
|
-
safeCwd =
|
|
269922
|
+
safeCwd = homedir38();
|
|
269637
269923
|
}
|
|
269638
269924
|
const resolution = resolveServeProject2(
|
|
269639
269925
|
{
|
|
@@ -269641,7 +269927,7 @@ var init_serve = __esm({
|
|
|
269641
269927
|
envProjectRoot: process.env.MEMORIX_PROJECT_ROOT,
|
|
269642
269928
|
initCwd: process.env.INIT_CWD,
|
|
269643
269929
|
processCwd: safeCwd,
|
|
269644
|
-
homeDir:
|
|
269930
|
+
homeDir: homedir38()
|
|
269645
269931
|
},
|
|
269646
269932
|
{ detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 }
|
|
269647
269933
|
);
|
|
@@ -271862,6 +272148,7 @@ var init_streamableHttp = __esm({
|
|
|
271862
272148
|
// src/server/mcp-binding-store.ts
|
|
271863
272149
|
var mcp_binding_store_exports = {};
|
|
271864
272150
|
__export(mcp_binding_store_exports, {
|
|
272151
|
+
DEFAULT_MCP_BINDING_TTL_MS: () => DEFAULT_MCP_BINDING_TTL_MS,
|
|
271865
272152
|
McpBindingStore: () => McpBindingStore
|
|
271866
272153
|
});
|
|
271867
272154
|
import { randomUUID as randomUUID19 } from "crypto";
|
|
@@ -271876,12 +272163,13 @@ function rowToBinding(row) {
|
|
|
271876
272163
|
...row.expires_at ? { expiresAt: String(row.expires_at) } : {}
|
|
271877
272164
|
};
|
|
271878
272165
|
}
|
|
271879
|
-
var McpBindingStore;
|
|
272166
|
+
var DEFAULT_MCP_BINDING_TTL_MS, McpBindingStore;
|
|
271880
272167
|
var init_mcp_binding_store = __esm({
|
|
271881
272168
|
"src/server/mcp-binding-store.ts"() {
|
|
271882
272169
|
"use strict";
|
|
271883
272170
|
init_esm_shims();
|
|
271884
272171
|
init_sqlite_db();
|
|
272172
|
+
DEFAULT_MCP_BINDING_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
271885
272173
|
McpBindingStore = class {
|
|
271886
272174
|
db = null;
|
|
271887
272175
|
async init(dataDir) {
|
|
@@ -271893,6 +272181,7 @@ var init_mcp_binding_store = __esm({
|
|
|
271893
272181
|
}
|
|
271894
272182
|
create(input) {
|
|
271895
272183
|
const now3 = /* @__PURE__ */ new Date();
|
|
272184
|
+
const ttlMs = input.ttlMs ?? DEFAULT_MCP_BINDING_TTL_MS;
|
|
271896
272185
|
const binding = {
|
|
271897
272186
|
handleId: `mxh_${randomUUID19().replace(/-/g, "")}`,
|
|
271898
272187
|
projectId: input.projectId,
|
|
@@ -271900,7 +272189,7 @@ var init_mcp_binding_store = __esm({
|
|
|
271900
272189
|
dataDir: input.dataDir,
|
|
271901
272190
|
createdAt: now3.toISOString(),
|
|
271902
272191
|
lastUsedAt: now3.toISOString(),
|
|
271903
|
-
...
|
|
272192
|
+
...ttlMs > 0 ? { expiresAt: new Date(now3.getTime() + ttlMs).toISOString() } : {}
|
|
271904
272193
|
};
|
|
271905
272194
|
this.requireDb().prepare(`
|
|
271906
272195
|
INSERT INTO mcp_bindings (handle_id, project_id, project_root, data_dir, created_at, last_used_at, expires_at)
|
|
@@ -271922,8 +272211,15 @@ var init_mcp_binding_store = __esm({
|
|
|
271922
272211
|
const binding = this.get(handleId);
|
|
271923
272212
|
if (!binding) return void 0;
|
|
271924
272213
|
const lastUsedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
271925
|
-
|
|
271926
|
-
|
|
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);
|
|
271927
272223
|
}
|
|
271928
272224
|
/** Reuse the most recent valid extension handle for a verified project root. */
|
|
271929
272225
|
findByProjectRoot(projectRoot) {
|
|
@@ -272013,16 +272309,25 @@ var init_control_plane_maintenance = __esm({
|
|
|
272013
272309
|
// src/cli/commands/serve-http.ts
|
|
272014
272310
|
var serve_http_exports = {};
|
|
272015
272311
|
__export(serve_http_exports, {
|
|
272312
|
+
DEFAULT_HTTP_BODY_LIMIT_BYTES: () => DEFAULT_HTTP_BODY_LIMIT_BYTES,
|
|
272016
272313
|
DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
|
|
272017
272314
|
EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
|
|
272315
|
+
HttpPayloadTooLargeError: () => HttpPayloadTooLargeError,
|
|
272316
|
+
MAX_HTTP_BODY_LIMIT_BYTES: () => MAX_HTTP_BODY_LIMIT_BYTES,
|
|
272018
272317
|
MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
|
|
272318
|
+
MIN_HTTP_BODY_LIMIT_BYTES: () => MIN_HTTP_BODY_LIMIT_BYTES,
|
|
272019
272319
|
_testing: () => _testing2,
|
|
272020
272320
|
default: () => serve_http_default,
|
|
272321
|
+
isLoopbackHost: () => isLoopbackHost,
|
|
272021
272322
|
isServerDiscoverRequest: () => isServerDiscoverRequest,
|
|
272323
|
+
parseHttpBodyLimit: () => parseHttpBodyLimit,
|
|
272324
|
+
parseJsonBody: () => parseJsonBody,
|
|
272022
272325
|
parseSessionTimeoutMs: () => parseSessionTimeoutMs,
|
|
272023
|
-
resolveServeHttpDefaults: () => resolveServeHttpDefaults
|
|
272326
|
+
resolveServeHttpDefaults: () => resolveServeHttpDefaults,
|
|
272327
|
+
validateHttpBindSecurity: () => validateHttpBindSecurity
|
|
272024
272328
|
});
|
|
272025
272329
|
import { getHeapStatistics } from "v8";
|
|
272330
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
272026
272331
|
function isServerDiscoverRequest(body) {
|
|
272027
272332
|
return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
|
|
272028
272333
|
}
|
|
@@ -272033,6 +272338,78 @@ function parseSessionTimeoutMs(raw) {
|
|
|
272033
272338
|
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
272034
272339
|
return Math.floor(parsed);
|
|
272035
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
|
+
}
|
|
272036
272413
|
function resolveServeHttpDefaults(argsPort, configured) {
|
|
272037
272414
|
const configuredPort = typeof configured.port === "number" && Number.isFinite(configured.port) ? configured.port : void 0;
|
|
272038
272415
|
const port = parseTcpPortOrReport(argsPort, configuredPort ?? 3211);
|
|
@@ -272042,7 +272419,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
|
|
|
272042
272419
|
const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
|
|
272043
272420
|
return { port, transportNotice };
|
|
272044
272421
|
}
|
|
272045
|
-
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;
|
|
272046
272423
|
var init_serve_http = __esm({
|
|
272047
272424
|
"src/cli/commands/serve-http.ts"() {
|
|
272048
272425
|
"use strict";
|
|
@@ -272058,7 +272435,17 @@ var init_serve_http = __esm({
|
|
|
272058
272435
|
init_mcp_root_path();
|
|
272059
272436
|
DEFAULT_SESSION_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
|
|
272060
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;
|
|
272061
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
|
+
};
|
|
272062
272449
|
serve_http_default = defineCommand({
|
|
272063
272450
|
meta: {
|
|
272064
272451
|
name: "serve-http",
|
|
@@ -272095,7 +272482,7 @@ var init_serve_http = __esm({
|
|
|
272095
272482
|
const { createProjectBindingController: createProjectBindingController2 } = await Promise.resolve().then(() => (init_request_context(), request_context_exports));
|
|
272096
272483
|
const { findGitInSubdirs: findGitInSubdirs2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
272097
272484
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync13 } = await import("fs");
|
|
272098
|
-
const { homedir:
|
|
272485
|
+
const { homedir: homedir38 } = await import("os");
|
|
272099
272486
|
const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
|
|
272100
272487
|
const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
|
|
272101
272488
|
const { createModernMcpBridge: createModernMcpBridge2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
|
|
@@ -272111,12 +272498,20 @@ var init_serve_http = __esm({
|
|
|
272111
272498
|
if (resolved.transportNotice) console.error(resolved.transportNotice);
|
|
272112
272499
|
const port = resolved.port;
|
|
272113
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
|
+
}
|
|
272114
272509
|
const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "team" });
|
|
272115
272510
|
let safeCwd;
|
|
272116
272511
|
try {
|
|
272117
272512
|
safeCwd = process.cwd();
|
|
272118
272513
|
} catch {
|
|
272119
|
-
safeCwd =
|
|
272514
|
+
safeCwd = homedir38();
|
|
272120
272515
|
}
|
|
272121
272516
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
272122
272517
|
const { writeLastProjectRoot: writeLastProjectRoot2 } = await Promise.resolve().then(() => (init_launch_root(), launch_root_exports));
|
|
@@ -272127,7 +272522,7 @@ var init_serve_http = __esm({
|
|
|
272127
272522
|
cwdArg: args.cwd,
|
|
272128
272523
|
envProjectRoot: process.env.MEMORIX_PROJECT_ROOT,
|
|
272129
272524
|
processCwd: safeCwd,
|
|
272130
|
-
homeDir:
|
|
272525
|
+
homeDir: homedir38()
|
|
272131
272526
|
},
|
|
272132
272527
|
{ detectProject: detectorMod.detectProject, findGitInSubdirs: detectorMod.findGitInSubdirs, isSystemDirectory: detectorMod.isSystemDirectory }
|
|
272133
272528
|
);
|
|
@@ -272141,6 +272536,7 @@ var init_serve_http = __esm({
|
|
|
272141
272536
|
console.error(`[memorix] HTTP transport starting on ${host}:${port}`);
|
|
272142
272537
|
console.error(`[memorix] Project root: ${projectRoot}`);
|
|
272143
272538
|
const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
|
|
272539
|
+
const httpBodyLimitBytes = parseHttpBodyLimit(process.env.MEMORIX_HTTP_MAX_BODY_BYTES);
|
|
272144
272540
|
console.error(
|
|
272145
272541
|
sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
|
|
272146
272542
|
);
|
|
@@ -272211,21 +272607,6 @@ var init_serve_http = __esm({
|
|
|
272211
272607
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
272212
272608
|
res.end(message);
|
|
272213
272609
|
}
|
|
272214
|
-
function parseBody(req) {
|
|
272215
|
-
return new Promise((resolve9, reject) => {
|
|
272216
|
-
const chunks = [];
|
|
272217
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
272218
|
-
req.on("end", () => {
|
|
272219
|
-
try {
|
|
272220
|
-
const body = Buffer.concat(chunks).toString("utf-8");
|
|
272221
|
-
resolve9(body ? JSON.parse(body) : void 0);
|
|
272222
|
-
} catch (err) {
|
|
272223
|
-
reject(err);
|
|
272224
|
-
}
|
|
272225
|
-
});
|
|
272226
|
-
req.on("error", reject);
|
|
272227
|
-
});
|
|
272228
|
-
}
|
|
272229
272610
|
function patchMcpResponse(res) {
|
|
272230
272611
|
const origWriteHead = res.writeHead;
|
|
272231
272612
|
const origWrite = res.write;
|
|
@@ -272315,7 +272696,7 @@ var init_serve_http = __esm({
|
|
|
272315
272696
|
res.setHeader("Vary", "Origin");
|
|
272316
272697
|
}
|
|
272317
272698
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
272318
|
-
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");
|
|
272319
272700
|
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id, Mcp-Project-Handle, Mcp-Protocol-Version, Mcp-Method, Mcp-Name");
|
|
272320
272701
|
res.setHeader("Mcp-Protocol-Version", MCP_SDK_COMPAT_PROTOCOL_VERSION);
|
|
272321
272702
|
res.setHeader("Mcp-Method", req.method || "UNKNOWN");
|
|
@@ -272323,7 +272704,7 @@ var init_serve_http = __esm({
|
|
|
272323
272704
|
}
|
|
272324
272705
|
async function handlePost(req, res) {
|
|
272325
272706
|
const sessionId = req.headers["mcp-session-id"];
|
|
272326
|
-
const body = await
|
|
272707
|
+
const body = await parseJsonBody(req, httpBodyLimitBytes);
|
|
272327
272708
|
const params = body && typeof body === "object" && !Array.isArray(body) ? body.params : void 0;
|
|
272328
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"));
|
|
272329
272710
|
if (claimlessDiscovery) {
|
|
@@ -272627,7 +273008,7 @@ var init_serve_http = __esm({
|
|
|
272627
273008
|
sendJson2({ error: "Maintenance actions require POST." }, 405);
|
|
272628
273009
|
return;
|
|
272629
273010
|
}
|
|
272630
|
-
const bodyValue = await
|
|
273011
|
+
const bodyValue = await parseJsonBody(req, Math.min(httpBodyLimitBytes, 1024 * 1024));
|
|
272631
273012
|
const body = bodyValue && typeof bodyValue === "object" && !Array.isArray(bodyValue) ? bodyValue : {};
|
|
272632
273013
|
const { projectId: maintenanceProjectId, dataDir: maintenanceDataDir } = await resolveRequestProject(url2);
|
|
272633
273014
|
const maintenanceStore = await getDashboardObservationStore(maintenanceDataDir);
|
|
@@ -272762,9 +273143,9 @@ var init_serve_http = __esm({
|
|
|
272762
273143
|
}
|
|
272763
273144
|
if (apiPath === "/stats") {
|
|
272764
273145
|
const { projectId: statsProjectId, dataDir: statsDataDir } = await resolveRequestProject(url2);
|
|
272765
|
-
const { initGraphStore: initGraphStore2
|
|
272766
|
-
await initGraphStore2(statsDataDir);
|
|
272767
|
-
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() };
|
|
272768
273149
|
const observations2 = await loadDashboardProjectObservations(statsDataDir, statsProjectId, "active");
|
|
272769
273150
|
const feedback = await loadProjectFeedback(statsDataDir, statsProjectId);
|
|
272770
273151
|
const statsStore = await getDashboardObservationStore(statsDataDir);
|
|
@@ -272926,9 +273307,9 @@ var init_serve_http = __esm({
|
|
|
272926
273307
|
}
|
|
272927
273308
|
if (apiPath === "/graph") {
|
|
272928
273309
|
const { projectId: graphProjectId, dataDir: graphDataDir } = await resolveRequestProject(url2);
|
|
272929
|
-
const { initGraphStore: initGraphStore2
|
|
272930
|
-
await initGraphStore2(graphDataDir);
|
|
272931
|
-
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() };
|
|
272932
273313
|
const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, "active");
|
|
272933
273314
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
272934
273315
|
sendJson2({ entities: scoped.entities, relations: scoped.relations });
|
|
@@ -273001,12 +273382,12 @@ var init_serve_http = __esm({
|
|
|
273001
273382
|
const { projectId: kgProjectId, dataDir: kgDataDir } = await resolveRequestProject(url2);
|
|
273002
273383
|
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
273003
273384
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
273004
|
-
const { initGraphStore: initGraphStore2
|
|
273385
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273005
273386
|
await initMiniSkillStore2(kgDataDir);
|
|
273006
|
-
await initGraphStore2(kgDataDir);
|
|
273387
|
+
const gStore = await initGraphStore2(kgDataDir, kgProjectId);
|
|
273007
273388
|
const allObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, "active");
|
|
273008
273389
|
const skills = await getMiniSkillStore2().loadByProject(kgProjectId);
|
|
273009
|
-
const fullGraph = { entities:
|
|
273390
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
273010
273391
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
273011
273392
|
const graph = generateKnowledgeGraph2({
|
|
273012
273393
|
projectId: kgProjectId,
|
|
@@ -273022,7 +273403,7 @@ var init_serve_http = __esm({
|
|
|
273022
273403
|
const { projectId: configProjectId } = await resolveRequestProject(url2);
|
|
273023
273404
|
const os10 = await import("os");
|
|
273024
273405
|
const { existsSync: existsSync36 } = await import("fs");
|
|
273025
|
-
const { join:
|
|
273406
|
+
const { join: join36 } = await import("path");
|
|
273026
273407
|
const { loadYamlConfig: loadYamlConfig2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
|
|
273027
273408
|
const { loadFileConfig: loadFileConfig2, loadDotenv: loadDotenv2, getLoadedEnvFiles: getLoadedEnvFiles2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
273028
273409
|
const isStartupProject = configProjectId === defaultProject.id;
|
|
@@ -273036,11 +273417,11 @@ var init_serve_http = __esm({
|
|
|
273036
273417
|
const home = os10.homedir();
|
|
273037
273418
|
if (effectiveRoot) {
|
|
273038
273419
|
const paths = {
|
|
273039
|
-
"project memorix.yml":
|
|
273040
|
-
"user memorix.yml":
|
|
273041
|
-
"project .env":
|
|
273042
|
-
"user .env":
|
|
273043
|
-
"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")
|
|
273044
273425
|
};
|
|
273045
273426
|
for (const [key, fpath] of Object.entries(paths)) {
|
|
273046
273427
|
files[key] = { exists: existsSync36(fpath), path: fpath };
|
|
@@ -273049,9 +273430,9 @@ var init_serve_http = __esm({
|
|
|
273049
273430
|
files["project memorix.yml"] = { exists: false, path: "unavailable", unavailable: true };
|
|
273050
273431
|
files["project .env"] = { exists: false, path: "unavailable", unavailable: true };
|
|
273051
273432
|
const userPaths = {
|
|
273052
|
-
"user memorix.yml":
|
|
273053
|
-
"user .env":
|
|
273054
|
-
"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")
|
|
273055
273436
|
};
|
|
273056
273437
|
for (const [key, fpath] of Object.entries(userPaths)) {
|
|
273057
273438
|
files[key] = { exists: existsSync36(fpath), path: fpath };
|
|
@@ -273267,9 +273648,8 @@ var init_serve_http = __esm({
|
|
|
273267
273648
|
} else {
|
|
273268
273649
|
await store2.remove(obsId);
|
|
273269
273650
|
try {
|
|
273270
|
-
const { initGraphStore: initGraphStore2
|
|
273271
|
-
await initGraphStore2(delDataDir);
|
|
273272
|
-
const gStore = getGraphStore2();
|
|
273651
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
273652
|
+
const gStore = await initGraphStore2(delDataDir, delProjectId);
|
|
273273
273653
|
const prefix = `[#${obsId}] `;
|
|
273274
273654
|
const deletions = [];
|
|
273275
273655
|
for (const entity of gStore.loadEntities()) {
|
|
@@ -273285,9 +273665,9 @@ var init_serve_http = __esm({
|
|
|
273285
273665
|
}
|
|
273286
273666
|
if (apiPath === "/export") {
|
|
273287
273667
|
const { projectId: expProjectId, projectName: expProjectName, dataDir: expDataDir } = await resolveRequestProject(url2);
|
|
273288
|
-
const { initGraphStore: initGraphStore2
|
|
273289
|
-
await initGraphStore2(expDataDir);
|
|
273290
|
-
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() };
|
|
273291
273671
|
const observations2 = await loadDashboardProjectObservations(expDataDir, expProjectId, "active");
|
|
273292
273672
|
const expStore = await getDashboardObservationStore(expDataDir);
|
|
273293
273673
|
const nextId2 = await expStore.loadIdCounter();
|
|
@@ -273325,7 +273705,8 @@ var init_serve_http = __esm({
|
|
|
273325
273705
|
}, 503);
|
|
273326
273706
|
return;
|
|
273327
273707
|
}
|
|
273328
|
-
|
|
273708
|
+
const status = err instanceof HttpPayloadTooLargeError ? err.statusCode : 500;
|
|
273709
|
+
sendJson2({ error: err instanceof Error ? err.message : "Unknown error" }, status);
|
|
273329
273710
|
}
|
|
273330
273711
|
}
|
|
273331
273712
|
let httpReadyAt = null;
|
|
@@ -273337,6 +273718,14 @@ var init_serve_http = __esm({
|
|
|
273337
273718
|
return;
|
|
273338
273719
|
}
|
|
273339
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
|
+
}
|
|
273340
273729
|
if (url2.pathname === "/health") {
|
|
273341
273730
|
const { getEmbeddingRuntimeHealth: getEmbeddingRuntimeHealth2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
273342
273731
|
const memory = process.memoryUsage();
|
|
@@ -273407,10 +273796,15 @@ var init_serve_http = __esm({
|
|
|
273407
273796
|
} catch (err) {
|
|
273408
273797
|
console.error("[memorix] HTTP handler error:", err);
|
|
273409
273798
|
if (!res.headersSent) {
|
|
273410
|
-
|
|
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" });
|
|
273411
273802
|
res.end(JSON.stringify({
|
|
273412
273803
|
jsonrpc: "2.0",
|
|
273413
|
-
error: {
|
|
273804
|
+
error: {
|
|
273805
|
+
code: status === 413 ? -32013 : -32603,
|
|
273806
|
+
message: status === 413 ? message : "Internal server error"
|
|
273807
|
+
},
|
|
273414
273808
|
id: null
|
|
273415
273809
|
}));
|
|
273416
273810
|
}
|
|
@@ -273425,31 +273819,62 @@ var init_serve_http = __esm({
|
|
|
273425
273819
|
});
|
|
273426
273820
|
httpServer.keepAliveTimeout = 6e4;
|
|
273427
273821
|
httpServer.headersTimeout = 65e3;
|
|
273428
|
-
|
|
273429
|
-
|
|
273430
|
-
|
|
273431
|
-
|
|
273432
|
-
|
|
273433
|
-
|
|
273434
|
-
|
|
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);
|
|
273435
273855
|
}
|
|
273436
|
-
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`);
|
|
273437
|
-
console.error(`[memorix] Mode: Control Plane (HTTP MCP + multi-session live dashboard)`);
|
|
273438
|
-
console.error(`[memorix] MCP Server listening on http://${host}:${port}/mcp`);
|
|
273439
|
-
console.error(`[memorix] Dashboard: http://${host}:${port}/`);
|
|
273440
|
-
console.error(`[memorix] Coordination: http://${host}:${port}/api/team`);
|
|
273441
|
-
console.error(`[memorix] Port: ${port}`);
|
|
273442
|
-
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`);
|
|
273443
|
-
console.error(`[memorix] Sessions at startup: ${sessions.size} (live count available at /api/team)`);
|
|
273444
|
-
console.error(`[memorix] Agents can connect via: { "transport": "http", "url": "http://localhost:${port}/mcp" }`);
|
|
273445
|
-
Promise.resolve().then(() => (init_update_checker(), update_checker_exports)).then((m6) => m6.checkForUpdates()).catch(() => {
|
|
273446
|
-
});
|
|
273447
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
|
+
}
|
|
273448
273872
|
const SESSION_TIMEOUT_MS = sessionTimeoutMs;
|
|
273449
273873
|
const gcInterval = setInterval(() => {
|
|
273450
273874
|
maybeProbeSummary();
|
|
273451
273875
|
const now3 = Date.now();
|
|
273452
273876
|
forgetExpiredSessions(now3);
|
|
273877
|
+
statelessBindingStore.cleanupExpired(now3);
|
|
273453
273878
|
if (SESSION_TIMEOUT_MS === 0) return;
|
|
273454
273879
|
for (const [sid, state] of sessions) {
|
|
273455
273880
|
const lastActive = sessionLastActivity.get(sid) ?? 0;
|
|
@@ -273464,7 +273889,10 @@ var init_serve_http = __esm({
|
|
|
273464
273889
|
}
|
|
273465
273890
|
}, 6e4);
|
|
273466
273891
|
gcInterval.unref();
|
|
273892
|
+
let shutdownStarted = false;
|
|
273467
273893
|
const shutdown = async () => {
|
|
273894
|
+
if (shutdownStarted) return;
|
|
273895
|
+
shutdownStarted = true;
|
|
273468
273896
|
if (suppressedProbeCount > 0) {
|
|
273469
273897
|
console.error(`[memorix] ${suppressedProbeCount} probe connection(s) suppressed during this session`);
|
|
273470
273898
|
}
|
|
@@ -273498,7 +273926,17 @@ var init_serve_http = __esm({
|
|
|
273498
273926
|
console.error(`[memorix] Error closing session ${sid}:`, err);
|
|
273499
273927
|
}
|
|
273500
273928
|
}
|
|
273501
|
-
|
|
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();
|
|
273502
273940
|
process.exit(0);
|
|
273503
273941
|
};
|
|
273504
273942
|
process.on("SIGINT", shutdown);
|
|
@@ -273537,7 +273975,13 @@ var init_serve_http = __esm({
|
|
|
273537
273975
|
});
|
|
273538
273976
|
_testing2 = {
|
|
273539
273977
|
DEFAULT_SESSION_TIMEOUT_MS,
|
|
273978
|
+
DEFAULT_HTTP_BODY_LIMIT_BYTES,
|
|
273979
|
+
MAX_HTTP_BODY_LIMIT_BYTES,
|
|
273980
|
+
MIN_HTTP_BODY_LIMIT_BYTES,
|
|
273540
273981
|
parseSessionTimeoutMs,
|
|
273982
|
+
parseHttpBodyLimit,
|
|
273983
|
+
isLoopbackHost,
|
|
273984
|
+
validateHttpBindSecurity,
|
|
273541
273985
|
parseTcpPortOrReport
|
|
273542
273986
|
};
|
|
273543
273987
|
}
|
|
@@ -273988,8 +274432,10 @@ var init_journal = __esm({
|
|
|
273988
274432
|
});
|
|
273989
274433
|
|
|
273990
274434
|
// src/sync/policy.ts
|
|
273991
|
-
function syncEligibility(observation,
|
|
273992
|
-
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
|
+
}
|
|
273993
274439
|
if ((observation.visibility ?? "project") !== "project") return { eligible: false, reason: "non-project-visibility" };
|
|
273994
274440
|
if ((observation.sharedWithAgentIds?.length ?? 0) > 0) {
|
|
273995
274441
|
return { eligible: false, reason: "agent-targeted" };
|
|
@@ -274000,11 +274446,11 @@ function syncEligibility(observation, projectId) {
|
|
|
274000
274446
|
if (observation.valueCategory === "ephemeral") return { eligible: false, reason: "ephemeral-value" };
|
|
274001
274447
|
return { eligible: true };
|
|
274002
274448
|
}
|
|
274003
|
-
function eligibleObservations(observations2,
|
|
274449
|
+
function eligibleObservations(observations2, scopeId, scope = "project") {
|
|
274004
274450
|
const eligible = [];
|
|
274005
274451
|
let excluded = 0;
|
|
274006
274452
|
for (const observation of observations2) {
|
|
274007
|
-
if (syncEligibility(observation,
|
|
274453
|
+
if (syncEligibility(observation, scopeId, scope).eligible) eligible.push(observation);
|
|
274008
274454
|
else excluded++;
|
|
274009
274455
|
}
|
|
274010
274456
|
return { eligible, excluded };
|
|
@@ -274021,13 +274467,26 @@ import { createHash as createHash22 } from "crypto";
|
|
|
274021
274467
|
function syncNamespace(projectId) {
|
|
274022
274468
|
return `project-${createHash22("sha256").update(projectId).digest("hex").slice(0, 32)}`;
|
|
274023
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
|
+
}
|
|
274024
274480
|
function isSafeSyncDeviceId(value) {
|
|
274025
274481
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value);
|
|
274026
274482
|
}
|
|
274483
|
+
var USER_SYNC_SCOPE_ID, USER_SYNC_NAMESPACE;
|
|
274027
274484
|
var init_namespace = __esm({
|
|
274028
274485
|
"src/sync/namespace.ts"() {
|
|
274029
274486
|
"use strict";
|
|
274030
274487
|
init_esm_shims();
|
|
274488
|
+
USER_SYNC_SCOPE_ID = "__user__";
|
|
274489
|
+
USER_SYNC_NAMESPACE = "user-";
|
|
274031
274490
|
}
|
|
274032
274491
|
});
|
|
274033
274492
|
|
|
@@ -274038,6 +274497,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274038
274497
|
remote: remote.kind,
|
|
274039
274498
|
namespace: remote.namespace,
|
|
274040
274499
|
projectId: store2.projectId(),
|
|
274500
|
+
scope: store2.scope(),
|
|
274041
274501
|
deviceId: opts.deviceId,
|
|
274042
274502
|
pushed: 0,
|
|
274043
274503
|
pulledBatches: 0,
|
|
@@ -274065,7 +274525,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274065
274525
|
report.pending = pending.length;
|
|
274066
274526
|
if (!opts.dryRun) {
|
|
274067
274527
|
for (const pendingBatch of pending) {
|
|
274068
|
-
validateBatches([pendingBatch.batch], store2.projectId(), remote.namespace);
|
|
274528
|
+
validateBatches([pendingBatch.batch], store2.projectId(), remote.namespace, store2.scope());
|
|
274069
274529
|
if (pendingBatch.batch.deviceId !== opts.deviceId) {
|
|
274070
274530
|
throw new Error("[memorix] sync outbox rejected: batch belongs to another device");
|
|
274071
274531
|
}
|
|
@@ -274077,7 +274537,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274077
274537
|
report.pending = 0;
|
|
274078
274538
|
}
|
|
274079
274539
|
const current = await store2.loadAll();
|
|
274080
|
-
const filtered = eligibleObservations(current, store2.projectId());
|
|
274540
|
+
const filtered = eligibleObservations(current, store2.projectId(), store2.scope());
|
|
274081
274541
|
report.eligible = filtered.eligible.length;
|
|
274082
274542
|
report.excluded = filtered.excluded;
|
|
274083
274543
|
const state = await store2.loadState();
|
|
@@ -274100,7 +274560,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274100
274560
|
producedAt: nowIso(),
|
|
274101
274561
|
entries: chunk.map((c6) => c6.entry)
|
|
274102
274562
|
};
|
|
274103
|
-
if (!opts.dryRun) validateBatches([batch], store2.projectId(), remote.namespace);
|
|
274563
|
+
if (!opts.dryRun) validateBatches([batch], store2.projectId(), remote.namespace, store2.scope());
|
|
274104
274564
|
report.pushed += chunk.length;
|
|
274105
274565
|
if (!opts.dryRun) {
|
|
274106
274566
|
const states = chunk.map((c6) => ({ ...c6.nextState, shippedSeq: sequence }));
|
|
@@ -274121,7 +274581,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274121
274581
|
while (hasMore) {
|
|
274122
274582
|
const page = await remote.pull(pullSince, PULL_PAGE_SIZE, pageToken);
|
|
274123
274583
|
report.pulledBatches += page.batches.length;
|
|
274124
|
-
validateBatches(page.batches, store2.projectId(), remote.namespace);
|
|
274584
|
+
validateBatches(page.batches, store2.projectId(), remote.namespace, store2.scope());
|
|
274125
274585
|
for (const batch of page.batches) {
|
|
274126
274586
|
if (batch.deviceId === opts.deviceId) {
|
|
274127
274587
|
cursor.applied[batch.deviceId] = Math.max(cursor.applied[batch.deviceId] ?? 0, batch.sequence);
|
|
@@ -274191,7 +274651,7 @@ async function runSync(store2, remote, opts) {
|
|
|
274191
274651
|
await remote.close();
|
|
274192
274652
|
}
|
|
274193
274653
|
}
|
|
274194
|
-
function validateBatches(batches, projectId, namespace) {
|
|
274654
|
+
function validateBatches(batches, projectId, namespace, scope = "project") {
|
|
274195
274655
|
for (const batch of batches) {
|
|
274196
274656
|
if (batch.formatVersion !== 3 || batch.projectId !== projectId || batch.namespace !== namespace) {
|
|
274197
274657
|
throw new Error("[memorix] sync batch rejected: wrong format, project, or remote namespace");
|
|
@@ -274203,7 +274663,7 @@ function validateBatches(batches, projectId, namespace) {
|
|
|
274203
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)) {
|
|
274204
274664
|
throw new Error("[memorix] sync batch rejected: malformed change entry");
|
|
274205
274665
|
}
|
|
274206
|
-
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)) {
|
|
274207
274667
|
throw new Error("[memorix] sync batch rejected: ineligible observation payload");
|
|
274208
274668
|
}
|
|
274209
274669
|
const incomingHash = entry.row ? contentHash(entry.row) : "";
|
|
@@ -274275,9 +274735,11 @@ var init_engine6 = __esm({
|
|
|
274275
274735
|
// src/sync/store-port.ts
|
|
274276
274736
|
import os7 from "os";
|
|
274277
274737
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
274278
|
-
function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
274738
|
+
function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
|
|
274279
274739
|
const db2 = getDatabase(dataDir);
|
|
274280
|
-
|
|
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);
|
|
274281
274743
|
const getMeta = db2.prepare("SELECT value FROM sync_meta WHERE key = ?");
|
|
274282
274744
|
const setMeta = db2.prepare("INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)");
|
|
274283
274745
|
const selState = db2.prepare("SELECT projectId, syncKey, obsId, revision, writer, kind, contentHash, shippedSeq FROM sync_row_state WHERE projectId = ?");
|
|
@@ -274317,7 +274779,7 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274317
274779
|
SET deviceId = ?, batchJson = ?
|
|
274318
274780
|
WHERE projectId = ? AND namespace = ? AND deviceId = ? AND sequence = ?`
|
|
274319
274781
|
);
|
|
274320
|
-
const namespace = syncNamespace(projectId);
|
|
274782
|
+
const namespace = scope === "user" ? userSyncNamespace() : syncNamespace(projectId);
|
|
274321
274783
|
const deviceFingerprint = process.env.MEMORIX_SYNC_DEVICE_FINGERPRINT?.trim() || `${process.platform}|${os7.hostname()}|${os7.homedir()}`;
|
|
274322
274784
|
function ensureDevice() {
|
|
274323
274785
|
const row = getMeta.get(DEVICE_KEY);
|
|
@@ -274345,7 +274807,8 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274345
274807
|
return `cursor:${namespace}`;
|
|
274346
274808
|
}
|
|
274347
274809
|
return {
|
|
274348
|
-
projectId: () =>
|
|
274810
|
+
projectId: () => scopeId,
|
|
274811
|
+
scope: () => scope,
|
|
274349
274812
|
namespace: () => namespace,
|
|
274350
274813
|
deviceId: ensureDevice,
|
|
274351
274814
|
assertCanPublish,
|
|
@@ -274385,14 +274848,14 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274385
274848
|
setMeta.run(cursorKey(), JSON.stringify(cursor));
|
|
274386
274849
|
},
|
|
274387
274850
|
async loadPendingBatches() {
|
|
274388
|
-
return selPending.all(
|
|
274851
|
+
return selPending.all(scopeId, namespace, ensureDevice()).map((row) => ({
|
|
274389
274852
|
batch: JSON.parse(row.batchJson),
|
|
274390
274853
|
states: JSON.parse(row.statesJson)
|
|
274391
274854
|
}));
|
|
274392
274855
|
},
|
|
274393
274856
|
async enqueueBatch(pending) {
|
|
274394
274857
|
insertPending.run({
|
|
274395
|
-
projectId,
|
|
274858
|
+
projectId: scopeId,
|
|
274396
274859
|
namespace,
|
|
274397
274860
|
deviceId: pending.batch.deviceId,
|
|
274398
274861
|
sequence: pending.batch.sequence,
|
|
@@ -274402,13 +274865,13 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274402
274865
|
});
|
|
274403
274866
|
},
|
|
274404
274867
|
async markBatchShipped(sequence) {
|
|
274405
|
-
deletePending.run(
|
|
274868
|
+
deletePending.run(scopeId, namespace, ensureDevice(), sequence);
|
|
274406
274869
|
},
|
|
274407
274870
|
async loadAll() {
|
|
274408
|
-
return store2.loadByProject(projectId);
|
|
274871
|
+
return scope === "user" ? store2.loadAll() : store2.loadByProject(projectId);
|
|
274409
274872
|
},
|
|
274410
274873
|
async loadState() {
|
|
274411
|
-
const rows = selState.all(
|
|
274874
|
+
const rows = selState.all(scopeId);
|
|
274412
274875
|
const map2 = /* @__PURE__ */ new Map();
|
|
274413
274876
|
for (const r6 of rows) map2.set(r6.syncKey, r6);
|
|
274414
274877
|
return map2;
|
|
@@ -274422,8 +274885,10 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274422
274885
|
async allocateId() {
|
|
274423
274886
|
return store2.atomic(async (tx) => {
|
|
274424
274887
|
const nextId2 = await tx.loadIdCounter();
|
|
274425
|
-
|
|
274426
|
-
|
|
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;
|
|
274427
274892
|
});
|
|
274428
274893
|
},
|
|
274429
274894
|
async applyInsert(row, state) {
|
|
@@ -274453,7 +274918,11 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274453
274918
|
async applyRemove(id, state) {
|
|
274454
274919
|
await store2.atomic(async (tx) => {
|
|
274455
274920
|
const existing = await tx.getById(id);
|
|
274456
|
-
|
|
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) {
|
|
274457
274926
|
throw new Error("[memorix] sync refused to remove an observation from another project");
|
|
274458
274927
|
}
|
|
274459
274928
|
await tx.remove(id);
|
|
@@ -274482,6 +274951,15 @@ function createSqliteSyncStore(dataDir, store2, projectId) {
|
|
|
274482
274951
|
}
|
|
274483
274952
|
};
|
|
274484
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
|
+
}
|
|
274485
274963
|
function ensureSyncSchema(db2, projectId) {
|
|
274486
274964
|
db2.exec(CREATE_SYNC_TABLES);
|
|
274487
274965
|
const columns = db2.prepare("PRAGMA table_info(sync_row_state)").all();
|
|
@@ -275392,9 +275870,30 @@ var sync_store_exports = {};
|
|
|
275392
275870
|
__export(sync_store_exports, {
|
|
275393
275871
|
default: () => sync_store_default
|
|
275394
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
|
+
}
|
|
275395
275894
|
function renderReport(report) {
|
|
275396
275895
|
const lines = [
|
|
275397
|
-
`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]" : ""}`,
|
|
275398
275897
|
`- Pushed changes: ${report.pushed}`,
|
|
275399
275898
|
`- Pulled batches: ${report.pulledBatches}`,
|
|
275400
275899
|
`- Applied upserts: ${report.applied}`,
|
|
@@ -275420,9 +275919,11 @@ var init_sync_store = __esm({
|
|
|
275420
275919
|
init_dist2();
|
|
275421
275920
|
init_operator_shared();
|
|
275422
275921
|
init_obs_store();
|
|
275922
|
+
init_persistence();
|
|
275423
275923
|
init_engine6();
|
|
275424
275924
|
init_store_port();
|
|
275425
275925
|
init_remote_factory();
|
|
275926
|
+
init_namespace();
|
|
275426
275927
|
sync_store_default = defineCommand({
|
|
275427
275928
|
meta: {
|
|
275428
275929
|
name: "sync-store",
|
|
@@ -275434,23 +275935,34 @@ var init_sync_store = __esm({
|
|
|
275434
275935
|
"push-only": { type: "boolean", description: "Only push local changes", default: false },
|
|
275435
275936
|
"pull-only": { type: "boolean", description: "Only pull remote changes", default: false },
|
|
275436
275937
|
through: { type: "string", description: "Compaction cutoff: device=sequence,device=sequence" },
|
|
275437
|
-
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
|
+
}
|
|
275438
275944
|
},
|
|
275439
275945
|
run: async ({ args }) => {
|
|
275440
275946
|
const asJson = Boolean(args.json);
|
|
275441
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
|
+
}
|
|
275442
275956
|
if (action === "device") {
|
|
275443
|
-
const { project: project2, dataDir: dataDir2 } = await getCliProjectContext();
|
|
275444
|
-
await initObservationStore(dataDir2);
|
|
275445
|
-
const syncStore2 = createSqliteSyncStore(dataDir2, getObservationStore(), project2.id);
|
|
275446
275957
|
const subaction = args._?.[1] || "";
|
|
275447
275958
|
if (subaction !== "rotate") {
|
|
275448
275959
|
emitError('expected "memorix sync store device rotate"', asJson);
|
|
275449
275960
|
process.exitCode = 2;
|
|
275450
275961
|
return;
|
|
275451
275962
|
}
|
|
275963
|
+
const { projectId: projectId2, syncStore: syncStore2 } = await openSyncStore("user");
|
|
275452
275964
|
const deviceId = syncStore2.rotateDevice();
|
|
275453
|
-
emitResult({ project:
|
|
275965
|
+
emitResult({ project: projectId2, scope: "user", deviceId, rotated: true }, `Sync device identity rotated: ${deviceId}`, asJson);
|
|
275454
275966
|
return;
|
|
275455
275967
|
}
|
|
275456
275968
|
if (action !== "push" && action !== "pull" && action !== "status" && action !== "compact") {
|
|
@@ -275474,10 +275986,7 @@ var init_sync_store = __esm({
|
|
|
275474
275986
|
);
|
|
275475
275987
|
return;
|
|
275476
275988
|
}
|
|
275477
|
-
const {
|
|
275478
|
-
await initObservationStore(dataDir);
|
|
275479
|
-
const obsStore = getObservationStore();
|
|
275480
|
-
const syncStore = createSqliteSyncStore(dataDir, obsStore, project.id);
|
|
275989
|
+
const { projectId, syncStore } = await openSyncStore(scope);
|
|
275481
275990
|
if (action === "compact") {
|
|
275482
275991
|
if (!args.yes && !args.dry) {
|
|
275483
275992
|
emitError("remote compaction deletes relay events; repeat with --yes after specifying --through device=sequence", asJson);
|
|
@@ -275512,7 +276021,7 @@ var init_sync_store = __esm({
|
|
|
275512
276021
|
try {
|
|
275513
276022
|
await remote2.init({ create: false });
|
|
275514
276023
|
const result = await remote2.compact(through, { dryRun: Boolean(args.dry) });
|
|
275515
|
-
emitResult({ project:
|
|
276024
|
+
emitResult({ project: projectId, scope, through, ...result, dryRun: Boolean(args.dry) }, `Compaction candidates: ${result.candidates}; deleted: ${result.deleted}`, asJson);
|
|
275516
276025
|
} catch (err) {
|
|
275517
276026
|
emitError(err.message, asJson);
|
|
275518
276027
|
process.exitCode = 1;
|
|
@@ -275537,7 +276046,7 @@ var init_sync_store = __esm({
|
|
|
275537
276046
|
push: true,
|
|
275538
276047
|
pull: true
|
|
275539
276048
|
});
|
|
275540
|
-
emitResult({ project:
|
|
276049
|
+
emitResult({ project: projectId, ...report }, renderReport(report), asJson);
|
|
275541
276050
|
} catch (err) {
|
|
275542
276051
|
emitError(err.message, asJson);
|
|
275543
276052
|
process.exitCode = 1;
|
|
@@ -275561,7 +276070,7 @@ var init_sync_store = __esm({
|
|
|
275561
276070
|
push: doPush,
|
|
275562
276071
|
pull: doPull
|
|
275563
276072
|
});
|
|
275564
|
-
emitResult({ project:
|
|
276073
|
+
emitResult({ project: projectId, ...report }, renderReport(report), asJson);
|
|
275565
276074
|
} catch (err) {
|
|
275566
276075
|
emitError(err.message, asJson);
|
|
275567
276076
|
process.exitCode = 1;
|
|
@@ -275623,6 +276132,32 @@ var init_sync = __esm({
|
|
|
275623
276132
|
type: "boolean",
|
|
275624
276133
|
description: "Emit machine-readable JSON output",
|
|
275625
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
|
|
275626
276161
|
}
|
|
275627
276162
|
},
|
|
275628
276163
|
run: async ({ args }) => {
|
|
@@ -279290,6 +279825,7 @@ var init_media = __esm({
|
|
|
279290
279825
|
concepts: { type: "string", description: "Comma-separated concepts when attaching" },
|
|
279291
279826
|
visibility: { type: "string", description: "Observation visibility when attaching" },
|
|
279292
279827
|
prompt: { type: "string", description: "Generation prompt" },
|
|
279828
|
+
"first-frame-image-url": { type: "string", description: "Public HTTPS first-frame image URL for MiniMax video generation; stored with the queued job" },
|
|
279293
279829
|
image: { type: "string", description: "Path to a reference image for image-to-image generation" },
|
|
279294
279830
|
model: { type: "string", description: "Provider model" },
|
|
279295
279831
|
provider: { type: "string", description: "Audio transcription provider: openai or groq" },
|
|
@@ -279340,6 +279876,7 @@ var init_media = __esm({
|
|
|
279340
279876
|
dataDir,
|
|
279341
279877
|
projectId: project.id,
|
|
279342
279878
|
prompt: prompt2,
|
|
279879
|
+
firstFrameImageUrl: args["first-frame-image-url"],
|
|
279343
279880
|
model: parseMiniMaxVideoModel(args.model),
|
|
279344
279881
|
region: parseMiniMaxRegion(args.region),
|
|
279345
279882
|
duration: parseMiniMaxVideoDuration(args.duration),
|
|
@@ -280238,8 +280775,20 @@ async function doStartUnlocked(port) {
|
|
|
280238
280775
|
if (healthy) {
|
|
280239
280776
|
process.stderr.write("[OK] Control plane is running and healthy.\n");
|
|
280240
280777
|
} else {
|
|
280241
|
-
process.stderr.write("[
|
|
280242
|
-
|
|
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;
|
|
280243
280792
|
}
|
|
280244
280793
|
const footer = [
|
|
280245
280794
|
"",
|
|
@@ -280627,7 +281176,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
280627
281176
|
import { existsSync as existsSync31, realpathSync as realpathSync2 } from "fs";
|
|
280628
281177
|
import { mkdir as mkdir9, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
280629
281178
|
import { basename as basename5, dirname as dirname8, resolve as resolve7 } from "path";
|
|
280630
|
-
import { homedir as
|
|
281179
|
+
import { homedir as homedir36 } from "os";
|
|
280631
281180
|
function requestedAgents(agent) {
|
|
280632
281181
|
const all = getSetupAgentTargets();
|
|
280633
281182
|
if (!agent || agent === "all") return all;
|
|
@@ -280705,16 +281254,16 @@ function asRecordArray(value) {
|
|
|
280705
281254
|
return Array.isArray(value) ? value.map(asRecord3).filter((entry) => entry !== null) : [];
|
|
280706
281255
|
}
|
|
280707
281256
|
function codexPluginPath() {
|
|
280708
|
-
return `${
|
|
281257
|
+
return `${homedir36()}/plugins/${CODEX_PLUGIN_NAME}`;
|
|
280709
281258
|
}
|
|
280710
281259
|
function codexPluginMcpPath() {
|
|
280711
281260
|
return `${codexPluginPath()}/.mcp.json`;
|
|
280712
281261
|
}
|
|
280713
281262
|
function codexMarketplacePath() {
|
|
280714
|
-
return `${
|
|
281263
|
+
return `${homedir36()}/.agents/plugins/marketplace.json`;
|
|
280715
281264
|
}
|
|
280716
281265
|
function codexConfigPath() {
|
|
280717
|
-
return `${
|
|
281266
|
+
return `${homedir36()}/.codex/config.toml`;
|
|
280718
281267
|
}
|
|
280719
281268
|
function normalizeMarketplacePath(value) {
|
|
280720
281269
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -281029,7 +281578,7 @@ function canonicalProjectPath(projectRoot) {
|
|
|
281029
281578
|
return canonical.replace(/\\/g, "/");
|
|
281030
281579
|
}
|
|
281031
281580
|
function getClaudeLocalConfigPath() {
|
|
281032
|
-
return `${
|
|
281581
|
+
return `${homedir36()}/.claude.json`;
|
|
281033
281582
|
}
|
|
281034
281583
|
function coerceMcpServer(name, value) {
|
|
281035
281584
|
const entry = asRecord3(value);
|
|
@@ -281191,7 +281740,58 @@ async function inspectCodexPluginMcp() {
|
|
|
281191
281740
|
};
|
|
281192
281741
|
}
|
|
281193
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
|
+
}
|
|
281194
281793
|
async function inspectMcp(agent, projectRoot, scope) {
|
|
281794
|
+
if (agent === "grok") return inspectOptionalGrokMcp(scope);
|
|
281195
281795
|
if (!isMcpConfigAgent(agent)) {
|
|
281196
281796
|
return { status: "skipped", issues: ["mcp-managed-by-package"], checks: [] };
|
|
281197
281797
|
}
|
|
@@ -281288,7 +281888,7 @@ async function inspectGuidance(agent, projectRoot, scope) {
|
|
|
281288
281888
|
}
|
|
281289
281889
|
const checks = [];
|
|
281290
281890
|
for (const targetScope of requestedGuidanceScopes(scope)) {
|
|
281291
|
-
const root = targetScope === "project" ? projectRoot :
|
|
281891
|
+
const root = targetScope === "project" ? projectRoot : homedir36();
|
|
281292
281892
|
const rulesPath = getAgentRulesPath(agent, root, targetScope === "global");
|
|
281293
281893
|
if (!existsSync31(rulesPath)) {
|
|
281294
281894
|
checks.push({
|
|
@@ -281569,8 +282169,8 @@ var init_doctor = __esm({
|
|
|
281569
282169
|
return;
|
|
281570
282170
|
}
|
|
281571
282171
|
const { existsSync: existsSync36, readFileSync: readFileSync25 } = await import("fs");
|
|
281572
|
-
const { join:
|
|
281573
|
-
const { homedir:
|
|
282172
|
+
const { join: join36, basename: basename7 } = await import("path");
|
|
282173
|
+
const { homedir: homedir38 } = await import("os");
|
|
281574
282174
|
const { execSync: execSync5 } = await import("child_process");
|
|
281575
282175
|
const report = {};
|
|
281576
282176
|
const issues = [];
|
|
@@ -281632,8 +282232,8 @@ var init_doctor = __esm({
|
|
|
281632
282232
|
}
|
|
281633
282233
|
lines.push("");
|
|
281634
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");
|
|
281635
|
-
const memorixDir =
|
|
281636
|
-
const bgStatePath =
|
|
282235
|
+
const memorixDir = join36(homedir38(), ".memorix");
|
|
282236
|
+
const bgStatePath = join36(memorixDir, "background.json");
|
|
281637
282237
|
let bgRunning = false;
|
|
281638
282238
|
let bgPort = 0;
|
|
281639
282239
|
let bgPid = 0;
|
|
@@ -281753,7 +282353,7 @@ var init_doctor = __esm({
|
|
|
281753
282353
|
}
|
|
281754
282354
|
}
|
|
281755
282355
|
try {
|
|
281756
|
-
const cachePath =
|
|
282356
|
+
const cachePath = join36(memorixDir, "embedding-cache.json");
|
|
281757
282357
|
if (existsSync36(cachePath)) {
|
|
281758
282358
|
const cache5 = JSON.parse(readFileSync25(cachePath, "utf-8"));
|
|
281759
282359
|
const cacheCount = Object.keys(cache5).length;
|
|
@@ -282007,7 +282607,7 @@ var init_doctor = __esm({
|
|
|
282007
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");
|
|
282008
282608
|
let conflictsFound = false;
|
|
282009
282609
|
if (dataDir) {
|
|
282010
|
-
const lockFile =
|
|
282610
|
+
const lockFile = join36(dataDir, ".memorix.lock");
|
|
282011
282611
|
if (existsSync36(lockFile)) {
|
|
282012
282612
|
try {
|
|
282013
282613
|
const lockData = JSON.parse(readFileSync25(lockFile, "utf-8"));
|
|
@@ -284219,7 +284819,7 @@ var init_pipeline_trace = __esm({
|
|
|
284219
284819
|
// src/orchestrate/worktree.ts
|
|
284220
284820
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
284221
284821
|
import { existsSync as existsSync33, rmSync as rmSync2 } from "fs";
|
|
284222
|
-
import { join as
|
|
284822
|
+
import { join as join34, basename as basename6 } from "path";
|
|
284223
284823
|
function runGit4(cwd, args, timeout = 5e3) {
|
|
284224
284824
|
return String(execFileSync5("git", args, {
|
|
284225
284825
|
cwd,
|
|
@@ -284258,7 +284858,7 @@ function isManagedWorktreePath(worktreePath, worktreeBase) {
|
|
|
284258
284858
|
function createWorktree(projectDir2, taskId, pipelineId) {
|
|
284259
284859
|
const shortId2 = taskId.slice(0, 8);
|
|
284260
284860
|
const shortPipeline = pipelineId.slice(0, 8);
|
|
284261
|
-
const worktreeBase =
|
|
284861
|
+
const worktreeBase = join34(projectDir2, WORKTREE_DIR, `task-${shortId2}`);
|
|
284262
284862
|
const branchBase = `pipeline/${shortPipeline}/task-${shortId2}`;
|
|
284263
284863
|
for (let attempt = 1; attempt <= 100; attempt++) {
|
|
284264
284864
|
const suffix = attempt === 1 ? "" : `-${attempt}`;
|
|
@@ -284348,7 +284948,7 @@ function listWorktrees(projectDir2) {
|
|
|
284348
284948
|
}
|
|
284349
284949
|
}
|
|
284350
284950
|
if (current) worktrees.push(current);
|
|
284351
|
-
const worktreeBase =
|
|
284951
|
+
const worktreeBase = join34(projectDir2, WORKTREE_DIR);
|
|
284352
284952
|
return worktrees.filter((w4) => isManagedWorktreePath(w4.path, worktreeBase));
|
|
284353
284953
|
} catch {
|
|
284354
284954
|
return [];
|
|
@@ -284770,16 +285370,16 @@ var init_cost_tracker = __esm({
|
|
|
284770
285370
|
|
|
284771
285371
|
// src/orchestrate/evidence.ts
|
|
284772
285372
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
284773
|
-
import { join as
|
|
285373
|
+
import { join as join35 } from "path";
|
|
284774
285374
|
function writeTaskEvidence(projectDir2, pipelineId, evidence) {
|
|
284775
285375
|
try {
|
|
284776
|
-
const dir =
|
|
285376
|
+
const dir = join35(projectDir2, ".pipeline", pipelineId, "evidence", evidence.taskId.slice(0, 12));
|
|
284777
285377
|
mkdirSync11(dir, { recursive: true });
|
|
284778
285378
|
if (evidence.prompt) {
|
|
284779
|
-
safeWrite(
|
|
285379
|
+
safeWrite(join35(dir, "prompt.md"), evidence.prompt);
|
|
284780
285380
|
}
|
|
284781
285381
|
if (evidence.tailOutput) {
|
|
284782
|
-
safeWrite(
|
|
285382
|
+
safeWrite(join35(dir, "output.txt"), evidence.tailOutput);
|
|
284783
285383
|
}
|
|
284784
285384
|
if (evidence.gateResults) {
|
|
284785
285385
|
for (const g4 of evidence.gateResults) {
|
|
@@ -284788,7 +285388,7 @@ Status: ${g4.passed ? "PASS" : "FAIL"}
|
|
|
284788
285388
|
Duration: ${(g4.durationMs / 1e3).toFixed(1)}s
|
|
284789
285389
|
|
|
284790
285390
|
${g4.output}`;
|
|
284791
|
-
safeWrite(
|
|
285391
|
+
safeWrite(join35(dir, `${g4.gate}.txt`), content);
|
|
284792
285392
|
}
|
|
284793
285393
|
}
|
|
284794
285394
|
const resultJson = {
|
|
@@ -284802,7 +285402,7 @@ ${g4.output}`;
|
|
|
284802
285402
|
tokenUsage: evidence.tokenUsage,
|
|
284803
285403
|
costUSD: evidence.costUSD
|
|
284804
285404
|
};
|
|
284805
|
-
safeWrite(
|
|
285405
|
+
safeWrite(join35(dir, "result.json"), JSON.stringify(resultJson, null, 2));
|
|
284806
285406
|
return dir;
|
|
284807
285407
|
} catch {
|
|
284808
285408
|
return null;
|
|
@@ -284810,7 +285410,7 @@ ${g4.output}`;
|
|
|
284810
285410
|
}
|
|
284811
285411
|
function writePipelineSummary(projectDir2, summary) {
|
|
284812
285412
|
try {
|
|
284813
|
-
const dir =
|
|
285413
|
+
const dir = join35(projectDir2, ".pipeline", summary.pipelineId);
|
|
284814
285414
|
mkdirSync11(dir, { recursive: true });
|
|
284815
285415
|
const elapsed = (summary.elapsedMs / 1e3).toFixed(0);
|
|
284816
285416
|
const lines = [
|
|
@@ -284831,8 +285431,8 @@ function writePipelineSummary(projectDir2, summary) {
|
|
|
284831
285431
|
lines.push(`- [${status}] ${t4.taskDescription.slice(0, 80)} \u2014 ${t4.agentName}, ${dur}s${fixes}`);
|
|
284832
285432
|
}
|
|
284833
285433
|
const content = lines.join("\n") + "\n";
|
|
284834
|
-
safeWrite(
|
|
284835
|
-
return
|
|
285434
|
+
safeWrite(join35(dir, "summary.md"), content);
|
|
285435
|
+
return join35(dir, "summary.md");
|
|
284836
285436
|
} catch {
|
|
284837
285437
|
return null;
|
|
284838
285438
|
}
|
|
@@ -291211,11 +291811,11 @@ var GUIDES = {
|
|
|
291211
291811
|
"memorix sync rules --action status",
|
|
291212
291812
|
"memorix sync workspace --action scan",
|
|
291213
291813
|
"memorix sync store status --json",
|
|
291214
|
-
"memorix sync store push|pull [--dry] [--json]",
|
|
291814
|
+
"memorix sync store push|pull [--dry] [--json] [--scope project|user]",
|
|
291215
291815
|
"memorix sync store compact --dry --through device=sequence",
|
|
291216
291816
|
"memorix sync store device rotate"
|
|
291217
291817
|
],
|
|
291218
|
-
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."]
|
|
291219
291819
|
},
|
|
291220
291820
|
ingest: {
|
|
291221
291821
|
summary: "Ingest Git and image evidence into project memory.",
|