@simonsbs/keylore 1.0.0-rc4 → 1.0.0-rc6
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/.env.example +5 -1
- package/README.md +27 -15
- package/bin/keylore-http.js +10 -2
- package/data/catalog.json +4 -0
- package/dist/app.js +2 -2
- package/dist/config.js +11 -7
- package/dist/domain/types.js +60 -46
- package/dist/http/admin-ui.js +1068 -371
- package/dist/http/server.js +173 -0
- package/dist/http-service.js +167 -0
- package/dist/mcp/create-server.js +4 -4
- package/dist/repositories/credential-repository.js +37 -7
- package/dist/repositories/pg-credential-repository.js +50 -11
- package/dist/services/backup-service.js +10 -4
- package/dist/services/core-mode-service.js +17 -1
- package/dist/storage/database.js +85 -2
- package/dist/storage/in-memory-database.js +10 -1
- package/dist/storage/migrations.js +2 -2
- package/migrations/009_v1_context_split.sql +30 -0
- package/package.json +2 -1
|
@@ -99,7 +99,12 @@ export class CoreModeService {
|
|
|
99
99
|
expiresAt: parsed.expiresAt,
|
|
100
100
|
rotationPolicy: parsed.rotationPolicy,
|
|
101
101
|
lastValidatedAt: null,
|
|
102
|
-
selectionNotes: parsed.selectionNotes,
|
|
102
|
+
selectionNotes: parsed.llmContext?.trim() || parsed.selectionNotes?.trim() || "",
|
|
103
|
+
userContext: parsed.userContext?.trim() ||
|
|
104
|
+
parsed.llmContext?.trim() ||
|
|
105
|
+
parsed.selectionNotes?.trim() ||
|
|
106
|
+
"",
|
|
107
|
+
llmContext: parsed.llmContext?.trim() || parsed.selectionNotes?.trim() || "",
|
|
103
108
|
binding,
|
|
104
109
|
tags: parsed.tags,
|
|
105
110
|
status: parsed.status,
|
|
@@ -138,6 +143,17 @@ export class CoreModeService {
|
|
|
138
143
|
await this.syncCoreAllowRule(updated);
|
|
139
144
|
return updated;
|
|
140
145
|
}
|
|
146
|
+
async replaceLocalSecret(context, credentialId, secretValue) {
|
|
147
|
+
const existing = await this.broker.getCredential(context, credentialId);
|
|
148
|
+
if (!existing) {
|
|
149
|
+
throw new Error(`Credential ${credentialId} not found.`);
|
|
150
|
+
}
|
|
151
|
+
if (existing.owner !== "local") {
|
|
152
|
+
throw new Error("Only locally stored tokens can be replaced through the admin UI.");
|
|
153
|
+
}
|
|
154
|
+
await this.localSecrets.put(`local:${existing.tenantId}:${existing.id}`, secretValue);
|
|
155
|
+
return existing;
|
|
156
|
+
}
|
|
141
157
|
async deleteCredential(context, credentialId) {
|
|
142
158
|
const existing = await this.broker.getCredential(context, credentialId);
|
|
143
159
|
if (!existing) {
|
package/dist/storage/database.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
1
4
|
import { Pool } from "pg";
|
|
2
5
|
class PostgresDatabase {
|
|
3
6
|
pool;
|
|
@@ -7,11 +10,20 @@ class PostgresDatabase {
|
|
|
7
10
|
async query(text, params) {
|
|
8
11
|
return this.pool.query(text, params);
|
|
9
12
|
}
|
|
13
|
+
async exec(text) {
|
|
14
|
+
await this.pool.query(text);
|
|
15
|
+
}
|
|
10
16
|
async withTransaction(fn) {
|
|
11
17
|
const client = await this.pool.connect();
|
|
12
18
|
try {
|
|
13
19
|
await client.query("BEGIN");
|
|
14
|
-
const
|
|
20
|
+
const transactionalClient = {
|
|
21
|
+
query: (text, params) => client.query(text, params),
|
|
22
|
+
exec: async (text) => {
|
|
23
|
+
await client.query(text);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const result = await fn(transactionalClient);
|
|
15
27
|
await client.query("COMMIT");
|
|
16
28
|
return result;
|
|
17
29
|
}
|
|
@@ -30,10 +42,81 @@ class PostgresDatabase {
|
|
|
30
42
|
await this.pool.end();
|
|
31
43
|
}
|
|
32
44
|
}
|
|
33
|
-
|
|
45
|
+
class LocalDatabase {
|
|
46
|
+
database;
|
|
47
|
+
queue = Promise.resolve();
|
|
48
|
+
constructor(database) {
|
|
49
|
+
this.database = database;
|
|
50
|
+
}
|
|
51
|
+
enqueue(fn) {
|
|
52
|
+
const next = this.queue.then(fn, fn);
|
|
53
|
+
this.queue = next.then(() => undefined, () => undefined);
|
|
54
|
+
return next;
|
|
55
|
+
}
|
|
56
|
+
normalize(result) {
|
|
57
|
+
const rows = result.rows;
|
|
58
|
+
return {
|
|
59
|
+
command: "",
|
|
60
|
+
rowCount: result.affectedRows && result.affectedRows > 0 ? result.affectedRows : rows.length,
|
|
61
|
+
oid: 0,
|
|
62
|
+
rows,
|
|
63
|
+
fields: (result.fields ?? []),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async query(text, params) {
|
|
67
|
+
return this.enqueue(async () => this.normalize(await this.database.query(text, params)));
|
|
68
|
+
}
|
|
69
|
+
async exec(text) {
|
|
70
|
+
await this.enqueue(async () => {
|
|
71
|
+
await this.database.exec(text);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async withTransaction(fn) {
|
|
75
|
+
return this.enqueue(async () => {
|
|
76
|
+
await this.database.exec("BEGIN");
|
|
77
|
+
const client = {
|
|
78
|
+
query: async (text, params) => this.normalize(await this.database.query(text, params)),
|
|
79
|
+
exec: async (text) => {
|
|
80
|
+
await this.database.exec(text);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
const result = await fn(client);
|
|
85
|
+
await this.database.exec("COMMIT");
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
await this.database.exec("ROLLBACK");
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async healthcheck() {
|
|
95
|
+
await this.enqueue(async () => {
|
|
96
|
+
await this.database.query("SELECT 1");
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async close() {
|
|
100
|
+
await this.enqueue(async () => {
|
|
101
|
+
await this.database.close();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function createPostgresDatabase(config) {
|
|
34
106
|
const pool = new Pool({
|
|
35
107
|
connectionString: config.databaseUrl,
|
|
36
108
|
max: config.databasePoolMax,
|
|
37
109
|
});
|
|
38
110
|
return new PostgresDatabase(pool);
|
|
39
111
|
}
|
|
112
|
+
async function createLocalDatabase(config) {
|
|
113
|
+
await fs.mkdir(path.dirname(config.localDatabasePath), { recursive: true });
|
|
114
|
+
const database = new PGlite(config.localDatabasePath);
|
|
115
|
+
return new LocalDatabase(database);
|
|
116
|
+
}
|
|
117
|
+
export async function createSqlDatabase(config) {
|
|
118
|
+
if (config.databaseMode === "postgres") {
|
|
119
|
+
return createPostgresDatabase(config);
|
|
120
|
+
}
|
|
121
|
+
return createLocalDatabase(config);
|
|
122
|
+
}
|
|
@@ -7,11 +7,20 @@ class InMemoryDatabase {
|
|
|
7
7
|
async query(text, params) {
|
|
8
8
|
return this.pool.query(text, params);
|
|
9
9
|
}
|
|
10
|
+
async exec(text) {
|
|
11
|
+
await this.pool.query(text);
|
|
12
|
+
}
|
|
10
13
|
async withTransaction(fn) {
|
|
11
14
|
const client = await this.pool.connect();
|
|
12
15
|
try {
|
|
13
16
|
await client.query("BEGIN");
|
|
14
|
-
const
|
|
17
|
+
const transactionalClient = {
|
|
18
|
+
query: (text, params) => client.query(text, params),
|
|
19
|
+
exec: async (text) => {
|
|
20
|
+
await client.query(text);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const result = await fn(transactionalClient);
|
|
15
24
|
await client.query("COMMIT");
|
|
16
25
|
return result;
|
|
17
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
export async function runMigrations(database, migrationsDir) {
|
|
4
|
-
await database.
|
|
4
|
+
await database.exec(`
|
|
5
5
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
6
6
|
version TEXT PRIMARY KEY,
|
|
7
7
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
@@ -20,7 +20,7 @@ export async function runMigrations(database, migrationsDir) {
|
|
|
20
20
|
}
|
|
21
21
|
const sql = await fs.readFile(path.join(migrationsDir, fileName), "utf8");
|
|
22
22
|
await database.withTransaction(async (client) => {
|
|
23
|
-
await client.
|
|
23
|
+
await client.exec(sql);
|
|
24
24
|
await client.query("INSERT INTO schema_migrations(version) VALUES ($1)", [version]);
|
|
25
25
|
});
|
|
26
26
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
ALTER TABLE credentials
|
|
2
|
+
ADD COLUMN IF NOT EXISTS user_context TEXT;
|
|
3
|
+
|
|
4
|
+
ALTER TABLE credentials
|
|
5
|
+
ADD COLUMN IF NOT EXISTS llm_context TEXT;
|
|
6
|
+
|
|
7
|
+
UPDATE credentials
|
|
8
|
+
SET
|
|
9
|
+
llm_context = CASE
|
|
10
|
+
WHEN llm_context IS NULL OR llm_context = '' THEN selection_notes
|
|
11
|
+
ELSE llm_context
|
|
12
|
+
END,
|
|
13
|
+
user_context = CASE
|
|
14
|
+
WHEN user_context IS NULL OR user_context = '' THEN
|
|
15
|
+
CASE
|
|
16
|
+
WHEN llm_context IS NULL OR llm_context = '' THEN selection_notes
|
|
17
|
+
ELSE llm_context
|
|
18
|
+
END
|
|
19
|
+
ELSE user_context
|
|
20
|
+
END
|
|
21
|
+
WHERE llm_context IS NULL
|
|
22
|
+
OR llm_context = ''
|
|
23
|
+
OR user_context IS NULL
|
|
24
|
+
OR user_context = '';
|
|
25
|
+
|
|
26
|
+
ALTER TABLE credentials
|
|
27
|
+
ALTER COLUMN llm_context SET NOT NULL;
|
|
28
|
+
|
|
29
|
+
ALTER TABLE credentials
|
|
30
|
+
ALTER COLUMN user_context SET NOT NULL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonsbs/keylore",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc6",
|
|
4
4
|
"description": "MCP credential broker and searchable credential catalogue for LLM coding tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"test": "node --test --import tsx src/test/**/*.test.ts"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
+
"@electric-sql/pglite": "^0.3.16",
|
|
66
67
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
67
68
|
"pg": "^8.20.0",
|
|
68
69
|
"pg-mem": "^3.0.14",
|