open-managed-agents 0.1.0
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/LICENSE +93 -0
- package/README.md +248 -0
- package/bin/oma.mjs +499 -0
- package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
- package/dist/scripts/alpha-smoke-models.mjs +92 -0
- package/dist/scripts/alpha-smoke.mjs +581 -0
- package/dist/scripts/oma-doctor.js +230 -0
- package/dist/scripts/oma-models.js +299 -0
- package/dist/scripts/oma-workspaces.js +133 -0
- package/dist/src/control-plane/admin/auth.js +43 -0
- package/dist/src/control-plane/admin/routes.js +76 -0
- package/dist/src/control-plane/admin/service.js +102 -0
- package/dist/src/control-plane/admission.js +90 -0
- package/dist/src/control-plane/agents/routes.js +63 -0
- package/dist/src/control-plane/agents/service.js +687 -0
- package/dist/src/control-plane/agents/store.js +391 -0
- package/dist/src/control-plane/agents/types.js +1 -0
- package/dist/src/control-plane/api-constants.js +3 -0
- package/dist/src/control-plane/app.js +907 -0
- package/dist/src/control-plane/console/auth.js +68 -0
- package/dist/src/control-plane/console/static.js +112 -0
- package/dist/src/control-plane/deployment-runtime-config.js +328 -0
- package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
- package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
- package/dist/src/control-plane/deployment-storage.js +354 -0
- package/dist/src/control-plane/egress/image.js +48 -0
- package/dist/src/control-plane/egress/policy.js +571 -0
- package/dist/src/control-plane/egress/presets.js +77 -0
- package/dist/src/control-plane/egress/proxy.js +72 -0
- package/dist/src/control-plane/egress/ssrf.js +106 -0
- package/dist/src/control-plane/egress/utils/debug.js +14 -0
- package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
- package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
- package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
- package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
- package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
- package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
- package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
- package/dist/src/control-plane/environments/routes.js +54 -0
- package/dist/src/control-plane/environments/service.js +112 -0
- package/dist/src/control-plane/environments/store.js +132 -0
- package/dist/src/control-plane/environments/types.js +1 -0
- package/dist/src/control-plane/errors.js +81 -0
- package/dist/src/control-plane/events/broadcaster.js +217 -0
- package/dist/src/control-plane/events/constants.js +2 -0
- package/dist/src/control-plane/events/persist.js +39 -0
- package/dist/src/control-plane/events/request.js +190 -0
- package/dist/src/control-plane/events/routes.js +159 -0
- package/dist/src/control-plane/events/runtime-helpers.js +134 -0
- package/dist/src/control-plane/events/service.js +1985 -0
- package/dist/src/control-plane/events/session-guards.js +29 -0
- package/dist/src/control-plane/events/sse.js +6 -0
- package/dist/src/control-plane/events/store.js +772 -0
- package/dist/src/control-plane/events/tool-persistence.js +102 -0
- package/dist/src/control-plane/events/types.js +21 -0
- package/dist/src/control-plane/files/routes.js +121 -0
- package/dist/src/control-plane/files/service.js +65 -0
- package/dist/src/control-plane/files/store-common.js +87 -0
- package/dist/src/control-plane/files/store-local.js +488 -0
- package/dist/src/control-plane/files/store-memory.js +270 -0
- package/dist/src/control-plane/files/store.js +2 -0
- package/dist/src/control-plane/files/types.js +7 -0
- package/dist/src/control-plane/http.js +25 -0
- package/dist/src/control-plane/ids.js +43 -0
- package/dist/src/control-plane/logging.js +263 -0
- package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
- package/dist/src/control-plane/models/catalog.js +78 -0
- package/dist/src/control-plane/models/config-security.js +376 -0
- package/dist/src/control-plane/models/deployment-config.js +93 -0
- package/dist/src/control-plane/models/routes.js +32 -0
- package/dist/src/control-plane/models/service.js +138 -0
- package/dist/src/control-plane/observability/instruments.js +65 -0
- package/dist/src/control-plane/observability/metrics.js +125 -0
- package/dist/src/control-plane/observability/routes.js +95 -0
- package/dist/src/control-plane/openapi/document.js +282 -0
- package/dist/src/control-plane/openapi/routes.js +78 -0
- package/dist/src/control-plane/request-idempotency.js +85 -0
- package/dist/src/control-plane/secrets/envelope.js +132 -0
- package/dist/src/control-plane/secrets/master-key.js +44 -0
- package/dist/src/control-plane/secrets/routes.js +20 -0
- package/dist/src/control-plane/secrets/service.js +71 -0
- package/dist/src/control-plane/secrets/store.js +212 -0
- package/dist/src/control-plane/secrets/types.js +3 -0
- package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
- package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
- package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
- package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
- package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
- package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
- package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
- package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
- package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
- package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
- package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
- package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
- package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
- package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
- package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
- package/dist/src/control-plane/sessions/pi/translator.js +100 -0
- package/dist/src/control-plane/sessions/request.js +164 -0
- package/dist/src/control-plane/sessions/resources.js +63 -0
- package/dist/src/control-plane/sessions/routes.js +88 -0
- package/dist/src/control-plane/sessions/serialize.js +24 -0
- package/dist/src/control-plane/sessions/service.js +733 -0
- package/dist/src/control-plane/sessions/store.js +538 -0
- package/dist/src/control-plane/sessions/types.js +1 -0
- package/dist/src/control-plane/skills/archive.js +169 -0
- package/dist/src/control-plane/skills/routes.js +41 -0
- package/dist/src/control-plane/skills/service.js +29 -0
- package/dist/src/control-plane/skills/store.js +233 -0
- package/dist/src/control-plane/skills/types.js +5 -0
- package/dist/src/control-plane/sqlite-transaction.js +15 -0
- package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
- package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
- package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
- package/dist/src/control-plane/vaults/routes.js +66 -0
- package/dist/src/control-plane/vaults/service.js +553 -0
- package/dist/src/control-plane/vaults/store.js +691 -0
- package/dist/src/control-plane/vaults/types.js +1 -0
- package/dist/src/control-plane/wake-loop.js +65 -0
- package/dist/src/control-plane/wiring.js +92 -0
- package/dist/src/control-plane/workspace.js +4 -0
- package/dist/src/control-plane/workspaces/store.js +158 -0
- package/dist/src/egress-proxy-main.js +103 -0
- package/dist/src/main.js +124 -0
- package/dist/src/types/agents.js +1 -0
- package/dist/src/types/common.js +1 -0
- package/dist/src/types/environments.js +1 -0
- package/dist/src/types/events.js +121 -0
- package/dist/src/types/files.js +1 -0
- package/dist/src/types/json.js +19 -0
- package/dist/src/types/sessions.js +1 -0
- package/dist/ui/managed-agents-console/README.md +52 -0
- package/dist/ui/managed-agents-console/docs/agents.md +23 -0
- package/dist/ui/managed-agents-console/docs/console.md +23 -0
- package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
- package/dist/ui/managed-agents-console/docs/environments.md +27 -0
- package/dist/ui/managed-agents-console/docs/events.md +19 -0
- package/dist/ui/managed-agents-console/docs/files.md +17 -0
- package/dist/ui/managed-agents-console/docs/github.md +11 -0
- package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
- package/dist/ui/managed-agents-console/docs/memory.md +11 -0
- package/dist/ui/managed-agents-console/docs/migration.md +17 -0
- package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
- package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
- package/dist/ui/managed-agents-console/docs/overview.md +41 -0
- package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
- package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
- package/dist/ui/managed-agents-console/docs/reference.md +27 -0
- package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
- package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
- package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
- package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
- package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
- package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
- package/dist/ui/managed-agents-console/docs/skills.md +15 -0
- package/dist/ui/managed-agents-console/docs/tools.md +23 -0
- package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
- package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
- package/dist/ui/managed-agents-console/index.html +47 -0
- package/dist/ui/managed-agents-console/serve.mjs +116 -0
- package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
- package/dist/ui/managed-agents-console/src/api.js +1043 -0
- package/dist/ui/managed-agents-console/src/app.jsx +633 -0
- package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
- package/dist/ui/managed-agents-console/src/console.css +733 -0
- package/dist/ui/managed-agents-console/src/data.js +188 -0
- package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
- package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
- package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
- package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
- package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
- package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
- package/dist/ui/managed-agents-console/src/sse.js +185 -0
- package/dist/ui/managed-agents-console/src/states.jsx +90 -0
- package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
- package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
- package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
- package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
- package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
- package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
- package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
- package/dist/ui/openapi-docs/VENDOR.md +27 -0
- package/dist/ui/openapi-docs/index.html +16 -0
- package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
- package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
- package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
- package/package.json +53 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { withSqliteTransaction } from "../sqlite-transaction.js";
|
|
3
|
+
export const VAULT_SECRET_PREFIX = "vault/";
|
|
4
|
+
const SCHEMA = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS vaults (
|
|
6
|
+
id TEXT PRIMARY KEY,
|
|
7
|
+
workspace_id TEXT NOT NULL,
|
|
8
|
+
type TEXT NOT NULL,
|
|
9
|
+
display_name TEXT NOT NULL,
|
|
10
|
+
metadata TEXT NOT NULL,
|
|
11
|
+
created_at TEXT NOT NULL,
|
|
12
|
+
updated_at TEXT NOT NULL,
|
|
13
|
+
archived_at TEXT
|
|
14
|
+
);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS vaults_by_workspace
|
|
16
|
+
ON vaults (workspace_id, id);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS vault_credentials (
|
|
19
|
+
id TEXT PRIMARY KEY,
|
|
20
|
+
workspace_id TEXT NOT NULL,
|
|
21
|
+
vault_id TEXT NOT NULL,
|
|
22
|
+
type TEXT NOT NULL,
|
|
23
|
+
display_name TEXT,
|
|
24
|
+
metadata TEXT NOT NULL,
|
|
25
|
+
auth_type TEXT NOT NULL,
|
|
26
|
+
mcp_server_url TEXT NOT NULL,
|
|
27
|
+
token_endpoint TEXT,
|
|
28
|
+
client_id TEXT,
|
|
29
|
+
scope TEXT,
|
|
30
|
+
token_endpoint_auth_type TEXT,
|
|
31
|
+
expires_at TEXT,
|
|
32
|
+
auth_version INTEGER NOT NULL DEFAULT 1,
|
|
33
|
+
refresh_status TEXT,
|
|
34
|
+
refresh_attempts INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
next_refresh_at TEXT,
|
|
36
|
+
auth_hint_at TEXT,
|
|
37
|
+
created_at TEXT NOT NULL,
|
|
38
|
+
updated_at TEXT NOT NULL,
|
|
39
|
+
archived_at TEXT
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS vault_credentials_by_vault
|
|
42
|
+
ON vault_credentials (workspace_id, vault_id, id);
|
|
43
|
+
CREATE UNIQUE INDEX IF NOT EXISTS vault_credentials_active_server_url
|
|
44
|
+
ON vault_credentials (workspace_id, vault_id, mcp_server_url)
|
|
45
|
+
WHERE archived_at IS NULL;
|
|
46
|
+
`;
|
|
47
|
+
export class SqliteVaultStore {
|
|
48
|
+
db;
|
|
49
|
+
secrets;
|
|
50
|
+
insertVaultStmt;
|
|
51
|
+
retrieveVaultActiveStmt;
|
|
52
|
+
retrieveVaultAnyStmt;
|
|
53
|
+
updateVaultStmt;
|
|
54
|
+
archiveVaultStmt;
|
|
55
|
+
deleteVaultStmt;
|
|
56
|
+
deleteCredentialsByVaultStmt;
|
|
57
|
+
credentialRowsByVaultStmt;
|
|
58
|
+
insertCredentialStmt;
|
|
59
|
+
retrieveCredentialActiveStmt;
|
|
60
|
+
retrieveCredentialAnyStmt;
|
|
61
|
+
updateCredentialMetadataStmt;
|
|
62
|
+
archiveCredentialStmt;
|
|
63
|
+
deleteCredentialStmt;
|
|
64
|
+
countActiveCredentialsStmt;
|
|
65
|
+
resolveCredentialStmt;
|
|
66
|
+
readCredentialRuntimeMetadataStmt;
|
|
67
|
+
persistAuthHintStmt;
|
|
68
|
+
persistOauthRefreshSuccessStmt;
|
|
69
|
+
persistOauthRefreshFailureStmt;
|
|
70
|
+
listDueRefreshesStmt;
|
|
71
|
+
nextDueRefreshAtStmt;
|
|
72
|
+
listVaultStmts = new Map();
|
|
73
|
+
listCredentialStmts = new Map();
|
|
74
|
+
listAdminCredentialMetadataStmts = new Map();
|
|
75
|
+
constructor(db, secrets) {
|
|
76
|
+
this.db = db;
|
|
77
|
+
this.secrets = secrets;
|
|
78
|
+
this.db.exec(SCHEMA);
|
|
79
|
+
ensureVaultCredentialColumns(this.db);
|
|
80
|
+
this.insertVaultStmt = this.db.prepare(`INSERT INTO vaults (
|
|
81
|
+
id, workspace_id, type, display_name, metadata, created_at, updated_at, archived_at
|
|
82
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
83
|
+
this.retrieveVaultActiveStmt = this.db.prepare(`SELECT * FROM vaults
|
|
84
|
+
WHERE workspace_id = ? AND id = ? AND archived_at IS NULL`);
|
|
85
|
+
this.retrieveVaultAnyStmt = this.db.prepare(`SELECT * FROM vaults WHERE workspace_id = ? AND id = ?`);
|
|
86
|
+
this.updateVaultStmt = this.db.prepare(`UPDATE vaults
|
|
87
|
+
SET display_name = ?, metadata = ?, updated_at = ?
|
|
88
|
+
WHERE workspace_id = ? AND id = ? AND archived_at IS NULL`);
|
|
89
|
+
this.archiveVaultStmt = this.db.prepare(`UPDATE vaults
|
|
90
|
+
SET archived_at = COALESCE(archived_at, ?), updated_at = ?
|
|
91
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
92
|
+
this.deleteVaultStmt = this.db.prepare(`DELETE FROM vaults WHERE workspace_id = ? AND id = ?`);
|
|
93
|
+
this.deleteCredentialsByVaultStmt = this.db.prepare(`DELETE FROM vault_credentials WHERE workspace_id = ? AND vault_id = ?`);
|
|
94
|
+
this.credentialRowsByVaultStmt = this.db.prepare(`SELECT * FROM vault_credentials WHERE workspace_id = ? AND vault_id = ?`);
|
|
95
|
+
this.insertCredentialStmt = this.db.prepare(`INSERT INTO vault_credentials (
|
|
96
|
+
id, workspace_id, vault_id, type, display_name, metadata, auth_type,
|
|
97
|
+
mcp_server_url, token_endpoint, client_id, scope, token_endpoint_auth_type,
|
|
98
|
+
expires_at, auth_version, next_refresh_at, created_at, updated_at, archived_at
|
|
99
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
100
|
+
this.retrieveCredentialActiveStmt = this.db.prepare(`SELECT * FROM vault_credentials
|
|
101
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ? AND archived_at IS NULL`);
|
|
102
|
+
this.retrieveCredentialAnyStmt = this.db.prepare(`SELECT * FROM vault_credentials
|
|
103
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?`);
|
|
104
|
+
this.updateCredentialMetadataStmt = this.db.prepare(`UPDATE vault_credentials
|
|
105
|
+
SET display_name = ?, metadata = ?, expires_at = ?, auth_version = ?,
|
|
106
|
+
refresh_status = CASE WHEN ? THEN NULL ELSE refresh_status END,
|
|
107
|
+
refresh_attempts = CASE WHEN ? THEN 0 ELSE refresh_attempts END,
|
|
108
|
+
next_refresh_at = CASE WHEN ? THEN ? ELSE next_refresh_at END,
|
|
109
|
+
auth_hint_at = CASE WHEN ? THEN NULL ELSE auth_hint_at END,
|
|
110
|
+
updated_at = ?
|
|
111
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ? AND archived_at IS NULL`);
|
|
112
|
+
this.archiveCredentialStmt = this.db.prepare(`UPDATE vault_credentials
|
|
113
|
+
SET archived_at = COALESCE(archived_at, ?), auth_version = auth_version + 1,
|
|
114
|
+
updated_at = ?
|
|
115
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?`);
|
|
116
|
+
this.deleteCredentialStmt = this.db.prepare(`DELETE FROM vault_credentials
|
|
117
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?`);
|
|
118
|
+
this.countActiveCredentialsStmt = this.db.prepare(`SELECT COUNT(*) AS n FROM vault_credentials
|
|
119
|
+
WHERE workspace_id = ? AND vault_id = ? AND archived_at IS NULL`);
|
|
120
|
+
this.resolveCredentialStmt = this.db.prepare(`SELECT * FROM vault_credentials
|
|
121
|
+
WHERE workspace_id = ? AND vault_id = ? AND mcp_server_url = ? AND archived_at IS NULL
|
|
122
|
+
ORDER BY id ASC
|
|
123
|
+
LIMIT 1`);
|
|
124
|
+
this.readCredentialRuntimeMetadataStmt = this.db.prepare(`SELECT id, vault_id, auth_type, token_endpoint, client_id,
|
|
125
|
+
token_endpoint_auth_type, auth_version, expires_at, refresh_status,
|
|
126
|
+
auth_hint_at, next_refresh_at, refresh_attempts
|
|
127
|
+
FROM vault_credentials
|
|
128
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ? AND archived_at IS NULL`);
|
|
129
|
+
this.persistAuthHintStmt = this.db.prepare(`UPDATE vault_credentials
|
|
130
|
+
SET auth_hint_at = ?
|
|
131
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?
|
|
132
|
+
AND auth_type = 'mcp_oauth'
|
|
133
|
+
AND auth_version = ?
|
|
134
|
+
AND archived_at IS NULL`);
|
|
135
|
+
this.persistOauthRefreshSuccessStmt = this.db.prepare(`UPDATE vault_credentials
|
|
136
|
+
SET expires_at = ?, scope = ?, auth_version = auth_version + 1,
|
|
137
|
+
refresh_status = 'ok', refresh_attempts = 0, next_refresh_at = ?,
|
|
138
|
+
auth_hint_at = NULL, updated_at = ?
|
|
139
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?
|
|
140
|
+
AND auth_type = 'mcp_oauth'
|
|
141
|
+
AND auth_version = ?
|
|
142
|
+
AND archived_at IS NULL`);
|
|
143
|
+
this.persistOauthRefreshFailureStmt = this.db.prepare(`UPDATE vault_credentials
|
|
144
|
+
SET refresh_status = ?, refresh_attempts = ?, next_refresh_at = ?,
|
|
145
|
+
auth_hint_at = NULL
|
|
146
|
+
WHERE workspace_id = ? AND vault_id = ? AND id = ?
|
|
147
|
+
AND auth_type = 'mcp_oauth'
|
|
148
|
+
AND auth_version = ?
|
|
149
|
+
AND archived_at IS NULL`);
|
|
150
|
+
this.listDueRefreshesStmt = this.db.prepare(`SELECT workspace_id, vault_id, id, auth_version, next_refresh_at
|
|
151
|
+
FROM vault_credentials
|
|
152
|
+
WHERE auth_type = 'mcp_oauth'
|
|
153
|
+
AND archived_at IS NULL
|
|
154
|
+
AND next_refresh_at IS NOT NULL
|
|
155
|
+
AND next_refresh_at <= ?
|
|
156
|
+
AND COALESCE(refresh_status, '') != 'invalid'
|
|
157
|
+
ORDER BY next_refresh_at ASC, id ASC
|
|
158
|
+
LIMIT ?`);
|
|
159
|
+
this.nextDueRefreshAtStmt = this.db.prepare(`SELECT MIN(next_refresh_at) AS next_refresh_at
|
|
160
|
+
FROM vault_credentials
|
|
161
|
+
WHERE auth_type = 'mcp_oauth'
|
|
162
|
+
AND archived_at IS NULL
|
|
163
|
+
AND next_refresh_at IS NOT NULL
|
|
164
|
+
AND COALESCE(refresh_status, '') != 'invalid'`);
|
|
165
|
+
}
|
|
166
|
+
static open(path = ":memory:", secrets) {
|
|
167
|
+
return new SqliteVaultStore(new DatabaseSync(path), secrets);
|
|
168
|
+
}
|
|
169
|
+
createVault(record) {
|
|
170
|
+
const v = record.row;
|
|
171
|
+
this.insertVaultStmt.run(v.id, v.workspace_id, v.type, v.display_name, JSON.stringify(v.metadata), v.created_at, v.updated_at, v.archived_at);
|
|
172
|
+
return v;
|
|
173
|
+
}
|
|
174
|
+
retrieveVault(workspaceId, vaultId) {
|
|
175
|
+
const row = this.retrieveVaultActiveStmt.get(workspaceId, vaultId);
|
|
176
|
+
return row ? deserializeVault(row) : undefined;
|
|
177
|
+
}
|
|
178
|
+
retrieveVaultAny(workspaceId, vaultId) {
|
|
179
|
+
const row = this.retrieveVaultAnyStmt.get(workspaceId, vaultId);
|
|
180
|
+
return row ? deserializeVault(row) : undefined;
|
|
181
|
+
}
|
|
182
|
+
listVaults(workspaceId, opts = {}) {
|
|
183
|
+
if (opts.page === "")
|
|
184
|
+
return { data: [], has_more: false, next_page: null };
|
|
185
|
+
const limit = normalizeLimit(opts.limit);
|
|
186
|
+
const rows = this.listVaultStmt({
|
|
187
|
+
includeArchived: opts.includeArchived ?? false,
|
|
188
|
+
hasPage: opts.page !== undefined,
|
|
189
|
+
}).all(...selectListArgs(workspaceId, limit + 1, opts.page));
|
|
190
|
+
const data = rows.slice(0, limit).map(deserializeVault);
|
|
191
|
+
return {
|
|
192
|
+
data,
|
|
193
|
+
has_more: rows.length > limit,
|
|
194
|
+
next_page: rows.length > limit ? data[data.length - 1]?.id ?? null : null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
listWorkspaceCredentialAdminMetadata(workspaceId, opts = {}) {
|
|
198
|
+
if (opts.page === "")
|
|
199
|
+
return { data: [], has_more: false, next_page: null };
|
|
200
|
+
const limit = normalizeLimit(opts.limit);
|
|
201
|
+
const rows = this.listAdminCredentialMetadataStmt({ hasPage: opts.page !== undefined })
|
|
202
|
+
.all(...selectListArgs(workspaceId, limit + 1, opts.page));
|
|
203
|
+
const data = rows.slice(0, limit).map(adminCredentialMetadata);
|
|
204
|
+
return {
|
|
205
|
+
data,
|
|
206
|
+
has_more: rows.length > limit,
|
|
207
|
+
next_page: rows.length > limit ? data[data.length - 1]?.credentialId ?? null : null,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
updateVault(workspaceId, vaultId, updates, updatedAt) {
|
|
211
|
+
const existing = this.retrieveVault(workspaceId, vaultId);
|
|
212
|
+
if (!existing)
|
|
213
|
+
return undefined;
|
|
214
|
+
this.updateVaultStmt.run(updates.displayName ?? existing.display_name, JSON.stringify(updates.metadata ?? existing.metadata), updatedAt, workspaceId, vaultId);
|
|
215
|
+
return this.retrieveVaultAny(workspaceId, vaultId);
|
|
216
|
+
}
|
|
217
|
+
archiveVault(workspaceId, vaultId, archivedAt) {
|
|
218
|
+
const existing = this.retrieveVaultAny(workspaceId, vaultId);
|
|
219
|
+
if (!existing)
|
|
220
|
+
return undefined;
|
|
221
|
+
return withSqliteTransaction(this.db, () => {
|
|
222
|
+
const credentials = this.credentialsForVault(workspaceId, vaultId);
|
|
223
|
+
for (const credential of credentials) {
|
|
224
|
+
this.secrets?.delete(workspaceId, secretName(vaultId, credential.id));
|
|
225
|
+
}
|
|
226
|
+
this.archiveVaultStmt.run(archivedAt, archivedAt, workspaceId, vaultId);
|
|
227
|
+
for (const credential of credentials) {
|
|
228
|
+
this.archiveCredentialStmt.run(archivedAt, archivedAt, workspaceId, vaultId, credential.id);
|
|
229
|
+
}
|
|
230
|
+
return this.retrieveVaultAny(workspaceId, vaultId);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
deleteVault(workspaceId, vaultId) {
|
|
234
|
+
const existing = this.retrieveVaultAny(workspaceId, vaultId);
|
|
235
|
+
if (!existing)
|
|
236
|
+
return undefined;
|
|
237
|
+
return withSqliteTransaction(this.db, () => {
|
|
238
|
+
for (const credential of this.credentialsForVault(workspaceId, vaultId)) {
|
|
239
|
+
this.secrets?.delete(workspaceId, secretName(vaultId, credential.id));
|
|
240
|
+
}
|
|
241
|
+
this.deleteCredentialsByVaultStmt.run(workspaceId, vaultId);
|
|
242
|
+
this.deleteVaultStmt.run(workspaceId, vaultId);
|
|
243
|
+
return existing;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
createCredential(record) {
|
|
247
|
+
return withSqliteTransaction(this.db, () => {
|
|
248
|
+
const c = record.row;
|
|
249
|
+
this.requireSecrets().put(c.workspace_id, secretName(c.vault_id, c.id), record.token);
|
|
250
|
+
this.insertCredentialStmt.run(c.id, c.workspace_id, c.vault_id, c.type, c.display_name, JSON.stringify(c.metadata), c.auth.type, c.auth.mcp_server_url, c.auth.type === "mcp_oauth" ? c.auth.refresh?.token_endpoint ?? null : null, c.auth.type === "mcp_oauth" ? c.auth.refresh?.client_id ?? null : null, c.auth.type === "mcp_oauth" ? c.auth.refresh?.scope ?? null : null, c.auth.type === "mcp_oauth"
|
|
251
|
+
? c.auth.refresh?.token_endpoint_auth.type ?? null
|
|
252
|
+
: null, c.auth.type === "mcp_oauth" ? c.auth.expires_at ?? null : null, c.auth_version, record.nextRefreshAt ?? null, c.created_at, c.updated_at, c.archived_at);
|
|
253
|
+
return c;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
retrieveCredential(workspaceId, vaultId, credentialId) {
|
|
257
|
+
const row = this.retrieveCredentialActiveStmt.get(workspaceId, vaultId, credentialId);
|
|
258
|
+
return row ? deserializeCredential(row) : undefined;
|
|
259
|
+
}
|
|
260
|
+
retrieveCredentialAny(workspaceId, vaultId, credentialId) {
|
|
261
|
+
const row = this.retrieveCredentialAnyStmt.get(workspaceId, vaultId, credentialId);
|
|
262
|
+
return row ? deserializeCredential(row) : undefined;
|
|
263
|
+
}
|
|
264
|
+
listCredentials(workspaceId, vaultId, opts = {}) {
|
|
265
|
+
if (opts.page === "")
|
|
266
|
+
return { data: [], has_more: false, next_page: null };
|
|
267
|
+
const limit = normalizeLimit(opts.limit);
|
|
268
|
+
const rows = this.listCredentialStmt({
|
|
269
|
+
includeArchived: opts.includeArchived ?? false,
|
|
270
|
+
hasPage: opts.page !== undefined,
|
|
271
|
+
}).all(...selectCredentialListArgs(workspaceId, vaultId, limit + 1, opts.page));
|
|
272
|
+
const data = rows.slice(0, limit).map(deserializeCredential);
|
|
273
|
+
return {
|
|
274
|
+
data,
|
|
275
|
+
has_more: rows.length > limit,
|
|
276
|
+
next_page: rows.length > limit ? data[data.length - 1]?.id ?? null : null,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
updateCredential(workspaceId, vaultId, credentialId, updates, updatedAt, scheduling) {
|
|
280
|
+
const existing = this.retrieveCredential(workspaceId, vaultId, credentialId);
|
|
281
|
+
if (!existing)
|
|
282
|
+
return undefined;
|
|
283
|
+
return withSqliteTransaction(this.db, () => {
|
|
284
|
+
if (updates.auth !== undefined) {
|
|
285
|
+
this.putUpdatedSecret(workspaceId, vaultId, credentialId, existing, updates.auth);
|
|
286
|
+
}
|
|
287
|
+
const nextAuthVersion = updates.auth === undefined ? existing.auth_version : existing.auth_version + 1;
|
|
288
|
+
const nextExpiresAt = updates.auth?.type === "mcp_oauth" && updates.auth.expiresAt !== undefined
|
|
289
|
+
? updates.auth.expiresAt
|
|
290
|
+
: existing.auth.type === "mcp_oauth"
|
|
291
|
+
? existing.auth.expires_at ?? null
|
|
292
|
+
: null;
|
|
293
|
+
this.updateCredentialMetadataStmt.run(updates.displayName === undefined
|
|
294
|
+
? existing.display_name
|
|
295
|
+
: updates.displayName, JSON.stringify(updates.metadata ?? existing.metadata), nextExpiresAt, nextAuthVersion, updates.auth === undefined ? 0 : 1, updates.auth === undefined ? 0 : 1, updates.auth === undefined ? 0 : 1, scheduling?.nextRefreshAt ?? null, updates.auth === undefined ? 0 : 1, updatedAt, workspaceId, vaultId, credentialId);
|
|
296
|
+
return this.retrieveCredentialAny(workspaceId, vaultId, credentialId);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
archiveCredential(workspaceId, vaultId, credentialId, archivedAt) {
|
|
300
|
+
const existing = this.retrieveCredentialAny(workspaceId, vaultId, credentialId);
|
|
301
|
+
if (!existing)
|
|
302
|
+
return undefined;
|
|
303
|
+
return withSqliteTransaction(this.db, () => {
|
|
304
|
+
this.secrets?.delete(workspaceId, secretName(vaultId, credentialId));
|
|
305
|
+
this.archiveCredentialStmt.run(archivedAt, archivedAt, workspaceId, vaultId, credentialId);
|
|
306
|
+
return this.retrieveCredentialAny(workspaceId, vaultId, credentialId);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
deleteCredential(workspaceId, vaultId, credentialId) {
|
|
310
|
+
const existing = this.retrieveCredentialAny(workspaceId, vaultId, credentialId);
|
|
311
|
+
if (!existing)
|
|
312
|
+
return undefined;
|
|
313
|
+
return withSqliteTransaction(this.db, () => {
|
|
314
|
+
this.secrets?.delete(workspaceId, secretName(vaultId, credentialId));
|
|
315
|
+
this.deleteCredentialStmt.run(workspaceId, vaultId, credentialId);
|
|
316
|
+
return existing;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
countActiveCredentials(workspaceId, vaultId) {
|
|
320
|
+
return this.countActiveCredentialsStmt.get(workspaceId, vaultId).n;
|
|
321
|
+
}
|
|
322
|
+
resolveCredential(workspaceId, vaultIds, serverUrl) {
|
|
323
|
+
for (const vaultId of vaultIds) {
|
|
324
|
+
const row = this.resolveCredentialStmt.get(workspaceId, vaultId, serverUrl);
|
|
325
|
+
if (!row)
|
|
326
|
+
continue;
|
|
327
|
+
const token = this.secrets?.reveal(workspaceId, secretName(vaultId, row.id));
|
|
328
|
+
if (token === undefined)
|
|
329
|
+
return undefined;
|
|
330
|
+
const accessToken = row.auth_type === "mcp_oauth" ? oauthAccessToken(token) : token;
|
|
331
|
+
if (accessToken === undefined)
|
|
332
|
+
return undefined;
|
|
333
|
+
return {
|
|
334
|
+
vaultId,
|
|
335
|
+
credentialId: row.id,
|
|
336
|
+
authType: row.auth_type,
|
|
337
|
+
authVersion: row.auth_version,
|
|
338
|
+
...(row.expires_at === null ? {} : { expiresAt: row.expires_at }),
|
|
339
|
+
refreshStatus: row.refresh_status,
|
|
340
|
+
authHintAt: row.auth_hint_at,
|
|
341
|
+
updatedAt: row.updated_at,
|
|
342
|
+
token: accessToken,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
readCredentialRuntimeMetadata(workspaceId, vaultId, credentialId) {
|
|
348
|
+
const row = this.readCredentialRuntimeMetadataStmt.get(workspaceId, vaultId, credentialId);
|
|
349
|
+
return row === undefined ? undefined : runtimeMetadata(row);
|
|
350
|
+
}
|
|
351
|
+
persistAuthHint(input) {
|
|
352
|
+
return withSqliteTransaction(this.db, () => {
|
|
353
|
+
const result = this.persistAuthHintStmt.run(input.authHintAt, input.workspaceId, input.vaultId, input.credentialId, input.expectedAuthVersion);
|
|
354
|
+
const metadata = this.readCredentialRuntimeMetadata(input.workspaceId, input.vaultId, input.credentialId);
|
|
355
|
+
return result.changes === 1 && metadata !== undefined
|
|
356
|
+
? { status: "updated", metadata }
|
|
357
|
+
: { status: "stale", metadata };
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
readOauthRefreshState(workspaceId, vaultId, credentialId) {
|
|
361
|
+
return this.readOauthRefreshStateInternal(workspaceId, vaultId, credentialId);
|
|
362
|
+
}
|
|
363
|
+
persistOauthRefreshSuccess(input) {
|
|
364
|
+
return withSqliteTransaction(this.db, () => {
|
|
365
|
+
const current = this.readOauthRefreshStateInternal(input.workspaceId, input.vaultId, input.credentialId);
|
|
366
|
+
if (current === undefined)
|
|
367
|
+
return { status: "stale", state: undefined };
|
|
368
|
+
const result = this.persistOauthRefreshSuccessStmt.run(input.expiresAt === undefined ? current.expiresAt ?? null : input.expiresAt, input.scope === undefined ? current.refresh?.scope ?? null : input.scope, input.nextRefreshAt ?? null, input.updatedAt, input.workspaceId, input.vaultId, input.credentialId, input.expectedAuthVersion);
|
|
369
|
+
if (result.changes !== 1) {
|
|
370
|
+
return {
|
|
371
|
+
status: "stale",
|
|
372
|
+
state: this.readOauthRefreshStateInternal(input.workspaceId, input.vaultId, input.credentialId),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const nextRefreshToken = input.refreshToken ?? current.secrets.refreshToken;
|
|
376
|
+
const nextSecret = {
|
|
377
|
+
access_token: input.accessToken,
|
|
378
|
+
...(nextRefreshToken === undefined
|
|
379
|
+
? {}
|
|
380
|
+
: { refresh_token: nextRefreshToken }),
|
|
381
|
+
...(current.secrets.clientSecret === undefined
|
|
382
|
+
? {}
|
|
383
|
+
: { client_secret: current.secrets.clientSecret }),
|
|
384
|
+
};
|
|
385
|
+
this.requireSecrets().put(input.workspaceId, secretName(input.vaultId, input.credentialId), JSON.stringify(nextSecret));
|
|
386
|
+
return {
|
|
387
|
+
status: "updated",
|
|
388
|
+
state: this.readOauthRefreshStateInternal(input.workspaceId, input.vaultId, input.credentialId),
|
|
389
|
+
};
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
persistOauthRefreshFailure(input) {
|
|
393
|
+
return withSqliteTransaction(this.db, () => {
|
|
394
|
+
const result = this.persistOauthRefreshFailureStmt.run(input.status, input.refreshAttempts, input.nextRefreshAt, input.workspaceId, input.vaultId, input.credentialId, input.expectedAuthVersion);
|
|
395
|
+
return result.changes === 1
|
|
396
|
+
? {
|
|
397
|
+
status: "updated",
|
|
398
|
+
state: this.readOauthRefreshStateInternal(input.workspaceId, input.vaultId, input.credentialId),
|
|
399
|
+
}
|
|
400
|
+
: {
|
|
401
|
+
status: "stale",
|
|
402
|
+
state: this.readOauthRefreshStateInternal(input.workspaceId, input.vaultId, input.credentialId),
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
listDueRefreshes(now, limit = 50) {
|
|
407
|
+
const rows = this.listDueRefreshesStmt.all(now, limit);
|
|
408
|
+
return rows.map((row) => ({
|
|
409
|
+
workspaceId: row.workspace_id,
|
|
410
|
+
vaultId: row.vault_id,
|
|
411
|
+
credentialId: row.id,
|
|
412
|
+
authVersion: row.auth_version,
|
|
413
|
+
nextRefreshAt: row.next_refresh_at,
|
|
414
|
+
}));
|
|
415
|
+
}
|
|
416
|
+
nextDueRefreshAt(_now) {
|
|
417
|
+
const row = this.nextDueRefreshAtStmt.get();
|
|
418
|
+
return row?.next_refresh_at ?? null;
|
|
419
|
+
}
|
|
420
|
+
close() {
|
|
421
|
+
this.db.close();
|
|
422
|
+
}
|
|
423
|
+
requireSecrets() {
|
|
424
|
+
if (this.secrets === undefined) {
|
|
425
|
+
throw new Error("Secrets require a master key: set OMA_MASTER_KEY or OMA_MASTER_KEY_FILE on the deployment");
|
|
426
|
+
}
|
|
427
|
+
return this.secrets;
|
|
428
|
+
}
|
|
429
|
+
putUpdatedSecret(workspaceId, vaultId, credentialId, existing, auth) {
|
|
430
|
+
if (auth.type !== existing.auth.type) {
|
|
431
|
+
throw new Error("Credential auth type cannot be changed");
|
|
432
|
+
}
|
|
433
|
+
const name = secretName(vaultId, credentialId);
|
|
434
|
+
if (auth.type === "static_bearer") {
|
|
435
|
+
this.requireSecrets().put(workspaceId, name, auth.token);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const current = this.requireSecrets().reveal(workspaceId, name);
|
|
439
|
+
const parsed = current === undefined ? {} : parseOauthSecret(current);
|
|
440
|
+
const next = {
|
|
441
|
+
...parsed,
|
|
442
|
+
...(auth.accessToken === undefined ? {} : { access_token: auth.accessToken }),
|
|
443
|
+
...(auth.refreshToken === undefined
|
|
444
|
+
? {}
|
|
445
|
+
: { refresh_token: auth.refreshToken }),
|
|
446
|
+
};
|
|
447
|
+
this.requireSecrets().put(workspaceId, name, JSON.stringify(next));
|
|
448
|
+
}
|
|
449
|
+
credentialsForVault(workspaceId, vaultId) {
|
|
450
|
+
return this.credentialRowsByVaultStmt.all(workspaceId, vaultId).map(deserializeCredential);
|
|
451
|
+
}
|
|
452
|
+
readOauthRefreshStateInternal(workspaceId, vaultId, credentialId) {
|
|
453
|
+
const row = this.retrieveCredentialActiveStmt.get(workspaceId, vaultId, credentialId);
|
|
454
|
+
if (!row || row.auth_type !== "mcp_oauth")
|
|
455
|
+
return undefined;
|
|
456
|
+
const raw = this.secrets?.reveal(workspaceId, secretName(vaultId, credentialId));
|
|
457
|
+
const parsed = raw === undefined ? {} : parseOauthSecret(raw);
|
|
458
|
+
const auth = deserializeCredentialAuth(row);
|
|
459
|
+
if (auth.type !== "mcp_oauth")
|
|
460
|
+
return undefined;
|
|
461
|
+
return {
|
|
462
|
+
workspaceId,
|
|
463
|
+
vaultId,
|
|
464
|
+
credentialId,
|
|
465
|
+
authVersion: row.auth_version,
|
|
466
|
+
mcpServerUrl: row.mcp_server_url,
|
|
467
|
+
...(row.expires_at === null ? {} : { expiresAt: row.expires_at }),
|
|
468
|
+
...(auth.refresh === undefined
|
|
469
|
+
? {}
|
|
470
|
+
: {
|
|
471
|
+
refresh: {
|
|
472
|
+
tokenEndpoint: auth.refresh.token_endpoint,
|
|
473
|
+
clientId: auth.refresh.client_id,
|
|
474
|
+
...(auth.refresh.scope === undefined
|
|
475
|
+
? {}
|
|
476
|
+
: { scope: auth.refresh.scope }),
|
|
477
|
+
tokenEndpointAuth: auth.refresh.token_endpoint_auth,
|
|
478
|
+
},
|
|
479
|
+
}),
|
|
480
|
+
secrets: {
|
|
481
|
+
...(typeof parsed.access_token === "string"
|
|
482
|
+
? { accessToken: parsed.access_token }
|
|
483
|
+
: {}),
|
|
484
|
+
...(typeof parsed.refresh_token === "string"
|
|
485
|
+
? { refreshToken: parsed.refresh_token }
|
|
486
|
+
: {}),
|
|
487
|
+
...(typeof parsed.client_secret === "string"
|
|
488
|
+
? { clientSecret: parsed.client_secret }
|
|
489
|
+
: {}),
|
|
490
|
+
},
|
|
491
|
+
refreshStatus: row.refresh_status,
|
|
492
|
+
refreshAttempts: row.refresh_attempts,
|
|
493
|
+
nextRefreshAt: row.next_refresh_at,
|
|
494
|
+
authHintAt: row.auth_hint_at,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
listVaultStmt(opts) {
|
|
498
|
+
const key = JSON.stringify(opts);
|
|
499
|
+
const existing = this.listVaultStmts.get(key);
|
|
500
|
+
if (existing)
|
|
501
|
+
return existing;
|
|
502
|
+
const predicates = ["workspace_id = ?"];
|
|
503
|
+
if (!opts.includeArchived)
|
|
504
|
+
predicates.push("archived_at IS NULL");
|
|
505
|
+
if (opts.hasPage)
|
|
506
|
+
predicates.push("id < ?");
|
|
507
|
+
const stmt = this.db.prepare(`SELECT * FROM vaults
|
|
508
|
+
WHERE ${predicates.join(" AND ")}
|
|
509
|
+
ORDER BY id DESC
|
|
510
|
+
LIMIT ?`);
|
|
511
|
+
this.listVaultStmts.set(key, stmt);
|
|
512
|
+
return stmt;
|
|
513
|
+
}
|
|
514
|
+
listCredentialStmt(opts) {
|
|
515
|
+
const key = JSON.stringify(opts);
|
|
516
|
+
const existing = this.listCredentialStmts.get(key);
|
|
517
|
+
if (existing)
|
|
518
|
+
return existing;
|
|
519
|
+
const predicates = ["workspace_id = ?", "vault_id = ?"];
|
|
520
|
+
if (!opts.includeArchived)
|
|
521
|
+
predicates.push("archived_at IS NULL");
|
|
522
|
+
if (opts.hasPage)
|
|
523
|
+
predicates.push("id < ?");
|
|
524
|
+
const stmt = this.db.prepare(`SELECT * FROM vault_credentials
|
|
525
|
+
WHERE ${predicates.join(" AND ")}
|
|
526
|
+
ORDER BY id DESC
|
|
527
|
+
LIMIT ?`);
|
|
528
|
+
this.listCredentialStmts.set(key, stmt);
|
|
529
|
+
return stmt;
|
|
530
|
+
}
|
|
531
|
+
listAdminCredentialMetadataStmt(opts) {
|
|
532
|
+
const key = JSON.stringify(opts);
|
|
533
|
+
const existing = this.listAdminCredentialMetadataStmts.get(key);
|
|
534
|
+
if (existing)
|
|
535
|
+
return existing;
|
|
536
|
+
const predicates = ["c.workspace_id = ?"];
|
|
537
|
+
if (opts.hasPage)
|
|
538
|
+
predicates.push("c.id < ?");
|
|
539
|
+
const stmt = this.db.prepare(`SELECT c.id, c.vault_id, c.auth_type, c.token_endpoint, c.client_id,
|
|
540
|
+
c.token_endpoint_auth_type, c.auth_version, c.expires_at,
|
|
541
|
+
c.refresh_status, c.refresh_attempts, c.next_refresh_at, c.auth_hint_at,
|
|
542
|
+
c.mcp_server_url, c.display_name AS credential_display_name,
|
|
543
|
+
c.archived_at AS credential_archived_at,
|
|
544
|
+
v.display_name AS vault_display_name, v.archived_at AS vault_archived_at
|
|
545
|
+
FROM vault_credentials c
|
|
546
|
+
INNER JOIN vaults v ON v.workspace_id = c.workspace_id AND v.id = c.vault_id
|
|
547
|
+
WHERE ${predicates.join(" AND ")}
|
|
548
|
+
ORDER BY c.id DESC
|
|
549
|
+
LIMIT ?`);
|
|
550
|
+
this.listAdminCredentialMetadataStmts.set(key, stmt);
|
|
551
|
+
return stmt;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
export function vaultSecretName(vaultId, credentialId) {
|
|
555
|
+
return secretName(vaultId, credentialId);
|
|
556
|
+
}
|
|
557
|
+
function secretName(vaultId, credentialId) {
|
|
558
|
+
return `${VAULT_SECRET_PREFIX}${vaultId}/${credentialId}`;
|
|
559
|
+
}
|
|
560
|
+
function selectListArgs(workspaceId, limit, page) {
|
|
561
|
+
return page === undefined ? [workspaceId, limit] : [workspaceId, page, limit];
|
|
562
|
+
}
|
|
563
|
+
function selectCredentialListArgs(workspaceId, vaultId, limit, page) {
|
|
564
|
+
return page === undefined
|
|
565
|
+
? [workspaceId, vaultId, limit]
|
|
566
|
+
: [workspaceId, vaultId, page, limit];
|
|
567
|
+
}
|
|
568
|
+
function normalizeLimit(limit) {
|
|
569
|
+
if (limit === undefined)
|
|
570
|
+
return 20;
|
|
571
|
+
if (!Number.isSafeInteger(limit) || limit <= 0)
|
|
572
|
+
return 20;
|
|
573
|
+
return Math.min(limit, 100);
|
|
574
|
+
}
|
|
575
|
+
function deserializeVault(row) {
|
|
576
|
+
return {
|
|
577
|
+
id: row.id,
|
|
578
|
+
workspace_id: row.workspace_id,
|
|
579
|
+
type: "vault",
|
|
580
|
+
display_name: row.display_name,
|
|
581
|
+
metadata: JSON.parse(row.metadata),
|
|
582
|
+
created_at: row.created_at,
|
|
583
|
+
updated_at: row.updated_at,
|
|
584
|
+
archived_at: row.archived_at,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function deserializeCredential(row) {
|
|
588
|
+
const auth = deserializeCredentialAuth(row);
|
|
589
|
+
return {
|
|
590
|
+
id: row.id,
|
|
591
|
+
workspace_id: row.workspace_id,
|
|
592
|
+
vault_id: row.vault_id,
|
|
593
|
+
type: "vault_credential",
|
|
594
|
+
display_name: row.display_name,
|
|
595
|
+
metadata: JSON.parse(row.metadata),
|
|
596
|
+
auth,
|
|
597
|
+
auth_version: row.auth_version,
|
|
598
|
+
created_at: row.created_at,
|
|
599
|
+
updated_at: row.updated_at,
|
|
600
|
+
archived_at: row.archived_at,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function deserializeCredentialAuth(row) {
|
|
604
|
+
if (row.auth_type === "static_bearer") {
|
|
605
|
+
return {
|
|
606
|
+
type: "static_bearer",
|
|
607
|
+
mcp_server_url: row.mcp_server_url,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
type: "mcp_oauth",
|
|
612
|
+
mcp_server_url: row.mcp_server_url,
|
|
613
|
+
...(row.expires_at === null ? {} : { expires_at: row.expires_at }),
|
|
614
|
+
...(row.token_endpoint === null ||
|
|
615
|
+
row.client_id === null ||
|
|
616
|
+
row.token_endpoint_auth_type === null
|
|
617
|
+
? {}
|
|
618
|
+
: {
|
|
619
|
+
refresh: {
|
|
620
|
+
token_endpoint: row.token_endpoint,
|
|
621
|
+
client_id: row.client_id,
|
|
622
|
+
...(row.scope === null ? {} : { scope: row.scope }),
|
|
623
|
+
token_endpoint_auth: { type: row.token_endpoint_auth_type },
|
|
624
|
+
},
|
|
625
|
+
}),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function runtimeMetadata(row) {
|
|
629
|
+
return {
|
|
630
|
+
vaultId: row.vault_id,
|
|
631
|
+
credentialId: row.id,
|
|
632
|
+
authType: row.auth_type,
|
|
633
|
+
hasRefresh: row.auth_type === "mcp_oauth" &&
|
|
634
|
+
row.token_endpoint !== null &&
|
|
635
|
+
row.client_id !== null &&
|
|
636
|
+
row.token_endpoint_auth_type !== null,
|
|
637
|
+
authVersion: row.auth_version,
|
|
638
|
+
...(row.expires_at === null ? {} : { expiresAt: row.expires_at }),
|
|
639
|
+
refreshStatus: row.refresh_status,
|
|
640
|
+
authHintAt: row.auth_hint_at,
|
|
641
|
+
nextRefreshAt: row.next_refresh_at,
|
|
642
|
+
refreshAttempts: row.refresh_attempts,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function adminCredentialMetadata(row) {
|
|
646
|
+
return {
|
|
647
|
+
...runtimeMetadata(row),
|
|
648
|
+
vaultDisplayName: row.vault_display_name,
|
|
649
|
+
vaultArchivedAt: row.vault_archived_at,
|
|
650
|
+
credentialDisplayName: row.credential_display_name,
|
|
651
|
+
credentialArchivedAt: row.credential_archived_at,
|
|
652
|
+
mcpServerUrl: row.mcp_server_url,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function ensureVaultCredentialColumns(db) {
|
|
656
|
+
const columns = db.prepare("PRAGMA table_info(vault_credentials)").all();
|
|
657
|
+
const names = new Set(columns.map((column) => column.name));
|
|
658
|
+
for (const [name, definition] of [
|
|
659
|
+
["token_endpoint", "TEXT"],
|
|
660
|
+
["client_id", "TEXT"],
|
|
661
|
+
["scope", "TEXT"],
|
|
662
|
+
["token_endpoint_auth_type", "TEXT"],
|
|
663
|
+
["expires_at", "TEXT"],
|
|
664
|
+
["auth_version", "INTEGER NOT NULL DEFAULT 1"],
|
|
665
|
+
["refresh_status", "TEXT"],
|
|
666
|
+
["refresh_attempts", "INTEGER NOT NULL DEFAULT 0"],
|
|
667
|
+
["next_refresh_at", "TEXT"],
|
|
668
|
+
["auth_hint_at", "TEXT"],
|
|
669
|
+
]) {
|
|
670
|
+
if (!names.has(name)) {
|
|
671
|
+
db.exec(`ALTER TABLE vault_credentials ADD COLUMN ${name} ${definition}`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function oauthAccessToken(value) {
|
|
676
|
+
const parsed = parseOauthSecret(value);
|
|
677
|
+
return typeof parsed.access_token === "string" && parsed.access_token.length > 0
|
|
678
|
+
? parsed.access_token
|
|
679
|
+
: undefined;
|
|
680
|
+
}
|
|
681
|
+
function parseOauthSecret(value) {
|
|
682
|
+
try {
|
|
683
|
+
const parsed = JSON.parse(value);
|
|
684
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
685
|
+
? parsed
|
|
686
|
+
: {};
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
return {};
|
|
690
|
+
}
|
|
691
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|