@wopr-network/defcon 0.2.0 → 0.2.2

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.
Files changed (102) hide show
  1. package/dist/src/execution/cli.js +0 -0
  2. package/package.json +3 -2
  3. package/dist/api/router.d.ts +0 -24
  4. package/dist/api/router.js +0 -44
  5. package/dist/api/server.d.ts +0 -13
  6. package/dist/api/server.js +0 -280
  7. package/dist/api/wire-types.d.ts +0 -46
  8. package/dist/api/wire-types.js +0 -5
  9. package/dist/config/db-path.d.ts +0 -1
  10. package/dist/config/db-path.js +0 -1
  11. package/dist/config/exporter.d.ts +0 -3
  12. package/dist/config/exporter.js +0 -87
  13. package/dist/config/index.d.ts +0 -4
  14. package/dist/config/index.js +0 -4
  15. package/dist/config/seed-loader.d.ts +0 -10
  16. package/dist/config/seed-loader.js +0 -108
  17. package/dist/config/zod-schemas.d.ts +0 -165
  18. package/dist/config/zod-schemas.js +0 -283
  19. package/dist/cors.d.ts +0 -8
  20. package/dist/cors.js +0 -21
  21. package/dist/engine/constants.d.ts +0 -1
  22. package/dist/engine/constants.js +0 -1
  23. package/dist/engine/engine.d.ts +0 -69
  24. package/dist/engine/engine.js +0 -485
  25. package/dist/engine/event-emitter.d.ts +0 -9
  26. package/dist/engine/event-emitter.js +0 -19
  27. package/dist/engine/event-types.d.ts +0 -105
  28. package/dist/engine/event-types.js +0 -1
  29. package/dist/engine/flow-spawner.d.ts +0 -8
  30. package/dist/engine/flow-spawner.js +0 -28
  31. package/dist/engine/gate-command-validator.d.ts +0 -6
  32. package/dist/engine/gate-command-validator.js +0 -46
  33. package/dist/engine/gate-evaluator.d.ts +0 -12
  34. package/dist/engine/gate-evaluator.js +0 -233
  35. package/dist/engine/handlebars.d.ts +0 -9
  36. package/dist/engine/handlebars.js +0 -51
  37. package/dist/engine/index.d.ts +0 -12
  38. package/dist/engine/index.js +0 -7
  39. package/dist/engine/invocation-builder.d.ts +0 -18
  40. package/dist/engine/invocation-builder.js +0 -58
  41. package/dist/engine/on-enter.d.ts +0 -8
  42. package/dist/engine/on-enter.js +0 -102
  43. package/dist/engine/ssrf-guard.d.ts +0 -22
  44. package/dist/engine/ssrf-guard.js +0 -159
  45. package/dist/engine/state-machine.d.ts +0 -12
  46. package/dist/engine/state-machine.js +0 -74
  47. package/dist/execution/active-runner.d.ts +0 -45
  48. package/dist/execution/active-runner.js +0 -165
  49. package/dist/execution/admin-schemas.d.ts +0 -116
  50. package/dist/execution/admin-schemas.js +0 -125
  51. package/dist/execution/cli.d.ts +0 -57
  52. package/dist/execution/cli.js +0 -498
  53. package/dist/execution/handlers/admin.d.ts +0 -67
  54. package/dist/execution/handlers/admin.js +0 -200
  55. package/dist/execution/handlers/flow.d.ts +0 -25
  56. package/dist/execution/handlers/flow.js +0 -289
  57. package/dist/execution/handlers/query.d.ts +0 -31
  58. package/dist/execution/handlers/query.js +0 -64
  59. package/dist/execution/index.d.ts +0 -4
  60. package/dist/execution/index.js +0 -3
  61. package/dist/execution/mcp-helpers.d.ts +0 -42
  62. package/dist/execution/mcp-helpers.js +0 -23
  63. package/dist/execution/mcp-server.d.ts +0 -33
  64. package/dist/execution/mcp-server.js +0 -1020
  65. package/dist/execution/provision-worktree.d.ts +0 -16
  66. package/dist/execution/provision-worktree.js +0 -123
  67. package/dist/execution/tool-schemas.d.ts +0 -40
  68. package/dist/execution/tool-schemas.js +0 -44
  69. package/dist/logger.d.ts +0 -8
  70. package/dist/logger.js +0 -12
  71. package/dist/main.d.ts +0 -14
  72. package/dist/main.js +0 -28
  73. package/dist/repositories/drizzle/entity.repo.d.ts +0 -27
  74. package/dist/repositories/drizzle/entity.repo.js +0 -190
  75. package/dist/repositories/drizzle/event.repo.d.ts +0 -12
  76. package/dist/repositories/drizzle/event.repo.js +0 -24
  77. package/dist/repositories/drizzle/flow.repo.d.ts +0 -22
  78. package/dist/repositories/drizzle/flow.repo.js +0 -364
  79. package/dist/repositories/drizzle/gate.repo.d.ts +0 -16
  80. package/dist/repositories/drizzle/gate.repo.js +0 -98
  81. package/dist/repositories/drizzle/index.d.ts +0 -6
  82. package/dist/repositories/drizzle/index.js +0 -7
  83. package/dist/repositories/drizzle/invocation.repo.d.ts +0 -23
  84. package/dist/repositories/drizzle/invocation.repo.js +0 -199
  85. package/dist/repositories/drizzle/schema.d.ts +0 -1932
  86. package/dist/repositories/drizzle/schema.js +0 -155
  87. package/dist/repositories/drizzle/transition-log.repo.d.ts +0 -11
  88. package/dist/repositories/drizzle/transition-log.repo.js +0 -42
  89. package/dist/repositories/interfaces.d.ts +0 -321
  90. package/dist/repositories/interfaces.js +0 -2
  91. package/dist/utils/redact.d.ts +0 -2
  92. package/dist/utils/redact.js +0 -62
  93. package/gates/blocking-graph.d.ts +0 -26
  94. package/gates/blocking-graph.js +0 -102
  95. package/gates/test/bad-return-gate.d.ts +0 -1
  96. package/gates/test/bad-return-gate.js +0 -4
  97. package/gates/test/passing-gate.d.ts +0 -2
  98. package/gates/test/passing-gate.js +0 -3
  99. package/gates/test/slow-gate.d.ts +0 -2
  100. package/gates/test/slow-gate.js +0 -5
  101. package/gates/test/throwing-gate.d.ts +0 -1
  102. package/gates/test/throwing-gate.js +0 -3
@@ -1,16 +0,0 @@
1
- export interface ProvisionWorktreeResult {
2
- worktreePath: string;
3
- branch: string;
4
- repo: string;
5
- }
6
- export declare function parseIssueNumber(issueKey: string): string;
7
- export declare function repoName(repo: string): string;
8
- export declare function validateRepoName(name: string): string;
9
- export declare function buildBranch(issueKey: string): string;
10
- export declare function buildWorktreePath(repo: string, issueKey: string, basePath: string): string;
11
- export declare function provisionWorktree(opts: {
12
- repo: string;
13
- issueKey: string;
14
- basePath?: string;
15
- cloneRoot?: string;
16
- }): ProvisionWorktreeResult;
@@ -1,123 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- export function parseIssueNumber(issueKey) {
6
- const match = issueKey.match(/^[A-Za-z]+[-](\d+)$/);
7
- if (!match) {
8
- throw new Error(`Invalid issue key: ${issueKey}. Expected format: WOP-123`);
9
- }
10
- return match[1];
11
- }
12
- export function repoName(repo) {
13
- const parts = repo.replace(/\/$/, "").split("/");
14
- return parts[parts.length - 1] || repo;
15
- }
16
- export function validateRepoName(name) {
17
- if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
18
- throw new Error(`Invalid repo name: ${name}`);
19
- }
20
- if (name === "." || name === "..") {
21
- throw new Error(`Invalid repo name: ${name}`);
22
- }
23
- return name;
24
- }
25
- export function buildBranch(issueKey) {
26
- const num = parseIssueNumber(issueKey);
27
- return `agent/coder-${num}/${issueKey.toLowerCase()}`;
28
- }
29
- export function buildWorktreePath(repo, issueKey, basePath) {
30
- const name = repoName(repo);
31
- const num = parseIssueNumber(issueKey);
32
- return join(basePath, `wopr-${name}-coder-${num}`);
33
- }
34
- function run(cmd, args, cwd) {
35
- try {
36
- return execFileSync(cmd, args, {
37
- cwd,
38
- encoding: "utf-8",
39
- stdio: ["pipe", "pipe", "pipe"],
40
- }).trim();
41
- }
42
- catch (err) {
43
- const e = err;
44
- throw new Error(`${cmd} ${args.join(" ")} failed: ${e.stderr ?? e.message ?? String(err)}`);
45
- }
46
- }
47
- export function provisionWorktree(opts) {
48
- const basePath = opts.basePath ?? join(homedir(), "worktrees");
49
- const cloneRoot = opts.cloneRoot ?? homedir();
50
- const name = validateRepoName(repoName(opts.repo));
51
- const clonePath = join(cloneRoot, name);
52
- if (!resolve(clonePath).startsWith(`${resolve(cloneRoot)}/`) && resolve(clonePath) !== resolve(cloneRoot)) {
53
- throw new Error(`Repo name escapes cloneRoot: ${name}`);
54
- }
55
- const worktreePath = buildWorktreePath(opts.repo, opts.issueKey, basePath);
56
- const branch = buildBranch(opts.issueKey);
57
- // Idempotent: if worktree already exists, verify and return
58
- if (existsSync(worktreePath)) {
59
- try {
60
- run("git", ["rev-parse", "--git-dir"], worktreePath);
61
- const currentBranch = run("git", ["rev-parse", "--abbrev-ref", "HEAD"], worktreePath);
62
- if (currentBranch !== branch) {
63
- throw new Error(`Worktree at ${worktreePath} is on branch ${currentBranch}, expected ${branch}`);
64
- }
65
- const remoteUrl = run("git", ["remote", "get-url", "origin"], worktreePath);
66
- const repoPath = opts.repo.replace(/\.git$/, "");
67
- const urlMatchesRepo = remoteUrl.endsWith(`/${repoPath}`) ||
68
- remoteUrl.endsWith(`/${repoPath}.git`) ||
69
- remoteUrl.endsWith(`:${repoPath}`) ||
70
- remoteUrl.endsWith(`:${repoPath}.git`);
71
- if (!urlMatchesRepo) {
72
- throw new Error(`Worktree at ${worktreePath} has unexpected remote: ${remoteUrl} (expected to contain ${opts.repo})`);
73
- }
74
- return { worktreePath, branch, repo: opts.repo };
75
- }
76
- catch (err) {
77
- if (err instanceof Error && err.message.startsWith("Worktree at")) {
78
- throw err;
79
- }
80
- throw new Error(`Path ${worktreePath} exists but is not a git worktree`);
81
- }
82
- }
83
- // Clone if repo not present
84
- if (!existsSync(clonePath)) {
85
- const cloneUrl = `https://github.com/${opts.repo}.git`;
86
- process.stderr.write(`Cloning ${cloneUrl} to ${clonePath}...\n`);
87
- run("git", ["clone", cloneUrl, clonePath]);
88
- }
89
- // Fetch latest
90
- process.stderr.write(`Fetching origin in ${clonePath}...\n`);
91
- run("git", ["fetch", "origin"], clonePath);
92
- // Create worktree
93
- process.stderr.write(`Creating worktree at ${worktreePath}...\n`);
94
- const defaultBranchRef = run("git", ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"], clonePath);
95
- const defaultBranch = defaultBranchRef.replace("origin/", "");
96
- try {
97
- run("git", ["worktree", "add", worktreePath, "-b", branch, `origin/${defaultBranch}`], clonePath);
98
- }
99
- catch (err) {
100
- // Branch may already exist — check if a worktree with the right branch is now present
101
- if (existsSync(worktreePath)) {
102
- try {
103
- run("git", ["rev-parse", "--git-dir"], worktreePath);
104
- const currentBranch = run("git", ["rev-parse", "--abbrev-ref", "HEAD"], worktreePath);
105
- if (currentBranch === branch) {
106
- // Worktree already exists on the correct branch — idempotent success
107
- return { worktreePath, branch, repo: opts.repo };
108
- }
109
- }
110
- catch {
111
- // fall through to rethrow original error
112
- }
113
- }
114
- throw err;
115
- }
116
- // Install dependencies
117
- const hasPnpmLock = existsSync(join(worktreePath, "pnpm-lock.yaml"));
118
- const hasYarnLock = existsSync(join(worktreePath, "yarn.lock"));
119
- const installCmd = hasPnpmLock ? "pnpm" : hasYarnLock ? "yarn" : "npm";
120
- process.stderr.write(`Running ${installCmd} install in ${worktreePath}...\n`);
121
- execFileSync(installCmd, ["install"], { cwd: worktreePath, stdio: "inherit" });
122
- return { worktreePath, branch, repo: opts.repo };
123
- }
@@ -1,40 +0,0 @@
1
- import { z } from "zod/v4";
2
- export declare const FlowClaimSchema: z.ZodObject<{
3
- worker_id: z.ZodOptional<z.ZodString>;
4
- role: z.ZodString;
5
- flow: z.ZodOptional<z.ZodString>;
6
- }, z.core.$strip>;
7
- export declare const FlowGetPromptSchema: z.ZodObject<{
8
- entity_id: z.ZodString;
9
- }, z.core.$strip>;
10
- export declare const FlowReportSchema: z.ZodObject<{
11
- entity_id: z.ZodString;
12
- signal: z.ZodString;
13
- artifacts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
- worker_id: z.ZodOptional<z.ZodString>;
15
- }, z.core.$strip>;
16
- export declare const FlowFailSchema: z.ZodObject<{
17
- entity_id: z.ZodString;
18
- error: z.ZodString;
19
- }, z.core.$strip>;
20
- export declare const QueryEntitySchema: z.ZodObject<{
21
- id: z.ZodString;
22
- }, z.core.$strip>;
23
- export declare const QueryEntitiesSchema: z.ZodObject<{
24
- flow: z.ZodString;
25
- state: z.ZodString;
26
- limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
27
- }, z.core.$strip>;
28
- export declare const QueryInvocationsSchema: z.ZodObject<{
29
- entity_id: z.ZodString;
30
- }, z.core.$strip>;
31
- export declare const QueryFlowSchema: z.ZodObject<{
32
- name: z.ZodString;
33
- }, z.core.$strip>;
34
- export declare const FlowSeedSchema: z.ZodObject<{
35
- flow: z.ZodString;
36
- refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
37
- adapter: z.ZodString;
38
- id: z.ZodString;
39
- }, z.core.$loose>>>;
40
- }, z.core.$strip>;
@@ -1,44 +0,0 @@
1
- import { z } from "zod/v4";
2
- export const FlowClaimSchema = z.object({
3
- worker_id: z.string().min(1).optional(),
4
- role: z.string().min(1),
5
- flow: z.string().min(1).optional(),
6
- });
7
- export const FlowGetPromptSchema = z.object({
8
- entity_id: z.string().min(1),
9
- });
10
- export const FlowReportSchema = z.object({
11
- entity_id: z.string().min(1),
12
- signal: z.string().min(1),
13
- artifacts: z.record(z.string(), z.unknown()).optional(),
14
- worker_id: z.string().min(1).optional(),
15
- });
16
- export const FlowFailSchema = z.object({
17
- entity_id: z.string().min(1),
18
- error: z.string().min(1),
19
- });
20
- export const QueryEntitySchema = z.object({
21
- id: z.string().min(1),
22
- });
23
- export const QueryEntitiesSchema = z.object({
24
- flow: z.string().min(1),
25
- state: z.string().min(1),
26
- limit: z.coerce.number().int().min(1).max(100).optional(),
27
- });
28
- export const QueryInvocationsSchema = z.object({
29
- entity_id: z.string().min(1),
30
- });
31
- export const QueryFlowSchema = z.object({
32
- name: z.string().min(1),
33
- });
34
- export const FlowSeedSchema = z.object({
35
- flow: z.string().min(1),
36
- refs: z
37
- .record(z.string(), z
38
- .object({
39
- adapter: z.string().min(1),
40
- id: z.string().min(1),
41
- })
42
- .passthrough())
43
- .optional(),
44
- });
package/dist/logger.d.ts DELETED
@@ -1,8 +0,0 @@
1
- export interface Logger {
2
- error(msg: string, ...args: unknown[]): void;
3
- warn(msg: string, ...args: unknown[]): void;
4
- info(msg: string, ...args: unknown[]): void;
5
- debug(msg: string, ...args: unknown[]): void;
6
- }
7
- export declare const consoleLogger: Logger;
8
- export declare const noopLogger: Logger;
package/dist/logger.js DELETED
@@ -1,12 +0,0 @@
1
- export const consoleLogger = {
2
- error: (msg, ...args) => console.error(msg, ...args),
3
- warn: (msg, ...args) => console.warn(msg, ...args),
4
- info: (msg, ...args) => console.info(msg, ...args),
5
- debug: (msg, ...args) => console.debug(msg, ...args),
6
- };
7
- export const noopLogger = {
8
- error: () => { },
9
- warn: () => { },
10
- info: () => { },
11
- debug: () => { },
12
- };
package/dist/main.d.ts DELETED
@@ -1,14 +0,0 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
3
- import * as schema from "./repositories/drizzle/schema.js";
4
- export declare function createDatabase(dbPath?: string): {
5
- db: ReturnType<typeof drizzle<typeof schema>>;
6
- sqlite: Database.Database;
7
- };
8
- export declare function runMigrations(db: ReturnType<typeof drizzle>, migrationsFolder?: string): void;
9
- export declare function bootstrap(dbPath?: string): {
10
- db: ReturnType<typeof drizzle>;
11
- sqlite: Database.Database;
12
- };
13
- export * from "./api/wire-types.js";
14
- export * from "./engine/index.js";
package/dist/main.js DELETED
@@ -1,28 +0,0 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
3
- import { migrate } from "drizzle-orm/better-sqlite3/migrator";
4
- import { DB_PATH } from "./config/db-path.js";
5
- import * as schema from "./repositories/drizzle/schema.js";
6
- export function createDatabase(dbPath = DB_PATH) {
7
- const sqlite = new Database(dbPath);
8
- sqlite.pragma("journal_mode = WAL");
9
- sqlite.pragma("foreign_keys = ON");
10
- const db = drizzle(sqlite, { schema });
11
- return { db, sqlite };
12
- }
13
- export function runMigrations(db, migrationsFolder = "./drizzle") {
14
- migrate(db, { migrationsFolder });
15
- }
16
- export function bootstrap(dbPath = DB_PATH) {
17
- const { db, sqlite } = createDatabase(dbPath);
18
- try {
19
- runMigrations(db);
20
- }
21
- catch (err) {
22
- sqlite.close();
23
- throw err;
24
- }
25
- return { db, sqlite };
26
- }
27
- export * from "./api/wire-types.js";
28
- export * from "./engine/index.js";
@@ -1,27 +0,0 @@
1
- import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
- import type { Artifacts, Entity, IEntityRepository, Refs } from "../interfaces.js";
3
- import type * as schema from "./schema.js";
4
- type Db = BetterSQLite3Database<typeof schema>;
5
- export declare class DrizzleEntityRepository implements IEntityRepository {
6
- private db;
7
- constructor(db: Db);
8
- private toEntity;
9
- create(flowId: string, initialState: string, refs?: Refs): Promise<Entity>;
10
- get(id: string): Promise<Entity | null>;
11
- findByFlowAndState(flowId: string, state: string, limit?: number): Promise<Entity[]>;
12
- hasAnyInFlowAndState(flowId: string, stateNames: string[]): Promise<boolean>;
13
- transition(id: string, toState: string, _trigger: string, artifacts?: Partial<Artifacts>, _invocationId?: string | null): Promise<Entity>;
14
- updateArtifacts(id: string, artifacts: Partial<Artifacts>): Promise<void>;
15
- claim(flowId: string, state: string, agentId: string): Promise<Entity | null>;
16
- claimById(entityId: string, agentId: string): Promise<Entity | null>;
17
- release(entityId: string, agentId: string): Promise<void>;
18
- appendSpawnedChild(parentId: string, entry: {
19
- childId: string;
20
- childFlow: string;
21
- spawnedAt: string;
22
- }): Promise<void>;
23
- reapExpired(ttlMs: number): Promise<string[]>;
24
- setAffinity(entityId: string, workerId: string, role: string, expiresAt: Date): Promise<void>;
25
- clearExpiredAffinity(): Promise<string[]>;
26
- }
27
- export {};
@@ -1,190 +0,0 @@
1
- import { and, eq, inArray, isNull, lt, not } from "drizzle-orm";
2
- import { entities } from "./schema.js";
3
- export class DrizzleEntityRepository {
4
- db;
5
- constructor(db) {
6
- this.db = db;
7
- }
8
- toEntity(row) {
9
- return {
10
- id: row.id,
11
- flowId: row.flowId,
12
- state: row.state,
13
- refs: row.refs,
14
- artifacts: row.artifacts,
15
- claimedBy: row.claimedBy,
16
- claimedAt: row.claimedAt ? new Date(row.claimedAt) : null,
17
- flowVersion: row.flowVersion ?? 1,
18
- priority: row.priority ?? 0,
19
- createdAt: new Date(row.createdAt ?? 0),
20
- updatedAt: new Date(row.updatedAt ?? 0),
21
- affinityWorkerId: row.affinityWorkerId ?? null,
22
- affinityRole: row.affinityRole ?? null,
23
- affinityExpiresAt: row.affinityExpiresAt ? new Date(row.affinityExpiresAt) : null,
24
- };
25
- }
26
- async create(flowId, initialState, refs) {
27
- const now = Date.now();
28
- const id = crypto.randomUUID();
29
- const row = {
30
- id,
31
- flowId,
32
- state: initialState,
33
- refs: refs ?? null,
34
- artifacts: null,
35
- claimedBy: null,
36
- claimedAt: null,
37
- flowVersion: 1,
38
- createdAt: now,
39
- updatedAt: now,
40
- affinityWorkerId: null,
41
- affinityRole: null,
42
- affinityExpiresAt: null,
43
- };
44
- await this.db.insert(entities).values(row);
45
- return this.toEntity(row);
46
- }
47
- async get(id) {
48
- const rows = await this.db.select().from(entities).where(eq(entities.id, id)).limit(1);
49
- return rows.length > 0 ? this.toEntity(rows[0]) : null;
50
- }
51
- async findByFlowAndState(flowId, state, limit) {
52
- const query = this.db
53
- .select()
54
- .from(entities)
55
- .where(and(eq(entities.flowId, flowId), eq(entities.state, state)));
56
- const rows = await (limit !== undefined ? query.limit(limit) : query);
57
- return rows.map((r) => this.toEntity(r));
58
- }
59
- async hasAnyInFlowAndState(flowId, stateNames) {
60
- if (stateNames.length === 0)
61
- return false;
62
- const rows = await this.db
63
- .select({ id: entities.id })
64
- .from(entities)
65
- .where(and(eq(entities.flowId, flowId), inArray(entities.state, stateNames)))
66
- .limit(1);
67
- return rows.length > 0;
68
- }
69
- async transition(id, toState, _trigger, artifacts, _invocationId) {
70
- return this.db.transaction((tx) => {
71
- const rows = tx.select().from(entities).where(eq(entities.id, id)).limit(1).all();
72
- if (rows.length === 0)
73
- throw new Error(`Entity not found: ${id}`);
74
- const row = rows[0];
75
- const now = Date.now();
76
- const mergedArtifacts = artifacts
77
- ? { ...(row.artifacts ?? {}), ...artifacts }
78
- : row.artifacts;
79
- tx.update(entities)
80
- .set({ state: toState, artifacts: mergedArtifacts, updatedAt: now })
81
- .where(eq(entities.id, id))
82
- .run();
83
- return this.toEntity({
84
- ...row,
85
- state: toState,
86
- artifacts: mergedArtifacts,
87
- updatedAt: now,
88
- });
89
- });
90
- }
91
- async updateArtifacts(id, artifacts) {
92
- const rows = await this.db.select().from(entities).where(eq(entities.id, id)).limit(1);
93
- if (rows.length === 0)
94
- throw new Error(`Entity not found: ${id}`);
95
- const existing = rows[0].artifacts ?? {};
96
- await this.db
97
- .update(entities)
98
- .set({ artifacts: { ...existing, ...artifacts }, updatedAt: Date.now() })
99
- .where(eq(entities.id, id));
100
- }
101
- async claim(flowId, state, agentId) {
102
- return this.db.transaction((tx) => {
103
- const rows = tx
104
- .select()
105
- .from(entities)
106
- .where(and(eq(entities.flowId, flowId), eq(entities.state, state), isNull(entities.claimedBy)))
107
- .limit(1)
108
- .all();
109
- if (rows.length === 0)
110
- return null;
111
- const row = rows[0];
112
- const now = Date.now();
113
- tx.update(entities)
114
- .set({ claimedBy: agentId, claimedAt: now, updatedAt: now })
115
- .where(eq(entities.id, row.id))
116
- .run();
117
- return this.toEntity({
118
- ...row,
119
- claimedBy: agentId,
120
- claimedAt: now,
121
- updatedAt: now,
122
- });
123
- });
124
- }
125
- async claimById(entityId, agentId) {
126
- const now = Date.now();
127
- const result = this.db
128
- .update(entities)
129
- .set({ claimedBy: agentId, claimedAt: now, updatedAt: now })
130
- .where(and(eq(entities.id, entityId), isNull(entities.claimedBy)))
131
- .run();
132
- if (result.changes === 0)
133
- return null;
134
- const rows = this.db.select().from(entities).where(eq(entities.id, entityId)).limit(1).all();
135
- return rows.length > 0 ? this.toEntity(rows[0]) : null;
136
- }
137
- async release(entityId, agentId) {
138
- await this.db
139
- .update(entities)
140
- .set({ claimedBy: null, claimedAt: null, updatedAt: Date.now() })
141
- .where(and(eq(entities.id, entityId), eq(entities.claimedBy, agentId)))
142
- .run();
143
- }
144
- async appendSpawnedChild(parentId, entry) {
145
- this.db.transaction((tx) => {
146
- const rows = tx.select().from(entities).where(eq(entities.id, parentId)).limit(1).all();
147
- if (rows.length === 0)
148
- throw new Error(`Entity ${parentId} not found`);
149
- const row = rows[0];
150
- const artifacts = row.artifacts ?? {};
151
- const existing = (Array.isArray(artifacts.spawnedChildren) ? artifacts.spawnedChildren : []);
152
- tx.update(entities)
153
- .set({ artifacts: { ...artifacts, spawnedChildren: [...existing, entry] }, updatedAt: Date.now() })
154
- .where(eq(entities.id, parentId))
155
- .run();
156
- });
157
- }
158
- async reapExpired(ttlMs) {
159
- const cutoff = Date.now() - ttlMs;
160
- const rows = this.db
161
- .update(entities)
162
- .set({ claimedBy: null, claimedAt: null, updatedAt: Date.now() })
163
- .where(and(not(isNull(entities.claimedBy)), lt(entities.claimedAt, cutoff)))
164
- .returning({ id: entities.id })
165
- .all();
166
- return rows.map((r) => r.id);
167
- }
168
- async setAffinity(entityId, workerId, role, expiresAt) {
169
- await this.db
170
- .update(entities)
171
- .set({
172
- affinityWorkerId: workerId,
173
- affinityRole: role,
174
- affinityExpiresAt: expiresAt.getTime(),
175
- updatedAt: Date.now(),
176
- })
177
- .where(eq(entities.id, entityId))
178
- .run();
179
- }
180
- async clearExpiredAffinity() {
181
- const now = Date.now();
182
- const rows = this.db
183
- .update(entities)
184
- .set({ affinityWorkerId: null, affinityRole: null, affinityExpiresAt: null, updatedAt: now })
185
- .where(and(not(isNull(entities.affinityWorkerId)), lt(entities.affinityExpiresAt, now)))
186
- .returning({ id: entities.id })
187
- .all();
188
- return rows.map((r) => r.id);
189
- }
190
- }
@@ -1,12 +0,0 @@
1
- import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
- import type { IEventRepository } from "../interfaces.js";
3
- import type * as schema from "./schema.js";
4
- import { events } from "./schema.js";
5
- type Db = BetterSQLite3Database<typeof schema>;
6
- export declare class DrizzleEventRepository implements IEventRepository {
7
- private readonly db;
8
- constructor(db: Db);
9
- emitDefinitionChanged(flowId: string | null, tool: string, payload: Record<string, unknown>): Promise<void>;
10
- findAll(): (typeof events.$inferSelect)[];
11
- }
12
- export {};
@@ -1,24 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { events } from "./schema.js";
3
- export class DrizzleEventRepository {
4
- db;
5
- constructor(db) {
6
- this.db = db;
7
- }
8
- async emitDefinitionChanged(flowId, tool, payload) {
9
- this.db
10
- .insert(events)
11
- .values({
12
- id: randomUUID(),
13
- type: "definition.changed",
14
- entityId: null,
15
- flowId: flowId || null,
16
- payload: { tool, ...payload },
17
- emittedAt: Date.now(),
18
- })
19
- .run();
20
- }
21
- findAll() {
22
- return this.db.select().from(events).all();
23
- }
24
- }
@@ -1,22 +0,0 @@
1
- import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
- import type { CreateFlowInput, CreateStateInput, CreateTransitionInput, Flow, FlowVersion, IFlowRepository, State, Transition, UpdateFlowInput, UpdateStateInput, UpdateTransitionInput } from "../interfaces.js";
3
- import type * as schema from "./schema.js";
4
- type Db = BetterSQLite3Database<typeof schema>;
5
- export declare class DrizzleFlowRepository implements IFlowRepository {
6
- private db;
7
- constructor(db: Db);
8
- private hydrateFlow;
9
- create(input: CreateFlowInput): Promise<Flow>;
10
- get(id: string): Promise<Flow | null>;
11
- getByName(name: string): Promise<Flow | null>;
12
- list(): Promise<Flow[]>;
13
- listAll(): Promise<Flow[]>;
14
- update(id: string, changes: UpdateFlowInput): Promise<Flow>;
15
- addState(flowId: string, state: CreateStateInput): Promise<State>;
16
- updateState(stateId: string, changes: UpdateStateInput): Promise<State>;
17
- addTransition(flowId: string, transition: CreateTransitionInput): Promise<Transition>;
18
- updateTransition(transitionId: string, changes: UpdateTransitionInput): Promise<Transition>;
19
- snapshot(flowId: string): Promise<FlowVersion>;
20
- restore(flowId: string, version: number): Promise<void>;
21
- }
22
- export {};