@grackle-ai/database 0.73.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/README.md +34 -0
- package/dist/credential-providers.d.ts +41 -0
- package/dist/credential-providers.d.ts.map +1 -0
- package/dist/credential-providers.js +97 -0
- package/dist/credential-providers.js.map +1 -0
- package/dist/crypto.d.ts +5 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +75 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db-seed.d.ts +16 -0
- package/dist/db-seed.d.ts.map +1 -0
- package/dist/db-seed.js +176 -0
- package/dist/db-seed.js.map +1 -0
- package/dist/db.d.ts +43 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +478 -0
- package/dist/db.js.map +1 -0
- package/dist/env-registry.d.ts +29 -0
- package/dist/env-registry.d.ts.map +1 -0
- package/dist/env-registry.js +78 -0
- package/dist/env-registry.js.map +1 -0
- package/dist/event-store.d.ts +19 -0
- package/dist/event-store.d.ts.map +1 -0
- package/dist/event-store.js +16 -0
- package/dist/event-store.js.map +1 -0
- package/dist/finding-store.d.ts +7 -0
- package/dist/finding-store.d.ts.map +1 -0
- package/dist/finding-store.js +45 -0
- package/dist/finding-store.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/json-helpers.d.ts +7 -0
- package/dist/json-helpers.d.ts.map +1 -0
- package/dist/json-helpers.js +22 -0
- package/dist/json-helpers.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +12 -0
- package/dist/paths.js.map +1 -0
- package/dist/persona-store.d.ts +15 -0
- package/dist/persona-store.d.ts.map +1 -0
- package/dist/persona-store.js +57 -0
- package/dist/persona-store.js.map +1 -0
- package/dist/schema.d.ts +1784 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +152 -0
- package/dist/schema.js.map +1 -0
- package/dist/session-store.d.ts +57 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +204 -0
- package/dist/session-store.js.map +1 -0
- package/dist/settings-store.d.ts +14 -0
- package/dist/settings-store.d.ts.map +1 -0
- package/dist/settings-store.js +30 -0
- package/dist/settings-store.js.map +1 -0
- package/dist/task-store.d.ts +71 -0
- package/dist/task-store.d.ts.map +1 -0
- package/dist/task-store.js +301 -0
- package/dist/task-store.js.map +1 -0
- package/dist/test-db.d.ts +13 -0
- package/dist/test-db.d.ts.map +1 -0
- package/dist/test-db.js +14 -0
- package/dist/test-db.js.map +1 -0
- package/dist/token-store.d.ts +25 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +58 -0
- package/dist/token-store.js.map +1 -0
- package/dist/utils/slugify.d.ts +20 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +22 -0
- package/dist/utils/slugify.js.map +1 -0
- package/dist/workspace-store.d.ts +29 -0
- package/dist/workspace-store.d.ts.map +1 -0
- package/dist/workspace-store.js +74 -0
- package/dist/workspace-store.js.map +1 -0
- package/package.json +48 -0
package/dist/db.js
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { DB_FILENAME } from "@grackle-ai/common";
|
|
6
|
+
import { grackleHome } from "./paths.js";
|
|
7
|
+
import * as schema from "./schema.js";
|
|
8
|
+
/** Raw better-sqlite3 instance. Available after {@link openDatabase} has been called. */
|
|
9
|
+
let sqlite;
|
|
10
|
+
/**
|
|
11
|
+
* Drizzle ORM instance wrapping the SQLite database.
|
|
12
|
+
* Available after {@link openDatabase} has been called.
|
|
13
|
+
* Exported as the default export via ESM live binding so that store modules
|
|
14
|
+
* that do `import db from "./db.js"` see the initialized value after startup.
|
|
15
|
+
*/
|
|
16
|
+
let db;
|
|
17
|
+
/**
|
|
18
|
+
* Open the SQLite database and initialize the Drizzle ORM instance.
|
|
19
|
+
* Call once at startup before using `db` or `sqlite`.
|
|
20
|
+
* If already initialized, returns silently.
|
|
21
|
+
*
|
|
22
|
+
* @param dbPath - Optional path to the database file. Defaults to `~/.grackle/grackle.db`.
|
|
23
|
+
*/
|
|
24
|
+
export function openDatabase(dbPath) {
|
|
25
|
+
if (sqlite) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const resolvedPath = dbPath ?? join(grackleHome, DB_FILENAME);
|
|
29
|
+
// Ensure the grackle home directory exists (skip when a custom path is provided,
|
|
30
|
+
// e.g. tests that point at an in-memory or temp-dir database).
|
|
31
|
+
if (!dbPath) {
|
|
32
|
+
mkdirSync(grackleHome, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
sqlite = new Database(resolvedPath);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (err instanceof Error && err.message.includes("Could not locate the bindings file")) {
|
|
39
|
+
process.stderr.write([
|
|
40
|
+
"",
|
|
41
|
+
"ERROR: better-sqlite3 native binding not found.",
|
|
42
|
+
"",
|
|
43
|
+
"The install script for better-sqlite3 was skipped, so the required native",
|
|
44
|
+
"module was never built. This commonly happens with pnpm v8+, which blocks",
|
|
45
|
+
"package install scripts by default.",
|
|
46
|
+
"",
|
|
47
|
+
"To fix this, run one of the following:",
|
|
48
|
+
"",
|
|
49
|
+
" Option 1 — approve builds interactively:",
|
|
50
|
+
" pnpm approve-builds",
|
|
51
|
+
"",
|
|
52
|
+
" Option 2 — add to your project's package.json, then reinstall:",
|
|
53
|
+
' { "pnpm": { "onlyBuiltDependencies": ["better-sqlite3"] } }',
|
|
54
|
+
" pnpm install",
|
|
55
|
+
"",
|
|
56
|
+
"After fixing, restart grackle.",
|
|
57
|
+
"",
|
|
58
|
+
].join("\n"));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (err instanceof Error && err.message.includes("NODE_MODULE_VERSION")) {
|
|
62
|
+
process.stderr.write([
|
|
63
|
+
"",
|
|
64
|
+
"ERROR: better-sqlite3 was compiled for a different Node.js version.",
|
|
65
|
+
`(Current NODE_MODULE_VERSION: ${process.versions.modules})`,
|
|
66
|
+
"",
|
|
67
|
+
"This usually means grackle was installed with one Node version but is",
|
|
68
|
+
"being run with another. Grackle requires Node >= 22.",
|
|
69
|
+
"",
|
|
70
|
+
"To fix: reinstall grackle with your current Node version:",
|
|
71
|
+
" npm install -g @grackle-ai/cli",
|
|
72
|
+
"",
|
|
73
|
+
`Original error: ${err.message}`,
|
|
74
|
+
"",
|
|
75
|
+
].join("\n"));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
// Enable WAL mode for better concurrent read performance
|
|
81
|
+
sqlite.pragma("journal_mode = WAL");
|
|
82
|
+
// Enable foreign key enforcement (off by default in SQLite)
|
|
83
|
+
sqlite.pragma("foreign_keys = ON");
|
|
84
|
+
db = drizzle(sqlite, { schema });
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Initialize all database tables and run migrations.
|
|
88
|
+
* Call once at startup after {@link openDatabase}, or pass an in-memory
|
|
89
|
+
* SQLite instance for testing.
|
|
90
|
+
*
|
|
91
|
+
* @param sqliteOverride - Optional SQLite instance to use instead of the module-level one.
|
|
92
|
+
* @returns Collected migration errors from idempotent try-catch steps.
|
|
93
|
+
*/
|
|
94
|
+
export function initDatabase(sqliteOverride) {
|
|
95
|
+
const conn = sqliteOverride ?? sqlite;
|
|
96
|
+
if (!conn) {
|
|
97
|
+
throw new Error("Database not initialized. Call openDatabase() first or provide a sqliteOverride.");
|
|
98
|
+
}
|
|
99
|
+
const migrationErrors = [];
|
|
100
|
+
/** Check whether an error is an expected idempotent migration failure. */
|
|
101
|
+
const isExpectedIdempotencyError = (err) => {
|
|
102
|
+
if (!(err instanceof Error)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const msg = err.message.toLowerCase();
|
|
106
|
+
return (msg.includes("already exists") ||
|
|
107
|
+
msg.includes("duplicate column name") ||
|
|
108
|
+
msg.includes("no such table") ||
|
|
109
|
+
msg.includes("no such column"));
|
|
110
|
+
};
|
|
111
|
+
/** Run a migration step, collecting expected idempotency errors. */
|
|
112
|
+
const tryMigration = (name, fn) => {
|
|
113
|
+
try {
|
|
114
|
+
fn();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (isExpectedIdempotencyError(error)) {
|
|
118
|
+
migrationErrors.push({ name, error });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
// Migration: rename projects table to workspaces
|
|
126
|
+
tryMigration("rename-projects-to-workspaces", () => {
|
|
127
|
+
conn.exec("ALTER TABLE projects RENAME TO workspaces");
|
|
128
|
+
});
|
|
129
|
+
// Migration: rename project_id column to workspace_id on tasks
|
|
130
|
+
tryMigration("rename-tasks-project-id", () => {
|
|
131
|
+
conn.exec("ALTER TABLE tasks RENAME COLUMN project_id TO workspace_id");
|
|
132
|
+
});
|
|
133
|
+
// Migration: rename project_id column to workspace_id on findings
|
|
134
|
+
tryMigration("rename-findings-project-id", () => {
|
|
135
|
+
conn.exec("ALTER TABLE findings RENAME COLUMN project_id TO workspace_id");
|
|
136
|
+
});
|
|
137
|
+
// Migration: drop old findings index after column rename
|
|
138
|
+
tryMigration("drop-idx-findings-project", () => {
|
|
139
|
+
conn.exec("DROP INDEX IF EXISTS idx_findings_project");
|
|
140
|
+
});
|
|
141
|
+
// Migration: rename default_env_id → environment_id on workspaces
|
|
142
|
+
// Must run BEFORE the backfill block below which references environment_id.
|
|
143
|
+
tryMigration("rename-workspaces-default-env-id", () => {
|
|
144
|
+
conn.exec("ALTER TABLE workspaces RENAME COLUMN default_env_id TO environment_id");
|
|
145
|
+
});
|
|
146
|
+
// Create tables — idempotent, safe to run every startup
|
|
147
|
+
conn.exec(`
|
|
148
|
+
CREATE TABLE IF NOT EXISTS environments (
|
|
149
|
+
id TEXT PRIMARY KEY,
|
|
150
|
+
display_name TEXT NOT NULL,
|
|
151
|
+
adapter_type TEXT NOT NULL,
|
|
152
|
+
adapter_config TEXT NOT NULL,
|
|
153
|
+
default_runtime TEXT NOT NULL DEFAULT 'claude-code',
|
|
154
|
+
bootstrapped INTEGER NOT NULL DEFAULT 0,
|
|
155
|
+
status TEXT NOT NULL DEFAULT 'disconnected',
|
|
156
|
+
last_seen TEXT,
|
|
157
|
+
env_info TEXT,
|
|
158
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
159
|
+
powerline_token TEXT NOT NULL DEFAULT ''
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
163
|
+
id TEXT PRIMARY KEY,
|
|
164
|
+
env_id TEXT NOT NULL REFERENCES environments(id),
|
|
165
|
+
runtime TEXT NOT NULL,
|
|
166
|
+
runtime_session_id TEXT,
|
|
167
|
+
prompt TEXT NOT NULL,
|
|
168
|
+
model TEXT NOT NULL,
|
|
169
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
170
|
+
log_path TEXT,
|
|
171
|
+
turns INTEGER NOT NULL DEFAULT 0,
|
|
172
|
+
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
173
|
+
suspended_at TEXT,
|
|
174
|
+
ended_at TEXT,
|
|
175
|
+
end_reason TEXT,
|
|
176
|
+
error TEXT,
|
|
177
|
+
task_id TEXT NOT NULL DEFAULT '',
|
|
178
|
+
persona_id TEXT NOT NULL DEFAULT ''
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
CREATE TABLE IF NOT EXISTS tokens (
|
|
182
|
+
id TEXT PRIMARY KEY,
|
|
183
|
+
config TEXT NOT NULL,
|
|
184
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
188
|
+
id TEXT PRIMARY KEY,
|
|
189
|
+
name TEXT NOT NULL,
|
|
190
|
+
description TEXT NOT NULL DEFAULT '',
|
|
191
|
+
repo_url TEXT NOT NULL DEFAULT '',
|
|
192
|
+
environment_id TEXT NOT NULL DEFAULT '' REFERENCES environments(id),
|
|
193
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
194
|
+
use_worktrees INTEGER NOT NULL DEFAULT 1,
|
|
195
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
196
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
200
|
+
id TEXT PRIMARY KEY,
|
|
201
|
+
workspace_id TEXT REFERENCES workspaces(id),
|
|
202
|
+
title TEXT NOT NULL,
|
|
203
|
+
description TEXT NOT NULL DEFAULT '',
|
|
204
|
+
status TEXT NOT NULL DEFAULT 'not_started',
|
|
205
|
+
branch TEXT NOT NULL DEFAULT '',
|
|
206
|
+
depends_on TEXT NOT NULL DEFAULT '[]',
|
|
207
|
+
started_at TEXT,
|
|
208
|
+
completed_at TEXT,
|
|
209
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
210
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
211
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
212
|
+
parent_task_id TEXT NOT NULL DEFAULT '',
|
|
213
|
+
depth INTEGER NOT NULL DEFAULT 0,
|
|
214
|
+
can_decompose INTEGER NOT NULL DEFAULT 0
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
CREATE TABLE IF NOT EXISTS findings (
|
|
218
|
+
id TEXT PRIMARY KEY,
|
|
219
|
+
workspace_id TEXT NOT NULL REFERENCES workspaces(id),
|
|
220
|
+
task_id TEXT NOT NULL DEFAULT '',
|
|
221
|
+
session_id TEXT NOT NULL DEFAULT '',
|
|
222
|
+
category TEXT NOT NULL DEFAULT 'general',
|
|
223
|
+
title TEXT NOT NULL,
|
|
224
|
+
content TEXT NOT NULL,
|
|
225
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
226
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
CREATE TABLE IF NOT EXISTS personas (
|
|
230
|
+
id TEXT PRIMARY KEY,
|
|
231
|
+
name TEXT NOT NULL UNIQUE,
|
|
232
|
+
description TEXT NOT NULL DEFAULT '',
|
|
233
|
+
system_prompt TEXT NOT NULL,
|
|
234
|
+
tool_config TEXT NOT NULL DEFAULT '{}',
|
|
235
|
+
runtime TEXT NOT NULL DEFAULT '',
|
|
236
|
+
model TEXT NOT NULL DEFAULT '',
|
|
237
|
+
max_turns INTEGER NOT NULL DEFAULT 0,
|
|
238
|
+
mcp_servers TEXT NOT NULL DEFAULT '[]',
|
|
239
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
240
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
244
|
+
key TEXT PRIMARY KEY,
|
|
245
|
+
value TEXT NOT NULL
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
CREATE INDEX IF NOT EXISTS idx_findings_workspace ON findings(workspace_id);
|
|
249
|
+
|
|
250
|
+
CREATE TABLE IF NOT EXISTS domain_events (
|
|
251
|
+
id TEXT PRIMARY KEY,
|
|
252
|
+
type TEXT NOT NULL,
|
|
253
|
+
timestamp TEXT NOT NULL,
|
|
254
|
+
payload TEXT NOT NULL
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CREATE INDEX IF NOT EXISTS idx_domain_events_type ON domain_events(type);
|
|
258
|
+
CREATE INDEX IF NOT EXISTS idx_domain_events_timestamp ON domain_events(timestamp);
|
|
259
|
+
`);
|
|
260
|
+
// Migration: add powerline_token column if missing (older databases)
|
|
261
|
+
tryMigration("add-environments-powerline-token", () => {
|
|
262
|
+
conn.exec("ALTER TABLE environments ADD COLUMN powerline_token TEXT NOT NULL DEFAULT ''");
|
|
263
|
+
});
|
|
264
|
+
// Migration: rename sidecar_token → powerline_token (from older databases)
|
|
265
|
+
tryMigration("rename-environments-sidecar-token", () => {
|
|
266
|
+
conn.exec("ALTER TABLE environments RENAME COLUMN sidecar_token TO powerline_token");
|
|
267
|
+
});
|
|
268
|
+
// Migration: backfill NULLs in stage-2 tables from older schemas that lacked NOT NULL
|
|
269
|
+
conn.exec(`
|
|
270
|
+
UPDATE workspaces SET description = '' WHERE description IS NULL;
|
|
271
|
+
UPDATE workspaces SET repo_url = '' WHERE repo_url IS NULL;
|
|
272
|
+
UPDATE workspaces SET environment_id = COALESCE(
|
|
273
|
+
(SELECT id FROM environments LIMIT 1), ''
|
|
274
|
+
) WHERE environment_id IS NULL OR environment_id = '';
|
|
275
|
+
UPDATE workspaces SET status = 'active' WHERE status IS NULL;
|
|
276
|
+
UPDATE workspaces SET created_at = datetime('now') WHERE created_at IS NULL;
|
|
277
|
+
UPDATE workspaces SET updated_at = datetime('now') WHERE updated_at IS NULL;
|
|
278
|
+
|
|
279
|
+
UPDATE tasks SET description = '' WHERE description IS NULL;
|
|
280
|
+
UPDATE tasks SET status = 'not_started' WHERE status IS NULL;
|
|
281
|
+
UPDATE tasks SET branch = '' WHERE branch IS NULL;
|
|
282
|
+
UPDATE tasks SET depends_on = '[]' WHERE depends_on IS NULL;
|
|
283
|
+
UPDATE tasks SET created_at = datetime('now') WHERE created_at IS NULL;
|
|
284
|
+
UPDATE tasks SET updated_at = datetime('now') WHERE updated_at IS NULL;
|
|
285
|
+
UPDATE tasks SET sort_order = 0 WHERE sort_order IS NULL;
|
|
286
|
+
|
|
287
|
+
UPDATE findings SET task_id = '' WHERE task_id IS NULL;
|
|
288
|
+
UPDATE findings SET session_id = '' WHERE session_id IS NULL;
|
|
289
|
+
UPDATE findings SET category = 'general' WHERE category IS NULL;
|
|
290
|
+
UPDATE findings SET tags = '[]' WHERE tags IS NULL;
|
|
291
|
+
UPDATE findings SET created_at = datetime('now') WHERE created_at IS NULL;
|
|
292
|
+
`);
|
|
293
|
+
// Migration: add parent_task_id and depth columns if missing (older databases)
|
|
294
|
+
tryMigration("add-tasks-parent-task-id", () => {
|
|
295
|
+
conn.exec("ALTER TABLE tasks ADD COLUMN parent_task_id TEXT NOT NULL DEFAULT ''");
|
|
296
|
+
});
|
|
297
|
+
tryMigration("add-tasks-depth", () => {
|
|
298
|
+
conn.exec("ALTER TABLE tasks ADD COLUMN depth INTEGER NOT NULL DEFAULT 0");
|
|
299
|
+
});
|
|
300
|
+
// Migration: add can_decompose column if missing (older databases)
|
|
301
|
+
tryMigration("add-tasks-can-decompose", () => {
|
|
302
|
+
conn.exec("ALTER TABLE tasks ADD COLUMN can_decompose INTEGER NOT NULL DEFAULT 0");
|
|
303
|
+
// Backfill: mark root tasks and tasks with existing children as decomposable
|
|
304
|
+
conn.exec(`
|
|
305
|
+
UPDATE tasks
|
|
306
|
+
SET can_decompose = 1
|
|
307
|
+
WHERE parent_task_id IS NULL OR parent_task_id = ''
|
|
308
|
+
OR id IN (
|
|
309
|
+
SELECT DISTINCT parent_task_id
|
|
310
|
+
FROM tasks
|
|
311
|
+
WHERE parent_task_id IS NOT NULL AND parent_task_id <> ''
|
|
312
|
+
)
|
|
313
|
+
`);
|
|
314
|
+
});
|
|
315
|
+
// Migration: add persona_id column to tasks if missing
|
|
316
|
+
tryMigration("add-tasks-persona-id", () => {
|
|
317
|
+
conn.exec("ALTER TABLE tasks ADD COLUMN persona_id TEXT NOT NULL DEFAULT ''");
|
|
318
|
+
});
|
|
319
|
+
// Migration: add task_id column to sessions if missing
|
|
320
|
+
tryMigration("add-sessions-task-id", () => {
|
|
321
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN task_id TEXT NOT NULL DEFAULT ''");
|
|
322
|
+
});
|
|
323
|
+
// Migration: add use_worktrees column to workspaces if missing (older databases)
|
|
324
|
+
tryMigration("add-workspaces-use-worktrees", () => {
|
|
325
|
+
conn.exec("ALTER TABLE workspaces ADD COLUMN use_worktrees INTEGER NOT NULL DEFAULT 1");
|
|
326
|
+
});
|
|
327
|
+
// Migration: add worktree_base_path column to workspaces if missing (older databases)
|
|
328
|
+
tryMigration("add-workspaces-worktree-base-path", () => {
|
|
329
|
+
conn.exec("ALTER TABLE workspaces ADD COLUMN worktree_base_path TEXT NOT NULL DEFAULT ''");
|
|
330
|
+
});
|
|
331
|
+
// Migration: backfill task_id on existing sessions from tasks.session_id.
|
|
332
|
+
// Guard with try/catch since session_id column may have been dropped already.
|
|
333
|
+
tryMigration("backfill-sessions-task-id", () => {
|
|
334
|
+
conn.exec(`
|
|
335
|
+
UPDATE sessions SET task_id = (
|
|
336
|
+
SELECT id FROM tasks WHERE tasks.session_id = sessions.id LIMIT 1
|
|
337
|
+
) WHERE task_id = '' AND EXISTS (
|
|
338
|
+
SELECT 1 FROM tasks WHERE tasks.session_id = sessions.id
|
|
339
|
+
)
|
|
340
|
+
`);
|
|
341
|
+
});
|
|
342
|
+
// Migration: add persona_id column to sessions if missing
|
|
343
|
+
tryMigration("add-sessions-persona-id", () => {
|
|
344
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN persona_id TEXT NOT NULL DEFAULT ''");
|
|
345
|
+
});
|
|
346
|
+
// Migration: copy persona_id from tasks to sessions before dropping
|
|
347
|
+
tryMigration("copy-persona-id-tasks-to-sessions", () => {
|
|
348
|
+
conn.exec(`
|
|
349
|
+
UPDATE sessions SET persona_id = (
|
|
350
|
+
SELECT persona_id FROM tasks WHERE tasks.session_id = sessions.id LIMIT 1
|
|
351
|
+
) WHERE persona_id = '' AND task_id != ''
|
|
352
|
+
`);
|
|
353
|
+
});
|
|
354
|
+
// Migration: drop columns that moved off the tasks table
|
|
355
|
+
tryMigration("drop-tasks-session-id", () => {
|
|
356
|
+
conn.exec("ALTER TABLE tasks DROP COLUMN session_id");
|
|
357
|
+
});
|
|
358
|
+
tryMigration("drop-tasks-env-id", () => {
|
|
359
|
+
conn.exec("ALTER TABLE tasks DROP COLUMN env_id");
|
|
360
|
+
});
|
|
361
|
+
tryMigration("drop-tasks-persona-id", () => {
|
|
362
|
+
conn.exec("ALTER TABLE tasks DROP COLUMN persona_id");
|
|
363
|
+
});
|
|
364
|
+
// Migration: normalize existing started_at values from SQLite datetime('now')
|
|
365
|
+
// format (YYYY-MM-DD HH:MM:SS) to ISO 8601 (YYYY-MM-DDTHH:MM:SS.000Z) so
|
|
366
|
+
// ordering is consistent with newly inserted rows.
|
|
367
|
+
conn.exec(`
|
|
368
|
+
UPDATE sessions
|
|
369
|
+
SET started_at = replace(started_at, ' ', 'T') || '.000Z'
|
|
370
|
+
WHERE started_at NOT LIKE '%T%'
|
|
371
|
+
`);
|
|
372
|
+
// Migration: normalize task statuses to simplified model
|
|
373
|
+
conn.exec(`
|
|
374
|
+
UPDATE tasks SET status = 'not_started' WHERE status IN ('pending', 'assigned');
|
|
375
|
+
UPDATE tasks SET status = 'complete' WHERE status = 'done';
|
|
376
|
+
UPDATE tasks SET status = 'not_started' WHERE status IN ('in_progress', 'waiting_input', 'review');
|
|
377
|
+
`);
|
|
378
|
+
// Migration: normalize session statuses
|
|
379
|
+
conn.exec(`
|
|
380
|
+
UPDATE sessions SET status = 'idle' WHERE status = 'waiting_input';
|
|
381
|
+
UPDATE sessions SET status = 'interrupted' WHERE status = 'killed';
|
|
382
|
+
`);
|
|
383
|
+
// Migration: drop stale columns from tasks
|
|
384
|
+
tryMigration("drop-tasks-assigned-at", () => {
|
|
385
|
+
conn.exec("ALTER TABLE tasks DROP COLUMN assigned_at");
|
|
386
|
+
});
|
|
387
|
+
tryMigration("drop-tasks-review-notes", () => {
|
|
388
|
+
conn.exec("ALTER TABLE tasks DROP COLUMN review_notes");
|
|
389
|
+
});
|
|
390
|
+
// Index for efficient session-by-task lookups
|
|
391
|
+
conn.exec("CREATE INDEX IF NOT EXISTS idx_sessions_task_id ON sessions(task_id)");
|
|
392
|
+
// Migration: add default_persona_id to workspaces and tasks
|
|
393
|
+
tryMigration("add-workspaces-default-persona-id", () => {
|
|
394
|
+
conn.exec("ALTER TABLE workspaces ADD COLUMN default_persona_id TEXT NOT NULL DEFAULT ''");
|
|
395
|
+
});
|
|
396
|
+
tryMigration("add-tasks-default-persona-id", () => {
|
|
397
|
+
conn.exec("ALTER TABLE tasks ADD COLUMN default_persona_id TEXT NOT NULL DEFAULT ''");
|
|
398
|
+
});
|
|
399
|
+
// Migration: add type and script columns to personas if missing
|
|
400
|
+
tryMigration("add-personas-type", () => {
|
|
401
|
+
conn.exec("ALTER TABLE personas ADD COLUMN type TEXT NOT NULL DEFAULT 'agent'");
|
|
402
|
+
});
|
|
403
|
+
tryMigration("add-personas-script", () => {
|
|
404
|
+
conn.exec("ALTER TABLE personas ADD COLUMN script TEXT NOT NULL DEFAULT ''");
|
|
405
|
+
});
|
|
406
|
+
tryMigration("add-sessions-parent-session-id", () => {
|
|
407
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN parent_session_id TEXT NOT NULL DEFAULT ''");
|
|
408
|
+
});
|
|
409
|
+
tryMigration("add-sessions-pipe-mode", () => {
|
|
410
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN pipe_mode TEXT NOT NULL DEFAULT ''");
|
|
411
|
+
});
|
|
412
|
+
tryMigration("add-sessions-input-tokens", () => {
|
|
413
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN input_tokens INTEGER NOT NULL DEFAULT 0");
|
|
414
|
+
});
|
|
415
|
+
tryMigration("add-sessions-output-tokens", () => {
|
|
416
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN output_tokens INTEGER NOT NULL DEFAULT 0");
|
|
417
|
+
});
|
|
418
|
+
tryMigration("add-sessions-cost-usd", () => {
|
|
419
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN cost_usd REAL NOT NULL DEFAULT 0");
|
|
420
|
+
});
|
|
421
|
+
// Migration: add end_reason column to sessions if missing (older databases)
|
|
422
|
+
tryMigration("add-sessions-end-reason", () => {
|
|
423
|
+
conn.exec("ALTER TABLE sessions ADD COLUMN end_reason TEXT");
|
|
424
|
+
});
|
|
425
|
+
// Migration: normalize old session statuses to STOPPED + end_reason
|
|
426
|
+
conn.exec(`
|
|
427
|
+
UPDATE sessions SET end_reason = 'completed', status = 'stopped'
|
|
428
|
+
WHERE status = 'completed';
|
|
429
|
+
UPDATE sessions SET end_reason = 'interrupted', status = 'stopped'
|
|
430
|
+
WHERE status IN ('failed', 'interrupted');
|
|
431
|
+
UPDATE sessions SET end_reason = 'completed', status = 'stopped'
|
|
432
|
+
WHERE status = 'hibernating';
|
|
433
|
+
`);
|
|
434
|
+
// Migration: make workspace_id nullable on tasks.
|
|
435
|
+
// SQLite doesn't support ALTER COLUMN, so we recreate the table.
|
|
436
|
+
// Guard: only run if the column currently has NOT NULL.
|
|
437
|
+
{
|
|
438
|
+
const tableInfo = conn.prepare("PRAGMA table_info(tasks)").all();
|
|
439
|
+
const workspaceIdCol = tableInfo.find((c) => c.name === "workspace_id");
|
|
440
|
+
if (workspaceIdCol?.notnull === 1) {
|
|
441
|
+
conn.exec(`
|
|
442
|
+
BEGIN;
|
|
443
|
+
CREATE TABLE tasks_new (
|
|
444
|
+
id TEXT PRIMARY KEY,
|
|
445
|
+
workspace_id TEXT REFERENCES workspaces(id),
|
|
446
|
+
title TEXT NOT NULL,
|
|
447
|
+
description TEXT NOT NULL DEFAULT '',
|
|
448
|
+
status TEXT NOT NULL DEFAULT 'not_started',
|
|
449
|
+
branch TEXT NOT NULL DEFAULT '',
|
|
450
|
+
depends_on TEXT NOT NULL DEFAULT '[]',
|
|
451
|
+
started_at TEXT,
|
|
452
|
+
completed_at TEXT,
|
|
453
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
454
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
455
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
456
|
+
parent_task_id TEXT NOT NULL DEFAULT '',
|
|
457
|
+
depth INTEGER NOT NULL DEFAULT 0,
|
|
458
|
+
can_decompose INTEGER NOT NULL DEFAULT 0,
|
|
459
|
+
default_persona_id TEXT NOT NULL DEFAULT ''
|
|
460
|
+
);
|
|
461
|
+
INSERT INTO tasks_new SELECT
|
|
462
|
+
id, workspace_id, title, description, status, branch, depends_on,
|
|
463
|
+
started_at, completed_at, created_at, updated_at, sort_order,
|
|
464
|
+
parent_task_id, depth, can_decompose, default_persona_id
|
|
465
|
+
FROM tasks;
|
|
466
|
+
DROP TABLE tasks;
|
|
467
|
+
ALTER TABLE tasks_new RENAME TO tasks;
|
|
468
|
+
COMMIT;
|
|
469
|
+
`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Migration: add index on workspaces.environment_id for efficient lookup
|
|
473
|
+
conn.exec("CREATE INDEX IF NOT EXISTS idx_workspaces_environment_id ON workspaces(environment_id)");
|
|
474
|
+
return { migrationErrors };
|
|
475
|
+
}
|
|
476
|
+
export { sqlite };
|
|
477
|
+
export { db as default };
|
|
478
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type EnvironmentRow } from "./schema.js";
|
|
2
|
+
import type { EnvironmentStatus } from "@grackle-ai/common";
|
|
3
|
+
export type { EnvironmentRow };
|
|
4
|
+
/** Return all registered environments. */
|
|
5
|
+
export declare function listEnvironments(): EnvironmentRow[];
|
|
6
|
+
/** Retrieve a single environment by ID. */
|
|
7
|
+
export declare function getEnvironment(id: string): EnvironmentRow | undefined;
|
|
8
|
+
/** Insert a new environment record with a randomly-generated PowerLine token. */
|
|
9
|
+
export declare function addEnvironment(id: string, displayName: string, adapterType: string, adapterConfig: string): void;
|
|
10
|
+
/** Delete an environment record from the database. */
|
|
11
|
+
export declare function removeEnvironment(id: string): void;
|
|
12
|
+
/** Update an environment's connection status and touch `last_seen`. */
|
|
13
|
+
export declare function updateEnvironmentStatus(id: string, status: EnvironmentStatus): void;
|
|
14
|
+
/** Mark an environment as having completed first-time bootstrap. */
|
|
15
|
+
export declare function markBootstrapped(id: string): void;
|
|
16
|
+
/** Store serialized environment info (e.g. OS, node version) from the PowerLine. */
|
|
17
|
+
export declare function setEnvInfo(id: string, info: string): void;
|
|
18
|
+
/** Update the adapter config JSON for an existing environment. */
|
|
19
|
+
export declare function updateAdapterConfig(id: string, config: string): void;
|
|
20
|
+
/** Updatable fields for an existing environment. */
|
|
21
|
+
export interface UpdateEnvironmentFields {
|
|
22
|
+
displayName?: string;
|
|
23
|
+
adapterConfig?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Update mutable fields (displayName, adapterConfig) of an existing environment. */
|
|
26
|
+
export declare function updateEnvironment(id: string, fields: UpdateEnvironmentFields): void;
|
|
27
|
+
/** Reset all environment statuses to disconnected on server startup. */
|
|
28
|
+
export declare function resetAllStatuses(): void;
|
|
29
|
+
//# sourceMappingURL=env-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-registry.d.ts","sourceRoot":"","sources":["../src/env-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,0CAA0C;AAC1C,wBAAgB,gBAAgB,IAAI,cAAc,EAAE,CAEnD;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAErE;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,IAAI,CASN;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAKnF;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAKjD;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKpE;AAED,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAenF;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import db from "./db.js";
|
|
2
|
+
import { environments } from "./schema.js";
|
|
3
|
+
import { eq, sql } from "drizzle-orm";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
const POWERLINE_TOKEN_BYTE_LENGTH = 32;
|
|
6
|
+
/** Return all registered environments. */
|
|
7
|
+
export function listEnvironments() {
|
|
8
|
+
return db.select().from(environments).all();
|
|
9
|
+
}
|
|
10
|
+
/** Retrieve a single environment by ID. */
|
|
11
|
+
export function getEnvironment(id) {
|
|
12
|
+
return db.select().from(environments).where(eq(environments.id, id)).get();
|
|
13
|
+
}
|
|
14
|
+
/** Insert a new environment record with a randomly-generated PowerLine token. */
|
|
15
|
+
export function addEnvironment(id, displayName, adapterType, adapterConfig) {
|
|
16
|
+
const powerlineToken = randomBytes(POWERLINE_TOKEN_BYTE_LENGTH).toString("hex");
|
|
17
|
+
db.insert(environments).values({
|
|
18
|
+
id,
|
|
19
|
+
displayName,
|
|
20
|
+
adapterType,
|
|
21
|
+
adapterConfig,
|
|
22
|
+
powerlineToken,
|
|
23
|
+
}).run();
|
|
24
|
+
}
|
|
25
|
+
/** Delete an environment record from the database. */
|
|
26
|
+
export function removeEnvironment(id) {
|
|
27
|
+
db.delete(environments).where(eq(environments.id, id)).run();
|
|
28
|
+
}
|
|
29
|
+
/** Update an environment's connection status and touch `last_seen`. */
|
|
30
|
+
export function updateEnvironmentStatus(id, status) {
|
|
31
|
+
db.update(environments)
|
|
32
|
+
.set({ status, lastSeen: sql `datetime('now')` })
|
|
33
|
+
.where(eq(environments.id, id))
|
|
34
|
+
.run();
|
|
35
|
+
}
|
|
36
|
+
/** Mark an environment as having completed first-time bootstrap. */
|
|
37
|
+
export function markBootstrapped(id) {
|
|
38
|
+
db.update(environments)
|
|
39
|
+
.set({ bootstrapped: true })
|
|
40
|
+
.where(eq(environments.id, id))
|
|
41
|
+
.run();
|
|
42
|
+
}
|
|
43
|
+
/** Store serialized environment info (e.g. OS, node version) from the PowerLine. */
|
|
44
|
+
export function setEnvInfo(id, info) {
|
|
45
|
+
db.update(environments)
|
|
46
|
+
.set({ envInfo: info })
|
|
47
|
+
.where(eq(environments.id, id))
|
|
48
|
+
.run();
|
|
49
|
+
}
|
|
50
|
+
/** Update the adapter config JSON for an existing environment. */
|
|
51
|
+
export function updateAdapterConfig(id, config) {
|
|
52
|
+
db.update(environments)
|
|
53
|
+
.set({ adapterConfig: config })
|
|
54
|
+
.where(eq(environments.id, id))
|
|
55
|
+
.run();
|
|
56
|
+
}
|
|
57
|
+
/** Update mutable fields (displayName, adapterConfig) of an existing environment. */
|
|
58
|
+
export function updateEnvironment(id, fields) {
|
|
59
|
+
const updates = {};
|
|
60
|
+
if (fields.displayName !== undefined) {
|
|
61
|
+
updates.displayName = fields.displayName;
|
|
62
|
+
}
|
|
63
|
+
if (fields.adapterConfig !== undefined) {
|
|
64
|
+
updates.adapterConfig = fields.adapterConfig;
|
|
65
|
+
}
|
|
66
|
+
if (Object.keys(updates).length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
db.update(environments)
|
|
70
|
+
.set(updates)
|
|
71
|
+
.where(eq(environments.id, id))
|
|
72
|
+
.run();
|
|
73
|
+
}
|
|
74
|
+
/** Reset all environment statuses to disconnected on server startup. */
|
|
75
|
+
export function resetAllStatuses() {
|
|
76
|
+
db.update(environments).set({ status: "disconnected" }).run();
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=env-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-registry.js","sourceRoot":"","sources":["../src/env-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** A domain event to be persisted. */
|
|
2
|
+
export interface DomainEvent {
|
|
3
|
+
/** ULID — chronologically sortable unique identifier. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Dot-notation event type (e.g. "task.created"). */
|
|
6
|
+
type: string;
|
|
7
|
+
/** ISO 8601 timestamp. */
|
|
8
|
+
timestamp: string;
|
|
9
|
+
/** Domain-specific payload. */
|
|
10
|
+
payload: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Persist a domain event to the `domain_events` SQLite table.
|
|
14
|
+
* Called synchronously by `emit()` — SQLite in WAL mode handles this efficiently.
|
|
15
|
+
*
|
|
16
|
+
* @param event - The fully-formed domain event to persist.
|
|
17
|
+
*/
|
|
18
|
+
export declare function persistEvent(event: DomainEvent): void;
|
|
19
|
+
//# sourceMappingURL=event-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store.d.ts","sourceRoot":"","sources":["../src/event-store.ts"],"names":[],"mappings":"AAEA,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAKD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAOrD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import db from "./db.js";
|
|
2
|
+
/** Prepared statement for inserting domain events (lazy-initialized). */
|
|
3
|
+
let insertStmt;
|
|
4
|
+
/**
|
|
5
|
+
* Persist a domain event to the `domain_events` SQLite table.
|
|
6
|
+
* Called synchronously by `emit()` — SQLite in WAL mode handles this efficiently.
|
|
7
|
+
*
|
|
8
|
+
* @param event - The fully-formed domain event to persist.
|
|
9
|
+
*/
|
|
10
|
+
export function persistEvent(event) {
|
|
11
|
+
if (!insertStmt) {
|
|
12
|
+
insertStmt = db.$client.prepare("INSERT INTO domain_events (id, type, timestamp, payload) VALUES (?, ?, ?, ?)");
|
|
13
|
+
}
|
|
14
|
+
insertStmt.run([event.id, event.type, event.timestamp, JSON.stringify(event.payload)]);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=event-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store.js","sourceRoot":"","sources":["../src/event-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAczB,yEAAyE;AACzE,IAAI,UAA6D,CAAC;AAElE;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAC7B,8EAA8E,CAC/E,CAAC;IACJ,CAAC;IACD,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACzF,CAAC"}
|