swarm-mail 0.1.3 → 0.2.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 +4 -0
- package/bin/daemon-cli.ts +304 -0
- package/dist/beads/adapter.d.ts +38 -0
- package/dist/beads/adapter.d.ts.map +1 -0
- package/dist/beads/blocked-cache.d.ts +21 -0
- package/dist/beads/blocked-cache.d.ts.map +1 -0
- package/dist/beads/comments.d.ts +21 -0
- package/dist/beads/comments.d.ts.map +1 -0
- package/dist/beads/dependencies.d.ts +58 -0
- package/dist/beads/dependencies.d.ts.map +1 -0
- package/dist/beads/events.d.ts +163 -0
- package/dist/beads/events.d.ts.map +1 -0
- package/dist/beads/flush-manager.d.ts +71 -0
- package/dist/beads/flush-manager.d.ts.map +1 -0
- package/dist/beads/index.d.ts +25 -0
- package/dist/beads/index.d.ts.map +1 -0
- package/dist/beads/jsonl.d.ts +103 -0
- package/dist/beads/jsonl.d.ts.map +1 -0
- package/dist/beads/labels.d.ts +21 -0
- package/dist/beads/labels.d.ts.map +1 -0
- package/dist/beads/merge.d.ts +99 -0
- package/dist/beads/merge.d.ts.map +1 -0
- package/dist/beads/migrations.d.ts +41 -0
- package/dist/beads/migrations.d.ts.map +1 -0
- package/dist/beads/operations.d.ts +56 -0
- package/dist/beads/operations.d.ts.map +1 -0
- package/dist/beads/projections.d.ts +103 -0
- package/dist/beads/projections.d.ts.map +1 -0
- package/dist/beads/queries.d.ts +77 -0
- package/dist/beads/queries.d.ts.map +1 -0
- package/dist/beads/store.d.ts +98 -0
- package/dist/beads/store.d.ts.map +1 -0
- package/dist/beads/validation.d.ts +75 -0
- package/dist/beads/validation.d.ts.map +1 -0
- package/dist/daemon.d.ts +139 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8314 -4513
- package/dist/pglite.d.ts +78 -5
- package/dist/pglite.d.ts.map +1 -1
- package/dist/socket-adapter.d.ts +78 -0
- package/dist/socket-adapter.d.ts.map +1 -0
- package/dist/streams/debug.d.ts.map +1 -1
- package/dist/streams/effect/ask.integration-test.d.ts +8 -0
- package/dist/streams/effect/ask.integration-test.d.ts.map +1 -0
- package/dist/streams/effect/cursor.integration-test.d.ts +2 -0
- package/dist/streams/effect/cursor.integration-test.d.ts.map +1 -0
- package/dist/streams/index.d.ts.map +1 -1
- package/dist/streams/leader-election.d.ts +44 -0
- package/dist/streams/leader-election.d.ts.map +1 -0
- package/dist/streams/migrations.d.ts +1 -1
- package/dist/streams/migrations.d.ts.map +1 -1
- package/dist/streams/store.d.ts.map +1 -1
- package/dist/streams/store.integration-test.d.ts +2 -0
- package/dist/streams/store.integration-test.d.ts.map +1 -0
- package/dist/types/beads-adapter.d.ts +397 -0
- package/dist/types/beads-adapter.d.ts.map +1 -0
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
Event sourcing primitives for multi-agent coordination. Local-first, no external servers.
|
|
32
32
|
|
|
33
|
+
**[🌐 swarmtools.ai](https://swarmtools.ai)** | **[📖 Full Documentation](https://swarmtools.ai/docs)**
|
|
34
|
+
|
|
33
35
|
```
|
|
34
36
|
┌─────────────────────────────────────────────────────────────┐
|
|
35
37
|
│ SWARM MAIL STACK │
|
|
@@ -190,6 +192,8 @@ Materialized views derived from events:
|
|
|
190
192
|
|
|
191
193
|
## API Reference
|
|
192
194
|
|
|
195
|
+
For complete API documentation, see [swarmtools.ai/docs](https://swarmtools.ai/docs).
|
|
196
|
+
|
|
193
197
|
### SwarmMailAdapter
|
|
194
198
|
|
|
195
199
|
```typescript
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* swarm-mail-daemon CLI
|
|
4
|
+
*
|
|
5
|
+
* Command-line interface for managing the swarm-mail pglite-server daemon.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* start [options] - Start the daemon
|
|
9
|
+
* stop - Stop the daemon
|
|
10
|
+
* status - Show daemon status
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```bash
|
|
14
|
+
* # Start daemon on default port
|
|
15
|
+
* swarm-mail-daemon start
|
|
16
|
+
*
|
|
17
|
+
* # Start with custom port
|
|
18
|
+
* swarm-mail-daemon start --port 5555
|
|
19
|
+
*
|
|
20
|
+
* # Start with Unix socket
|
|
21
|
+
* swarm-mail-daemon start --path /tmp/swarm-mail.sock
|
|
22
|
+
*
|
|
23
|
+
* # Check status
|
|
24
|
+
* swarm-mail-daemon status
|
|
25
|
+
*
|
|
26
|
+
* # Stop daemon
|
|
27
|
+
* swarm-mail-daemon stop
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { existsSync } from "node:fs";
|
|
32
|
+
import { readFile } from "node:fs/promises";
|
|
33
|
+
import { parseArgs } from "node:util";
|
|
34
|
+
import {
|
|
35
|
+
type DaemonOptions,
|
|
36
|
+
getPidFilePath,
|
|
37
|
+
healthCheck,
|
|
38
|
+
isDaemonRunning,
|
|
39
|
+
startDaemon,
|
|
40
|
+
stopDaemon,
|
|
41
|
+
} from "../src/daemon";
|
|
42
|
+
|
|
43
|
+
// Colors for terminal output
|
|
44
|
+
const colors = {
|
|
45
|
+
reset: "\x1b[0m",
|
|
46
|
+
red: "\x1b[31m",
|
|
47
|
+
green: "\x1b[32m",
|
|
48
|
+
yellow: "\x1b[33m",
|
|
49
|
+
blue: "\x1b[34m",
|
|
50
|
+
dim: "\x1b[2m",
|
|
51
|
+
bold: "\x1b[1m",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function success(msg: string) {
|
|
55
|
+
console.log(`${colors.green}✓${colors.reset} ${msg}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function error(msg: string) {
|
|
59
|
+
console.error(`${colors.red}✗${colors.reset} ${msg}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function info(msg: string) {
|
|
63
|
+
console.log(`${colors.blue}ℹ${colors.reset} ${msg}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function showHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
${colors.bold}swarm-mail-daemon${colors.reset} - Manage pglite-server daemon for swarm-mail
|
|
69
|
+
|
|
70
|
+
${colors.bold}USAGE${colors.reset}
|
|
71
|
+
swarm-mail-daemon <command> [options]
|
|
72
|
+
|
|
73
|
+
${colors.bold}COMMANDS${colors.reset}
|
|
74
|
+
start [options] Start the daemon
|
|
75
|
+
stop Stop the daemon
|
|
76
|
+
status Show daemon status
|
|
77
|
+
|
|
78
|
+
${colors.bold}START OPTIONS${colors.reset}
|
|
79
|
+
--port <number> TCP port to bind (default: 5433)
|
|
80
|
+
--host <string> Host to bind (default: 127.0.0.1)
|
|
81
|
+
--path <string> Unix socket path (alternative to port/host)
|
|
82
|
+
--db <string> Database path (default: .opencode/streams or ~/.opencode/streams)
|
|
83
|
+
--project <string> Project path for PID file location
|
|
84
|
+
|
|
85
|
+
${colors.bold}EXAMPLES${colors.reset}
|
|
86
|
+
# Start daemon on default port
|
|
87
|
+
swarm-mail-daemon start
|
|
88
|
+
|
|
89
|
+
# Start with custom port
|
|
90
|
+
swarm-mail-daemon start --port 5555
|
|
91
|
+
|
|
92
|
+
# Start with Unix socket
|
|
93
|
+
swarm-mail-daemon start --path /tmp/swarm-mail.sock
|
|
94
|
+
|
|
95
|
+
# Start with custom database path
|
|
96
|
+
swarm-mail-daemon start --db /custom/db/path
|
|
97
|
+
|
|
98
|
+
# Check status
|
|
99
|
+
swarm-mail-daemon status
|
|
100
|
+
|
|
101
|
+
# Stop daemon
|
|
102
|
+
swarm-mail-daemon stop
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Read PID from PID file
|
|
108
|
+
*/
|
|
109
|
+
async function readPid(projectPath?: string): Promise<number | null> {
|
|
110
|
+
const pidFilePath = getPidFilePath(projectPath);
|
|
111
|
+
if (!existsSync(pidFilePath)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const content = await readFile(pidFilePath, "utf-8");
|
|
116
|
+
const pid = Number.parseInt(content.trim(), 10);
|
|
117
|
+
if (Number.isNaN(pid) || pid <= 0) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return pid;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Start command handler
|
|
128
|
+
*/
|
|
129
|
+
async function startCommand(args: string[]): Promise<void> {
|
|
130
|
+
const { values } = parseArgs({
|
|
131
|
+
args,
|
|
132
|
+
options: {
|
|
133
|
+
port: { type: "string", short: "p" },
|
|
134
|
+
host: { type: "string", short: "h" },
|
|
135
|
+
path: { type: "string" },
|
|
136
|
+
db: { type: "string" },
|
|
137
|
+
project: { type: "string" },
|
|
138
|
+
help: { type: "boolean" },
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (values.help) {
|
|
143
|
+
showHelp();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const options: DaemonOptions = {
|
|
148
|
+
port: values.port ? Number.parseInt(values.port, 10) : undefined,
|
|
149
|
+
host: values.host,
|
|
150
|
+
path: values.path,
|
|
151
|
+
dbPath: values.db,
|
|
152
|
+
projectPath: values.project,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// Check if already running
|
|
157
|
+
if (await isDaemonRunning(options.projectPath)) {
|
|
158
|
+
const pid = await readPid(options.projectPath);
|
|
159
|
+
const connInfo = options.path
|
|
160
|
+
? `socket=${options.path}`
|
|
161
|
+
: `port=${options.port || 5433}`;
|
|
162
|
+
info(`Daemon already running (PID: ${pid}, ${connInfo})`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
info("Starting daemon...");
|
|
167
|
+
const daemonInfo = await startDaemon(options);
|
|
168
|
+
|
|
169
|
+
const connInfo = daemonInfo.socketPath
|
|
170
|
+
? `socket=${daemonInfo.socketPath}`
|
|
171
|
+
: `port=${daemonInfo.port}`;
|
|
172
|
+
success(`Daemon started (PID: ${daemonInfo.pid}, ${connInfo})`);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
error(
|
|
175
|
+
`Failed to start daemon: ${err instanceof Error ? err.message : String(err)}`,
|
|
176
|
+
);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Stop command handler
|
|
183
|
+
*/
|
|
184
|
+
async function stopCommand(args: string[]): Promise<void> {
|
|
185
|
+
const { values } = parseArgs({
|
|
186
|
+
args,
|
|
187
|
+
options: {
|
|
188
|
+
project: { type: "string" },
|
|
189
|
+
help: { type: "boolean" },
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (values.help) {
|
|
194
|
+
showHelp();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const pid = await readPid(values.project);
|
|
200
|
+
|
|
201
|
+
if (!pid || !(await isDaemonRunning(values.project))) {
|
|
202
|
+
info("Daemon is not running");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
info(`Stopping daemon (PID: ${pid})...`);
|
|
207
|
+
await stopDaemon(values.project);
|
|
208
|
+
success("Daemon stopped");
|
|
209
|
+
} catch (err) {
|
|
210
|
+
error(
|
|
211
|
+
`Failed to stop daemon: ${err instanceof Error ? err.message : String(err)}`,
|
|
212
|
+
);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Status command handler
|
|
219
|
+
*/
|
|
220
|
+
async function statusCommand(args: string[]): Promise<void> {
|
|
221
|
+
const { values } = parseArgs({
|
|
222
|
+
args,
|
|
223
|
+
options: {
|
|
224
|
+
port: { type: "string", short: "p" },
|
|
225
|
+
host: { type: "string", short: "h" },
|
|
226
|
+
path: { type: "string" },
|
|
227
|
+
project: { type: "string" },
|
|
228
|
+
help: { type: "boolean" },
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (values.help) {
|
|
233
|
+
showHelp();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const projectPath = values.project;
|
|
238
|
+
const pid = await readPid(projectPath);
|
|
239
|
+
const running = await isDaemonRunning(projectPath);
|
|
240
|
+
|
|
241
|
+
if (!running) {
|
|
242
|
+
console.log(`${colors.bold}Status:${colors.reset} ${colors.red}Stopped${colors.reset}`);
|
|
243
|
+
console.log(`${colors.bold}PID File:${colors.reset} ${getPidFilePath(projectPath)}`);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Daemon is running - check health
|
|
248
|
+
const port = values.port ? Number.parseInt(values.port, 10) : 5433;
|
|
249
|
+
const host = values.host || "127.0.0.1";
|
|
250
|
+
const path = values.path;
|
|
251
|
+
|
|
252
|
+
const healthOptions = path ? { path } : { port, host };
|
|
253
|
+
const healthy = await healthCheck(healthOptions);
|
|
254
|
+
|
|
255
|
+
console.log(
|
|
256
|
+
`${colors.bold}Status:${colors.reset} ${colors.green}Running${colors.reset}`,
|
|
257
|
+
);
|
|
258
|
+
console.log(`${colors.bold}PID:${colors.reset} ${pid}`);
|
|
259
|
+
console.log(`${colors.bold}PID File:${colors.reset} ${getPidFilePath(projectPath)}`);
|
|
260
|
+
|
|
261
|
+
if (path) {
|
|
262
|
+
console.log(`${colors.bold}Socket:${colors.reset} ${path}`);
|
|
263
|
+
} else {
|
|
264
|
+
console.log(`${colors.bold}Host:${colors.reset} ${host}`);
|
|
265
|
+
console.log(`${colors.bold}Port:${colors.reset} ${port}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
console.log(
|
|
269
|
+
`${colors.bold}Health:${colors.reset} ${healthy ? `${colors.green}OK${colors.reset}` : `${colors.red}Failed${colors.reset}`}`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Main CLI entrypoint
|
|
275
|
+
*/
|
|
276
|
+
async function main() {
|
|
277
|
+
const [command, ...args] = process.argv.slice(2);
|
|
278
|
+
|
|
279
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
280
|
+
showHelp();
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
switch (command) {
|
|
285
|
+
case "start":
|
|
286
|
+
await startCommand(args);
|
|
287
|
+
break;
|
|
288
|
+
case "stop":
|
|
289
|
+
await stopCommand(args);
|
|
290
|
+
break;
|
|
291
|
+
case "status":
|
|
292
|
+
await statusCommand(args);
|
|
293
|
+
break;
|
|
294
|
+
default:
|
|
295
|
+
error(`Unknown command: ${command}`);
|
|
296
|
+
showHelp();
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
main().catch((err) => {
|
|
302
|
+
error(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beads Adapter - Factory for creating BeadsAdapter instances
|
|
3
|
+
*
|
|
4
|
+
* This file implements the adapter pattern for beads event sourcing,
|
|
5
|
+
* enabling dependency injection of the database.
|
|
6
|
+
*
|
|
7
|
+
* ## Design Pattern
|
|
8
|
+
* - Accept DatabaseAdapter via factory parameter
|
|
9
|
+
* - Return BeadsAdapter interface
|
|
10
|
+
* - Delegate to store.ts for event operations
|
|
11
|
+
* - Delegate to projections.ts for queries
|
|
12
|
+
* - No direct database access (all via adapter)
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { wrapPGlite } from '@opencode/swarm-mail/pglite';
|
|
17
|
+
* import { createBeadsAdapter } from '@opencode/swarm-mail/beads';
|
|
18
|
+
*
|
|
19
|
+
* const pglite = new PGlite('./streams.db');
|
|
20
|
+
* const db = wrapPGlite(pglite);
|
|
21
|
+
* const beads = createBeadsAdapter(db, '/path/to/project');
|
|
22
|
+
*
|
|
23
|
+
* // Use the adapter
|
|
24
|
+
* await beads.createBead(projectKey, { title: "Task", type: "task", priority: 2 });
|
|
25
|
+
* const bead = await beads.getBead(projectKey, "bd-123");
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
import type { DatabaseAdapter } from "../types/database.js";
|
|
29
|
+
import type { BeadsAdapter } from "../types/beads-adapter.js";
|
|
30
|
+
/**
|
|
31
|
+
* Create a BeadsAdapter instance
|
|
32
|
+
*
|
|
33
|
+
* @param db - DatabaseAdapter instance (PGLite, SQLite, PostgreSQL, etc.)
|
|
34
|
+
* @param projectKey - Project identifier (typically the project path)
|
|
35
|
+
* @returns BeadsAdapter interface
|
|
36
|
+
*/
|
|
37
|
+
export declare function createBeadsAdapter(db: DatabaseAdapter, projectKey: string): BeadsAdapter;
|
|
38
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/beads/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AA6B9D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,GACjB,YAAY,CAkiBd"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blocked Beads Cache Management
|
|
3
|
+
*
|
|
4
|
+
* Convenience re-exports for blocked cache operations.
|
|
5
|
+
* The actual implementation is in dependencies.ts to avoid circular dependencies.
|
|
6
|
+
*
|
|
7
|
+
* ## Cache Strategy
|
|
8
|
+
* - blocked_beads_cache table stores bead_id → blocker_ids[]
|
|
9
|
+
* - Rebuilt when dependencies change or bead status changes
|
|
10
|
+
* - Enables fast "ready work" queries without recursive CTEs
|
|
11
|
+
*
|
|
12
|
+
* ## Performance
|
|
13
|
+
* - Cache rebuild: <50ms for typical projects
|
|
14
|
+
* - Ready work query: 25x faster with cache (752ms → 29ms on 10K beads)
|
|
15
|
+
*
|
|
16
|
+
* Reference: steveyegge/beads/internal/storage/sqlite/blocked_cache.go
|
|
17
|
+
*
|
|
18
|
+
* @module beads/blocked-cache
|
|
19
|
+
*/
|
|
20
|
+
export { rebuildBeadBlockedCache, rebuildAllBlockedCaches, invalidateBlockedCache, } from "./dependencies.js";
|
|
21
|
+
//# sourceMappingURL=blocked-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocked-cache.d.ts","sourceRoot":"","sources":["../../src/beads/blocked-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment Operations
|
|
3
|
+
*
|
|
4
|
+
* Comment management with threading support.
|
|
5
|
+
* Comments can have parent_id for nested discussions.
|
|
6
|
+
*
|
|
7
|
+
* Reference: steveyegge/beads/internal/storage/sqlite/comments.go
|
|
8
|
+
*
|
|
9
|
+
* @module beads/comments
|
|
10
|
+
*/
|
|
11
|
+
import type { DatabaseAdapter } from "../types/database.js";
|
|
12
|
+
import type { BeadComment } from "../types/beads-adapter.js";
|
|
13
|
+
/**
|
|
14
|
+
* Get a specific comment by ID
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCommentById(db: DatabaseAdapter, commentId: number): Promise<BeadComment | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Get comment thread (comment + all replies)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCommentThread(db: DatabaseAdapter, rootCommentId: number): Promise<BeadComment[]>;
|
|
21
|
+
//# sourceMappingURL=comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/beads/comments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;GAEG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,eAAe,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAM7B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,eAAe,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgBxB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph Operations
|
|
3
|
+
*
|
|
4
|
+
* Provides dependency management with cycle detection and blocked bead tracking.
|
|
5
|
+
*
|
|
6
|
+
* ## Dependency Types
|
|
7
|
+
* - **blocks**: Hard dependency - target must be closed before source can start
|
|
8
|
+
* - **blocked-by**: Inverse of blocks (computed, not stored)
|
|
9
|
+
* - **related**: Soft relationship - doesn't affect ready state
|
|
10
|
+
* - **discovered-from**: Tracking relationship - found while working on another bead
|
|
11
|
+
*
|
|
12
|
+
* ## Cycle Prevention
|
|
13
|
+
* All dependency types are checked for cycles to maintain a DAG (Directed Acyclic Graph).
|
|
14
|
+
* This ensures:
|
|
15
|
+
* - Ready work calculation works correctly
|
|
16
|
+
* - Dependency traversal doesn't loop
|
|
17
|
+
* - Semantic clarity (no circular dependencies)
|
|
18
|
+
*
|
|
19
|
+
* Reference: steveyegge/beads/internal/storage/sqlite/dependencies.go
|
|
20
|
+
*
|
|
21
|
+
* @module beads/dependencies
|
|
22
|
+
*/
|
|
23
|
+
import type { DatabaseAdapter } from "../types/database.js";
|
|
24
|
+
/**
|
|
25
|
+
* Check if adding a dependency would create a cycle
|
|
26
|
+
*
|
|
27
|
+
* Uses recursive CTE to traverse from dependsOnId to see if we can reach beadId.
|
|
28
|
+
* If yes, adding "beadId depends on dependsOnId" would complete a cycle.
|
|
29
|
+
*/
|
|
30
|
+
export declare function wouldCreateCycle(db: DatabaseAdapter, beadId: string, dependsOnId: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Get all open blockers for a bead (including transitive)
|
|
33
|
+
*
|
|
34
|
+
* Returns bead IDs of all beads blocking this one that aren't closed.
|
|
35
|
+
* Only considers "blocks" relationship type.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getOpenBlockers(db: DatabaseAdapter, projectKey: string, beadId: string): Promise<string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Rebuild blocked cache for a specific bead
|
|
40
|
+
*
|
|
41
|
+
* Finds all open blockers and updates the cache.
|
|
42
|
+
* If no open blockers, removes from cache (bead is unblocked).
|
|
43
|
+
*/
|
|
44
|
+
export declare function rebuildBeadBlockedCache(db: DatabaseAdapter, projectKey: string, beadId: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Rebuild blocked cache for all beads in a project
|
|
47
|
+
*
|
|
48
|
+
* Used after bulk operations or status changes that affect blocking.
|
|
49
|
+
*/
|
|
50
|
+
export declare function rebuildAllBlockedCaches(db: DatabaseAdapter, projectKey: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Invalidate blocked cache when dependencies change
|
|
53
|
+
*
|
|
54
|
+
* Marks beads as needing cache rebuild.
|
|
55
|
+
* In this simple implementation, we just rebuild immediately.
|
|
56
|
+
*/
|
|
57
|
+
export declare function invalidateBlockedCache(db: DatabaseAdapter, projectKey: string, beadId: string): Promise<void>;
|
|
58
|
+
//# sourceMappingURL=dependencies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../src/beads/dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,eAAe,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bead Event Types - Minimal type definitions for swarm-mail
|
|
3
|
+
*
|
|
4
|
+
* These are simplified type definitions that match the bead-events from
|
|
5
|
+
* opencode-swarm-plugin but avoid cross-package TypeScript imports.
|
|
6
|
+
*
|
|
7
|
+
* The actual event schemas with Zod validation live in:
|
|
8
|
+
* packages/opencode-swarm-plugin/src/schemas/bead-events.ts
|
|
9
|
+
*
|
|
10
|
+
* This file provides just enough type information for the store to work.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Base bead event (all events extend this)
|
|
14
|
+
*/
|
|
15
|
+
export interface BaseBeadEvent {
|
|
16
|
+
id?: number;
|
|
17
|
+
type: string;
|
|
18
|
+
project_key: string;
|
|
19
|
+
bead_id: string;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
sequence?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Union of all bead event types
|
|
25
|
+
*
|
|
26
|
+
* This matches the discriminated union in bead-events.ts but as pure TypeScript
|
|
27
|
+
*/
|
|
28
|
+
export type BeadEvent = BeadCreatedEvent | BeadUpdatedEvent | BeadStatusChangedEvent | BeadClosedEvent | BeadReopenedEvent | BeadDeletedEvent | BeadDependencyAddedEvent | BeadDependencyRemovedEvent | BeadLabelAddedEvent | BeadLabelRemovedEvent | BeadCommentAddedEvent | BeadCommentUpdatedEvent | BeadCommentDeletedEvent | BeadEpicChildAddedEvent | BeadEpicChildRemovedEvent | BeadEpicClosureEligibleEvent | BeadAssignedEvent | BeadWorkStartedEvent | BeadCompactedEvent;
|
|
29
|
+
export interface BeadCreatedEvent extends BaseBeadEvent {
|
|
30
|
+
type: "bead_created";
|
|
31
|
+
title: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
issue_type: "bug" | "feature" | "task" | "epic" | "chore";
|
|
34
|
+
priority: number;
|
|
35
|
+
parent_id?: string;
|
|
36
|
+
created_by?: string;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
export interface BeadUpdatedEvent extends BaseBeadEvent {
|
|
40
|
+
type: "bead_updated";
|
|
41
|
+
updated_by?: string;
|
|
42
|
+
changes: {
|
|
43
|
+
title?: {
|
|
44
|
+
old: string;
|
|
45
|
+
new: string;
|
|
46
|
+
};
|
|
47
|
+
description?: {
|
|
48
|
+
old: string;
|
|
49
|
+
new: string;
|
|
50
|
+
};
|
|
51
|
+
priority?: {
|
|
52
|
+
old: number;
|
|
53
|
+
new: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface BeadStatusChangedEvent extends BaseBeadEvent {
|
|
58
|
+
type: "bead_status_changed";
|
|
59
|
+
from_status: "open" | "in_progress" | "blocked" | "closed" | "tombstone";
|
|
60
|
+
to_status: "open" | "in_progress" | "blocked" | "closed" | "tombstone";
|
|
61
|
+
changed_by?: string;
|
|
62
|
+
reason?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface BeadClosedEvent extends BaseBeadEvent {
|
|
65
|
+
type: "bead_closed";
|
|
66
|
+
reason: string;
|
|
67
|
+
closed_by?: string;
|
|
68
|
+
files_touched?: string[];
|
|
69
|
+
duration_ms?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface BeadReopenedEvent extends BaseBeadEvent {
|
|
72
|
+
type: "bead_reopened";
|
|
73
|
+
reason?: string;
|
|
74
|
+
reopened_by?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface BeadDeletedEvent extends BaseBeadEvent {
|
|
77
|
+
type: "bead_deleted";
|
|
78
|
+
reason?: string;
|
|
79
|
+
deleted_by?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface BeadDependencyAddedEvent extends BaseBeadEvent {
|
|
82
|
+
type: "bead_dependency_added";
|
|
83
|
+
dependency: {
|
|
84
|
+
target: string;
|
|
85
|
+
type: "blocks" | "blocked-by" | "related" | "discovered-from";
|
|
86
|
+
};
|
|
87
|
+
added_by?: string;
|
|
88
|
+
reason?: string;
|
|
89
|
+
}
|
|
90
|
+
export interface BeadDependencyRemovedEvent extends BaseBeadEvent {
|
|
91
|
+
type: "bead_dependency_removed";
|
|
92
|
+
dependency: {
|
|
93
|
+
target: string;
|
|
94
|
+
type: "blocks" | "blocked-by" | "related" | "discovered-from";
|
|
95
|
+
};
|
|
96
|
+
removed_by?: string;
|
|
97
|
+
reason?: string;
|
|
98
|
+
}
|
|
99
|
+
export interface BeadLabelAddedEvent extends BaseBeadEvent {
|
|
100
|
+
type: "bead_label_added";
|
|
101
|
+
label: string;
|
|
102
|
+
added_by?: string;
|
|
103
|
+
}
|
|
104
|
+
export interface BeadLabelRemovedEvent extends BaseBeadEvent {
|
|
105
|
+
type: "bead_label_removed";
|
|
106
|
+
label: string;
|
|
107
|
+
removed_by?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface BeadCommentAddedEvent extends BaseBeadEvent {
|
|
110
|
+
type: "bead_comment_added";
|
|
111
|
+
comment_id?: number;
|
|
112
|
+
author: string;
|
|
113
|
+
body: string;
|
|
114
|
+
parent_comment_id?: number;
|
|
115
|
+
metadata?: Record<string, unknown>;
|
|
116
|
+
}
|
|
117
|
+
export interface BeadCommentUpdatedEvent extends BaseBeadEvent {
|
|
118
|
+
type: "bead_comment_updated";
|
|
119
|
+
comment_id: number;
|
|
120
|
+
old_body: string;
|
|
121
|
+
new_body: string;
|
|
122
|
+
updated_by: string;
|
|
123
|
+
}
|
|
124
|
+
export interface BeadCommentDeletedEvent extends BaseBeadEvent {
|
|
125
|
+
type: "bead_comment_deleted";
|
|
126
|
+
comment_id: number;
|
|
127
|
+
deleted_by: string;
|
|
128
|
+
reason?: string;
|
|
129
|
+
}
|
|
130
|
+
export interface BeadEpicChildAddedEvent extends BaseBeadEvent {
|
|
131
|
+
type: "bead_epic_child_added";
|
|
132
|
+
child_id: string;
|
|
133
|
+
child_index?: number;
|
|
134
|
+
added_by?: string;
|
|
135
|
+
}
|
|
136
|
+
export interface BeadEpicChildRemovedEvent extends BaseBeadEvent {
|
|
137
|
+
type: "bead_epic_child_removed";
|
|
138
|
+
child_id: string;
|
|
139
|
+
removed_by?: string;
|
|
140
|
+
reason?: string;
|
|
141
|
+
}
|
|
142
|
+
export interface BeadEpicClosureEligibleEvent extends BaseBeadEvent {
|
|
143
|
+
type: "bead_epic_closure_eligible";
|
|
144
|
+
child_ids: string[];
|
|
145
|
+
total_duration_ms?: number;
|
|
146
|
+
all_files_touched?: string[];
|
|
147
|
+
}
|
|
148
|
+
export interface BeadAssignedEvent extends BaseBeadEvent {
|
|
149
|
+
type: "bead_assigned";
|
|
150
|
+
agent_name: string;
|
|
151
|
+
task_description?: string;
|
|
152
|
+
}
|
|
153
|
+
export interface BeadWorkStartedEvent extends BaseBeadEvent {
|
|
154
|
+
type: "bead_work_started";
|
|
155
|
+
agent_name: string;
|
|
156
|
+
reserved_files?: string[];
|
|
157
|
+
}
|
|
158
|
+
export interface BeadCompactedEvent extends BaseBeadEvent {
|
|
159
|
+
type: "bead_compacted";
|
|
160
|
+
events_archived: number;
|
|
161
|
+
new_start_sequence: number;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/beads/events.ts"],"names":[],"mappings":"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"}
|