heyio 0.42.0 → 1.0.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 +40 -52
- package/dist/api/auth.js +35 -38
- package/dist/api/server.js +157 -1139
- package/dist/config.js +49 -32
- package/dist/copilot/agents.js +72 -1055
- package/dist/copilot/client.js +6 -17
- package/dist/copilot/io-scheduler.js +55 -139
- package/dist/copilot/model-router.js +100 -72
- package/dist/copilot/orchestrator.js +91 -515
- package/dist/copilot/scheduler.js +67 -189
- package/dist/copilot/skills.js +41 -366
- package/dist/copilot/system-message.js +40 -200
- package/dist/copilot/tools.js +191 -2042
- package/dist/daemon.js +54 -201
- package/dist/index.js +15 -133
- package/dist/mcp/config.js +23 -31
- package/dist/mcp/index.js +2 -3
- package/dist/mcp/registry.js +33 -88
- package/dist/notify.js +18 -100
- package/dist/paths.js +13 -24
- package/dist/setup.js +35 -0
- package/dist/store/db.js +111 -297
- package/dist/store/feed.js +29 -97
- package/dist/store/instances.js +56 -121
- package/dist/store/schedules.js +21 -73
- package/dist/store/squads.js +35 -186
- package/dist/store/tasks.js +25 -168
- package/dist/telegram/bot.js +20 -312
- package/dist/telegram/handlers.js +39 -3
- package/dist/watchdog.js +31 -45
- package/dist/wiki/fs.js +38 -155
- package/dist/wiki/search.js +31 -44
- package/package.json +5 -8
- package/web-dist/assets/ChatView-EFFiln1H.js +11 -0
- package/web-dist/assets/FeedView-bN4NMOL7.js +6 -0
- package/web-dist/assets/LoginView-CNtasq3n.js +1 -0
- package/web-dist/assets/McpView-C2CHiwsi.js +1 -0
- package/web-dist/assets/SchedulesView-CyilLban.js +1 -0
- package/web-dist/assets/SettingsView-1wLXKEF4.js +1 -0
- package/web-dist/assets/SkillsView-BLsD-0u0.js +1 -0
- package/web-dist/assets/SquadDetailView-CsCw2ZLp.js +21 -0
- package/web-dist/assets/SquadsView-DQ3vFlyO.js +6 -0
- package/web-dist/assets/WikiView-19M3oqnq.js +21 -0
- package/web-dist/assets/api-WGvTsXaE.js +1 -0
- package/web-dist/assets/index-D7M5O-_l.css +1 -0
- package/web-dist/assets/index-DZOS9syn.js +95 -0
- package/web-dist/assets/plus-BOvyX1BC.js +6 -0
- package/web-dist/assets/trash-2-DHoetkC4.js +6 -0
- package/web-dist/favicon.svg +4 -1
- package/web-dist/index.html +7 -10
- package/dist/api/logout.test.js +0 -129
- package/dist/api/mcp.test.js +0 -285
- package/dist/api/wiki.test.js +0 -283
- package/dist/auth/session-logic.js +0 -79
- package/dist/auth/session-logic.test.js +0 -201
- package/dist/copilot/auto-complete-instance.test.js +0 -104
- package/dist/copilot/cron.js +0 -136
- package/dist/copilot/event-summary.js +0 -286
- package/dist/copilot/instance-deactivate.test.js +0 -119
- package/dist/copilot/model-router.test.js +0 -71
- package/dist/copilot/review-backfill.js +0 -57
- package/dist/copilot/session-timeout.js +0 -112
- package/dist/copilot/session-timeout.test.js +0 -372
- package/dist/copilot/skills.test.js +0 -55
- package/dist/copilot/universes.js +0 -469
- package/dist/instance-watchdog.js +0 -104
- package/dist/instance-watchdog.test.js +0 -183
- package/dist/mcp/client.js +0 -109
- package/dist/mcp/client.test.js +0 -99
- package/dist/mcp/config.test.js +0 -49
- package/dist/mcp/registry.test.js +0 -79
- package/dist/notify.test.js +0 -232
- package/dist/store/feed.test.js +0 -279
- package/dist/store/instances.test.js +0 -310
- package/dist/store/io-schedules.js +0 -63
- package/dist/store/notifications.js +0 -79
- package/dist/store/notifications.test.js +0 -197
- package/dist/store/schedule-runs.js +0 -46
- package/dist/store/squads.test.js +0 -405
- package/dist/store/tasks.test.js +0 -150
- package/dist/store/worktrees.js +0 -83
- package/dist/tui/index.js +0 -286
- package/dist/update.js +0 -81
- package/dist/watchdog.test.js +0 -83
- package/dist/wiki/wiki-squad.test.js +0 -54
- package/web-dist/assets/AgentActivityView-CedxxE6K.js +0 -1
- package/web-dist/assets/ChatView-DMkYQo_V.js +0 -4
- package/web-dist/assets/FeedView-BH4q-31V.js +0 -1
- package/web-dist/assets/InboxView-BVwVP4EW.js +0 -1
- package/web-dist/assets/LoginView-DRPDhnwu.js +0 -1
- package/web-dist/assets/McpView-D8yWz-lq.js +0 -1
- package/web-dist/assets/SchedulesView-BzzyncGF.js +0 -1
- package/web-dist/assets/SettingsTabs.vue_vue_type_script_setup_true_lang-oW3ySu7Y.js +0 -1
- package/web-dist/assets/SkillsView-oxpYuhx7.js +0 -1
- package/web-dist/assets/SquadsView-CaKUIKlq.js +0 -1
- package/web-dist/assets/StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js +0 -1
- package/web-dist/assets/WikiView-C5jXUlfW.js +0 -1
- package/web-dist/assets/index-BrWzNw-N.css +0 -10
- package/web-dist/assets/index-f67odrrt.js +0 -81
- package/web-dist/icons.svg +0 -24
package/dist/store/feed.js
CHANGED
|
@@ -1,112 +1,44 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { getDb } from "./db.js";
|
|
2
|
-
export function
|
|
3
|
+
export function postFeedItem(source, title, content) {
|
|
3
4
|
const db = getDb();
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.run(input.type, input.title, input.body, input.source_type ?? null, input.source_ref ?? null, input.squad_slug ?? null, input.instance_id ?? null, input.task_id ?? null);
|
|
8
|
-
return db
|
|
9
|
-
.prepare("SELECT * FROM unified_feed WHERE id = ?")
|
|
10
|
-
.get(info.lastInsertRowid);
|
|
5
|
+
const id = randomUUID();
|
|
6
|
+
db.prepare("INSERT INTO feed_items (id, source, title, content) VALUES (?, ?, ?, ?)").run(id, source, title, content);
|
|
7
|
+
return db.prepare("SELECT * FROM feed_items WHERE id = ?").get(id);
|
|
11
8
|
}
|
|
12
|
-
export function
|
|
13
|
-
const
|
|
9
|
+
export function getFeedItems(opts) {
|
|
10
|
+
const db = getDb();
|
|
11
|
+
let sql = "SELECT * FROM feed_items WHERE 1=1";
|
|
14
12
|
const params = [];
|
|
15
|
-
if (opts?.type) {
|
|
16
|
-
conditions.push("type = ?");
|
|
17
|
-
params.push(opts.type);
|
|
18
|
-
}
|
|
19
13
|
if (opts?.unreadOnly) {
|
|
20
|
-
|
|
14
|
+
sql += " AND read = 0";
|
|
21
15
|
}
|
|
22
|
-
if (opts?.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
params.push(term, term);
|
|
16
|
+
if (opts?.source) {
|
|
17
|
+
sql += " AND source = ?";
|
|
18
|
+
params.push(opts.source);
|
|
26
19
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
sql += " ORDER BY created_at DESC";
|
|
21
|
+
if (opts?.limit) {
|
|
22
|
+
sql += " LIMIT ?";
|
|
23
|
+
params.push(opts.limit);
|
|
30
24
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return getDb()
|
|
35
|
-
.prepare(`SELECT * FROM unified_feed ${where} ORDER BY created_at DESC, id DESC LIMIT ?`)
|
|
36
|
-
.all(...params);
|
|
37
|
-
}
|
|
38
|
-
export function listFeedSquads() {
|
|
39
|
-
const rows = getDb()
|
|
40
|
-
.prepare(`SELECT DISTINCT substr(title, 2, instr(title, ']') - 2) AS squad
|
|
41
|
-
FROM unified_feed
|
|
42
|
-
WHERE title LIKE '[%]%'
|
|
43
|
-
ORDER BY squad`)
|
|
44
|
-
.all();
|
|
45
|
-
return rows.map((r) => r.squad);
|
|
46
|
-
}
|
|
47
|
-
export function countUnreadFeedEntries(type) {
|
|
48
|
-
if (type) {
|
|
49
|
-
const row = getDb()
|
|
50
|
-
.prepare("SELECT COUNT(*) AS n FROM unified_feed WHERE read_at IS NULL AND type = ?")
|
|
51
|
-
.get(type);
|
|
52
|
-
return row.n;
|
|
25
|
+
if (opts?.offset) {
|
|
26
|
+
sql += " OFFSET ?";
|
|
27
|
+
params.push(opts.offset);
|
|
53
28
|
}
|
|
54
|
-
|
|
55
|
-
.prepare("SELECT COUNT(*) AS n FROM unified_feed WHERE read_at IS NULL")
|
|
56
|
-
.get();
|
|
57
|
-
return row.n;
|
|
29
|
+
return db.prepare(sql).all(...params);
|
|
58
30
|
}
|
|
59
|
-
export function
|
|
31
|
+
export function markFeedItemRead(id) {
|
|
60
32
|
const db = getDb();
|
|
61
|
-
|
|
62
|
-
.prepare("UPDATE unified_feed SET read_at = CURRENT_TIMESTAMP WHERE id = ? AND read_at IS NULL")
|
|
63
|
-
.run(id);
|
|
64
|
-
if (info.changes > 0)
|
|
65
|
-
return true;
|
|
66
|
-
// Idempotent: return true if row exists (already read), false if missing
|
|
67
|
-
const exists = db.prepare("SELECT id FROM unified_feed WHERE id = ?").get(id);
|
|
68
|
-
return exists !== undefined;
|
|
69
|
-
}
|
|
70
|
-
export function markAllFeedEntriesRead(type) {
|
|
71
|
-
if (type) {
|
|
72
|
-
const info = getDb()
|
|
73
|
-
.prepare("UPDATE unified_feed SET read_at = CURRENT_TIMESTAMP WHERE read_at IS NULL AND type = ?")
|
|
74
|
-
.run(type);
|
|
75
|
-
return info.changes;
|
|
76
|
-
}
|
|
77
|
-
const info = getDb()
|
|
78
|
-
.prepare("UPDATE unified_feed SET read_at = CURRENT_TIMESTAMP WHERE read_at IS NULL")
|
|
79
|
-
.run();
|
|
80
|
-
return info.changes;
|
|
81
|
-
}
|
|
82
|
-
export function deleteFeedEntry(id) {
|
|
83
|
-
const info = getDb()
|
|
84
|
-
.prepare("DELETE FROM unified_feed WHERE id = ?")
|
|
85
|
-
.run(id);
|
|
86
|
-
return info.changes > 0;
|
|
87
|
-
}
|
|
88
|
-
export function pruneOldFeedEntries(olderThanDays) {
|
|
89
|
-
const info = getDb()
|
|
90
|
-
.prepare(`DELETE FROM unified_feed WHERE created_at < datetime('now', '-' || ? || ' days')`)
|
|
91
|
-
.run(olderThanDays);
|
|
92
|
-
return info.changes;
|
|
33
|
+
db.prepare("UPDATE feed_items SET read = 1 WHERE id = ?").run(id);
|
|
93
34
|
}
|
|
94
|
-
export function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const placeholders = ids.map(() => "?").join(", ");
|
|
98
|
-
const info = getDb()
|
|
99
|
-
.prepare(`UPDATE unified_feed SET read_at = CURRENT_TIMESTAMP WHERE id IN (${placeholders}) AND read_at IS NULL`)
|
|
100
|
-
.run(...ids);
|
|
101
|
-
return info.changes;
|
|
35
|
+
export function deleteFeedItem(id) {
|
|
36
|
+
const db = getDb();
|
|
37
|
+
db.prepare("DELETE FROM feed_items WHERE id = ?").run(id);
|
|
102
38
|
}
|
|
103
|
-
export function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const info = getDb()
|
|
108
|
-
.prepare(`DELETE FROM unified_feed WHERE id IN (${placeholders})`)
|
|
109
|
-
.run(...ids);
|
|
110
|
-
return info.changes;
|
|
39
|
+
export function getUnreadCount() {
|
|
40
|
+
const db = getDb();
|
|
41
|
+
const row = db.prepare("SELECT COUNT(*) as count FROM feed_items WHERE read = 0").get();
|
|
42
|
+
return row.count;
|
|
111
43
|
}
|
|
112
44
|
//# sourceMappingURL=feed.js.map
|
package/dist/store/instances.js
CHANGED
|
@@ -1,139 +1,74 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
1
3
|
import { getDb } from "./db.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { readSquadWikiPages } from "../wiki/fs.js";
|
|
5
|
-
export function ensureInstanceTables() {
|
|
4
|
+
const MAX_INSTANCES_PER_SQUAD = 3;
|
|
5
|
+
export async function createInstance(squadId, branch) {
|
|
6
6
|
const db = getDb();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
branch_name TEXT NOT NULL,
|
|
14
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
15
|
-
context_snapshot TEXT,
|
|
16
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
17
|
-
completed_at DATETIME
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
CREATE TABLE IF NOT EXISTS instance_decisions (
|
|
21
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
-
instance_id TEXT NOT NULL,
|
|
23
|
-
decision TEXT NOT NULL,
|
|
24
|
-
context TEXT,
|
|
25
|
-
merged_to_master INTEGER DEFAULT 0,
|
|
26
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
27
|
-
);
|
|
28
|
-
`);
|
|
29
|
-
}
|
|
30
|
-
export const MAX_CONCURRENT_INSTANCES = 3;
|
|
31
|
-
export function createInstance(input) {
|
|
32
|
-
const db = getDb();
|
|
33
|
-
const activeCount = db
|
|
34
|
-
.prepare("SELECT COUNT(*) as cnt FROM squad_instances WHERE master_squad_slug = ? AND status NOT IN ('done', 'failed')")
|
|
35
|
-
.get(input.masterSquadSlug).cnt;
|
|
36
|
-
if (activeCount >= MAX_CONCURRENT_INSTANCES) {
|
|
37
|
-
throw new Error(`Max concurrent instances (${MAX_CONCURRENT_INSTANCES}) reached for squad "${input.masterSquadSlug}"`);
|
|
7
|
+
// Check max instances
|
|
8
|
+
const active = db
|
|
9
|
+
.prepare("SELECT COUNT(*) as count FROM instances WHERE squad_id = ? AND status = 'active'")
|
|
10
|
+
.get(squadId);
|
|
11
|
+
if (active.count >= MAX_INSTANCES_PER_SQUAD) {
|
|
12
|
+
throw new Error(`Squad already has ${MAX_INSTANCES_PER_SQUAD} active instances. Destroy one first.`);
|
|
38
13
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export function getInstance(id) {
|
|
44
|
-
const db = getDb();
|
|
45
|
-
return db.prepare("SELECT * FROM squad_instances WHERE id = ?").get(id);
|
|
46
|
-
}
|
|
47
|
-
export function listInstances(masterSquadSlug, opts) {
|
|
48
|
-
const db = getDb();
|
|
49
|
-
const includeCompleted = opts?.includeCompleted ?? false;
|
|
50
|
-
if (includeCompleted) {
|
|
51
|
-
return db
|
|
52
|
-
.prepare("SELECT id, issue_ref, status, branch_name, created_at, completed_at FROM squad_instances WHERE master_squad_slug = ? ORDER BY created_at DESC")
|
|
53
|
-
.all(masterSquadSlug);
|
|
14
|
+
// Get squad repo to determine worktree location
|
|
15
|
+
const squad = db.prepare("SELECT * FROM squads WHERE id = ?").get(squadId);
|
|
16
|
+
if (!squad?.repo_url) {
|
|
17
|
+
throw new Error("Squad has no repo_url configured.");
|
|
54
18
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
19
|
+
const id = randomUUID();
|
|
20
|
+
const worktreePath = `/tmp/io-worktrees/${squadId}/${branch}`;
|
|
21
|
+
// Create git worktree
|
|
22
|
+
try {
|
|
23
|
+
execSync(`git worktree add ${worktreePath} -b ${branch}`, {
|
|
24
|
+
cwd: squad.repo_url.startsWith("/") ? squad.repo_url : process.cwd(),
|
|
25
|
+
stdio: "pipe",
|
|
26
|
+
});
|
|
63
27
|
}
|
|
64
|
-
|
|
65
|
-
|
|
28
|
+
catch (err) {
|
|
29
|
+
// Branch may already exist
|
|
30
|
+
execSync(`git worktree add ${worktreePath} ${branch}`, {
|
|
31
|
+
cwd: squad.repo_url.startsWith("/") ? squad.repo_url : process.cwd(),
|
|
32
|
+
stdio: "pipe",
|
|
33
|
+
});
|
|
66
34
|
}
|
|
35
|
+
db.prepare(`INSERT INTO instances (id, squad_id, branch, worktree_path, status)
|
|
36
|
+
VALUES (?, ?, ?, ?, 'active')`).run(id, squadId, branch, worktreePath);
|
|
37
|
+
return db.prepare("SELECT * FROM instances WHERE id = ?").get(id);
|
|
67
38
|
}
|
|
68
|
-
export function
|
|
39
|
+
export async function destroyInstance(instanceId) {
|
|
69
40
|
const db = getDb();
|
|
70
|
-
|
|
41
|
+
const instance = db
|
|
42
|
+
.prepare("SELECT * FROM instances WHERE id = ?")
|
|
43
|
+
.get(instanceId);
|
|
44
|
+
if (!instance)
|
|
45
|
+
throw new Error(`Instance ${instanceId} not found.`);
|
|
46
|
+
// Remove worktree
|
|
47
|
+
try {
|
|
48
|
+
execSync(`git worktree remove ${instance.worktree_path} --force`, {
|
|
49
|
+
stdio: "pipe",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Already removed or doesn't exist
|
|
54
|
+
}
|
|
55
|
+
db.prepare("UPDATE instances SET status = 'destroyed' WHERE id = ?").run(instanceId);
|
|
71
56
|
}
|
|
72
|
-
export function
|
|
57
|
+
export function getInstancesForSquad(squadId) {
|
|
73
58
|
const db = getDb();
|
|
74
59
|
return db
|
|
75
|
-
.prepare("SELECT
|
|
76
|
-
.all(
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Merge instance decisions back to master squad. Returns count merged.
|
|
80
|
-
*/
|
|
81
|
-
export function mergeInstanceDecisions(instanceId, masterSquadSlug) {
|
|
82
|
-
const db = getDb();
|
|
83
|
-
const decisions = db
|
|
84
|
-
.prepare("SELECT id, decision, context FROM instance_decisions WHERE instance_id = ? AND merged_to_master = 0")
|
|
85
|
-
.all(instanceId);
|
|
86
|
-
if (decisions.length === 0)
|
|
87
|
-
return 0;
|
|
88
|
-
const insertStmt = db.prepare("INSERT INTO squad_decisions (squad_slug, decision, context) VALUES (?, ?, ?)");
|
|
89
|
-
const markStmt = db.prepare("UPDATE instance_decisions SET merged_to_master = 1 WHERE id = ?");
|
|
90
|
-
const mergeAll = db.transaction(() => {
|
|
91
|
-
for (const d of decisions) {
|
|
92
|
-
const ctx = d.context
|
|
93
|
-
? `${d.context} [from instance: ${instanceId}]`
|
|
94
|
-
: `[from instance: ${instanceId}]`;
|
|
95
|
-
insertStmt.run(masterSquadSlug, d.decision, ctx);
|
|
96
|
-
markStmt.run(d.id);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
mergeAll();
|
|
100
|
-
return decisions.length;
|
|
60
|
+
.prepare("SELECT * FROM instances WHERE squad_id = ? AND status = 'active' ORDER BY created_at")
|
|
61
|
+
.all(squadId);
|
|
101
62
|
}
|
|
102
|
-
export function
|
|
63
|
+
export function touchInstanceActivity(instanceId) {
|
|
103
64
|
const db = getDb();
|
|
104
|
-
db.prepare("
|
|
105
|
-
db.prepare("DELETE FROM squad_instances WHERE id = ?").run(id);
|
|
65
|
+
db.prepare("UPDATE instances SET last_activity = datetime('now') WHERE id = ?").run(instanceId);
|
|
106
66
|
}
|
|
107
|
-
|
|
108
|
-
* Build a JSON snapshot of the master squad's recent decisions for context inheritance.
|
|
109
|
-
*/
|
|
110
|
-
export function buildContextSnapshot(masterSquadSlug, limit = 30) {
|
|
111
|
-
const decisions = getDecisions(masterSquadSlug, limit);
|
|
112
|
-
const wikiPages = readSquadWikiPages(masterSquadSlug);
|
|
113
|
-
const snapshot = {
|
|
114
|
-
decisions: decisions.map((d) => ({ decision: d.decision, context: d.context, created_at: d.created_at })),
|
|
115
|
-
};
|
|
116
|
-
if (wikiPages.length > 0) {
|
|
117
|
-
snapshot.wiki = wikiPages.map(p => ({ path: p.path, content: p.content }));
|
|
118
|
-
}
|
|
119
|
-
return JSON.stringify(snapshot);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Reconcile instances on startup: detect orphaned worktrees and mark stale active instances.
|
|
123
|
-
* Returns the number of instances cleaned up.
|
|
124
|
-
*/
|
|
125
|
-
export function reconcileInstances() {
|
|
67
|
+
export function getStaleInstances(minutesThreshold = 30) {
|
|
126
68
|
const db = getDb();
|
|
127
|
-
|
|
128
|
-
.prepare(
|
|
69
|
+
return db
|
|
70
|
+
.prepare(`SELECT * FROM instances WHERE status = 'active'
|
|
71
|
+
AND datetime(last_activity, '+${minutesThreshold} minutes') < datetime('now')`)
|
|
129
72
|
.all();
|
|
130
|
-
let cleaned = 0;
|
|
131
|
-
for (const inst of activeInstances) {
|
|
132
|
-
if (!worktreeExists(inst.worktree_path)) {
|
|
133
|
-
updateInstanceStatus(inst.id, "failed");
|
|
134
|
-
cleaned++;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return cleaned;
|
|
138
73
|
}
|
|
139
74
|
//# sourceMappingURL=instances.js.map
|
package/dist/store/schedules.js
CHANGED
|
@@ -1,83 +1,31 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { getDb } from "./db.js";
|
|
2
|
-
function rowToSchedule(row) {
|
|
3
|
-
let agenda = [];
|
|
4
|
-
try {
|
|
5
|
-
agenda = JSON.parse(row.agenda);
|
|
6
|
-
if (!Array.isArray(agenda))
|
|
7
|
-
agenda = [];
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
agenda = [];
|
|
11
|
-
}
|
|
12
|
-
return { ...row, agenda };
|
|
13
|
-
}
|
|
14
3
|
export function createSchedule(input) {
|
|
15
4
|
const db = getDb();
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.run(input.squadSlug, input.name, input.cronExpr, JSON.stringify(input.agenda), input.notes ?? null, input.nextRunAt);
|
|
21
|
-
const id = Number(info.lastInsertRowid);
|
|
22
|
-
return getSchedule(id);
|
|
5
|
+
const id = randomUUID();
|
|
6
|
+
db.prepare(`INSERT INTO schedules (id, type, squad_id, cron, agenda, prompt)
|
|
7
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(id, input.type, input.squad_id ?? null, input.cron, input.agenda ?? "", input.prompt ?? "");
|
|
8
|
+
return db.prepare("SELECT * FROM schedules WHERE id = ?").get(id);
|
|
23
9
|
}
|
|
24
|
-
export function
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
export function listSchedules(type) {
|
|
11
|
+
const db = getDb();
|
|
12
|
+
if (type) {
|
|
13
|
+
return db
|
|
14
|
+
.prepare("SELECT * FROM schedules WHERE type = ? ORDER BY created_at")
|
|
15
|
+
.all(type);
|
|
16
|
+
}
|
|
17
|
+
return db.prepare("SELECT * FROM schedules ORDER BY created_at").all();
|
|
29
18
|
}
|
|
30
|
-
export function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
.prepare("SELECT * FROM squad_schedules WHERE squad_slug = ? ORDER BY id ASC")
|
|
34
|
-
.all(squadSlug)
|
|
35
|
-
: getDb()
|
|
36
|
-
.prepare("SELECT * FROM squad_schedules ORDER BY squad_slug, id ASC")
|
|
37
|
-
.all();
|
|
38
|
-
return rows.map(rowToSchedule);
|
|
19
|
+
export function updateScheduleLastRun(id) {
|
|
20
|
+
const db = getDb();
|
|
21
|
+
db.prepare("UPDATE schedules SET last_run = datetime('now') WHERE id = ?").run(id);
|
|
39
22
|
}
|
|
40
|
-
export function
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
.prepare(`SELECT * FROM squad_schedules
|
|
44
|
-
WHERE enabled = 1
|
|
45
|
-
AND next_run_at IS NOT NULL
|
|
46
|
-
AND next_run_at <= ?
|
|
47
|
-
ORDER BY next_run_at ASC`)
|
|
48
|
-
.all(iso);
|
|
49
|
-
return rows.map(rowToSchedule);
|
|
23
|
+
export function toggleSchedule(id, enabled) {
|
|
24
|
+
const db = getDb();
|
|
25
|
+
db.prepare("UPDATE schedules SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
|
|
50
26
|
}
|
|
51
27
|
export function deleteSchedule(id) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
.run(id);
|
|
55
|
-
return info.changes > 0;
|
|
56
|
-
}
|
|
57
|
-
export function setScheduleEnabled(id, enabled) {
|
|
58
|
-
const info = getDb()
|
|
59
|
-
.prepare("UPDATE squad_schedules SET enabled = ? WHERE id = ?")
|
|
60
|
-
.run(enabled ? 1 : 0, id);
|
|
61
|
-
return info.changes > 0;
|
|
62
|
-
}
|
|
63
|
-
export function recordScheduleRun(id, ranAt, nextRunAt) {
|
|
64
|
-
getDb()
|
|
65
|
-
.prepare("UPDATE squad_schedules SET last_run_at = ?, next_run_at = ? WHERE id = ?")
|
|
66
|
-
.run(ranAt.toISOString(), nextRunAt, id);
|
|
67
|
-
}
|
|
68
|
-
export function updateNextRun(id, nextRunAt) {
|
|
69
|
-
getDb()
|
|
70
|
-
.prepare("UPDATE squad_schedules SET next_run_at = ? WHERE id = ?")
|
|
71
|
-
.run(nextRunAt, id);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Overwrite both last_run_at and next_run_at directly. Unlike
|
|
75
|
-
* recordScheduleRun this accepts NULL for last_run_at, which is needed when
|
|
76
|
-
* restoring a schedule's "never run" state after a manual run_now.
|
|
77
|
-
*/
|
|
78
|
-
export function setScheduleTimestamps(id, lastRunAt, nextRunAt) {
|
|
79
|
-
getDb()
|
|
80
|
-
.prepare("UPDATE squad_schedules SET last_run_at = ?, next_run_at = ? WHERE id = ?")
|
|
81
|
-
.run(lastRunAt, nextRunAt, id);
|
|
28
|
+
const db = getDb();
|
|
29
|
+
db.prepare("DELETE FROM schedules WHERE id = ?").run(id);
|
|
82
30
|
}
|
|
83
31
|
//# sourceMappingURL=schedules.js.map
|