bopodev-db 0.1.34 → 0.1.35
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 +1 -1
- package/dist/load-migrate-env.d.ts +5 -0
- package/dist/repositories/company-assistant-chat.d.ts +10 -0
- package/dist/repositories/legacy.d.ts +118 -10
- package/dist/schema.d.ts +532 -8
- package/package.json +2 -1
- package/src/load-migrate-env.ts +42 -0
- package/src/migrate.ts +17 -0
- package/src/migrations/0009_agent_issue_permissions.sql +2 -0
- package/src/migrations/0010_agent_lucide_icon.sql +1 -0
- package/src/migrations/0011_plugin_installs.sql +17 -0
- package/src/migrations/meta/_journal.json +21 -0
- package/src/repositories/company-assistant-chat.ts +51 -1
- package/src/repositories/legacy.ts +144 -5
- package/src/schema.ts +28 -5
package/src/migrate.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { applyDatabaseMigrations, createDb } from "./client";
|
|
2
|
+
import { resolveDefaultDbPath } from "./default-paths";
|
|
3
|
+
import { loadMigrateEnv } from "./load-migrate-env";
|
|
2
4
|
|
|
3
5
|
async function main() {
|
|
6
|
+
loadMigrateEnv();
|
|
7
|
+
logMigrationTarget();
|
|
4
8
|
const dbPath = normalizeOptionalDbPath(process.env.BOPO_DB_PATH);
|
|
5
9
|
const connection = await createDb(dbPath);
|
|
6
10
|
try {
|
|
@@ -18,3 +22,16 @@ function normalizeOptionalDbPath(value: string | undefined) {
|
|
|
18
22
|
const normalized = value?.trim();
|
|
19
23
|
return normalized && normalized.length > 0 ? normalized : undefined;
|
|
20
24
|
}
|
|
25
|
+
|
|
26
|
+
function logMigrationTarget() {
|
|
27
|
+
const external = process.env.DATABASE_URL?.trim();
|
|
28
|
+
if (external) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.log("[bopodev-db] Migrating database from DATABASE_URL (same sources as API: .env.local, .env at repo root).");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const configured = process.env.BOPO_DB_PATH?.trim();
|
|
34
|
+
const dataPath = configured && configured.length > 0 ? configured : resolveDefaultDbPath();
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log(`[bopodev-db] Migrating embedded Postgres at ${dataPath} (set DATABASE_URL to use an external Postgres).`);
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "agents" ADD COLUMN "lucide_icon_name" text DEFAULT '' NOT NULL;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
CREATE TABLE "plugin_installs" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"company_id" text NOT NULL REFERENCES "companies"("id") ON DELETE CASCADE,
|
|
4
|
+
"plugin_id" text NOT NULL REFERENCES "plugins"("id") ON DELETE CASCADE,
|
|
5
|
+
"plugin_version" text NOT NULL,
|
|
6
|
+
"source_type" text DEFAULT 'registry' NOT NULL,
|
|
7
|
+
"source_ref" text,
|
|
8
|
+
"integrity" text,
|
|
9
|
+
"build_hash" text,
|
|
10
|
+
"artifact_path" text,
|
|
11
|
+
"manifest_json" text DEFAULT '{}' NOT NULL,
|
|
12
|
+
"status" text DEFAULT 'active' NOT NULL,
|
|
13
|
+
"created_at" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
14
|
+
);
|
|
15
|
+
--> statement-breakpoint
|
|
16
|
+
CREATE INDEX "idx_plugin_installs_company_plugin_created"
|
|
17
|
+
ON "plugin_installs" ("company_id", "plugin_id", "created_at");
|
|
@@ -64,6 +64,27 @@
|
|
|
64
64
|
"when": 1743300000000,
|
|
65
65
|
"tag": "0008_goals_parent_goal_fk",
|
|
66
66
|
"breakpoints": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"idx": 9,
|
|
70
|
+
"version": "7",
|
|
71
|
+
"when": 1743400000000,
|
|
72
|
+
"tag": "0009_agent_issue_permissions",
|
|
73
|
+
"breakpoints": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"idx": 10,
|
|
77
|
+
"version": "7",
|
|
78
|
+
"when": 1743500000000,
|
|
79
|
+
"tag": "0010_agent_lucide_icon",
|
|
80
|
+
"breakpoints": true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"idx": 11,
|
|
84
|
+
"version": "7",
|
|
85
|
+
"when": 1743600000000,
|
|
86
|
+
"tag": "0011_plugin_installs",
|
|
87
|
+
"breakpoints": true
|
|
67
88
|
}
|
|
68
89
|
]
|
|
69
90
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, asc, count, desc, eq, gte, lt } from "drizzle-orm";
|
|
1
|
+
import { and, asc, count, desc, eq, gte, lt, sql } from "drizzle-orm";
|
|
2
2
|
import { nanoid } from "nanoid";
|
|
3
3
|
import type { BopoDb } from "../client";
|
|
4
4
|
import { companyAssistantMessages, companyAssistantThreads } from "../schema";
|
|
@@ -45,6 +45,56 @@ export async function getAssistantThreadById(db: BopoDb, companyId: string, thre
|
|
|
45
45
|
return row ?? null;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/** Removes the thread and its messages (cascade). Returns whether a row was deleted. */
|
|
49
|
+
export async function deleteAssistantThread(db: BopoDb, companyId: string, threadId: string): Promise<boolean> {
|
|
50
|
+
const [row] = await db
|
|
51
|
+
.delete(companyAssistantThreads)
|
|
52
|
+
.where(and(eq(companyAssistantThreads.id, threadId), eq(companyAssistantThreads.companyId, companyId)))
|
|
53
|
+
.returning({ id: companyAssistantThreads.id });
|
|
54
|
+
return Boolean(row);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type AssistantThreadSummary = {
|
|
58
|
+
id: string;
|
|
59
|
+
createdAt: Date;
|
|
60
|
+
updatedAt: Date;
|
|
61
|
+
previewBody: string | null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** Threads for the company, newest activity first, with last message body as preview (if any). */
|
|
65
|
+
export async function listAssistantThreadsForCompany(
|
|
66
|
+
db: BopoDb,
|
|
67
|
+
companyId: string,
|
|
68
|
+
limit = 50
|
|
69
|
+
): Promise<AssistantThreadSummary[]> {
|
|
70
|
+
const capped = Math.min(Math.max(1, limit), 100);
|
|
71
|
+
const result = await db.execute(sql`
|
|
72
|
+
SELECT t.id, t.created_at, t.updated_at, lm.body AS preview_body
|
|
73
|
+
FROM company_assistant_threads t
|
|
74
|
+
LEFT JOIN LATERAL (
|
|
75
|
+
SELECT body FROM company_assistant_messages
|
|
76
|
+
WHERE thread_id = t.id
|
|
77
|
+
ORDER BY created_at DESC
|
|
78
|
+
LIMIT 1
|
|
79
|
+
) lm ON true
|
|
80
|
+
WHERE t.company_id = ${companyId}
|
|
81
|
+
ORDER BY t.updated_at DESC
|
|
82
|
+
LIMIT ${capped}
|
|
83
|
+
`);
|
|
84
|
+
const rows = result as unknown as Array<{
|
|
85
|
+
id: string;
|
|
86
|
+
created_at: Date | string;
|
|
87
|
+
updated_at: Date | string;
|
|
88
|
+
preview_body: string | null;
|
|
89
|
+
}>;
|
|
90
|
+
return rows.map((r) => ({
|
|
91
|
+
id: r.id,
|
|
92
|
+
createdAt: r.created_at instanceof Date ? r.created_at : new Date(String(r.created_at)),
|
|
93
|
+
updatedAt: r.updated_at instanceof Date ? r.updated_at : new Date(String(r.updated_at)),
|
|
94
|
+
previewBody: r.preview_body
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
48
98
|
export async function touchAssistantThread(db: BopoDb, threadId: string) {
|
|
49
99
|
await db
|
|
50
100
|
.update(companyAssistantThreads)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, asc, desc, eq, gt, gte, inArray, lt, notInArray, sql } from "drizzle-orm";
|
|
1
|
+
import { and, asc, count, desc, eq, gt, gte, inArray, lt, notInArray, sql } from "drizzle-orm";
|
|
2
2
|
import { nanoid } from "nanoid";
|
|
3
3
|
import type { BopoDb } from "../client";
|
|
4
4
|
import {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
issueGoals,
|
|
19
19
|
issues,
|
|
20
20
|
pluginConfigs,
|
|
21
|
+
pluginInstalls,
|
|
21
22
|
pluginRuns,
|
|
22
23
|
plugins,
|
|
23
24
|
projectWorkspaces,
|
|
@@ -521,8 +522,8 @@ export async function createIssue(
|
|
|
521
522
|
assigneeAgentId?: string | null;
|
|
522
523
|
labels?: string[];
|
|
523
524
|
tags?: string[];
|
|
524
|
-
|
|
525
|
-
|
|
525
|
+
routineId?: string | null;
|
|
526
|
+
routineRunId?: string | null;
|
|
526
527
|
}
|
|
527
528
|
) {
|
|
528
529
|
await assertProjectBelongsToCompany(db, input.companyId, input.projectId);
|
|
@@ -549,8 +550,8 @@ export async function createIssue(
|
|
|
549
550
|
assigneeAgentId: input.assigneeAgentId ?? null,
|
|
550
551
|
labelsJson: JSON.stringify(input.labels ?? []),
|
|
551
552
|
tagsJson: JSON.stringify(input.tags ?? []),
|
|
552
|
-
|
|
553
|
-
|
|
553
|
+
routineId: input.routineId ?? null,
|
|
554
|
+
routineRunId: input.routineRunId ?? null
|
|
554
555
|
})
|
|
555
556
|
.returning();
|
|
556
557
|
if (!row) {
|
|
@@ -1079,6 +1080,8 @@ export async function createAgent(
|
|
|
1079
1080
|
heartbeatCron: string;
|
|
1080
1081
|
monthlyBudgetUsd: string;
|
|
1081
1082
|
canHireAgents?: boolean;
|
|
1083
|
+
canAssignAgents?: boolean;
|
|
1084
|
+
canCreateIssues?: boolean;
|
|
1082
1085
|
avatarSeed?: string;
|
|
1083
1086
|
runtimeCommand?: string | null;
|
|
1084
1087
|
runtimeArgsJson?: string;
|
|
@@ -1111,7 +1114,10 @@ export async function createAgent(
|
|
|
1111
1114
|
heartbeatCron: input.heartbeatCron,
|
|
1112
1115
|
monthlyBudgetUsd: input.monthlyBudgetUsd,
|
|
1113
1116
|
canHireAgents: input.canHireAgents ?? false,
|
|
1117
|
+
canAssignAgents: input.canAssignAgents ?? true,
|
|
1118
|
+
canCreateIssues: input.canCreateIssues ?? true,
|
|
1114
1119
|
avatarSeed,
|
|
1120
|
+
lucideIconName: "",
|
|
1115
1121
|
runtimeCommand: input.runtimeCommand ?? null,
|
|
1116
1122
|
runtimeArgsJson: input.runtimeArgsJson ?? "[]",
|
|
1117
1123
|
runtimeCwd: input.runtimeCwd ?? null,
|
|
@@ -1132,6 +1138,15 @@ export async function listAgents(db: BopoDb, companyId: string) {
|
|
|
1132
1138
|
return db.select().from(agents).where(eq(agents.companyId, companyId)).orderBy(desc(agents.createdAt));
|
|
1133
1139
|
}
|
|
1134
1140
|
|
|
1141
|
+
export async function getCompanyAgent(db: BopoDb, companyId: string, agentId: string) {
|
|
1142
|
+
const [row] = await db
|
|
1143
|
+
.select()
|
|
1144
|
+
.from(agents)
|
|
1145
|
+
.where(and(eq(agents.companyId, companyId), eq(agents.id, agentId)))
|
|
1146
|
+
.limit(1);
|
|
1147
|
+
return row ?? null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1135
1150
|
export async function updateAgent(
|
|
1136
1151
|
db: BopoDb,
|
|
1137
1152
|
input: {
|
|
@@ -1158,6 +1173,8 @@ export async function updateAgent(
|
|
|
1158
1173
|
heartbeatCron?: string;
|
|
1159
1174
|
monthlyBudgetUsd?: string;
|
|
1160
1175
|
canHireAgents?: boolean;
|
|
1176
|
+
canAssignAgents?: boolean;
|
|
1177
|
+
canCreateIssues?: boolean;
|
|
1161
1178
|
runtimeCommand?: string | null;
|
|
1162
1179
|
runtimeArgsJson?: string;
|
|
1163
1180
|
runtimeCwd?: string | null;
|
|
@@ -1169,6 +1186,8 @@ export async function updateAgent(
|
|
|
1169
1186
|
interruptGraceSec?: number;
|
|
1170
1187
|
runPolicyJson?: string;
|
|
1171
1188
|
stateBlob?: Record<string, unknown>;
|
|
1189
|
+
lucideIconName?: string;
|
|
1190
|
+
avatarSeed?: string;
|
|
1172
1191
|
}
|
|
1173
1192
|
) {
|
|
1174
1193
|
if (input.managerAgentId) {
|
|
@@ -1189,6 +1208,10 @@ export async function updateAgent(
|
|
|
1189
1208
|
heartbeatCron: input.heartbeatCron,
|
|
1190
1209
|
monthlyBudgetUsd: input.monthlyBudgetUsd,
|
|
1191
1210
|
canHireAgents: input.canHireAgents,
|
|
1211
|
+
canAssignAgents: input.canAssignAgents,
|
|
1212
|
+
canCreateIssues: input.canCreateIssues,
|
|
1213
|
+
lucideIconName: input.lucideIconName,
|
|
1214
|
+
avatarSeed: input.avatarSeed,
|
|
1192
1215
|
runtimeCommand: input.runtimeCommand,
|
|
1193
1216
|
runtimeArgsJson: input.runtimeArgsJson,
|
|
1194
1217
|
runtimeCwd: input.runtimeCwd,
|
|
@@ -2303,6 +2326,122 @@ export async function appendPluginRun(
|
|
|
2303
2326
|
return id;
|
|
2304
2327
|
}
|
|
2305
2328
|
|
|
2329
|
+
export async function appendPluginInstall(
|
|
2330
|
+
db: BopoDb,
|
|
2331
|
+
input: {
|
|
2332
|
+
companyId: string;
|
|
2333
|
+
pluginId: string;
|
|
2334
|
+
pluginVersion: string;
|
|
2335
|
+
sourceType: string;
|
|
2336
|
+
sourceRef?: string | null;
|
|
2337
|
+
integrity?: string | null;
|
|
2338
|
+
buildHash?: string | null;
|
|
2339
|
+
artifactPath?: string | null;
|
|
2340
|
+
manifestJson: string;
|
|
2341
|
+
status?: "active" | "superseded" | "rolled_back";
|
|
2342
|
+
}
|
|
2343
|
+
) {
|
|
2344
|
+
const id = nanoid(14);
|
|
2345
|
+
await db.insert(pluginInstalls).values({
|
|
2346
|
+
id,
|
|
2347
|
+
companyId: input.companyId,
|
|
2348
|
+
pluginId: input.pluginId,
|
|
2349
|
+
pluginVersion: input.pluginVersion,
|
|
2350
|
+
sourceType: input.sourceType,
|
|
2351
|
+
sourceRef: input.sourceRef ?? null,
|
|
2352
|
+
integrity: input.integrity ?? null,
|
|
2353
|
+
buildHash: input.buildHash ?? null,
|
|
2354
|
+
artifactPath: input.artifactPath ?? null,
|
|
2355
|
+
manifestJson: input.manifestJson,
|
|
2356
|
+
status: input.status ?? "active"
|
|
2357
|
+
});
|
|
2358
|
+
return id;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
export async function listPluginInstalls(
|
|
2362
|
+
db: BopoDb,
|
|
2363
|
+
input: {
|
|
2364
|
+
companyId: string;
|
|
2365
|
+
pluginId: string;
|
|
2366
|
+
limit?: number;
|
|
2367
|
+
}
|
|
2368
|
+
) {
|
|
2369
|
+
const limit = Math.min(Math.max(input.limit ?? 50, 1), 200);
|
|
2370
|
+
return db
|
|
2371
|
+
.select()
|
|
2372
|
+
.from(pluginInstalls)
|
|
2373
|
+
.where(and(eq(pluginInstalls.companyId, input.companyId), eq(pluginInstalls.pluginId, input.pluginId)))
|
|
2374
|
+
.orderBy(desc(pluginInstalls.createdAt))
|
|
2375
|
+
.limit(limit);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
/** Per-plugin counts of `plugin_installs` rows for rollback UX (need ≥2 revisions to roll back). */
|
|
2379
|
+
export async function countPluginInstallRevisionsByCompany(db: BopoDb, companyId: string): Promise<Map<string, number>> {
|
|
2380
|
+
const rows = await db
|
|
2381
|
+
.select({
|
|
2382
|
+
pluginId: pluginInstalls.pluginId,
|
|
2383
|
+
revisionCount: count()
|
|
2384
|
+
})
|
|
2385
|
+
.from(pluginInstalls)
|
|
2386
|
+
.where(eq(pluginInstalls.companyId, companyId))
|
|
2387
|
+
.groupBy(pluginInstalls.pluginId);
|
|
2388
|
+
return new Map(rows.map((row) => [row.pluginId, Number(row.revisionCount)]));
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
export async function getPluginInstallById(
|
|
2392
|
+
db: BopoDb,
|
|
2393
|
+
input: {
|
|
2394
|
+
companyId: string;
|
|
2395
|
+
pluginId: string;
|
|
2396
|
+
installId: string;
|
|
2397
|
+
}
|
|
2398
|
+
) {
|
|
2399
|
+
const [row] = await db
|
|
2400
|
+
.select()
|
|
2401
|
+
.from(pluginInstalls)
|
|
2402
|
+
.where(
|
|
2403
|
+
and(
|
|
2404
|
+
eq(pluginInstalls.companyId, input.companyId),
|
|
2405
|
+
eq(pluginInstalls.pluginId, input.pluginId),
|
|
2406
|
+
eq(pluginInstalls.id, input.installId)
|
|
2407
|
+
)
|
|
2408
|
+
)
|
|
2409
|
+
.limit(1);
|
|
2410
|
+
return row ?? null;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
export async function markPluginInstallsSuperseded(db: BopoDb, input: { companyId: string; pluginId: string }) {
|
|
2414
|
+
await db
|
|
2415
|
+
.update(pluginInstalls)
|
|
2416
|
+
.set({
|
|
2417
|
+
status: "superseded"
|
|
2418
|
+
})
|
|
2419
|
+
.where(and(eq(pluginInstalls.companyId, input.companyId), eq(pluginInstalls.pluginId, input.pluginId), eq(pluginInstalls.status, "active")));
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
export async function markPluginInstallStatus(
|
|
2423
|
+
db: BopoDb,
|
|
2424
|
+
input: {
|
|
2425
|
+
companyId: string;
|
|
2426
|
+
pluginId: string;
|
|
2427
|
+
installId: string;
|
|
2428
|
+
status: "active" | "superseded" | "rolled_back";
|
|
2429
|
+
}
|
|
2430
|
+
) {
|
|
2431
|
+
await db
|
|
2432
|
+
.update(pluginInstalls)
|
|
2433
|
+
.set({
|
|
2434
|
+
status: input.status
|
|
2435
|
+
})
|
|
2436
|
+
.where(
|
|
2437
|
+
and(
|
|
2438
|
+
eq(pluginInstalls.companyId, input.companyId),
|
|
2439
|
+
eq(pluginInstalls.pluginId, input.pluginId),
|
|
2440
|
+
eq(pluginInstalls.id, input.installId)
|
|
2441
|
+
)
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2306
2445
|
export async function listPluginRuns(
|
|
2307
2446
|
db: BopoDb,
|
|
2308
2447
|
input: { companyId: string; pluginId?: string; runId?: string; limit?: number }
|
package/src/schema.ts
CHANGED
|
@@ -86,7 +86,10 @@ export const agents = pgTable("agents", {
|
|
|
86
86
|
.default("0"),
|
|
87
87
|
tokenUsage: integer("token_usage").notNull().default(0),
|
|
88
88
|
canHireAgents: boolean("can_hire_agents").notNull().default(false),
|
|
89
|
+
canAssignAgents: boolean("can_assign_agents").notNull().default(true),
|
|
90
|
+
canCreateIssues: boolean("can_create_issues").notNull().default(true),
|
|
89
91
|
avatarSeed: text("avatar_seed").notNull().default(""),
|
|
92
|
+
lucideIconName: text("lucide_icon_name").notNull().default(""),
|
|
90
93
|
runtimeCommand: text("runtime_command"),
|
|
91
94
|
runtimeArgsJson: text("runtime_args_json").notNull().default("[]"),
|
|
92
95
|
runtimeCwd: text("runtime_cwd"),
|
|
@@ -122,9 +125,9 @@ export const issues = pgTable("issues", {
|
|
|
122
125
|
externalLink: text("external_link"),
|
|
123
126
|
isClaimed: boolean("is_claimed").notNull().default(false),
|
|
124
127
|
claimedByHeartbeatRunId: text("claimed_by_heartbeat_run_id"),
|
|
125
|
-
/** Set when issue was created by a scheduled/manual
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
/** Set when issue was created by a scheduled/manual routine run (FK enforced in SQL migration). */
|
|
129
|
+
routineId: text("loop_id"),
|
|
130
|
+
routineRunId: text("loop_run_id"),
|
|
128
131
|
createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
|
|
129
132
|
updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull()
|
|
130
133
|
});
|
|
@@ -158,7 +161,7 @@ export const workLoopTriggers = pgTable("work_loop_triggers", {
|
|
|
158
161
|
companyId: text("company_id")
|
|
159
162
|
.notNull()
|
|
160
163
|
.references(() => companies.id, { onDelete: "cascade" }),
|
|
161
|
-
|
|
164
|
+
routineId: text("work_loop_id")
|
|
162
165
|
.notNull()
|
|
163
166
|
.references(() => workLoops.id, { onDelete: "cascade" }),
|
|
164
167
|
kind: text("kind").notNull().default("schedule"),
|
|
@@ -178,7 +181,7 @@ export const workLoopRuns = pgTable("work_loop_runs", {
|
|
|
178
181
|
companyId: text("company_id")
|
|
179
182
|
.notNull()
|
|
180
183
|
.references(() => companies.id, { onDelete: "cascade" }),
|
|
181
|
-
|
|
184
|
+
routineId: text("work_loop_id")
|
|
182
185
|
.notNull()
|
|
183
186
|
.references(() => workLoops.id, { onDelete: "cascade" }),
|
|
184
187
|
triggerId: text("trigger_id").references(() => workLoopTriggers.id, { onDelete: "set null" }),
|
|
@@ -522,6 +525,25 @@ export const pluginRuns = pgTable("plugin_runs", {
|
|
|
522
525
|
createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull()
|
|
523
526
|
});
|
|
524
527
|
|
|
528
|
+
export const pluginInstalls = pgTable("plugin_installs", {
|
|
529
|
+
id: text("id").primaryKey(),
|
|
530
|
+
companyId: text("company_id")
|
|
531
|
+
.notNull()
|
|
532
|
+
.references(() => companies.id, { onDelete: "cascade" }),
|
|
533
|
+
pluginId: text("plugin_id")
|
|
534
|
+
.notNull()
|
|
535
|
+
.references(() => plugins.id, { onDelete: "cascade" }),
|
|
536
|
+
pluginVersion: text("plugin_version").notNull(),
|
|
537
|
+
sourceType: text("source_type").notNull().default("registry"),
|
|
538
|
+
sourceRef: text("source_ref"),
|
|
539
|
+
integrity: text("integrity"),
|
|
540
|
+
buildHash: text("build_hash"),
|
|
541
|
+
artifactPath: text("artifact_path"),
|
|
542
|
+
manifestJson: text("manifest_json").notNull().default("{}"),
|
|
543
|
+
status: text("status").notNull().default("active"),
|
|
544
|
+
createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull()
|
|
545
|
+
});
|
|
546
|
+
|
|
525
547
|
export const agentIssueLabels = pgTable(
|
|
526
548
|
"agent_issue_labels",
|
|
527
549
|
{
|
|
@@ -560,6 +582,7 @@ export const schema = {
|
|
|
560
582
|
plugins,
|
|
561
583
|
pluginConfigs,
|
|
562
584
|
pluginRuns,
|
|
585
|
+
pluginInstalls,
|
|
563
586
|
templates,
|
|
564
587
|
templateVersions,
|
|
565
588
|
templateInstalls,
|