bopodev-db 0.1.1
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/LICENSE +21 -0
- package/dist/bootstrap.d.ts +6 -0
- package/dist/client.d.ts +10 -0
- package/dist/index.d.ts +4 -0
- package/dist/repositories.d.ts +424 -0
- package/dist/schema.d.ts +4027 -0
- package/package.json +19 -0
- package/src/bootstrap.ts +213 -0
- package/src/client.ts +16 -0
- package/src/index.ts +4 -0
- package/src/repositories.ts +769 -0
- package/src/schema.ts +226 -0
- package/tsconfig.json +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bopodev-db",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@electric-sql/pglite": "^0.3.7",
|
|
10
|
+
"drizzle-orm": "^0.44.5",
|
|
11
|
+
"drizzle-zod": "^0.8.2",
|
|
12
|
+
"nanoid": "^5.1.5"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json --emitDeclarationOnly",
|
|
16
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
17
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/bootstrap.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { createDb } from "./client";
|
|
3
|
+
|
|
4
|
+
export async function bootstrapDatabase(dbPath?: string) {
|
|
5
|
+
const { db, client } = await createDb(dbPath);
|
|
6
|
+
|
|
7
|
+
await db.execute(sql`
|
|
8
|
+
CREATE TABLE IF NOT EXISTS companies (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
name TEXT NOT NULL,
|
|
11
|
+
mission TEXT,
|
|
12
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
13
|
+
);
|
|
14
|
+
`);
|
|
15
|
+
await db.execute(sql`
|
|
16
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
17
|
+
id TEXT PRIMARY KEY,
|
|
18
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
description TEXT,
|
|
21
|
+
status TEXT NOT NULL DEFAULT 'planned',
|
|
22
|
+
planned_start_at TIMESTAMP,
|
|
23
|
+
workspace_local_path TEXT,
|
|
24
|
+
workspace_github_repo TEXT,
|
|
25
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
26
|
+
);
|
|
27
|
+
`);
|
|
28
|
+
await db.execute(sql`
|
|
29
|
+
ALTER TABLE projects
|
|
30
|
+
ADD COLUMN IF NOT EXISTS status TEXT NOT NULL DEFAULT 'planned';
|
|
31
|
+
`);
|
|
32
|
+
await db.execute(sql`
|
|
33
|
+
ALTER TABLE projects
|
|
34
|
+
ADD COLUMN IF NOT EXISTS planned_start_at TIMESTAMP;
|
|
35
|
+
`);
|
|
36
|
+
await db.execute(sql`
|
|
37
|
+
ALTER TABLE projects
|
|
38
|
+
ADD COLUMN IF NOT EXISTS workspace_local_path TEXT;
|
|
39
|
+
`);
|
|
40
|
+
await db.execute(sql`
|
|
41
|
+
ALTER TABLE projects
|
|
42
|
+
ADD COLUMN IF NOT EXISTS workspace_github_repo TEXT;
|
|
43
|
+
`);
|
|
44
|
+
await db.execute(sql`
|
|
45
|
+
CREATE TABLE IF NOT EXISTS goals (
|
|
46
|
+
id TEXT PRIMARY KEY,
|
|
47
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
48
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
49
|
+
parent_goal_id TEXT,
|
|
50
|
+
level TEXT NOT NULL,
|
|
51
|
+
title TEXT NOT NULL,
|
|
52
|
+
description TEXT,
|
|
53
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
54
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
55
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
56
|
+
);
|
|
57
|
+
`);
|
|
58
|
+
await db.execute(sql`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
62
|
+
manager_agent_id TEXT,
|
|
63
|
+
role TEXT NOT NULL,
|
|
64
|
+
name TEXT NOT NULL,
|
|
65
|
+
provider_type TEXT NOT NULL,
|
|
66
|
+
status TEXT NOT NULL DEFAULT 'idle',
|
|
67
|
+
heartbeat_cron TEXT NOT NULL,
|
|
68
|
+
monthly_budget_usd NUMERIC(12, 4) NOT NULL DEFAULT 0,
|
|
69
|
+
used_budget_usd NUMERIC(12, 4) NOT NULL DEFAULT 0,
|
|
70
|
+
token_usage INTEGER NOT NULL DEFAULT 0,
|
|
71
|
+
can_hire_agents BOOLEAN NOT NULL DEFAULT false,
|
|
72
|
+
state_blob TEXT NOT NULL DEFAULT '{}',
|
|
73
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
74
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
75
|
+
);
|
|
76
|
+
`);
|
|
77
|
+
await db.execute(sql`
|
|
78
|
+
CREATE TABLE IF NOT EXISTS issues (
|
|
79
|
+
id TEXT PRIMARY KEY,
|
|
80
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
81
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
82
|
+
parent_issue_id TEXT,
|
|
83
|
+
title TEXT NOT NULL,
|
|
84
|
+
body TEXT,
|
|
85
|
+
status TEXT NOT NULL DEFAULT 'todo',
|
|
86
|
+
priority TEXT NOT NULL DEFAULT 'none',
|
|
87
|
+
assignee_agent_id TEXT,
|
|
88
|
+
labels_json TEXT NOT NULL DEFAULT '[]',
|
|
89
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
90
|
+
is_claimed BOOLEAN NOT NULL DEFAULT false,
|
|
91
|
+
claimed_by_heartbeat_run_id TEXT,
|
|
92
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
93
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
94
|
+
);
|
|
95
|
+
`);
|
|
96
|
+
await db.execute(sql`
|
|
97
|
+
CREATE TABLE IF NOT EXISTS issue_comments (
|
|
98
|
+
id TEXT PRIMARY KEY,
|
|
99
|
+
issue_id TEXT NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
|
|
100
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
101
|
+
author_type TEXT NOT NULL,
|
|
102
|
+
author_id TEXT,
|
|
103
|
+
body TEXT NOT NULL,
|
|
104
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
105
|
+
);
|
|
106
|
+
`);
|
|
107
|
+
await db.execute(sql`
|
|
108
|
+
CREATE TABLE IF NOT EXISTS activity_logs (
|
|
109
|
+
id TEXT PRIMARY KEY,
|
|
110
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
111
|
+
issue_id TEXT REFERENCES issues(id) ON DELETE SET NULL,
|
|
112
|
+
actor_type TEXT NOT NULL,
|
|
113
|
+
actor_id TEXT,
|
|
114
|
+
event_type TEXT NOT NULL,
|
|
115
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
116
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
117
|
+
);
|
|
118
|
+
`);
|
|
119
|
+
await db.execute(sql`
|
|
120
|
+
CREATE TABLE IF NOT EXISTS heartbeat_runs (
|
|
121
|
+
id TEXT PRIMARY KEY,
|
|
122
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
123
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
124
|
+
status TEXT NOT NULL DEFAULT 'started',
|
|
125
|
+
started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
126
|
+
finished_at TIMESTAMP,
|
|
127
|
+
message TEXT
|
|
128
|
+
);
|
|
129
|
+
`);
|
|
130
|
+
await db.execute(sql`
|
|
131
|
+
CREATE TABLE IF NOT EXISTS approval_requests (
|
|
132
|
+
id TEXT PRIMARY KEY,
|
|
133
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
134
|
+
requested_by_agent_id TEXT,
|
|
135
|
+
action TEXT NOT NULL,
|
|
136
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
137
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
138
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
139
|
+
resolved_at TIMESTAMP
|
|
140
|
+
);
|
|
141
|
+
`);
|
|
142
|
+
await db.execute(sql`
|
|
143
|
+
CREATE TABLE IF NOT EXISTS approval_inbox_states (
|
|
144
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
145
|
+
actor_id TEXT NOT NULL,
|
|
146
|
+
approval_id TEXT NOT NULL REFERENCES approval_requests(id) ON DELETE CASCADE,
|
|
147
|
+
seen_at TIMESTAMP,
|
|
148
|
+
dismissed_at TIMESTAMP,
|
|
149
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
150
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
151
|
+
PRIMARY KEY (company_id, actor_id, approval_id)
|
|
152
|
+
);
|
|
153
|
+
`);
|
|
154
|
+
await db.execute(sql`
|
|
155
|
+
CREATE TABLE IF NOT EXISTS cost_ledger (
|
|
156
|
+
id TEXT PRIMARY KEY,
|
|
157
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
158
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
159
|
+
issue_id TEXT REFERENCES issues(id) ON DELETE SET NULL,
|
|
160
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
161
|
+
provider_type TEXT NOT NULL,
|
|
162
|
+
token_input INTEGER NOT NULL DEFAULT 0,
|
|
163
|
+
token_output INTEGER NOT NULL DEFAULT 0,
|
|
164
|
+
usd_cost NUMERIC(12, 6) NOT NULL DEFAULT 0,
|
|
165
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
166
|
+
);
|
|
167
|
+
`);
|
|
168
|
+
await db.execute(sql`
|
|
169
|
+
CREATE TABLE IF NOT EXISTS audit_events (
|
|
170
|
+
id TEXT PRIMARY KEY,
|
|
171
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
172
|
+
actor_type TEXT NOT NULL,
|
|
173
|
+
actor_id TEXT,
|
|
174
|
+
event_type TEXT NOT NULL,
|
|
175
|
+
entity_type TEXT NOT NULL,
|
|
176
|
+
entity_id TEXT NOT NULL,
|
|
177
|
+
correlation_id TEXT,
|
|
178
|
+
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
179
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
180
|
+
);
|
|
181
|
+
`);
|
|
182
|
+
await db.execute(sql`
|
|
183
|
+
CREATE TABLE IF NOT EXISTS agent_issue_labels (
|
|
184
|
+
company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
|
185
|
+
issue_id TEXT NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
|
|
186
|
+
label TEXT NOT NULL,
|
|
187
|
+
PRIMARY KEY (company_id, issue_id, label)
|
|
188
|
+
);
|
|
189
|
+
`);
|
|
190
|
+
await db.execute(sql`
|
|
191
|
+
CREATE INDEX IF NOT EXISTS idx_issues_company_status
|
|
192
|
+
ON issues (company_id, status, updated_at);
|
|
193
|
+
`);
|
|
194
|
+
await db.execute(sql`
|
|
195
|
+
CREATE INDEX IF NOT EXISTS idx_audit_events_company_created
|
|
196
|
+
ON audit_events (company_id, created_at DESC);
|
|
197
|
+
`);
|
|
198
|
+
await db.execute(sql`
|
|
199
|
+
CREATE INDEX IF NOT EXISTS idx_cost_ledger_company_created
|
|
200
|
+
ON cost_ledger (company_id, created_at DESC);
|
|
201
|
+
`);
|
|
202
|
+
await db.execute(sql`
|
|
203
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_heartbeat_runs_single_started
|
|
204
|
+
ON heartbeat_runs (company_id, agent_id)
|
|
205
|
+
WHERE status = 'started';
|
|
206
|
+
`);
|
|
207
|
+
await db.execute(sql`
|
|
208
|
+
CREATE INDEX IF NOT EXISTS idx_approval_inbox_states_company_actor_updated
|
|
209
|
+
ON approval_inbox_states (company_id, actor_id, updated_at DESC);
|
|
210
|
+
`);
|
|
211
|
+
|
|
212
|
+
return { db, client };
|
|
213
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
4
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
5
|
+
import * as dbSchema from "./schema";
|
|
6
|
+
|
|
7
|
+
export type BopoDb = ReturnType<typeof drizzle<typeof dbSchema>>;
|
|
8
|
+
|
|
9
|
+
const defaultDbPath = resolve(process.cwd(), "data", "bopohq.db");
|
|
10
|
+
|
|
11
|
+
export async function createDb(dbPath = defaultDbPath) {
|
|
12
|
+
await mkdir(dirname(dbPath), { recursive: true });
|
|
13
|
+
const client = new PGlite(dbPath);
|
|
14
|
+
const db = drizzle({ client, schema: dbSchema });
|
|
15
|
+
return { db, client };
|
|
16
|
+
}
|
package/src/index.ts
ADDED