@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.
@@ -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) {
@@ -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 result = await fn(client);
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
- export function createPostgresDatabase(config) {
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 result = await fn(client);
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.query(`
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.query(sql);
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-rc4",
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",