@agent-deck/backend 1.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/dist/.tsbuildinfo +1 -0
- package/dist/cli-runtime.d.ts +4 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli-runtime.js +12 -0
- package/dist/cli-runtime.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-deck-context.d.ts +8 -0
- package/dist/lib/agent-deck-context.d.ts.map +1 -0
- package/dist/lib/agent-deck-context.js +48 -0
- package/dist/lib/agent-deck-context.js.map +1 -0
- package/dist/lib/bound-deck-scope.d.ts +15 -0
- package/dist/lib/bound-deck-scope.d.ts.map +1 -0
- package/dist/lib/bound-deck-scope.js +68 -0
- package/dist/lib/bound-deck-scope.js.map +1 -0
- package/dist/lib/client-scope.d.ts +14 -0
- package/dist/lib/client-scope.d.ts.map +1 -0
- package/dist/lib/client-scope.js +46 -0
- package/dist/lib/client-scope.js.map +1 -0
- package/dist/lib/paths.d.ts +2 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +24 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/version.d.ts +3 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +20 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/mcp-index.d.ts +2 -0
- package/dist/mcp-index.d.ts.map +1 -0
- package/dist/mcp-index.js +32 -0
- package/dist/mcp-index.js.map +1 -0
- package/dist/mcp-server.d.ts +25 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +1121 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-stdio.d.ts +2 -0
- package/dist/mcp-stdio.d.ts.map +1 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/models/database.d.ts +64 -0
- package/dist/models/database.d.ts.map +1 -0
- package/dist/models/database.js +965 -0
- package/dist/models/database.js.map +1 -0
- package/dist/playbooks/playbook-manager.d.ts +29 -0
- package/dist/playbooks/playbook-manager.d.ts.map +1 -0
- package/dist/playbooks/playbook-manager.js +198 -0
- package/dist/playbooks/playbook-manager.js.map +1 -0
- package/dist/playbooks/playbook-parser.d.ts +8 -0
- package/dist/playbooks/playbook-parser.d.ts.map +1 -0
- package/dist/playbooks/playbook-parser.js +76 -0
- package/dist/playbooks/playbook-parser.js.map +1 -0
- package/dist/playbooks/playbook-service.d.ts +9 -0
- package/dist/playbooks/playbook-service.d.ts.map +1 -0
- package/dist/playbooks/playbook-service.js +107 -0
- package/dist/playbooks/playbook-service.js.map +1 -0
- package/dist/routes/collection.d.ts +3 -0
- package/dist/routes/collection.d.ts.map +1 -0
- package/dist/routes/collection.js +34 -0
- package/dist/routes/collection.js.map +1 -0
- package/dist/routes/credentials.d.ts +3 -0
- package/dist/routes/credentials.d.ts.map +1 -0
- package/dist/routes/credentials.js +241 -0
- package/dist/routes/credentials.js.map +1 -0
- package/dist/routes/decks.d.ts +3 -0
- package/dist/routes/decks.d.ts.map +1 -0
- package/dist/routes/decks.js +430 -0
- package/dist/routes/decks.js.map +1 -0
- package/dist/routes/local-mcp.d.ts +3 -0
- package/dist/routes/local-mcp.d.ts.map +1 -0
- package/dist/routes/local-mcp.js +189 -0
- package/dist/routes/local-mcp.js.map +1 -0
- package/dist/routes/mcp.d.ts +3 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +170 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/oauth.d.ts +3 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +242 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/playbooks.d.ts +5 -0
- package/dist/routes/playbooks.d.ts.map +1 -0
- package/dist/routes/playbooks.js +220 -0
- package/dist/routes/playbooks.js.map +1 -0
- package/dist/routes/scope.d.ts +3 -0
- package/dist/routes/scope.d.ts.map +1 -0
- package/dist/routes/scope.js +107 -0
- package/dist/routes/scope.js.map +1 -0
- package/dist/routes/services.d.ts +3 -0
- package/dist/routes/services.d.ts.map +1 -0
- package/dist/routes/services.js +281 -0
- package/dist/routes/services.js.map +1 -0
- package/dist/routes/websocket.d.ts +11 -0
- package/dist/routes/websocket.d.ts.map +1 -0
- package/dist/routes/websocket.js +154 -0
- package/dist/routes/websocket.js.map +1 -0
- package/dist/scope/repo-deck.d.ts +10 -0
- package/dist/scope/repo-deck.d.ts.map +1 -0
- package/dist/scope/repo-deck.js +63 -0
- package/dist/scope/repo-deck.js.map +1 -0
- package/dist/server/index.d.ts +24 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +111 -0
- package/dist/server/index.js.map +1 -0
- package/dist/services/collection-warning-service.d.ts +18 -0
- package/dist/services/collection-warning-service.d.ts.map +1 -0
- package/dist/services/collection-warning-service.js +129 -0
- package/dist/services/collection-warning-service.js.map +1 -0
- package/dist/services/config-manager.d.ts +32 -0
- package/dist/services/config-manager.d.ts.map +1 -0
- package/dist/services/config-manager.js +119 -0
- package/dist/services/config-manager.js.map +1 -0
- package/dist/services/icon-resolver.d.ts +20 -0
- package/dist/services/icon-resolver.d.ts.map +1 -0
- package/dist/services/icon-resolver.js +224 -0
- package/dist/services/icon-resolver.js.map +1 -0
- package/dist/services/local-mcp-server-manager.d.ts +51 -0
- package/dist/services/local-mcp-server-manager.d.ts.map +1 -0
- package/dist/services/local-mcp-server-manager.js +246 -0
- package/dist/services/local-mcp-server-manager.js.map +1 -0
- package/dist/services/mcp-client-manager.d.ts +22 -0
- package/dist/services/mcp-client-manager.d.ts.map +1 -0
- package/dist/services/mcp-client-manager.js +257 -0
- package/dist/services/mcp-client-manager.js.map +1 -0
- package/dist/services/mcp-discovery-service.d.ts +31 -0
- package/dist/services/mcp-discovery-service.d.ts.map +1 -0
- package/dist/services/mcp-discovery-service.js +164 -0
- package/dist/services/mcp-discovery-service.js.map +1 -0
- package/dist/services/oauth-manager.d.ts +25 -0
- package/dist/services/oauth-manager.d.ts.map +1 -0
- package/dist/services/oauth-manager.js +365 -0
- package/dist/services/oauth-manager.js.map +1 -0
- package/dist/services/service-manager.d.ts +61 -0
- package/dist/services/service-manager.d.ts.map +1 -0
- package/dist/services/service-manager.js +447 -0
- package/dist/services/service-manager.js.map +1 -0
- package/dist/test-local-mcp-e2e.d.ts +3 -0
- package/dist/test-local-mcp-e2e.d.ts.map +1 -0
- package/dist/test-local-mcp-e2e.js +104 -0
- package/dist/test-local-mcp-e2e.js.map +1 -0
- package/dist/test-local-mcp.d.ts +3 -0
- package/dist/test-local-mcp.d.ts.map +1 -0
- package/dist/test-local-mcp.js +54 -0
- package/dist/test-local-mcp.js.map +1 -0
- package/dist/vault/credential-manager.d.ts +45 -0
- package/dist/vault/credential-manager.d.ts.map +1 -0
- package/dist/vault/credential-manager.js +237 -0
- package/dist/vault/credential-manager.js.map +1 -0
- package/dist/vault/index.d.ts +4 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +20 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/secret-store.d.ts +36 -0
- package/dist/vault/secret-store.d.ts.map +1 -0
- package/dist/vault/secret-store.js +207 -0
- package/dist/vault/secret-store.js.map +1 -0
- package/dist/vault/yaml-sync.d.ts +8 -0
- package/dist/vault/yaml-sync.d.ts.map +1 -0
- package/dist/vault/yaml-sync.js +60 -0
- package/dist/vault/yaml-sync.js.map +1 -0
- package/package.json +64 -0
- package/static-ui/assets/AgentDeckLogo2-z3pVqJJ3.png +0 -0
- package/static-ui/assets/index-BnA3AsqY.css +1 -0
- package/static-ui/assets/index-D1IuraRt.js +334 -0
- package/static-ui/favicon.png +0 -0
- package/static-ui/index.html +17 -0
|
@@ -0,0 +1,965 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DatabaseManager = void 0;
|
|
7
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
|
+
const shared_1 = require("@agent-deck/shared");
|
|
9
|
+
class DatabaseManager {
|
|
10
|
+
db;
|
|
11
|
+
constructor(dbPath = 'agent_deck.db') {
|
|
12
|
+
this.db = new better_sqlite3_1.default(dbPath);
|
|
13
|
+
this.initializeTables();
|
|
14
|
+
this.migrate();
|
|
15
|
+
}
|
|
16
|
+
migrate() {
|
|
17
|
+
const serviceColumns = this.db.prepare('PRAGMA table_info(services)').all();
|
|
18
|
+
if (!serviceColumns.some((column) => column.name === 'credential_id')) {
|
|
19
|
+
this.db.exec('ALTER TABLE services ADD COLUMN credential_id TEXT');
|
|
20
|
+
}
|
|
21
|
+
if (!serviceColumns.some((column) => column.name === 'icon_url')) {
|
|
22
|
+
this.db.exec('ALTER TABLE services ADD COLUMN icon_url TEXT');
|
|
23
|
+
}
|
|
24
|
+
if (!serviceColumns.some((column) => column.name === 'disabled_tools')) {
|
|
25
|
+
this.db.exec("ALTER TABLE services ADD COLUMN disabled_tools TEXT NOT NULL DEFAULT '[]'");
|
|
26
|
+
}
|
|
27
|
+
const credentialColumns = this.db.prepare('PRAGMA table_info(credentials)').all();
|
|
28
|
+
if (!credentialColumns.some((column) => column.name === 'docs_url')) {
|
|
29
|
+
this.db.exec('ALTER TABLE credentials ADD COLUMN docs_url TEXT');
|
|
30
|
+
}
|
|
31
|
+
if (!credentialColumns.some((column) => column.name === 'icon_url')) {
|
|
32
|
+
this.db.exec('ALTER TABLE credentials ADD COLUMN icon_url TEXT');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
initializeTables() {
|
|
36
|
+
// Services table
|
|
37
|
+
this.db.exec(`
|
|
38
|
+
CREATE TABLE IF NOT EXISTS services (
|
|
39
|
+
id TEXT PRIMARY KEY,
|
|
40
|
+
name TEXT NOT NULL UNIQUE,
|
|
41
|
+
type TEXT NOT NULL CHECK (type IN ('mcp', 'a2a', 'local-mcp')),
|
|
42
|
+
url TEXT NOT NULL,
|
|
43
|
+
health TEXT NOT NULL DEFAULT 'unknown',
|
|
44
|
+
description TEXT,
|
|
45
|
+
card_color TEXT NOT NULL DEFAULT '#7ed4da',
|
|
46
|
+
is_connected BOOLEAN NOT NULL DEFAULT 0,
|
|
47
|
+
last_ping TEXT,
|
|
48
|
+
registered_at TEXT NOT NULL,
|
|
49
|
+
updated_at TEXT NOT NULL,
|
|
50
|
+
headers TEXT,
|
|
51
|
+
|
|
52
|
+
-- OAuth fields
|
|
53
|
+
oauth_client_id TEXT,
|
|
54
|
+
oauth_client_secret TEXT,
|
|
55
|
+
oauth_authorization_url TEXT,
|
|
56
|
+
oauth_token_url TEXT,
|
|
57
|
+
oauth_redirect_uri TEXT,
|
|
58
|
+
oauth_scope TEXT,
|
|
59
|
+
oauth_access_token TEXT,
|
|
60
|
+
oauth_refresh_token TEXT,
|
|
61
|
+
oauth_token_expires_at TEXT,
|
|
62
|
+
oauth_state TEXT,
|
|
63
|
+
|
|
64
|
+
-- Local MCP server fields
|
|
65
|
+
local_command TEXT,
|
|
66
|
+
local_args TEXT,
|
|
67
|
+
local_working_dir TEXT,
|
|
68
|
+
local_env TEXT
|
|
69
|
+
)
|
|
70
|
+
`);
|
|
71
|
+
// Decks table
|
|
72
|
+
this.db.exec(`
|
|
73
|
+
CREATE TABLE IF NOT EXISTS decks (
|
|
74
|
+
id TEXT PRIMARY KEY,
|
|
75
|
+
name TEXT NOT NULL,
|
|
76
|
+
description TEXT,
|
|
77
|
+
is_active BOOLEAN NOT NULL DEFAULT 0,
|
|
78
|
+
created_at TEXT NOT NULL,
|
|
79
|
+
updated_at TEXT NOT NULL
|
|
80
|
+
)
|
|
81
|
+
`);
|
|
82
|
+
// Deck services junction table
|
|
83
|
+
this.db.exec(`
|
|
84
|
+
CREATE TABLE IF NOT EXISTS deck_services (
|
|
85
|
+
deck_id TEXT NOT NULL,
|
|
86
|
+
service_id TEXT NOT NULL,
|
|
87
|
+
position INTEGER NOT NULL,
|
|
88
|
+
FOREIGN KEY (deck_id) REFERENCES decks (id) ON DELETE CASCADE,
|
|
89
|
+
FOREIGN KEY (service_id) REFERENCES services (id) ON DELETE CASCADE,
|
|
90
|
+
PRIMARY KEY (deck_id, service_id)
|
|
91
|
+
)
|
|
92
|
+
`);
|
|
93
|
+
// Credentials table (metadata only — secrets in Keychain)
|
|
94
|
+
this.db.exec(`
|
|
95
|
+
CREATE TABLE IF NOT EXISTS credentials (
|
|
96
|
+
id TEXT PRIMARY KEY,
|
|
97
|
+
label TEXT NOT NULL,
|
|
98
|
+
scheme TEXT NOT NULL CHECK (scheme IN ('bearer', 'header', 'http_basic_user')),
|
|
99
|
+
header_name TEXT,
|
|
100
|
+
env_name TEXT NOT NULL,
|
|
101
|
+
keychain_account TEXT NOT NULL,
|
|
102
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
103
|
+
created_at TEXT NOT NULL,
|
|
104
|
+
updated_at TEXT NOT NULL
|
|
105
|
+
)
|
|
106
|
+
`);
|
|
107
|
+
// Deck credentials junction table
|
|
108
|
+
this.db.exec(`
|
|
109
|
+
CREATE TABLE IF NOT EXISTS deck_credentials (
|
|
110
|
+
deck_id TEXT NOT NULL,
|
|
111
|
+
credential_id TEXT NOT NULL,
|
|
112
|
+
position INTEGER NOT NULL,
|
|
113
|
+
FOREIGN KEY (deck_id) REFERENCES decks (id) ON DELETE CASCADE,
|
|
114
|
+
FOREIGN KEY (credential_id) REFERENCES credentials (id) ON DELETE CASCADE,
|
|
115
|
+
PRIMARY KEY (deck_id, credential_id)
|
|
116
|
+
)
|
|
117
|
+
`);
|
|
118
|
+
this.db.exec(`
|
|
119
|
+
CREATE TABLE IF NOT EXISTS playbooks (
|
|
120
|
+
id TEXT PRIMARY KEY,
|
|
121
|
+
title TEXT NOT NULL UNIQUE,
|
|
122
|
+
body TEXT NOT NULL DEFAULT '',
|
|
123
|
+
triggers TEXT NOT NULL DEFAULT '[]',
|
|
124
|
+
depends_on_credentials TEXT NOT NULL DEFAULT '[]',
|
|
125
|
+
depends_on_services TEXT NOT NULL DEFAULT '[]',
|
|
126
|
+
exec_command TEXT,
|
|
127
|
+
skill_path TEXT,
|
|
128
|
+
created_at TEXT NOT NULL,
|
|
129
|
+
updated_at TEXT NOT NULL
|
|
130
|
+
)
|
|
131
|
+
`);
|
|
132
|
+
this.db.exec(`
|
|
133
|
+
CREATE TABLE IF NOT EXISTS deck_playbooks (
|
|
134
|
+
deck_id TEXT NOT NULL,
|
|
135
|
+
playbook_id TEXT NOT NULL,
|
|
136
|
+
position INTEGER NOT NULL,
|
|
137
|
+
FOREIGN KEY (deck_id) REFERENCES decks (id) ON DELETE CASCADE,
|
|
138
|
+
FOREIGN KEY (playbook_id) REFERENCES playbooks (id) ON DELETE CASCADE,
|
|
139
|
+
PRIMARY KEY (deck_id, playbook_id)
|
|
140
|
+
)
|
|
141
|
+
`);
|
|
142
|
+
// Exec audit log (credential ids only, never secret values)
|
|
143
|
+
this.db.exec(`
|
|
144
|
+
CREATE TABLE IF NOT EXISTS exec_runs (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
deck_id TEXT,
|
|
147
|
+
manifest_path TEXT,
|
|
148
|
+
command TEXT NOT NULL,
|
|
149
|
+
exit_code INTEGER,
|
|
150
|
+
started_at TEXT NOT NULL,
|
|
151
|
+
finished_at TEXT,
|
|
152
|
+
FOREIGN KEY (deck_id) REFERENCES decks (id) ON DELETE SET NULL
|
|
153
|
+
)
|
|
154
|
+
`);
|
|
155
|
+
this.db.exec(`
|
|
156
|
+
CREATE TABLE IF NOT EXISTS exec_run_credentials (
|
|
157
|
+
exec_run_id TEXT NOT NULL,
|
|
158
|
+
credential_id TEXT NOT NULL,
|
|
159
|
+
FOREIGN KEY (exec_run_id) REFERENCES exec_runs (id) ON DELETE CASCADE,
|
|
160
|
+
FOREIGN KEY (credential_id) REFERENCES credentials (id) ON DELETE CASCADE,
|
|
161
|
+
PRIMARY KEY (exec_run_id, credential_id)
|
|
162
|
+
)
|
|
163
|
+
`);
|
|
164
|
+
// Create indexes for better performance
|
|
165
|
+
this.db.exec(`
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_services_type ON services(type);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_services_connected ON services(is_connected);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_decks_active ON decks(is_active);
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_deck_services_position ON deck_services(position);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS idx_credentials_env_name ON credentials(env_name);
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_deck_credentials_position ON deck_credentials(position);
|
|
172
|
+
CREATE INDEX IF NOT EXISTS idx_deck_playbooks_position ON deck_playbooks(position);
|
|
173
|
+
CREATE INDEX IF NOT EXISTS idx_exec_runs_started_at ON exec_runs(started_at);
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
mapCredentialRow(row) {
|
|
177
|
+
return {
|
|
178
|
+
id: row.id,
|
|
179
|
+
label: row.label,
|
|
180
|
+
scheme: row.scheme,
|
|
181
|
+
headerName: row.header_name ?? undefined,
|
|
182
|
+
envName: row.env_name,
|
|
183
|
+
keychainAccount: row.keychain_account,
|
|
184
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
185
|
+
docsUrl: row.docs_url ?? undefined,
|
|
186
|
+
iconUrl: row.icon_url ?? undefined,
|
|
187
|
+
hasSecret: false,
|
|
188
|
+
createdAt: row.created_at,
|
|
189
|
+
updatedAt: row.updated_at,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async getDeckCredentialsForDeck(deckId) {
|
|
193
|
+
const stmt = this.db.prepare(`
|
|
194
|
+
SELECT c.*, dc.position
|
|
195
|
+
FROM deck_credentials dc
|
|
196
|
+
JOIN credentials c ON c.id = dc.credential_id
|
|
197
|
+
WHERE dc.deck_id = ?
|
|
198
|
+
ORDER BY dc.position ASC
|
|
199
|
+
`);
|
|
200
|
+
const rows = stmt.all(deckId);
|
|
201
|
+
return rows.map((row) => this.mapCredentialRow(row));
|
|
202
|
+
}
|
|
203
|
+
// Service operations
|
|
204
|
+
async createService(input) {
|
|
205
|
+
const now = new Date().toISOString();
|
|
206
|
+
const service = {
|
|
207
|
+
id: (0, shared_1.generateId)(),
|
|
208
|
+
...input,
|
|
209
|
+
health: 'unknown',
|
|
210
|
+
cardColor: input.cardColor || '#7ed4da',
|
|
211
|
+
isConnected: false,
|
|
212
|
+
lastPing: undefined,
|
|
213
|
+
registeredAt: now,
|
|
214
|
+
updatedAt: now,
|
|
215
|
+
headers: input.headers,
|
|
216
|
+
oauthClientId: input.oauthClientId,
|
|
217
|
+
oauthClientSecret: input.oauthClientSecret,
|
|
218
|
+
oauthAuthorizationUrl: input.oauthAuthorizationUrl,
|
|
219
|
+
oauthTokenUrl: input.oauthTokenUrl,
|
|
220
|
+
oauthRedirectUri: input.oauthRedirectUri,
|
|
221
|
+
oauthScope: input.oauthScope,
|
|
222
|
+
oauthAccessToken: undefined,
|
|
223
|
+
oauthRefreshToken: undefined,
|
|
224
|
+
oauthTokenExpiresAt: undefined,
|
|
225
|
+
oauthState: undefined,
|
|
226
|
+
localCommand: input.localCommand,
|
|
227
|
+
localArgs: input.localArgs,
|
|
228
|
+
localWorkingDir: input.localWorkingDir,
|
|
229
|
+
localEnv: input.localEnv,
|
|
230
|
+
credentialId: input.credentialId,
|
|
231
|
+
iconUrl: input.iconUrl,
|
|
232
|
+
disabledToolNames: [],
|
|
233
|
+
};
|
|
234
|
+
const stmt = this.db.prepare(`
|
|
235
|
+
INSERT INTO services (
|
|
236
|
+
id, name, type, url, health, description, card_color, is_connected,
|
|
237
|
+
registered_at, updated_at, headers, credential_id, icon_url,
|
|
238
|
+
oauth_client_id, oauth_client_secret, oauth_authorization_url,
|
|
239
|
+
oauth_token_url, oauth_redirect_uri, oauth_scope,
|
|
240
|
+
local_command, local_args, local_working_dir, local_env
|
|
241
|
+
) VALUES (
|
|
242
|
+
@id, @name, @type, @url, @health, @description, @card_color, @is_connected,
|
|
243
|
+
@registered_at, @updated_at, @headers, @credential_id, @icon_url,
|
|
244
|
+
@oauth_client_id, @oauth_client_secret, @oauth_authorization_url,
|
|
245
|
+
@oauth_token_url, @oauth_redirect_uri, @oauth_scope,
|
|
246
|
+
@local_command, @local_args, @local_working_dir, @local_env
|
|
247
|
+
)
|
|
248
|
+
`);
|
|
249
|
+
stmt.run({
|
|
250
|
+
id: service.id,
|
|
251
|
+
name: service.name,
|
|
252
|
+
type: service.type,
|
|
253
|
+
url: service.url,
|
|
254
|
+
health: service.health,
|
|
255
|
+
description: service.description,
|
|
256
|
+
card_color: service.cardColor,
|
|
257
|
+
is_connected: service.isConnected ? 1 : 0,
|
|
258
|
+
registered_at: service.registeredAt,
|
|
259
|
+
updated_at: service.updatedAt,
|
|
260
|
+
headers: service.headers ? JSON.stringify(service.headers) : null,
|
|
261
|
+
credential_id: service.credentialId ?? null,
|
|
262
|
+
icon_url: service.iconUrl ?? null,
|
|
263
|
+
oauth_client_id: service.oauthClientId,
|
|
264
|
+
oauth_client_secret: service.oauthClientSecret,
|
|
265
|
+
oauth_authorization_url: service.oauthAuthorizationUrl,
|
|
266
|
+
oauth_token_url: service.oauthTokenUrl,
|
|
267
|
+
oauth_redirect_uri: service.oauthRedirectUri,
|
|
268
|
+
oauth_scope: service.oauthScope,
|
|
269
|
+
local_command: service.localCommand,
|
|
270
|
+
local_args: service.localArgs ? JSON.stringify(service.localArgs) : null,
|
|
271
|
+
local_working_dir: service.localWorkingDir,
|
|
272
|
+
local_env: service.localEnv ? JSON.stringify(service.localEnv) : null,
|
|
273
|
+
});
|
|
274
|
+
return service;
|
|
275
|
+
}
|
|
276
|
+
mapServiceRow(row) {
|
|
277
|
+
return {
|
|
278
|
+
id: row.id,
|
|
279
|
+
name: row.name,
|
|
280
|
+
type: row.type,
|
|
281
|
+
url: row.url,
|
|
282
|
+
health: row.health,
|
|
283
|
+
description: row.description,
|
|
284
|
+
cardColor: row.card_color,
|
|
285
|
+
isConnected: Boolean(row.is_connected),
|
|
286
|
+
lastPing: row.last_ping,
|
|
287
|
+
registeredAt: row.registered_at,
|
|
288
|
+
updatedAt: row.updated_at,
|
|
289
|
+
headers: row.headers ? JSON.parse(row.headers) : null,
|
|
290
|
+
oauthClientId: row.oauth_client_id,
|
|
291
|
+
oauthClientSecret: row.oauth_client_secret,
|
|
292
|
+
oauthAuthorizationUrl: row.oauth_authorization_url,
|
|
293
|
+
oauthTokenUrl: row.oauth_token_url,
|
|
294
|
+
oauthRedirectUri: row.oauth_redirect_uri,
|
|
295
|
+
oauthScope: row.oauth_scope,
|
|
296
|
+
oauthAccessToken: row.oauth_access_token,
|
|
297
|
+
oauthRefreshToken: row.oauth_refresh_token,
|
|
298
|
+
oauthTokenExpiresAt: row.oauth_token_expires_at,
|
|
299
|
+
oauthState: row.oauth_state,
|
|
300
|
+
localCommand: row.local_command,
|
|
301
|
+
localArgs: row.local_args ? JSON.parse(row.local_args) : null,
|
|
302
|
+
localWorkingDir: row.local_working_dir,
|
|
303
|
+
localEnv: row.local_env ? JSON.parse(row.local_env) : null,
|
|
304
|
+
credentialId: row.credential_id ?? undefined,
|
|
305
|
+
iconUrl: row.icon_url ?? undefined,
|
|
306
|
+
disabledToolNames: row.disabled_tools ? JSON.parse(row.disabled_tools) : [],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async getService(id) {
|
|
310
|
+
const stmt = this.db.prepare('SELECT * FROM services WHERE id = ?');
|
|
311
|
+
const row = stmt.get(id);
|
|
312
|
+
if (!row)
|
|
313
|
+
return null;
|
|
314
|
+
return this.mapServiceRow(row);
|
|
315
|
+
}
|
|
316
|
+
async getAllServices() {
|
|
317
|
+
const stmt = this.db.prepare('SELECT * FROM services ORDER BY registered_at DESC');
|
|
318
|
+
const rows = stmt.all();
|
|
319
|
+
return rows.map((row) => this.mapServiceRow(row));
|
|
320
|
+
}
|
|
321
|
+
async updateService(id, input) {
|
|
322
|
+
const existing = await this.getService(id);
|
|
323
|
+
if (!existing)
|
|
324
|
+
return null;
|
|
325
|
+
const updated = {
|
|
326
|
+
...existing,
|
|
327
|
+
...input,
|
|
328
|
+
updatedAt: new Date().toISOString(),
|
|
329
|
+
};
|
|
330
|
+
const stmt = this.db.prepare(`
|
|
331
|
+
UPDATE services SET
|
|
332
|
+
name = @name, description = @description, card_color = @card_color,
|
|
333
|
+
updated_at = @updated_at, headers = @headers, credential_id = @credential_id,
|
|
334
|
+
icon_url = @icon_url,
|
|
335
|
+
oauth_client_id = @oauth_client_id, oauth_client_secret = @oauth_client_secret,
|
|
336
|
+
oauth_authorization_url = @oauth_authorization_url, oauth_token_url = @oauth_token_url,
|
|
337
|
+
oauth_redirect_uri = @oauth_redirect_uri, oauth_scope = @oauth_scope,
|
|
338
|
+
local_command = @local_command, local_args = @local_args,
|
|
339
|
+
local_working_dir = @local_working_dir, local_env = @local_env
|
|
340
|
+
WHERE id = @id
|
|
341
|
+
`);
|
|
342
|
+
stmt.run({
|
|
343
|
+
id: updated.id,
|
|
344
|
+
name: updated.name,
|
|
345
|
+
description: updated.description,
|
|
346
|
+
card_color: updated.cardColor,
|
|
347
|
+
updated_at: updated.updatedAt,
|
|
348
|
+
headers: updated.headers ? JSON.stringify(updated.headers) : null,
|
|
349
|
+
credential_id: updated.credentialId ?? null,
|
|
350
|
+
icon_url: updated.iconUrl ?? null,
|
|
351
|
+
oauth_client_id: updated.oauthClientId,
|
|
352
|
+
oauth_client_secret: updated.oauthClientSecret,
|
|
353
|
+
oauth_authorization_url: updated.oauthAuthorizationUrl,
|
|
354
|
+
oauth_token_url: updated.oauthTokenUrl,
|
|
355
|
+
oauth_redirect_uri: updated.oauthRedirectUri,
|
|
356
|
+
oauth_scope: updated.oauthScope,
|
|
357
|
+
local_command: updated.localCommand,
|
|
358
|
+
local_args: updated.localArgs ? JSON.stringify(updated.localArgs) : null,
|
|
359
|
+
local_working_dir: updated.localWorkingDir,
|
|
360
|
+
local_env: updated.localEnv ? JSON.stringify(updated.localEnv) : null,
|
|
361
|
+
});
|
|
362
|
+
return updated;
|
|
363
|
+
}
|
|
364
|
+
async updateServiceDisabledTools(id, disabledTools) {
|
|
365
|
+
const existing = await this.getService(id);
|
|
366
|
+
if (!existing) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
const stmt = this.db.prepare(`
|
|
370
|
+
UPDATE services SET
|
|
371
|
+
disabled_tools = @disabled_tools,
|
|
372
|
+
updated_at = @updated_at
|
|
373
|
+
WHERE id = @id
|
|
374
|
+
`);
|
|
375
|
+
stmt.run({
|
|
376
|
+
id,
|
|
377
|
+
disabled_tools: JSON.stringify(disabledTools),
|
|
378
|
+
updated_at: new Date().toISOString(),
|
|
379
|
+
});
|
|
380
|
+
return this.getService(id);
|
|
381
|
+
}
|
|
382
|
+
async deleteService(id) {
|
|
383
|
+
const stmt = this.db.prepare('DELETE FROM services WHERE id = ?');
|
|
384
|
+
const result = stmt.run(id);
|
|
385
|
+
return result.changes > 0;
|
|
386
|
+
}
|
|
387
|
+
async updateServiceStatus(id, isConnected, health, lastPing) {
|
|
388
|
+
const stmt = this.db.prepare(`
|
|
389
|
+
UPDATE services SET
|
|
390
|
+
is_connected = @is_connected,
|
|
391
|
+
health = @health,
|
|
392
|
+
last_ping = @last_ping,
|
|
393
|
+
updated_at = @updated_at
|
|
394
|
+
WHERE id = @id
|
|
395
|
+
`);
|
|
396
|
+
stmt.run({
|
|
397
|
+
id,
|
|
398
|
+
is_connected: isConnected ? 1 : 0,
|
|
399
|
+
health,
|
|
400
|
+
last_ping: lastPing,
|
|
401
|
+
updated_at: new Date().toISOString(),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async updateOAuthTokens(id, accessToken, refreshToken, expiresAt) {
|
|
405
|
+
// Create headers with Authorization Bearer token
|
|
406
|
+
const headers = {
|
|
407
|
+
Authorization: `Bearer ${accessToken}`
|
|
408
|
+
};
|
|
409
|
+
const stmt = this.db.prepare(`
|
|
410
|
+
UPDATE services SET
|
|
411
|
+
oauth_access_token = @oauth_access_token,
|
|
412
|
+
oauth_refresh_token = @oauth_refresh_token,
|
|
413
|
+
oauth_token_expires_at = @oauth_token_expires_at,
|
|
414
|
+
headers = @headers,
|
|
415
|
+
updated_at = @updated_at
|
|
416
|
+
WHERE id = @id
|
|
417
|
+
`);
|
|
418
|
+
stmt.run({
|
|
419
|
+
id,
|
|
420
|
+
oauth_access_token: accessToken,
|
|
421
|
+
oauth_refresh_token: refreshToken,
|
|
422
|
+
oauth_token_expires_at: expiresAt,
|
|
423
|
+
headers: JSON.stringify(headers),
|
|
424
|
+
updated_at: new Date().toISOString(),
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// Deck operations
|
|
428
|
+
async createDeck(input) {
|
|
429
|
+
const now = new Date().toISOString();
|
|
430
|
+
const deck = {
|
|
431
|
+
id: (0, shared_1.generateId)(),
|
|
432
|
+
name: input.name,
|
|
433
|
+
description: input.description,
|
|
434
|
+
isActive: input.isActive || false,
|
|
435
|
+
services: [],
|
|
436
|
+
credentials: [],
|
|
437
|
+
playbooks: [],
|
|
438
|
+
createdAt: now,
|
|
439
|
+
updatedAt: now,
|
|
440
|
+
};
|
|
441
|
+
const stmt = this.db.prepare(`
|
|
442
|
+
INSERT INTO decks (id, name, description, is_active, created_at, updated_at)
|
|
443
|
+
VALUES (@id, @name, @description, @is_active, @created_at, @updated_at)
|
|
444
|
+
`);
|
|
445
|
+
stmt.run({
|
|
446
|
+
id: deck.id,
|
|
447
|
+
name: deck.name,
|
|
448
|
+
description: deck.description,
|
|
449
|
+
is_active: deck.isActive ? 1 : 0,
|
|
450
|
+
created_at: deck.createdAt,
|
|
451
|
+
updated_at: deck.updatedAt,
|
|
452
|
+
});
|
|
453
|
+
return deck;
|
|
454
|
+
}
|
|
455
|
+
async getDeck(id) {
|
|
456
|
+
const stmt = this.db.prepare('SELECT * FROM decks WHERE id = ?');
|
|
457
|
+
const row = stmt.get(id);
|
|
458
|
+
if (!row)
|
|
459
|
+
return null;
|
|
460
|
+
const deckServices = await this.getDeckServices(id);
|
|
461
|
+
const services = [];
|
|
462
|
+
for (const deckService of deckServices) {
|
|
463
|
+
const service = await this.getService(deckService.serviceId);
|
|
464
|
+
if (service) {
|
|
465
|
+
services.push(service);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const credentials = await this.getDeckCredentialsForDeck(id);
|
|
469
|
+
const playbooks = await this.getDeckPlaybooksForDeck(id);
|
|
470
|
+
return {
|
|
471
|
+
id: row.id,
|
|
472
|
+
name: row.name,
|
|
473
|
+
description: row.description,
|
|
474
|
+
isActive: Boolean(row.is_active),
|
|
475
|
+
services,
|
|
476
|
+
credentials,
|
|
477
|
+
playbooks,
|
|
478
|
+
createdAt: row.created_at,
|
|
479
|
+
updatedAt: row.updated_at,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
async getAllDecks() {
|
|
483
|
+
const stmt = this.db.prepare('SELECT * FROM decks ORDER BY created_at DESC');
|
|
484
|
+
const rows = stmt.all();
|
|
485
|
+
const decks = [];
|
|
486
|
+
for (const row of rows) {
|
|
487
|
+
const deckServices = await this.getDeckServices(row.id);
|
|
488
|
+
const services = [];
|
|
489
|
+
for (const deckService of deckServices) {
|
|
490
|
+
const service = await this.getService(deckService.serviceId);
|
|
491
|
+
if (service) {
|
|
492
|
+
services.push(service);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const credentials = await this.getDeckCredentialsForDeck(row.id);
|
|
496
|
+
const playbooks = await this.getDeckPlaybooksForDeck(row.id);
|
|
497
|
+
decks.push({
|
|
498
|
+
id: row.id,
|
|
499
|
+
name: row.name,
|
|
500
|
+
description: row.description,
|
|
501
|
+
isActive: Boolean(row.is_active),
|
|
502
|
+
services,
|
|
503
|
+
credentials,
|
|
504
|
+
playbooks,
|
|
505
|
+
createdAt: row.created_at,
|
|
506
|
+
updatedAt: row.updated_at,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
return decks;
|
|
510
|
+
}
|
|
511
|
+
async getActiveDeck() {
|
|
512
|
+
const stmt = this.db.prepare('SELECT * FROM decks WHERE is_active = 1 LIMIT 1');
|
|
513
|
+
const row = stmt.get();
|
|
514
|
+
if (!row)
|
|
515
|
+
return null;
|
|
516
|
+
const deckServices = await this.getDeckServices(row.id);
|
|
517
|
+
const services = [];
|
|
518
|
+
for (const deckService of deckServices) {
|
|
519
|
+
const service = await this.getService(deckService.serviceId);
|
|
520
|
+
if (service) {
|
|
521
|
+
services.push(service);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const credentials = await this.getDeckCredentialsForDeck(row.id);
|
|
525
|
+
const playbooks = await this.getDeckPlaybooksForDeck(row.id);
|
|
526
|
+
return {
|
|
527
|
+
id: row.id,
|
|
528
|
+
name: row.name,
|
|
529
|
+
description: row.description,
|
|
530
|
+
isActive: Boolean(row.is_active),
|
|
531
|
+
services,
|
|
532
|
+
credentials,
|
|
533
|
+
playbooks,
|
|
534
|
+
createdAt: row.created_at,
|
|
535
|
+
updatedAt: row.updated_at,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
async updateDeck(id, input) {
|
|
539
|
+
const existing = await this.getDeck(id);
|
|
540
|
+
if (!existing)
|
|
541
|
+
return null;
|
|
542
|
+
const updated = {
|
|
543
|
+
...existing,
|
|
544
|
+
...input,
|
|
545
|
+
updatedAt: new Date().toISOString(),
|
|
546
|
+
};
|
|
547
|
+
const stmt = this.db.prepare(`
|
|
548
|
+
UPDATE decks SET
|
|
549
|
+
name = @name, description = @description, is_active = @is_active, updated_at = @updated_at
|
|
550
|
+
WHERE id = @id
|
|
551
|
+
`);
|
|
552
|
+
stmt.run({
|
|
553
|
+
id: updated.id,
|
|
554
|
+
name: updated.name,
|
|
555
|
+
description: updated.description,
|
|
556
|
+
is_active: updated.isActive ? 1 : 0,
|
|
557
|
+
updated_at: updated.updatedAt,
|
|
558
|
+
});
|
|
559
|
+
return updated;
|
|
560
|
+
}
|
|
561
|
+
async deleteDeck(id) {
|
|
562
|
+
const stmt = this.db.prepare('DELETE FROM decks WHERE id = ?');
|
|
563
|
+
const result = stmt.run(id);
|
|
564
|
+
return result.changes > 0;
|
|
565
|
+
}
|
|
566
|
+
async setActiveDeck(id) {
|
|
567
|
+
// First, deactivate all decks
|
|
568
|
+
this.db.prepare('UPDATE decks SET is_active = 0').run();
|
|
569
|
+
// Then activate the specified deck
|
|
570
|
+
this.db.prepare('UPDATE decks SET is_active = 1 WHERE id = ?').run(id);
|
|
571
|
+
}
|
|
572
|
+
// Deck services operations
|
|
573
|
+
async getDeckServices(deckId) {
|
|
574
|
+
const stmt = this.db.prepare(`
|
|
575
|
+
SELECT deck_id, service_id, position FROM deck_services
|
|
576
|
+
WHERE deck_id = ?
|
|
577
|
+
ORDER BY position ASC
|
|
578
|
+
`);
|
|
579
|
+
const rows = stmt.all(deckId);
|
|
580
|
+
return rows.map(row => ({
|
|
581
|
+
deckId: row.deck_id,
|
|
582
|
+
serviceId: row.service_id,
|
|
583
|
+
position: row.position,
|
|
584
|
+
}));
|
|
585
|
+
}
|
|
586
|
+
async addServiceToDeck(input) {
|
|
587
|
+
// Get the next position
|
|
588
|
+
const positionStmt = this.db.prepare(`
|
|
589
|
+
SELECT COALESCE(MAX(position), -1) + 1 as next_position
|
|
590
|
+
FROM deck_services WHERE deck_id = ?
|
|
591
|
+
`);
|
|
592
|
+
const positionResult = positionStmt.get(input.deckId);
|
|
593
|
+
const position = input.position ?? positionResult.next_position;
|
|
594
|
+
const stmt = this.db.prepare(`
|
|
595
|
+
INSERT INTO deck_services (deck_id, service_id, position)
|
|
596
|
+
VALUES (@deck_id, @service_id, @position)
|
|
597
|
+
`);
|
|
598
|
+
stmt.run({
|
|
599
|
+
deck_id: input.deckId,
|
|
600
|
+
service_id: input.serviceId,
|
|
601
|
+
position,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
async removeServiceFromDeck(input) {
|
|
605
|
+
const stmt = this.db.prepare(`
|
|
606
|
+
DELETE FROM deck_services
|
|
607
|
+
WHERE deck_id = @deck_id AND service_id = @service_id
|
|
608
|
+
`);
|
|
609
|
+
stmt.run({
|
|
610
|
+
deck_id: input.deckId,
|
|
611
|
+
service_id: input.serviceId,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
async reorderDeckServices(input) {
|
|
615
|
+
const transaction = this.db.transaction(() => {
|
|
616
|
+
// Remove all services from the deck
|
|
617
|
+
this.db.prepare('DELETE FROM deck_services WHERE deck_id = ?').run(input.deckId);
|
|
618
|
+
// Add them back in the new order
|
|
619
|
+
const insertStmt = this.db.prepare(`
|
|
620
|
+
INSERT INTO deck_services (deck_id, service_id, position)
|
|
621
|
+
VALUES (@deck_id, @service_id, @position)
|
|
622
|
+
`);
|
|
623
|
+
input.serviceIds.forEach((serviceId, index) => {
|
|
624
|
+
insertStmt.run({
|
|
625
|
+
deck_id: input.deckId,
|
|
626
|
+
service_id: serviceId,
|
|
627
|
+
position: index,
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
transaction();
|
|
632
|
+
}
|
|
633
|
+
async clearDeckServices(deckId) {
|
|
634
|
+
const stmt = this.db.prepare('DELETE FROM deck_services WHERE deck_id = ?');
|
|
635
|
+
stmt.run(deckId);
|
|
636
|
+
}
|
|
637
|
+
// Credential operations
|
|
638
|
+
async createCredential(input) {
|
|
639
|
+
const now = new Date().toISOString();
|
|
640
|
+
const credential = {
|
|
641
|
+
...input,
|
|
642
|
+
createdAt: now,
|
|
643
|
+
updatedAt: now,
|
|
644
|
+
};
|
|
645
|
+
const stmt = this.db.prepare(`
|
|
646
|
+
INSERT INTO credentials (
|
|
647
|
+
id, label, scheme, header_name, env_name, keychain_account, tags, docs_url, icon_url, created_at, updated_at
|
|
648
|
+
) VALUES (
|
|
649
|
+
@id, @label, @scheme, @header_name, @env_name, @keychain_account, @tags, @docs_url, @icon_url, @created_at, @updated_at
|
|
650
|
+
)
|
|
651
|
+
`);
|
|
652
|
+
stmt.run({
|
|
653
|
+
id: credential.id,
|
|
654
|
+
label: credential.label,
|
|
655
|
+
scheme: credential.scheme,
|
|
656
|
+
header_name: credential.headerName ?? null,
|
|
657
|
+
env_name: credential.envName,
|
|
658
|
+
keychain_account: credential.keychainAccount,
|
|
659
|
+
tags: JSON.stringify(credential.tags ?? []),
|
|
660
|
+
docs_url: credential.docsUrl ?? null,
|
|
661
|
+
icon_url: credential.iconUrl ?? null,
|
|
662
|
+
created_at: credential.createdAt,
|
|
663
|
+
updated_at: credential.updatedAt,
|
|
664
|
+
});
|
|
665
|
+
return credential;
|
|
666
|
+
}
|
|
667
|
+
async getCredential(id) {
|
|
668
|
+
const stmt = this.db.prepare('SELECT * FROM credentials WHERE id = ?');
|
|
669
|
+
const row = stmt.get(id);
|
|
670
|
+
if (!row) {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
return this.mapCredentialRow(row);
|
|
674
|
+
}
|
|
675
|
+
async getAllCredentials() {
|
|
676
|
+
const stmt = this.db.prepare('SELECT * FROM credentials ORDER BY created_at DESC');
|
|
677
|
+
const rows = stmt.all();
|
|
678
|
+
return rows.map((row) => this.mapCredentialRow(row));
|
|
679
|
+
}
|
|
680
|
+
async updateCredential(id, input) {
|
|
681
|
+
const existing = await this.getCredential(id);
|
|
682
|
+
if (!existing) {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const updated = {
|
|
686
|
+
...existing,
|
|
687
|
+
...input,
|
|
688
|
+
id: existing.id,
|
|
689
|
+
updatedAt: new Date().toISOString(),
|
|
690
|
+
};
|
|
691
|
+
const stmt = this.db.prepare(`
|
|
692
|
+
UPDATE credentials SET
|
|
693
|
+
label = @label,
|
|
694
|
+
scheme = @scheme,
|
|
695
|
+
header_name = @header_name,
|
|
696
|
+
env_name = @env_name,
|
|
697
|
+
keychain_account = @keychain_account,
|
|
698
|
+
tags = @tags,
|
|
699
|
+
docs_url = @docs_url,
|
|
700
|
+
icon_url = @icon_url,
|
|
701
|
+
updated_at = @updated_at
|
|
702
|
+
WHERE id = @id
|
|
703
|
+
`);
|
|
704
|
+
stmt.run({
|
|
705
|
+
id: updated.id,
|
|
706
|
+
label: updated.label,
|
|
707
|
+
scheme: updated.scheme,
|
|
708
|
+
header_name: updated.headerName ?? null,
|
|
709
|
+
env_name: updated.envName,
|
|
710
|
+
keychain_account: updated.keychainAccount,
|
|
711
|
+
tags: JSON.stringify(updated.tags ?? []),
|
|
712
|
+
docs_url: updated.docsUrl ?? null,
|
|
713
|
+
icon_url: updated.iconUrl ?? null,
|
|
714
|
+
updated_at: updated.updatedAt,
|
|
715
|
+
});
|
|
716
|
+
return updated;
|
|
717
|
+
}
|
|
718
|
+
async touchCredential(id) {
|
|
719
|
+
this.db.prepare('UPDATE credentials SET updated_at = ? WHERE id = ?').run(new Date().toISOString(), id);
|
|
720
|
+
}
|
|
721
|
+
async deleteCredential(id) {
|
|
722
|
+
const stmt = this.db.prepare('DELETE FROM credentials WHERE id = ?');
|
|
723
|
+
const result = stmt.run(id);
|
|
724
|
+
return result.changes > 0;
|
|
725
|
+
}
|
|
726
|
+
async getDeckCredentials(deckId) {
|
|
727
|
+
const stmt = this.db.prepare(`
|
|
728
|
+
SELECT deck_id, credential_id, position FROM deck_credentials
|
|
729
|
+
WHERE deck_id = ?
|
|
730
|
+
ORDER BY position ASC
|
|
731
|
+
`);
|
|
732
|
+
const rows = stmt.all(deckId);
|
|
733
|
+
return rows.map((row) => ({
|
|
734
|
+
deckId: row.deck_id,
|
|
735
|
+
credentialId: row.credential_id,
|
|
736
|
+
position: row.position,
|
|
737
|
+
}));
|
|
738
|
+
}
|
|
739
|
+
async addCredentialToDeck(input) {
|
|
740
|
+
const positionStmt = this.db.prepare(`
|
|
741
|
+
SELECT COALESCE(MAX(position), -1) + 1 as next_position
|
|
742
|
+
FROM deck_credentials WHERE deck_id = ?
|
|
743
|
+
`);
|
|
744
|
+
const positionResult = positionStmt.get(input.deckId);
|
|
745
|
+
const position = input.position ?? positionResult.next_position;
|
|
746
|
+
const stmt = this.db.prepare(`
|
|
747
|
+
INSERT INTO deck_credentials (deck_id, credential_id, position)
|
|
748
|
+
VALUES (@deck_id, @credential_id, @position)
|
|
749
|
+
`);
|
|
750
|
+
stmt.run({
|
|
751
|
+
deck_id: input.deckId,
|
|
752
|
+
credential_id: input.credentialId,
|
|
753
|
+
position,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
async removeCredentialFromDeck(input) {
|
|
757
|
+
const stmt = this.db.prepare(`
|
|
758
|
+
DELETE FROM deck_credentials
|
|
759
|
+
WHERE deck_id = @deck_id AND credential_id = @credential_id
|
|
760
|
+
`);
|
|
761
|
+
stmt.run({
|
|
762
|
+
deck_id: input.deckId,
|
|
763
|
+
credential_id: input.credentialId,
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
async createExecRun(input) {
|
|
767
|
+
const execRun = {
|
|
768
|
+
id: (0, shared_1.generateId)(),
|
|
769
|
+
deckId: input.deckId,
|
|
770
|
+
manifestPath: input.manifestPath,
|
|
771
|
+
command: input.command,
|
|
772
|
+
credentialIds: input.credentialIds,
|
|
773
|
+
exitCode: input.exitCode,
|
|
774
|
+
startedAt: input.startedAt,
|
|
775
|
+
finishedAt: input.finishedAt,
|
|
776
|
+
};
|
|
777
|
+
const insertRun = this.db.prepare(`
|
|
778
|
+
INSERT INTO exec_runs (
|
|
779
|
+
id, deck_id, manifest_path, command, exit_code, started_at, finished_at
|
|
780
|
+
) VALUES (
|
|
781
|
+
@id, @deck_id, @manifest_path, @command, @exit_code, @started_at, @finished_at
|
|
782
|
+
)
|
|
783
|
+
`);
|
|
784
|
+
const insertCredential = this.db.prepare(`
|
|
785
|
+
INSERT INTO exec_run_credentials (exec_run_id, credential_id)
|
|
786
|
+
VALUES (@exec_run_id, @credential_id)
|
|
787
|
+
`);
|
|
788
|
+
const transaction = this.db.transaction(() => {
|
|
789
|
+
insertRun.run({
|
|
790
|
+
id: execRun.id,
|
|
791
|
+
deck_id: execRun.deckId ?? null,
|
|
792
|
+
manifest_path: execRun.manifestPath ?? null,
|
|
793
|
+
command: execRun.command,
|
|
794
|
+
exit_code: execRun.exitCode ?? null,
|
|
795
|
+
started_at: execRun.startedAt,
|
|
796
|
+
finished_at: execRun.finishedAt ?? null,
|
|
797
|
+
});
|
|
798
|
+
for (const credentialId of input.credentialIds) {
|
|
799
|
+
insertCredential.run({
|
|
800
|
+
exec_run_id: execRun.id,
|
|
801
|
+
credential_id: credentialId,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
transaction();
|
|
806
|
+
return execRun;
|
|
807
|
+
}
|
|
808
|
+
mapPlaybookRow(row) {
|
|
809
|
+
return {
|
|
810
|
+
id: row.id,
|
|
811
|
+
title: row.title,
|
|
812
|
+
body: row.body ?? '',
|
|
813
|
+
triggers: row.triggers ? JSON.parse(row.triggers) : [],
|
|
814
|
+
dependsOnCredentialIds: row.depends_on_credentials
|
|
815
|
+
? JSON.parse(row.depends_on_credentials)
|
|
816
|
+
: [],
|
|
817
|
+
dependsOnServiceIds: row.depends_on_services ? JSON.parse(row.depends_on_services) : [],
|
|
818
|
+
exec: row.exec_command ?? undefined,
|
|
819
|
+
skill: row.skill_path ?? undefined,
|
|
820
|
+
createdAt: row.created_at,
|
|
821
|
+
updatedAt: row.updated_at,
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async createPlaybook(input) {
|
|
825
|
+
const now = new Date().toISOString();
|
|
826
|
+
const playbook = {
|
|
827
|
+
id: input.id,
|
|
828
|
+
title: input.title,
|
|
829
|
+
body: input.body ?? '',
|
|
830
|
+
triggers: input.triggers ?? [],
|
|
831
|
+
dependsOnCredentialIds: input.dependsOnCredentialIds ?? [],
|
|
832
|
+
dependsOnServiceIds: input.dependsOnServiceIds ?? [],
|
|
833
|
+
exec: input.exec,
|
|
834
|
+
skill: input.skill,
|
|
835
|
+
createdAt: now,
|
|
836
|
+
updatedAt: now,
|
|
837
|
+
};
|
|
838
|
+
const stmt = this.db.prepare(`
|
|
839
|
+
INSERT INTO playbooks (
|
|
840
|
+
id, title, body, triggers, depends_on_credentials, depends_on_services,
|
|
841
|
+
exec_command, skill_path, created_at, updated_at
|
|
842
|
+
) VALUES (
|
|
843
|
+
@id, @title, @body, @triggers, @depends_on_credentials, @depends_on_services,
|
|
844
|
+
@exec_command, @skill_path, @created_at, @updated_at
|
|
845
|
+
)
|
|
846
|
+
`);
|
|
847
|
+
stmt.run({
|
|
848
|
+
id: playbook.id,
|
|
849
|
+
title: playbook.title,
|
|
850
|
+
body: playbook.body,
|
|
851
|
+
triggers: JSON.stringify(playbook.triggers),
|
|
852
|
+
depends_on_credentials: JSON.stringify(playbook.dependsOnCredentialIds),
|
|
853
|
+
depends_on_services: JSON.stringify(playbook.dependsOnServiceIds),
|
|
854
|
+
exec_command: playbook.exec ?? null,
|
|
855
|
+
skill_path: playbook.skill ?? null,
|
|
856
|
+
created_at: playbook.createdAt,
|
|
857
|
+
updated_at: playbook.updatedAt,
|
|
858
|
+
});
|
|
859
|
+
return playbook;
|
|
860
|
+
}
|
|
861
|
+
async getPlaybook(id) {
|
|
862
|
+
const row = this.db.prepare('SELECT * FROM playbooks WHERE id = ?').get(id);
|
|
863
|
+
return row ? this.mapPlaybookRow(row) : null;
|
|
864
|
+
}
|
|
865
|
+
async getAllPlaybooks() {
|
|
866
|
+
const rows = this.db.prepare('SELECT * FROM playbooks ORDER BY created_at DESC').all();
|
|
867
|
+
return rows.map((row) => this.mapPlaybookRow(row));
|
|
868
|
+
}
|
|
869
|
+
async updatePlaybook(id, input) {
|
|
870
|
+
const existing = await this.getPlaybook(id);
|
|
871
|
+
if (!existing) {
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
const updated = {
|
|
875
|
+
...existing,
|
|
876
|
+
...input,
|
|
877
|
+
updatedAt: new Date().toISOString(),
|
|
878
|
+
};
|
|
879
|
+
this.db.prepare(`
|
|
880
|
+
UPDATE playbooks SET
|
|
881
|
+
title = @title,
|
|
882
|
+
body = @body,
|
|
883
|
+
triggers = @triggers,
|
|
884
|
+
depends_on_credentials = @depends_on_credentials,
|
|
885
|
+
depends_on_services = @depends_on_services,
|
|
886
|
+
exec_command = @exec_command,
|
|
887
|
+
skill_path = @skill_path,
|
|
888
|
+
updated_at = @updated_at
|
|
889
|
+
WHERE id = @id
|
|
890
|
+
`).run({
|
|
891
|
+
id: updated.id,
|
|
892
|
+
title: updated.title,
|
|
893
|
+
body: updated.body,
|
|
894
|
+
triggers: JSON.stringify(updated.triggers),
|
|
895
|
+
depends_on_credentials: JSON.stringify(updated.dependsOnCredentialIds),
|
|
896
|
+
depends_on_services: JSON.stringify(updated.dependsOnServiceIds),
|
|
897
|
+
exec_command: updated.exec ?? null,
|
|
898
|
+
skill_path: updated.skill ?? null,
|
|
899
|
+
updated_at: updated.updatedAt,
|
|
900
|
+
});
|
|
901
|
+
return updated;
|
|
902
|
+
}
|
|
903
|
+
async deletePlaybook(id) {
|
|
904
|
+
const result = this.db.prepare('DELETE FROM playbooks WHERE id = ?').run(id);
|
|
905
|
+
return result.changes > 0;
|
|
906
|
+
}
|
|
907
|
+
async getPlaybooksDependingOnCredential(credentialId) {
|
|
908
|
+
return (await this.getAllPlaybooks()).filter((playbook) => playbook.dependsOnCredentialIds.includes(credentialId));
|
|
909
|
+
}
|
|
910
|
+
async getPlaybooksDependingOnService(serviceId) {
|
|
911
|
+
return (await this.getAllPlaybooks()).filter((playbook) => playbook.dependsOnServiceIds.includes(serviceId));
|
|
912
|
+
}
|
|
913
|
+
async getDeckPlaybooksForDeck(deckId) {
|
|
914
|
+
const rows = this.db.prepare(`
|
|
915
|
+
SELECT p.*, dp.position
|
|
916
|
+
FROM deck_playbooks dp
|
|
917
|
+
JOIN playbooks p ON p.id = dp.playbook_id
|
|
918
|
+
WHERE dp.deck_id = ?
|
|
919
|
+
ORDER BY dp.position ASC
|
|
920
|
+
`).all(deckId);
|
|
921
|
+
return rows.map((row) => this.mapPlaybookRow(row));
|
|
922
|
+
}
|
|
923
|
+
async addPlaybookToDeck(input) {
|
|
924
|
+
const positionResult = this.db.prepare(`
|
|
925
|
+
SELECT COALESCE(MAX(position), -1) + 1 as next_position
|
|
926
|
+
FROM deck_playbooks WHERE deck_id = ?
|
|
927
|
+
`).get(input.deckId);
|
|
928
|
+
const position = input.position ?? positionResult.next_position;
|
|
929
|
+
this.db.prepare(`
|
|
930
|
+
INSERT INTO deck_playbooks (deck_id, playbook_id, position)
|
|
931
|
+
VALUES (@deck_id, @playbook_id, @position)
|
|
932
|
+
`).run({
|
|
933
|
+
deck_id: input.deckId,
|
|
934
|
+
playbook_id: input.playbookId,
|
|
935
|
+
position,
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
async removePlaybookFromDeck(input) {
|
|
939
|
+
this.db.prepare(`
|
|
940
|
+
DELETE FROM deck_playbooks
|
|
941
|
+
WHERE deck_id = @deck_id AND playbook_id = @playbook_id
|
|
942
|
+
`).run({
|
|
943
|
+
deck_id: input.deckId,
|
|
944
|
+
playbook_id: input.playbookId,
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
async getDeckPlaybooks(deckId) {
|
|
948
|
+
const rows = this.db.prepare(`
|
|
949
|
+
SELECT deck_id, playbook_id, position FROM deck_playbooks
|
|
950
|
+
WHERE deck_id = ?
|
|
951
|
+
ORDER BY position ASC
|
|
952
|
+
`).all(deckId);
|
|
953
|
+
return rows.map((row) => ({
|
|
954
|
+
deckId: row.deck_id,
|
|
955
|
+
playbookId: row.playbook_id,
|
|
956
|
+
position: row.position,
|
|
957
|
+
}));
|
|
958
|
+
}
|
|
959
|
+
// Cleanup
|
|
960
|
+
close() {
|
|
961
|
+
this.db.close();
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
exports.DatabaseManager = DatabaseManager;
|
|
965
|
+
//# sourceMappingURL=database.js.map
|