@hasna/todos 0.9.18 → 0.9.19
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/cli/index.js +93 -17
- package/dist/index.js +93 -17
- package/dist/mcp/index.js +93 -17
- package/dist/server/index.js +93 -17
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2138,29 +2138,20 @@ function runMigrations(db) {
|
|
|
2138
2138
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
2139
2139
|
const currentLevel = result?.max_id ?? 0;
|
|
2140
2140
|
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
2141
|
-
|
|
2141
|
+
try {
|
|
2142
|
+
db.exec(MIGRATIONS[i]);
|
|
2143
|
+
} catch {}
|
|
2142
2144
|
}
|
|
2143
2145
|
} catch {
|
|
2144
2146
|
for (const migration of MIGRATIONS) {
|
|
2145
|
-
|
|
2147
|
+
try {
|
|
2148
|
+
db.exec(migration);
|
|
2149
|
+
} catch {}
|
|
2146
2150
|
}
|
|
2147
2151
|
}
|
|
2148
|
-
|
|
2152
|
+
ensureSchema(db);
|
|
2149
2153
|
}
|
|
2150
|
-
function
|
|
2151
|
-
try {
|
|
2152
|
-
const hasAgents = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'").get();
|
|
2153
|
-
if (!hasAgents) {
|
|
2154
|
-
db.exec(MIGRATIONS[4]);
|
|
2155
|
-
}
|
|
2156
|
-
} catch {}
|
|
2157
|
-
try {
|
|
2158
|
-
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
2159
|
-
} catch {
|
|
2160
|
-
try {
|
|
2161
|
-
db.exec(MIGRATIONS[5]);
|
|
2162
|
-
} catch {}
|
|
2163
|
-
}
|
|
2154
|
+
function ensureSchema(db) {
|
|
2164
2155
|
const ensureColumn = (table, column, type) => {
|
|
2165
2156
|
try {
|
|
2166
2157
|
db.query(`SELECT ${column} FROM ${table} LIMIT 0`).get();
|
|
@@ -2170,6 +2161,76 @@ function ensureTableMigrations(db) {
|
|
|
2170
2161
|
} catch {}
|
|
2171
2162
|
}
|
|
2172
2163
|
};
|
|
2164
|
+
const ensureTable = (name, sql) => {
|
|
2165
|
+
try {
|
|
2166
|
+
const exists = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
2167
|
+
if (!exists)
|
|
2168
|
+
db.exec(sql);
|
|
2169
|
+
} catch {}
|
|
2170
|
+
};
|
|
2171
|
+
const ensureIndex = (sql) => {
|
|
2172
|
+
try {
|
|
2173
|
+
db.exec(sql);
|
|
2174
|
+
} catch {}
|
|
2175
|
+
};
|
|
2176
|
+
ensureTable("agents", `
|
|
2177
|
+
CREATE TABLE agents (
|
|
2178
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
|
|
2179
|
+
role TEXT DEFAULT 'agent', permissions TEXT DEFAULT '["*"]',
|
|
2180
|
+
metadata TEXT DEFAULT '{}',
|
|
2181
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2182
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2183
|
+
)`);
|
|
2184
|
+
ensureTable("task_lists", `
|
|
2185
|
+
CREATE TABLE task_lists (
|
|
2186
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2187
|
+
slug TEXT NOT NULL, name TEXT NOT NULL, description TEXT,
|
|
2188
|
+
metadata TEXT DEFAULT '{}',
|
|
2189
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2190
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2191
|
+
UNIQUE(project_id, slug)
|
|
2192
|
+
)`);
|
|
2193
|
+
ensureTable("plans", `
|
|
2194
|
+
CREATE TABLE plans (
|
|
2195
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
2196
|
+
task_list_id TEXT, agent_id TEXT,
|
|
2197
|
+
name TEXT NOT NULL, description TEXT,
|
|
2198
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
2199
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2200
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2201
|
+
)`);
|
|
2202
|
+
ensureTable("task_tags", `
|
|
2203
|
+
CREATE TABLE task_tags (
|
|
2204
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2205
|
+
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
2206
|
+
)`);
|
|
2207
|
+
ensureTable("task_history", `
|
|
2208
|
+
CREATE TABLE task_history (
|
|
2209
|
+
id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2210
|
+
action TEXT NOT NULL, field TEXT, old_value TEXT, new_value TEXT, agent_id TEXT,
|
|
2211
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2212
|
+
)`);
|
|
2213
|
+
ensureTable("webhooks", `
|
|
2214
|
+
CREATE TABLE webhooks (
|
|
2215
|
+
id TEXT PRIMARY KEY, url TEXT NOT NULL, events TEXT NOT NULL DEFAULT '[]',
|
|
2216
|
+
secret TEXT, active INTEGER NOT NULL DEFAULT 1,
|
|
2217
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2218
|
+
)`);
|
|
2219
|
+
ensureTable("task_templates", `
|
|
2220
|
+
CREATE TABLE task_templates (
|
|
2221
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL, title_pattern TEXT NOT NULL,
|
|
2222
|
+
description TEXT, priority TEXT DEFAULT 'medium', tags TEXT DEFAULT '[]',
|
|
2223
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2224
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
2225
|
+
metadata TEXT DEFAULT '{}',
|
|
2226
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2227
|
+
)`);
|
|
2228
|
+
ensureColumn("projects", "task_list_id", "TEXT");
|
|
2229
|
+
ensureColumn("projects", "task_prefix", "TEXT");
|
|
2230
|
+
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
2231
|
+
ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
|
|
2232
|
+
ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
|
|
2233
|
+
ensureColumn("tasks", "short_id", "TEXT");
|
|
2173
2234
|
ensureColumn("tasks", "due_at", "TEXT");
|
|
2174
2235
|
ensureColumn("tasks", "estimated_minutes", "INTEGER");
|
|
2175
2236
|
ensureColumn("tasks", "requires_approval", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -2179,6 +2240,21 @@ function ensureTableMigrations(db) {
|
|
|
2179
2240
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
2180
2241
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
2181
2242
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
2243
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
2244
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
|
|
2245
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
|
|
2246
|
+
ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
|
|
2247
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name)");
|
|
2248
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id)");
|
|
2249
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
2250
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
2251
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
2252
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
2253
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
2254
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
2255
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
|
|
2256
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
|
|
2257
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
|
|
2182
2258
|
}
|
|
2183
2259
|
function backfillTaskTags(db) {
|
|
2184
2260
|
try {
|
package/dist/index.js
CHANGED
|
@@ -285,29 +285,20 @@ function runMigrations(db) {
|
|
|
285
285
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
286
286
|
const currentLevel = result?.max_id ?? 0;
|
|
287
287
|
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
288
|
-
|
|
288
|
+
try {
|
|
289
|
+
db.exec(MIGRATIONS[i]);
|
|
290
|
+
} catch {}
|
|
289
291
|
}
|
|
290
292
|
} catch {
|
|
291
293
|
for (const migration of MIGRATIONS) {
|
|
292
|
-
|
|
294
|
+
try {
|
|
295
|
+
db.exec(migration);
|
|
296
|
+
} catch {}
|
|
293
297
|
}
|
|
294
298
|
}
|
|
295
|
-
|
|
299
|
+
ensureSchema(db);
|
|
296
300
|
}
|
|
297
|
-
function
|
|
298
|
-
try {
|
|
299
|
-
const hasAgents = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'").get();
|
|
300
|
-
if (!hasAgents) {
|
|
301
|
-
db.exec(MIGRATIONS[4]);
|
|
302
|
-
}
|
|
303
|
-
} catch {}
|
|
304
|
-
try {
|
|
305
|
-
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
306
|
-
} catch {
|
|
307
|
-
try {
|
|
308
|
-
db.exec(MIGRATIONS[5]);
|
|
309
|
-
} catch {}
|
|
310
|
-
}
|
|
301
|
+
function ensureSchema(db) {
|
|
311
302
|
const ensureColumn = (table, column, type) => {
|
|
312
303
|
try {
|
|
313
304
|
db.query(`SELECT ${column} FROM ${table} LIMIT 0`).get();
|
|
@@ -317,6 +308,76 @@ function ensureTableMigrations(db) {
|
|
|
317
308
|
} catch {}
|
|
318
309
|
}
|
|
319
310
|
};
|
|
311
|
+
const ensureTable = (name, sql) => {
|
|
312
|
+
try {
|
|
313
|
+
const exists = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
314
|
+
if (!exists)
|
|
315
|
+
db.exec(sql);
|
|
316
|
+
} catch {}
|
|
317
|
+
};
|
|
318
|
+
const ensureIndex = (sql) => {
|
|
319
|
+
try {
|
|
320
|
+
db.exec(sql);
|
|
321
|
+
} catch {}
|
|
322
|
+
};
|
|
323
|
+
ensureTable("agents", `
|
|
324
|
+
CREATE TABLE agents (
|
|
325
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
|
|
326
|
+
role TEXT DEFAULT 'agent', permissions TEXT DEFAULT '["*"]',
|
|
327
|
+
metadata TEXT DEFAULT '{}',
|
|
328
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
329
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
330
|
+
)`);
|
|
331
|
+
ensureTable("task_lists", `
|
|
332
|
+
CREATE TABLE task_lists (
|
|
333
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
334
|
+
slug TEXT NOT NULL, name TEXT NOT NULL, description TEXT,
|
|
335
|
+
metadata TEXT DEFAULT '{}',
|
|
336
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
337
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
338
|
+
UNIQUE(project_id, slug)
|
|
339
|
+
)`);
|
|
340
|
+
ensureTable("plans", `
|
|
341
|
+
CREATE TABLE plans (
|
|
342
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
343
|
+
task_list_id TEXT, agent_id TEXT,
|
|
344
|
+
name TEXT NOT NULL, description TEXT,
|
|
345
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
346
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
347
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
348
|
+
)`);
|
|
349
|
+
ensureTable("task_tags", `
|
|
350
|
+
CREATE TABLE task_tags (
|
|
351
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
352
|
+
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
353
|
+
)`);
|
|
354
|
+
ensureTable("task_history", `
|
|
355
|
+
CREATE TABLE task_history (
|
|
356
|
+
id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
357
|
+
action TEXT NOT NULL, field TEXT, old_value TEXT, new_value TEXT, agent_id TEXT,
|
|
358
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
359
|
+
)`);
|
|
360
|
+
ensureTable("webhooks", `
|
|
361
|
+
CREATE TABLE webhooks (
|
|
362
|
+
id TEXT PRIMARY KEY, url TEXT NOT NULL, events TEXT NOT NULL DEFAULT '[]',
|
|
363
|
+
secret TEXT, active INTEGER NOT NULL DEFAULT 1,
|
|
364
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
365
|
+
)`);
|
|
366
|
+
ensureTable("task_templates", `
|
|
367
|
+
CREATE TABLE task_templates (
|
|
368
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL, title_pattern TEXT NOT NULL,
|
|
369
|
+
description TEXT, priority TEXT DEFAULT 'medium', tags TEXT DEFAULT '[]',
|
|
370
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
371
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
372
|
+
metadata TEXT DEFAULT '{}',
|
|
373
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
374
|
+
)`);
|
|
375
|
+
ensureColumn("projects", "task_list_id", "TEXT");
|
|
376
|
+
ensureColumn("projects", "task_prefix", "TEXT");
|
|
377
|
+
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
378
|
+
ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
|
|
379
|
+
ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
|
|
380
|
+
ensureColumn("tasks", "short_id", "TEXT");
|
|
320
381
|
ensureColumn("tasks", "due_at", "TEXT");
|
|
321
382
|
ensureColumn("tasks", "estimated_minutes", "INTEGER");
|
|
322
383
|
ensureColumn("tasks", "requires_approval", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -326,6 +387,21 @@ function ensureTableMigrations(db) {
|
|
|
326
387
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
327
388
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
328
389
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
390
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
391
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
|
|
392
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
|
|
393
|
+
ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
|
|
394
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name)");
|
|
395
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id)");
|
|
396
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
397
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
398
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
399
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
400
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
401
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
402
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
|
|
403
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
|
|
404
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
|
|
329
405
|
}
|
|
330
406
|
function backfillTaskTags(db) {
|
|
331
407
|
try {
|
package/dist/mcp/index.js
CHANGED
|
@@ -4346,29 +4346,20 @@ function runMigrations(db) {
|
|
|
4346
4346
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
4347
4347
|
const currentLevel = result?.max_id ?? 0;
|
|
4348
4348
|
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
4349
|
-
|
|
4349
|
+
try {
|
|
4350
|
+
db.exec(MIGRATIONS[i]);
|
|
4351
|
+
} catch {}
|
|
4350
4352
|
}
|
|
4351
4353
|
} catch {
|
|
4352
4354
|
for (const migration of MIGRATIONS) {
|
|
4353
|
-
|
|
4355
|
+
try {
|
|
4356
|
+
db.exec(migration);
|
|
4357
|
+
} catch {}
|
|
4354
4358
|
}
|
|
4355
4359
|
}
|
|
4356
|
-
|
|
4360
|
+
ensureSchema(db);
|
|
4357
4361
|
}
|
|
4358
|
-
function
|
|
4359
|
-
try {
|
|
4360
|
-
const hasAgents = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'").get();
|
|
4361
|
-
if (!hasAgents) {
|
|
4362
|
-
db.exec(MIGRATIONS[4]);
|
|
4363
|
-
}
|
|
4364
|
-
} catch {}
|
|
4365
|
-
try {
|
|
4366
|
-
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
4367
|
-
} catch {
|
|
4368
|
-
try {
|
|
4369
|
-
db.exec(MIGRATIONS[5]);
|
|
4370
|
-
} catch {}
|
|
4371
|
-
}
|
|
4362
|
+
function ensureSchema(db) {
|
|
4372
4363
|
const ensureColumn = (table, column, type) => {
|
|
4373
4364
|
try {
|
|
4374
4365
|
db.query(`SELECT ${column} FROM ${table} LIMIT 0`).get();
|
|
@@ -4378,6 +4369,76 @@ function ensureTableMigrations(db) {
|
|
|
4378
4369
|
} catch {}
|
|
4379
4370
|
}
|
|
4380
4371
|
};
|
|
4372
|
+
const ensureTable = (name, sql) => {
|
|
4373
|
+
try {
|
|
4374
|
+
const exists = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
4375
|
+
if (!exists)
|
|
4376
|
+
db.exec(sql);
|
|
4377
|
+
} catch {}
|
|
4378
|
+
};
|
|
4379
|
+
const ensureIndex = (sql) => {
|
|
4380
|
+
try {
|
|
4381
|
+
db.exec(sql);
|
|
4382
|
+
} catch {}
|
|
4383
|
+
};
|
|
4384
|
+
ensureTable("agents", `
|
|
4385
|
+
CREATE TABLE agents (
|
|
4386
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
|
|
4387
|
+
role TEXT DEFAULT 'agent', permissions TEXT DEFAULT '["*"]',
|
|
4388
|
+
metadata TEXT DEFAULT '{}',
|
|
4389
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4390
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4391
|
+
)`);
|
|
4392
|
+
ensureTable("task_lists", `
|
|
4393
|
+
CREATE TABLE task_lists (
|
|
4394
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
4395
|
+
slug TEXT NOT NULL, name TEXT NOT NULL, description TEXT,
|
|
4396
|
+
metadata TEXT DEFAULT '{}',
|
|
4397
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4398
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4399
|
+
UNIQUE(project_id, slug)
|
|
4400
|
+
)`);
|
|
4401
|
+
ensureTable("plans", `
|
|
4402
|
+
CREATE TABLE plans (
|
|
4403
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
4404
|
+
task_list_id TEXT, agent_id TEXT,
|
|
4405
|
+
name TEXT NOT NULL, description TEXT,
|
|
4406
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
4407
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4408
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4409
|
+
)`);
|
|
4410
|
+
ensureTable("task_tags", `
|
|
4411
|
+
CREATE TABLE task_tags (
|
|
4412
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
4413
|
+
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
4414
|
+
)`);
|
|
4415
|
+
ensureTable("task_history", `
|
|
4416
|
+
CREATE TABLE task_history (
|
|
4417
|
+
id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
4418
|
+
action TEXT NOT NULL, field TEXT, old_value TEXT, new_value TEXT, agent_id TEXT,
|
|
4419
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4420
|
+
)`);
|
|
4421
|
+
ensureTable("webhooks", `
|
|
4422
|
+
CREATE TABLE webhooks (
|
|
4423
|
+
id TEXT PRIMARY KEY, url TEXT NOT NULL, events TEXT NOT NULL DEFAULT '[]',
|
|
4424
|
+
secret TEXT, active INTEGER NOT NULL DEFAULT 1,
|
|
4425
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4426
|
+
)`);
|
|
4427
|
+
ensureTable("task_templates", `
|
|
4428
|
+
CREATE TABLE task_templates (
|
|
4429
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL, title_pattern TEXT NOT NULL,
|
|
4430
|
+
description TEXT, priority TEXT DEFAULT 'medium', tags TEXT DEFAULT '[]',
|
|
4431
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
4432
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
4433
|
+
metadata TEXT DEFAULT '{}',
|
|
4434
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4435
|
+
)`);
|
|
4436
|
+
ensureColumn("projects", "task_list_id", "TEXT");
|
|
4437
|
+
ensureColumn("projects", "task_prefix", "TEXT");
|
|
4438
|
+
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
4439
|
+
ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
|
|
4440
|
+
ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
|
|
4441
|
+
ensureColumn("tasks", "short_id", "TEXT");
|
|
4381
4442
|
ensureColumn("tasks", "due_at", "TEXT");
|
|
4382
4443
|
ensureColumn("tasks", "estimated_minutes", "INTEGER");
|
|
4383
4444
|
ensureColumn("tasks", "requires_approval", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -4387,6 +4448,21 @@ function ensureTableMigrations(db) {
|
|
|
4387
4448
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
4388
4449
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
4389
4450
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
4451
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
4452
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
|
|
4453
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
|
|
4454
|
+
ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
|
|
4455
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name)");
|
|
4456
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id)");
|
|
4457
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
4458
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
4459
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
4460
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
4461
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
4462
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
4463
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
|
|
4464
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
|
|
4465
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
|
|
4390
4466
|
}
|
|
4391
4467
|
function backfillTaskTags(db) {
|
|
4392
4468
|
try {
|
package/dist/server/index.js
CHANGED
|
@@ -149,29 +149,20 @@ function runMigrations(db) {
|
|
|
149
149
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
150
150
|
const currentLevel = result?.max_id ?? 0;
|
|
151
151
|
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
152
|
-
|
|
152
|
+
try {
|
|
153
|
+
db.exec(MIGRATIONS[i]);
|
|
154
|
+
} catch {}
|
|
153
155
|
}
|
|
154
156
|
} catch {
|
|
155
157
|
for (const migration of MIGRATIONS) {
|
|
156
|
-
|
|
158
|
+
try {
|
|
159
|
+
db.exec(migration);
|
|
160
|
+
} catch {}
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
|
-
|
|
163
|
+
ensureSchema(db);
|
|
160
164
|
}
|
|
161
|
-
function
|
|
162
|
-
try {
|
|
163
|
-
const hasAgents = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'").get();
|
|
164
|
-
if (!hasAgents) {
|
|
165
|
-
db.exec(MIGRATIONS[4]);
|
|
166
|
-
}
|
|
167
|
-
} catch {}
|
|
168
|
-
try {
|
|
169
|
-
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
170
|
-
} catch {
|
|
171
|
-
try {
|
|
172
|
-
db.exec(MIGRATIONS[5]);
|
|
173
|
-
} catch {}
|
|
174
|
-
}
|
|
165
|
+
function ensureSchema(db) {
|
|
175
166
|
const ensureColumn = (table, column, type) => {
|
|
176
167
|
try {
|
|
177
168
|
db.query(`SELECT ${column} FROM ${table} LIMIT 0`).get();
|
|
@@ -181,6 +172,76 @@ function ensureTableMigrations(db) {
|
|
|
181
172
|
} catch {}
|
|
182
173
|
}
|
|
183
174
|
};
|
|
175
|
+
const ensureTable = (name, sql) => {
|
|
176
|
+
try {
|
|
177
|
+
const exists = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
178
|
+
if (!exists)
|
|
179
|
+
db.exec(sql);
|
|
180
|
+
} catch {}
|
|
181
|
+
};
|
|
182
|
+
const ensureIndex = (sql) => {
|
|
183
|
+
try {
|
|
184
|
+
db.exec(sql);
|
|
185
|
+
} catch {}
|
|
186
|
+
};
|
|
187
|
+
ensureTable("agents", `
|
|
188
|
+
CREATE TABLE agents (
|
|
189
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
|
|
190
|
+
role TEXT DEFAULT 'agent', permissions TEXT DEFAULT '["*"]',
|
|
191
|
+
metadata TEXT DEFAULT '{}',
|
|
192
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
193
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
194
|
+
)`);
|
|
195
|
+
ensureTable("task_lists", `
|
|
196
|
+
CREATE TABLE task_lists (
|
|
197
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
198
|
+
slug TEXT NOT NULL, name TEXT NOT NULL, description TEXT,
|
|
199
|
+
metadata TEXT DEFAULT '{}',
|
|
200
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
201
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
202
|
+
UNIQUE(project_id, slug)
|
|
203
|
+
)`);
|
|
204
|
+
ensureTable("plans", `
|
|
205
|
+
CREATE TABLE plans (
|
|
206
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
207
|
+
task_list_id TEXT, agent_id TEXT,
|
|
208
|
+
name TEXT NOT NULL, description TEXT,
|
|
209
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
210
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
211
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
212
|
+
)`);
|
|
213
|
+
ensureTable("task_tags", `
|
|
214
|
+
CREATE TABLE task_tags (
|
|
215
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
216
|
+
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
217
|
+
)`);
|
|
218
|
+
ensureTable("task_history", `
|
|
219
|
+
CREATE TABLE task_history (
|
|
220
|
+
id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
221
|
+
action TEXT NOT NULL, field TEXT, old_value TEXT, new_value TEXT, agent_id TEXT,
|
|
222
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
223
|
+
)`);
|
|
224
|
+
ensureTable("webhooks", `
|
|
225
|
+
CREATE TABLE webhooks (
|
|
226
|
+
id TEXT PRIMARY KEY, url TEXT NOT NULL, events TEXT NOT NULL DEFAULT '[]',
|
|
227
|
+
secret TEXT, active INTEGER NOT NULL DEFAULT 1,
|
|
228
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
229
|
+
)`);
|
|
230
|
+
ensureTable("task_templates", `
|
|
231
|
+
CREATE TABLE task_templates (
|
|
232
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL, title_pattern TEXT NOT NULL,
|
|
233
|
+
description TEXT, priority TEXT DEFAULT 'medium', tags TEXT DEFAULT '[]',
|
|
234
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
235
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
236
|
+
metadata TEXT DEFAULT '{}',
|
|
237
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
238
|
+
)`);
|
|
239
|
+
ensureColumn("projects", "task_list_id", "TEXT");
|
|
240
|
+
ensureColumn("projects", "task_prefix", "TEXT");
|
|
241
|
+
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
242
|
+
ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
|
|
243
|
+
ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
|
|
244
|
+
ensureColumn("tasks", "short_id", "TEXT");
|
|
184
245
|
ensureColumn("tasks", "due_at", "TEXT");
|
|
185
246
|
ensureColumn("tasks", "estimated_minutes", "INTEGER");
|
|
186
247
|
ensureColumn("tasks", "requires_approval", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -190,6 +251,21 @@ function ensureTableMigrations(db) {
|
|
|
190
251
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
191
252
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
192
253
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
254
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
255
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
|
|
256
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
|
|
257
|
+
ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
|
|
258
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name)");
|
|
259
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id)");
|
|
260
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
261
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
262
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
263
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
264
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
265
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
266
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
|
|
267
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
|
|
268
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
|
|
193
269
|
}
|
|
194
270
|
function backfillTaskTags(db) {
|
|
195
271
|
try {
|