@shahmilsaari/memory-core 1.0.30 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OHVCPFEY.js → chunk-23GUWJ6F.js} +12 -2
- package/dist/{chunk-M7NKSXFS.js → chunk-GIPKVQSA.js} +113 -1
- package/dist/cli.js +95 -6
- package/dist/dashboard/assets/index-BFwqVRYO.js +2 -0
- package/dist/dashboard/assets/index-y7eHWJtq.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/{dashboard-server-OHVL64F2.js → dashboard-server-MD6NVL2F.js} +375 -3
- package/dist/{db-PRDHI2CN.js → db-FLFZZXG3.js} +23 -1
- package/package.json +11 -7
- package/dist/dashboard/assets/index-B4vD0HC8.css +0 -1
- package/dist/dashboard/assets/index-CesnieMi.js +0 -2
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
searchMemories,
|
|
15
15
|
updateMemory,
|
|
16
16
|
upsertMemory
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-GIPKVQSA.js";
|
|
18
18
|
import {
|
|
19
19
|
buildModuleDependencyEdges,
|
|
20
20
|
collectResolvedImports,
|
|
@@ -208,7 +208,17 @@ function buildArchRules(arch) {
|
|
|
208
208
|
{ id: "no-circular", name: "No circular dependencies", fromLayer: "*", toLayer: "*", allowed: false, severity: "critical", enforcement: "block" },
|
|
209
209
|
{ id: "components-no-routes", name: "Components must not import route modules", fromLayer: "components", toLayer: "routes", allowed: false, severity: "critical", enforcement: "block" },
|
|
210
210
|
{ id: "stores-no-components", name: "Stores must not import components", fromLayer: "stores", toLayer: "components", allowed: false, severity: "critical", enforcement: "block" },
|
|
211
|
-
{ id: "
|
|
211
|
+
{ id: "lib-no-routes", name: "Lib must not import routes", fromLayer: "lib", toLayer: "routes", allowed: false, severity: "critical", enforcement: "block" },
|
|
212
|
+
{ id: "lib-no-stores", name: "Lib must not import client stores", fromLayer: "lib", toLayer: "stores", allowed: false, severity: "critical", enforcement: "block" },
|
|
213
|
+
{ id: "lib-no-components", name: "Lib must not import components", fromLayer: "lib", toLayer: "components", allowed: false, severity: "critical", enforcement: "block" },
|
|
214
|
+
{ id: "utils-no-stores", name: "Utils must not import stores", fromLayer: "utils", toLayer: "stores", allowed: false, severity: "medium", enforcement: "warn" },
|
|
215
|
+
{ id: "utils-no-lib", name: "Utils must not import lib \u2014 keep utils pure", fromLayer: "utils", toLayer: "lib", allowed: false, severity: "medium", enforcement: "warn" },
|
|
216
|
+
{ id: "routes-use-components", name: "Routes use components", fromLayer: "routes", toLayer: "components", allowed: true, severity: "low", enforcement: "suggest" },
|
|
217
|
+
{ id: "routes-use-stores", name: "Routes use stores", fromLayer: "routes", toLayer: "stores", allowed: true, severity: "low", enforcement: "suggest" },
|
|
218
|
+
{ id: "routes-use-lib", name: "Routes use lib for server actions and API", fromLayer: "routes", toLayer: "lib", allowed: true, severity: "low", enforcement: "suggest" },
|
|
219
|
+
{ id: "components-use-stores", name: "Components use stores for reactive state", fromLayer: "components", toLayer: "stores", allowed: true, severity: "low", enforcement: "suggest" },
|
|
220
|
+
{ id: "components-use-lib", name: "Components use lib for API clients", fromLayer: "components", toLayer: "lib", allowed: true, severity: "low", enforcement: "suggest" },
|
|
221
|
+
{ id: "stores-use-lib", name: "Stores use lib for API and utilities", fromLayer: "stores", toLayer: "lib", allowed: true, severity: "low", enforcement: "suggest" }
|
|
212
222
|
] };
|
|
213
223
|
case "angular":
|
|
214
224
|
return { rules: [
|
|
@@ -71,6 +71,34 @@ async function runMigrations() {
|
|
|
71
71
|
WHERE content_hash IS NULL`
|
|
72
72
|
);
|
|
73
73
|
await client.query(`CREATE INDEX IF NOT EXISTS memories_content_hash_idx ON memories (content_hash)`);
|
|
74
|
+
await client.query(`
|
|
75
|
+
CREATE TABLE IF NOT EXISTS arch_profiles (
|
|
76
|
+
id SERIAL PRIMARY KEY,
|
|
77
|
+
name TEXT NOT NULL,
|
|
78
|
+
arch_type TEXT NOT NULL DEFAULT 'custom',
|
|
79
|
+
layers JSONB NOT NULL DEFAULT '[]',
|
|
80
|
+
rules JSONB NOT NULL DEFAULT '[]',
|
|
81
|
+
project_name TEXT,
|
|
82
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
83
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
84
|
+
)
|
|
85
|
+
`);
|
|
86
|
+
await client.query(`
|
|
87
|
+
CREATE UNIQUE INDEX IF NOT EXISTS arch_profiles_name_project_idx
|
|
88
|
+
ON arch_profiles (name, COALESCE(project_name, ''))
|
|
89
|
+
`);
|
|
90
|
+
await client.query(`
|
|
91
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
92
|
+
id SERIAL PRIMARY KEY,
|
|
93
|
+
email TEXT NOT NULL UNIQUE,
|
|
94
|
+
username TEXT NOT NULL UNIQUE,
|
|
95
|
+
password_hash TEXT NOT NULL,
|
|
96
|
+
role TEXT NOT NULL DEFAULT 'viewer',
|
|
97
|
+
team_name TEXT,
|
|
98
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
99
|
+
last_login TIMESTAMPTZ
|
|
100
|
+
)
|
|
101
|
+
`);
|
|
74
102
|
await client.query("COMMIT");
|
|
75
103
|
migrationsRun = true;
|
|
76
104
|
} catch (err) {
|
|
@@ -80,6 +108,38 @@ async function runMigrations() {
|
|
|
80
108
|
client.release();
|
|
81
109
|
}
|
|
82
110
|
}
|
|
111
|
+
async function saveArchProfile(input) {
|
|
112
|
+
await runMigrations();
|
|
113
|
+
const result = await getPool().query(
|
|
114
|
+
`INSERT INTO arch_profiles (name, arch_type, layers, rules, project_name)
|
|
115
|
+
VALUES ($1, $2, $3::jsonb, $4::jsonb, $5)
|
|
116
|
+
ON CONFLICT (name, COALESCE(project_name, ''))
|
|
117
|
+
DO UPDATE SET arch_type = EXCLUDED.arch_type, layers = EXCLUDED.layers, rules = EXCLUDED.rules, updated_at = NOW()
|
|
118
|
+
RETURNING *`,
|
|
119
|
+
[input.name, input.archType, JSON.stringify(input.layers), JSON.stringify(input.rules), input.projectName ?? null]
|
|
120
|
+
);
|
|
121
|
+
return result.rows[0];
|
|
122
|
+
}
|
|
123
|
+
async function listArchProfiles(projectName) {
|
|
124
|
+
await runMigrations();
|
|
125
|
+
const result = await getPool().query(
|
|
126
|
+
`SELECT * FROM arch_profiles
|
|
127
|
+
WHERE project_name IS NULL OR project_name = $1
|
|
128
|
+
ORDER BY updated_at DESC`,
|
|
129
|
+
[projectName ?? null]
|
|
130
|
+
);
|
|
131
|
+
return result.rows;
|
|
132
|
+
}
|
|
133
|
+
async function getArchProfile(id) {
|
|
134
|
+
await runMigrations();
|
|
135
|
+
const result = await getPool().query(`SELECT * FROM arch_profiles WHERE id = $1`, [id]);
|
|
136
|
+
return result.rows[0] ?? null;
|
|
137
|
+
}
|
|
138
|
+
async function deleteArchProfile(id) {
|
|
139
|
+
await runMigrations();
|
|
140
|
+
const result = await getPool().query(`DELETE FROM arch_profiles WHERE id = $1`, [id]);
|
|
141
|
+
return (result.rowCount ?? 0) > 0;
|
|
142
|
+
}
|
|
83
143
|
async function saveMemory(memory) {
|
|
84
144
|
await runMigrations();
|
|
85
145
|
const { type, scope, architecture, projectName, title, content, reason, context, tags, embedding } = memory;
|
|
@@ -283,12 +343,57 @@ async function closePool() {
|
|
|
283
343
|
migrationsRun = false;
|
|
284
344
|
}
|
|
285
345
|
}
|
|
346
|
+
async function createUser(input) {
|
|
347
|
+
await runMigrations();
|
|
348
|
+
const result = await getPool().query(
|
|
349
|
+
`INSERT INTO users (email, username, password_hash, role, team_name)
|
|
350
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
351
|
+
RETURNING *`,
|
|
352
|
+
[input.email, input.username, input.passwordHash, input.role ?? "viewer", input.teamName ?? null]
|
|
353
|
+
);
|
|
354
|
+
return result.rows[0];
|
|
355
|
+
}
|
|
356
|
+
async function getUserByEmail(email) {
|
|
357
|
+
await runMigrations();
|
|
358
|
+
const result = await getPool().query(`SELECT * FROM users WHERE email = $1`, [email]);
|
|
359
|
+
return result.rows[0] ?? null;
|
|
360
|
+
}
|
|
361
|
+
async function getUserById(id) {
|
|
362
|
+
await runMigrations();
|
|
363
|
+
const result = await getPool().query(`SELECT * FROM users WHERE id = $1`, [id]);
|
|
364
|
+
return result.rows[0] ?? null;
|
|
365
|
+
}
|
|
366
|
+
async function listUsers() {
|
|
367
|
+
await runMigrations();
|
|
368
|
+
const result = await getPool().query(
|
|
369
|
+
`SELECT id, email, username, role, team_name, created_at, last_login FROM users ORDER BY created_at ASC`
|
|
370
|
+
);
|
|
371
|
+
return result.rows;
|
|
372
|
+
}
|
|
373
|
+
async function updateLastLogin(id) {
|
|
374
|
+
await runMigrations();
|
|
375
|
+
await getPool().query(`UPDATE users SET last_login = NOW() WHERE id = $1`, [id]);
|
|
376
|
+
}
|
|
377
|
+
async function countUsers() {
|
|
378
|
+
await runMigrations();
|
|
379
|
+
const result = await getPool().query(`SELECT COUNT(*) FROM users`);
|
|
380
|
+
return parseInt(result.rows[0].count, 10);
|
|
381
|
+
}
|
|
382
|
+
async function deleteUser(id) {
|
|
383
|
+
await runMigrations();
|
|
384
|
+
const result = await getPool().query(`DELETE FROM users WHERE id = $1`, [id]);
|
|
385
|
+
return (result.rowCount ?? 0) > 0;
|
|
386
|
+
}
|
|
286
387
|
|
|
287
388
|
export {
|
|
288
389
|
Config,
|
|
289
390
|
hashMemoryContent,
|
|
290
391
|
getPool,
|
|
291
392
|
runMigrations,
|
|
393
|
+
saveArchProfile,
|
|
394
|
+
listArchProfiles,
|
|
395
|
+
getArchProfile,
|
|
396
|
+
deleteArchProfile,
|
|
292
397
|
saveMemory,
|
|
293
398
|
upsertMemory,
|
|
294
399
|
listMemories,
|
|
@@ -297,5 +402,12 @@ export {
|
|
|
297
402
|
deleteMemories,
|
|
298
403
|
updateMemory,
|
|
299
404
|
searchMemories,
|
|
300
|
-
closePool
|
|
405
|
+
closePool,
|
|
406
|
+
createUser,
|
|
407
|
+
getUserByEmail,
|
|
408
|
+
getUserById,
|
|
409
|
+
listUsers,
|
|
410
|
+
updateLastLogin,
|
|
411
|
+
countUsers,
|
|
412
|
+
deleteUser
|
|
301
413
|
};
|
package/dist/cli.js
CHANGED
|
@@ -29,13 +29,13 @@ import {
|
|
|
29
29
|
toPortableMemory,
|
|
30
30
|
uninstallHook,
|
|
31
31
|
writeMemoryFile
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-23GUWJ6F.js";
|
|
33
33
|
import "./chunk-PQBWHAZN.js";
|
|
34
34
|
import {
|
|
35
35
|
closePool,
|
|
36
36
|
getPool,
|
|
37
37
|
runMigrations
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-GIPKVQSA.js";
|
|
39
39
|
import "./chunk-ZZBQEXEO.js";
|
|
40
40
|
|
|
41
41
|
// src/cli.ts
|
|
@@ -1738,7 +1738,7 @@ program.command("tune").description("Review and disable noisy rules with high fa
|
|
|
1738
1738
|
console.log(chalk.dim("\n No changes made.\n"));
|
|
1739
1739
|
}
|
|
1740
1740
|
});
|
|
1741
|
-
program.command("dashboard").description("Start the live Svelte dashboard with WebSocket watch events").option("-p, --port <port>", "Dashboard port", "5178").option("--path <dir>", "Directory to watch (default: current directory)").option("--no-watch", "Serve the dashboard without starting file watch").action(async (opts) => {
|
|
1741
|
+
program.command("dashboard").description("Start the live Svelte dashboard with WebSocket watch events").option("-p, --port <port>", "Dashboard port", "5178").option("--path <dir>", "Directory to watch (default: current directory)").option("--no-watch", "Serve the dashboard without starting file watch").option("--auth", "Enable JWT authentication (requires MC_JWT_SECRET in .memory-core.env)").action(async (opts) => {
|
|
1742
1742
|
const resolveDashboardPath = () => {
|
|
1743
1743
|
if (typeof opts.path === "string" && opts.path.trim().length > 0) return opts.path;
|
|
1744
1744
|
const withEquals = process.argv.find((arg) => arg.startsWith("--path="));
|
|
@@ -1755,11 +1755,12 @@ program.command("dashboard").description("Start the live Svelte dashboard with W
|
|
|
1755
1755
|
}
|
|
1756
1756
|
return void 0;
|
|
1757
1757
|
};
|
|
1758
|
-
const { startDashboard } = await import("./dashboard-server-
|
|
1758
|
+
const { startDashboard } = await import("./dashboard-server-MD6NVL2F.js");
|
|
1759
1759
|
await startDashboard({
|
|
1760
1760
|
port: parseInt(opts.port, 10),
|
|
1761
1761
|
path: resolveDashboardPath(),
|
|
1762
|
-
watch: opts.watch
|
|
1762
|
+
watch: opts.watch,
|
|
1763
|
+
auth: opts.auth ?? false
|
|
1763
1764
|
});
|
|
1764
1765
|
});
|
|
1765
1766
|
program.command("seed").description("Load all predefined memories into the database").option("--arch <architecture>", "Only seed a specific architecture (e.g. clean-architecture)").option("--force", "Re-seed even if memories already exist", false).action(async (opts) => {
|
|
@@ -2388,7 +2389,7 @@ program.command("check").description("Check staged changes against architecture
|
|
|
2388
2389
|
}
|
|
2389
2390
|
let storedMemories = [];
|
|
2390
2391
|
try {
|
|
2391
|
-
const { listMemories } = await import("./db-
|
|
2392
|
+
const { listMemories } = await import("./db-FLFZZXG3.js");
|
|
2392
2393
|
const allMemories = await listMemories({ limit: 1e4 });
|
|
2393
2394
|
const ranked = allMemories.filter((m) => ["rule", "pattern", "decision"].includes(m.type));
|
|
2394
2395
|
const changedLayers = packet.layersAffected;
|
|
@@ -2660,4 +2661,92 @@ arch.command("incident").description("Log a production incident and capture it a
|
|
|
2660
2661
|
console.log(chalk.dim(" Discarded.\n"));
|
|
2661
2662
|
}
|
|
2662
2663
|
});
|
|
2664
|
+
var CREDENTIALS_PATH = join(homedir(), ".memory-core", "credentials.json");
|
|
2665
|
+
function readCredentials() {
|
|
2666
|
+
try {
|
|
2667
|
+
return JSON.parse(readFileSync(CREDENTIALS_PATH, "utf-8"));
|
|
2668
|
+
} catch {
|
|
2669
|
+
return null;
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
function writeCredentials(data) {
|
|
2673
|
+
mkdirSync(dirname(CREDENTIALS_PATH), { recursive: true });
|
|
2674
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
2675
|
+
}
|
|
2676
|
+
function clearCredentials() {
|
|
2677
|
+
try {
|
|
2678
|
+
writeFileSync(CREDENTIALS_PATH, "{}", "utf-8");
|
|
2679
|
+
} catch {
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
var authCmd = program.command("auth").description("Manage dashboard authentication");
|
|
2683
|
+
authCmd.command("setup").description("Create the first admin account (dashboard must be running with --auth)").option("--url <url>", "Dashboard URL", "http://localhost:5178").action(async (opts) => {
|
|
2684
|
+
const email = await input({ message: "Admin email:" });
|
|
2685
|
+
const username = await input({ message: "Admin username:" });
|
|
2686
|
+
const password = await input({ message: "Password (min 8 chars):", transformer: () => "****" });
|
|
2687
|
+
const teamName = await input({ message: "Team name (optional):" });
|
|
2688
|
+
const res = await fetch(`${opts.url}/api/auth/setup`, {
|
|
2689
|
+
method: "POST",
|
|
2690
|
+
headers: { "content-type": "application/json" },
|
|
2691
|
+
body: JSON.stringify({ email, username, password, teamName: teamName || void 0 })
|
|
2692
|
+
});
|
|
2693
|
+
const data = await res.json();
|
|
2694
|
+
if (!res.ok) {
|
|
2695
|
+
console.error(chalk.red(` \u2717 ${data.error ?? "Setup failed"}`));
|
|
2696
|
+
process.exit(1);
|
|
2697
|
+
}
|
|
2698
|
+
writeCredentials({ token: data.token, email, username, dashboardUrl: opts.url });
|
|
2699
|
+
console.log(chalk.green(`
|
|
2700
|
+
\u2713 Admin created and logged in as ${username}
|
|
2701
|
+
`));
|
|
2702
|
+
console.log(chalk.gray(` Credentials saved to ${CREDENTIALS_PATH}
|
|
2703
|
+
`));
|
|
2704
|
+
});
|
|
2705
|
+
authCmd.command("login").description("Log in to a running dashboard (stores JWT locally)").option("--url <url>", "Dashboard URL", "http://localhost:5178").action(async (opts) => {
|
|
2706
|
+
const email = await input({ message: "Email:" });
|
|
2707
|
+
const password = await input({ message: "Password:", transformer: () => "****" });
|
|
2708
|
+
const res = await fetch(`${opts.url}/api/auth/login`, {
|
|
2709
|
+
method: "POST",
|
|
2710
|
+
headers: { "content-type": "application/json" },
|
|
2711
|
+
body: JSON.stringify({ email, password })
|
|
2712
|
+
});
|
|
2713
|
+
const data = await res.json();
|
|
2714
|
+
if (!res.ok) {
|
|
2715
|
+
console.error(chalk.red(` \u2717 ${data.error ?? "Login failed"}`));
|
|
2716
|
+
process.exit(1);
|
|
2717
|
+
}
|
|
2718
|
+
const user = data.user;
|
|
2719
|
+
writeCredentials({ token: data.token, email, username: user.username, dashboardUrl: opts.url });
|
|
2720
|
+
console.log(chalk.green(`
|
|
2721
|
+
\u2713 Logged in as ${user.username}
|
|
2722
|
+
`));
|
|
2723
|
+
});
|
|
2724
|
+
authCmd.command("logout").description("Clear locally stored credentials").action(() => {
|
|
2725
|
+
clearCredentials();
|
|
2726
|
+
console.log(chalk.green("\n \u2713 Logged out\n"));
|
|
2727
|
+
});
|
|
2728
|
+
authCmd.command("status").description("Show current auth status").action(async () => {
|
|
2729
|
+
const creds = readCredentials();
|
|
2730
|
+
if (!creds?.token) {
|
|
2731
|
+
console.log(chalk.yellow("\n Not logged in\n"));
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
try {
|
|
2735
|
+
const res = await fetch(`${creds.dashboardUrl}/api/auth/me`, {
|
|
2736
|
+
headers: { authorization: `Bearer ${creds.token}` }
|
|
2737
|
+
});
|
|
2738
|
+
const data = await res.json();
|
|
2739
|
+
if (!res.ok) {
|
|
2740
|
+
console.log(chalk.yellow("\n Token invalid or expired \u2014 run `memory-core auth login`\n"));
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
const user = data.user;
|
|
2744
|
+
console.log(chalk.green(`
|
|
2745
|
+
Logged in as ${user.username} (${user.email}) [${user.role}]`));
|
|
2746
|
+
console.log(chalk.gray(` Dashboard: ${creds.dashboardUrl}
|
|
2747
|
+
`));
|
|
2748
|
+
} catch {
|
|
2749
|
+
console.log(chalk.yellow("\n Dashboard not reachable\n"));
|
|
2750
|
+
}
|
|
2751
|
+
});
|
|
2663
2752
|
program.parseAsync(process.argv);
|