File size: 3,068 Bytes
6111b2b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { getDbInstance } from "@/lib/db/core";
import { encrypt, decrypt } from "@/lib/db/encryption";
import type { AgentCredentials } from "@/lib/cloudAgent/baseAgent";

// The `cloud_agent_credentials` table is provisioned by migration
// `061_cloud_agent_credentials.sql` at database initialization (see
// src/lib/db/migrations/). Do not create it inline here — the project
// migration policy requires versioned, transaction-wrapped DDL.

/** Mask API key for display — show last 4 chars only */
export function maskApiKey(key: string): string {
  if (!key || key.length <= 4) return "****";
  return "****" + key.slice(-4);
}

/** Get decrypted credentials for a provider */
export function getCloudAgentCredentialFromDb(providerId: string): AgentCredentials | null {
  const db = getDbInstance();
  const row = db
    .prepare(
      "SELECT api_key_encrypted, base_url FROM cloud_agent_credentials WHERE provider_id = ?"
    )
    .get(providerId) as { api_key_encrypted: string; base_url: string | null } | undefined;

  if (!row) return null;

  const decryptedKey = decrypt(row.api_key_encrypted);
  if (!decryptedKey) return null;

  const creds: AgentCredentials = { apiKey: decryptedKey };
  if (row.base_url) creds.baseUrl = row.base_url;
  return creds;
}

/** List all credentials with masked keys */
export function listCloudAgentCredentials(): Array<{
  providerId: string;
  apiKey: string;
  baseUrl: string | null;
  updatedAt: string;
}> {
  const db = getDbInstance();
  const rows = db
    .prepare(
      "SELECT provider_id, api_key_encrypted, base_url, updated_at FROM cloud_agent_credentials"
    )
    .all() as {
    provider_id: string;
    api_key_encrypted: string;
    base_url: string | null;
    updated_at: string;
  }[];

  return rows.map((row) => {
    const decrypted = decrypt(row.api_key_encrypted) ?? "";
    return {
      providerId: row.provider_id,
      apiKey: maskApiKey(decrypted),
      baseUrl: row.base_url,
      updatedAt: row.updated_at,
    };
  });
}

/** Save or update credentials (encrypts API key at rest) */
export function saveCloudAgentCredential(

  providerId: string,

  apiKey: string,

  baseUrl?: string

): void {
  const encrypted = encrypt(apiKey);
  if (!encrypted) throw new Error("Failed to encrypt API key");

  const db = getDbInstance();
  db.prepare(
    `INSERT INTO cloud_agent_credentials (provider_id, api_key_encrypted, base_url, updated_at)

     VALUES (@providerId, @apiKey, @baseUrl, datetime('now'))

     ON CONFLICT(provider_id) DO UPDATE SET

       api_key_encrypted = excluded.api_key_encrypted,

       base_url = excluded.base_url,

       updated_at = excluded.updated_at`
  ).run({ providerId, apiKey: encrypted, baseUrl: baseUrl ?? null });
}

/** Delete credentials for a provider */
export function deleteCloudAgentCredential(providerId: string): void {
  const db = getDbInstance();
  db.prepare("DELETE FROM cloud_agent_credentials WHERE provider_id = ?").run(providerId);
}