Merge public-gh/master into paperclip-company-import-export

This commit is contained in:
Dotta
2026-03-16 17:02:39 -05:00
125 changed files with 38085 additions and 683 deletions
+56 -6
View File
@@ -1,5 +1,6 @@
import { existsSync, readFileSync, rmSync } from "node:fs";
import { createRequire } from "node:module";
import { createServer } from "node:net";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { ensurePostgresDatabase } from "./client.js";
@@ -28,6 +29,18 @@ export type MigrationConnection = {
stop: () => Promise<void>;
};
function toError(error: unknown, fallbackMessage: string): Error {
if (error instanceof Error) return error;
if (error === undefined) return new Error(fallbackMessage);
if (typeof error === "string") return new Error(`${fallbackMessage}: ${error}`);
try {
return new Error(`${fallbackMessage}: ${JSON.stringify(error)}`);
} catch {
return new Error(`${fallbackMessage}: ${String(error)}`);
}
}
function readRunningPostmasterPid(postmasterPidFile: string): number | null {
if (!existsSync(postmasterPidFile)) return null;
try {
@@ -51,6 +64,31 @@ function readPidFilePort(postmasterPidFile: string): number | null {
}
}
async function isPortInUse(port: number): Promise<boolean> {
return await new Promise((resolve) => {
const server = createServer();
server.unref();
server.once("error", (error: NodeJS.ErrnoException) => {
resolve(error.code === "EADDRINUSE");
});
server.listen(port, "127.0.0.1", () => {
server.close();
resolve(false);
});
});
}
async function findAvailablePort(startPort: number): Promise<number> {
const maxLookahead = 20;
let port = startPort;
for (let i = 0; i < maxLookahead; i += 1, port += 1) {
if (!(await isPortInUse(port))) return port;
}
throw new Error(
`Embedded PostgreSQL could not find a free port from ${startPort} to ${startPort + maxLookahead - 1}`,
);
}
async function loadEmbeddedPostgresCtor(): Promise<EmbeddedPostgresCtor> {
const require = createRequire(import.meta.url);
const resolveCandidates = [
@@ -76,6 +114,7 @@ async function ensureEmbeddedPostgresConnection(
preferredPort: number,
): Promise<MigrationConnection> {
const EmbeddedPostgres = await loadEmbeddedPostgresCtor();
const selectedPort = await findAvailablePort(preferredPort);
const postmasterPidFile = path.resolve(dataDir, "postmaster.pid");
const runningPid = readRunningPostmasterPid(postmasterPidFile);
const runningPort = readPidFilePort(postmasterPidFile);
@@ -95,7 +134,7 @@ async function ensureEmbeddedPostgresConnection(
databaseDir: dataDir,
user: "paperclip",
password: "paperclip",
port: preferredPort,
port: selectedPort,
persistent: true,
initdbFlags: ["--encoding=UTF8", "--locale=C"],
onLog: () => {},
@@ -103,19 +142,30 @@ async function ensureEmbeddedPostgresConnection(
});
if (!existsSync(path.resolve(dataDir, "PG_VERSION"))) {
await instance.initialise();
try {
await instance.initialise();
} catch (error) {
throw toError(
error,
`Failed to initialize embedded PostgreSQL cluster in ${dataDir} on port ${selectedPort}`,
);
}
}
if (existsSync(postmasterPidFile)) {
rmSync(postmasterPidFile, { force: true });
}
await instance.start();
try {
await instance.start();
} catch (error) {
throw toError(error, `Failed to start embedded PostgreSQL on port ${selectedPort}`);
}
const adminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/postgres`;
const adminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${selectedPort}/postgres`;
await ensurePostgresDatabase(adminConnectionString, "paperclip");
return {
connectionString: `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/paperclip`,
source: `embedded-postgres@${preferredPort}`,
connectionString: `postgres://paperclip:paperclip@127.0.0.1:${selectedPort}/paperclip`,
source: `embedded-postgres@${selectedPort}`,
stop: async () => {
await instance.stop();
},
+17 -1
View File
@@ -3,6 +3,18 @@ import { resolveMigrationConnection } from "./migration-runtime.js";
const jsonMode = process.argv.includes("--json");
function toError(error: unknown, context = "Migration status check failed"): Error {
if (error instanceof Error) return error;
if (error === undefined) return new Error(context);
if (typeof error === "string") return new Error(`${context}: ${error}`);
try {
return new Error(`${context}: ${JSON.stringify(error)}`);
} catch {
return new Error(`${context}: ${String(error)}`);
}
}
async function main(): Promise<void> {
const connection = await resolveMigrationConnection();
@@ -42,4 +54,8 @@ async function main(): Promise<void> {
}
}
await main();
main().catch((error) => {
const err = toError(error, "Migration status check failed");
process.stderr.write(`${err.stack ?? err.message}\n`);
process.exit(1);
});
@@ -0,0 +1,12 @@
CREATE TABLE "company_logos" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"asset_id" uuid NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "company_logos" ADD CONSTRAINT "company_logos_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "company_logos" ADD CONSTRAINT "company_logos_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "company_logos_company_uq" ON "company_logos" USING btree ("company_id");--> statement-breakpoint
CREATE UNIQUE INDEX "company_logos_asset_uq" ON "company_logos" USING btree ("asset_id");
@@ -0,0 +1,51 @@
CREATE TABLE "finance_events" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"agent_id" uuid,
"issue_id" uuid,
"project_id" uuid,
"goal_id" uuid,
"heartbeat_run_id" uuid,
"cost_event_id" uuid,
"billing_code" text,
"description" text,
"event_kind" text NOT NULL,
"direction" text DEFAULT 'debit' NOT NULL,
"biller" text NOT NULL,
"provider" text,
"execution_adapter_type" text,
"pricing_tier" text,
"region" text,
"model" text,
"quantity" integer,
"unit" text,
"amount_cents" integer NOT NULL,
"currency" text DEFAULT 'USD' NOT NULL,
"estimated" boolean DEFAULT false NOT NULL,
"external_invoice_id" text,
"metadata_json" jsonb,
"occurred_at" timestamp with time zone NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "cost_events" ADD COLUMN "heartbeat_run_id" uuid;--> statement-breakpoint
ALTER TABLE "cost_events" ADD COLUMN "biller" text DEFAULT 'unknown' NOT NULL;--> statement-breakpoint
ALTER TABLE "cost_events" ADD COLUMN "billing_type" text DEFAULT 'unknown' NOT NULL;--> statement-breakpoint
ALTER TABLE "cost_events" ADD COLUMN "cached_input_tokens" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_agent_id_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_goal_id_goals_id_fk" FOREIGN KEY ("goal_id") REFERENCES "public"."goals"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_heartbeat_run_id_heartbeat_runs_id_fk" FOREIGN KEY ("heartbeat_run_id") REFERENCES "public"."heartbeat_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "finance_events" ADD CONSTRAINT "finance_events_cost_event_id_cost_events_id_fk" FOREIGN KEY ("cost_event_id") REFERENCES "public"."cost_events"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "finance_events_company_occurred_idx" ON "finance_events" USING btree ("company_id","occurred_at");--> statement-breakpoint
CREATE INDEX "finance_events_company_biller_occurred_idx" ON "finance_events" USING btree ("company_id","biller","occurred_at");--> statement-breakpoint
CREATE INDEX "finance_events_company_kind_occurred_idx" ON "finance_events" USING btree ("company_id","event_kind","occurred_at");--> statement-breakpoint
CREATE INDEX "finance_events_company_direction_occurred_idx" ON "finance_events" USING btree ("company_id","direction","occurred_at");--> statement-breakpoint
CREATE INDEX "finance_events_company_heartbeat_run_idx" ON "finance_events" USING btree ("company_id","heartbeat_run_id");--> statement-breakpoint
CREATE INDEX "finance_events_company_cost_event_idx" ON "finance_events" USING btree ("company_id","cost_event_id");--> statement-breakpoint
ALTER TABLE "cost_events" ADD CONSTRAINT "cost_events_heartbeat_run_id_heartbeat_runs_id_fk" FOREIGN KEY ("heartbeat_run_id") REFERENCES "public"."heartbeat_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "cost_events_company_provider_occurred_idx" ON "cost_events" USING btree ("company_id","provider","occurred_at");--> statement-breakpoint
CREATE INDEX "cost_events_company_biller_occurred_idx" ON "cost_events" USING btree ("company_id","biller","occurred_at");--> statement-breakpoint
CREATE INDEX "cost_events_company_heartbeat_run_idx" ON "cost_events" USING btree ("company_id","heartbeat_run_id");
@@ -0,0 +1,102 @@
CREATE TABLE "budget_incidents" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"policy_id" uuid NOT NULL,
"scope_type" text NOT NULL,
"scope_id" uuid NOT NULL,
"metric" text NOT NULL,
"window_kind" text NOT NULL,
"window_start" timestamp with time zone NOT NULL,
"window_end" timestamp with time zone NOT NULL,
"threshold_type" text NOT NULL,
"amount_limit" integer NOT NULL,
"amount_observed" integer NOT NULL,
"status" text DEFAULT 'open' NOT NULL,
"approval_id" uuid,
"resolved_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "budget_policies" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"scope_type" text NOT NULL,
"scope_id" uuid NOT NULL,
"metric" text DEFAULT 'billed_cents' NOT NULL,
"window_kind" text NOT NULL,
"amount" integer DEFAULT 0 NOT NULL,
"warn_percent" integer DEFAULT 80 NOT NULL,
"hard_stop_enabled" boolean DEFAULT true NOT NULL,
"notify_enabled" boolean DEFAULT true NOT NULL,
"is_active" boolean DEFAULT true NOT NULL,
"created_by_user_id" text,
"updated_by_user_id" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "agents" ADD COLUMN "pause_reason" text;--> statement-breakpoint
ALTER TABLE "agents" ADD COLUMN "paused_at" timestamp with time zone;--> statement-breakpoint
ALTER TABLE "projects" ADD COLUMN "pause_reason" text;--> statement-breakpoint
ALTER TABLE "projects" ADD COLUMN "paused_at" timestamp with time zone;--> statement-breakpoint
INSERT INTO "budget_policies" (
"company_id",
"scope_type",
"scope_id",
"metric",
"window_kind",
"amount",
"warn_percent",
"hard_stop_enabled",
"notify_enabled",
"is_active"
)
SELECT
"id",
'company',
"id",
'billed_cents',
'calendar_month_utc',
"budget_monthly_cents",
80,
true,
true,
true
FROM "companies"
WHERE "budget_monthly_cents" > 0;--> statement-breakpoint
INSERT INTO "budget_policies" (
"company_id",
"scope_type",
"scope_id",
"metric",
"window_kind",
"amount",
"warn_percent",
"hard_stop_enabled",
"notify_enabled",
"is_active"
)
SELECT
"company_id",
'agent',
"id",
'billed_cents',
'calendar_month_utc',
"budget_monthly_cents",
80,
true,
true,
true
FROM "agents"
WHERE "budget_monthly_cents" > 0;--> statement-breakpoint
ALTER TABLE "budget_incidents" ADD CONSTRAINT "budget_incidents_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "budget_incidents" ADD CONSTRAINT "budget_incidents_policy_id_budget_policies_id_fk" FOREIGN KEY ("policy_id") REFERENCES "public"."budget_policies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "budget_incidents" ADD CONSTRAINT "budget_incidents_approval_id_approvals_id_fk" FOREIGN KEY ("approval_id") REFERENCES "public"."approvals"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "budget_policies" ADD CONSTRAINT "budget_policies_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "budget_incidents_company_status_idx" ON "budget_incidents" USING btree ("company_id","status");--> statement-breakpoint
CREATE INDEX "budget_incidents_company_scope_idx" ON "budget_incidents" USING btree ("company_id","scope_type","scope_id","status");--> statement-breakpoint
CREATE UNIQUE INDEX "budget_incidents_policy_window_threshold_idx" ON "budget_incidents" USING btree ("policy_id","window_start","threshold_type");--> statement-breakpoint
CREATE INDEX "budget_policies_company_scope_active_idx" ON "budget_policies" USING btree ("company_id","scope_type","scope_id","is_active");--> statement-breakpoint
CREATE INDEX "budget_policies_company_window_idx" ON "budget_policies" USING btree ("company_id","window_kind","metric");--> statement-breakpoint
CREATE UNIQUE INDEX "budget_policies_company_scope_metric_unique_idx" ON "budget_policies" USING btree ("company_id","scope_type","scope_id","metric","window_kind");
@@ -0,0 +1,2 @@
ALTER TABLE "companies" ADD COLUMN "pause_reason" text;--> statement-breakpoint
ALTER TABLE "companies" ADD COLUMN "paused_at" timestamp with time zone;
@@ -0,0 +1,2 @@
DROP INDEX "budget_incidents_policy_window_threshold_idx";--> statement-breakpoint
CREATE UNIQUE INDEX "budget_incidents_policy_window_threshold_idx" ON "budget_incidents" USING btree ("policy_id","window_start","threshold_type") WHERE "budget_incidents"."status" <> 'dismissed';
+105 -168
View File
@@ -1,5 +1,5 @@
{
"id": "5c4c1d61-6416-4280-8262-3035cd5e92a7",
"id": "ff007d90-e1a0-4df3-beab-a5be4a47273c",
"prevId": "fdb36f4e-6463-497d-b704-22d33be9b450",
"version": "7",
"dialect": "postgresql",
@@ -2179,6 +2179,110 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.company_logos": {
"name": "company_logos",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"company_id": {
"name": "company_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"asset_id": {
"name": "asset_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"company_logos_company_uq": {
"name": "company_logos_company_uq",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
},
"company_logos_asset_uq": {
"name": "company_logos_asset_uq",
"columns": [
{
"expression": "asset_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"company_logos_company_id_companies_id_fk": {
"name": "company_logos_company_id_companies_id_fk",
"tableFrom": "company_logos",
"tableTo": "companies",
"columnsFrom": [
"company_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"company_logos_asset_id_assets_id_fk": {
"name": "company_logos_asset_id_assets_id_fk",
"tableFrom": "company_logos",
"tableTo": "assets",
"columnsFrom": [
"asset_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.company_memberships": {
"name": "company_memberships",
"schema": "",
@@ -2657,173 +2761,6 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.company_skills": {
"name": "company_skills",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"company_id": {
"name": "company_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"slug": {
"name": "slug",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"markdown": {
"name": "markdown",
"type": "text",
"primaryKey": false,
"notNull": true
},
"source_type": {
"name": "source_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'local_path'"
},
"source_locator": {
"name": "source_locator",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_ref": {
"name": "source_ref",
"type": "text",
"primaryKey": false,
"notNull": false
},
"trust_level": {
"name": "trust_level",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'markdown_only'"
},
"compatibility": {
"name": "compatibility",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'compatible'"
},
"file_inventory": {
"name": "file_inventory",
"type": "jsonb",
"primaryKey": false,
"notNull": true,
"default": "'[]'::jsonb"
},
"metadata": {
"name": "metadata",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"company_skills_company_slug_idx": {
"name": "company_skills_company_slug_idx",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
},
"company_skills_company_name_idx": {
"name": "company_skills_company_name_idx",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "name",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"company_skills_company_id_companies_id_fk": {
"name": "company_skills_company_id_companies_id_fk",
"tableFrom": "company_skills",
"tableTo": "companies",
"columnsFrom": [
"company_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.cost_events": {
"name": "cost_events",
"schema": "",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+30 -2
View File
@@ -215,8 +215,36 @@
{
"idx": 30,
"version": "7",
"when": 1773542603948,
"tag": "0030_friendly_loners",
"when": 1773670925214,
"tag": "0030_rich_magneto",
"breakpoints": true
},
{
"idx": 31,
"version": "7",
"when": 1773511922713,
"tag": "0031_zippy_magma",
"breakpoints": true
},
{
"idx": 32,
"version": "7",
"when": 1773542934499,
"tag": "0032_pretty_doctor_octopus",
"breakpoints": true
},
{
"idx": 33,
"version": "7",
"when": 1773664961967,
"tag": "0033_shiny_black_tarantula",
"breakpoints": true
},
{
"idx": 34,
"version": "7",
"when": 1773697572188,
"tag": "0034_fat_dormammu",
"breakpoints": true
}
]
+2
View File
@@ -27,6 +27,8 @@ export const agents = pgTable(
runtimeConfig: jsonb("runtime_config").$type<Record<string, unknown>>().notNull().default({}),
budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0),
spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0),
pauseReason: text("pause_reason"),
pausedAt: timestamp("paused_at", { withTimezone: true }),
permissions: jsonb("permissions").$type<Record<string, unknown>>().notNull().default({}),
lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
@@ -0,0 +1,42 @@
import { sql } from "drizzle-orm";
import { index, integer, pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core";
import { approvals } from "./approvals.js";
import { budgetPolicies } from "./budget_policies.js";
import { companies } from "./companies.js";
export const budgetIncidents = pgTable(
"budget_incidents",
{
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id),
policyId: uuid("policy_id").notNull().references(() => budgetPolicies.id),
scopeType: text("scope_type").notNull(),
scopeId: uuid("scope_id").notNull(),
metric: text("metric").notNull(),
windowKind: text("window_kind").notNull(),
windowStart: timestamp("window_start", { withTimezone: true }).notNull(),
windowEnd: timestamp("window_end", { withTimezone: true }).notNull(),
thresholdType: text("threshold_type").notNull(),
amountLimit: integer("amount_limit").notNull(),
amountObserved: integer("amount_observed").notNull(),
status: text("status").notNull().default("open"),
approvalId: uuid("approval_id").references(() => approvals.id),
resolvedAt: timestamp("resolved_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyStatusIdx: index("budget_incidents_company_status_idx").on(table.companyId, table.status),
companyScopeIdx: index("budget_incidents_company_scope_idx").on(
table.companyId,
table.scopeType,
table.scopeId,
table.status,
),
policyWindowIdx: uniqueIndex("budget_incidents_policy_window_threshold_idx").on(
table.policyId,
table.windowStart,
table.thresholdType,
).where(sql`${table.status} <> 'dismissed'`),
}),
);
+43
View File
@@ -0,0 +1,43 @@
import { boolean, index, integer, pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
export const budgetPolicies = pgTable(
"budget_policies",
{
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id),
scopeType: text("scope_type").notNull(),
scopeId: uuid("scope_id").notNull(),
metric: text("metric").notNull().default("billed_cents"),
windowKind: text("window_kind").notNull(),
amount: integer("amount").notNull().default(0),
warnPercent: integer("warn_percent").notNull().default(80),
hardStopEnabled: boolean("hard_stop_enabled").notNull().default(true),
notifyEnabled: boolean("notify_enabled").notNull().default(true),
isActive: boolean("is_active").notNull().default(true),
createdByUserId: text("created_by_user_id"),
updatedByUserId: text("updated_by_user_id"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyScopeActiveIdx: index("budget_policies_company_scope_active_idx").on(
table.companyId,
table.scopeType,
table.scopeId,
table.isActive,
),
companyWindowIdx: index("budget_policies_company_window_idx").on(
table.companyId,
table.windowKind,
table.metric,
),
companyScopeMetricUniqueIdx: uniqueIndex("budget_policies_company_scope_metric_unique_idx").on(
table.companyId,
table.scopeType,
table.scopeId,
table.metric,
table.windowKind,
),
}),
);
+2
View File
@@ -7,6 +7,8 @@ export const companies = pgTable(
name: text("name").notNull(),
description: text("description"),
status: text("status").notNull().default("active"),
pauseReason: text("pause_reason"),
pausedAt: timestamp("paused_at", { withTimezone: true }),
issuePrefix: text("issue_prefix").notNull().default("PAP"),
issueCounter: integer("issue_counter").notNull().default(0),
budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0),
+18
View File
@@ -0,0 +1,18 @@
import { pgTable, uuid, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { assets } from "./assets.js";
export const companyLogos = pgTable(
"company_logos",
{
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }),
assetId: uuid("asset_id").notNull().references(() => assets.id, { onDelete: "cascade" }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyUq: uniqueIndex("company_logos_company_uq").on(table.companyId),
assetUq: uniqueIndex("company_logos_asset_uq").on(table.assetId),
}),
);
+19
View File
@@ -4,6 +4,7 @@ import { agents } from "./agents.js";
import { issues } from "./issues.js";
import { projects } from "./projects.js";
import { goals } from "./goals.js";
import { heartbeatRuns } from "./heartbeat_runs.js";
export const costEvents = pgTable(
"cost_events",
@@ -14,10 +15,14 @@ export const costEvents = pgTable(
issueId: uuid("issue_id").references(() => issues.id),
projectId: uuid("project_id").references(() => projects.id),
goalId: uuid("goal_id").references(() => goals.id),
heartbeatRunId: uuid("heartbeat_run_id").references(() => heartbeatRuns.id),
billingCode: text("billing_code"),
provider: text("provider").notNull(),
biller: text("biller").notNull().default("unknown"),
billingType: text("billing_type").notNull().default("unknown"),
model: text("model").notNull(),
inputTokens: integer("input_tokens").notNull().default(0),
cachedInputTokens: integer("cached_input_tokens").notNull().default(0),
outputTokens: integer("output_tokens").notNull().default(0),
costCents: integer("cost_cents").notNull(),
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
@@ -30,5 +35,19 @@ export const costEvents = pgTable(
table.agentId,
table.occurredAt,
),
companyProviderOccurredIdx: index("cost_events_company_provider_occurred_idx").on(
table.companyId,
table.provider,
table.occurredAt,
),
companyBillerOccurredIdx: index("cost_events_company_biller_occurred_idx").on(
table.companyId,
table.biller,
table.occurredAt,
),
companyHeartbeatRunIdx: index("cost_events_company_heartbeat_run_idx").on(
table.companyId,
table.heartbeatRunId,
),
}),
);
+67
View File
@@ -0,0 +1,67 @@
import { pgTable, uuid, text, timestamp, integer, index, boolean, jsonb } from "drizzle-orm/pg-core";
import { companies } from "./companies.js";
import { agents } from "./agents.js";
import { issues } from "./issues.js";
import { projects } from "./projects.js";
import { goals } from "./goals.js";
import { heartbeatRuns } from "./heartbeat_runs.js";
import { costEvents } from "./cost_events.js";
export const financeEvents = pgTable(
"finance_events",
{
id: uuid("id").primaryKey().defaultRandom(),
companyId: uuid("company_id").notNull().references(() => companies.id),
agentId: uuid("agent_id").references(() => agents.id),
issueId: uuid("issue_id").references(() => issues.id),
projectId: uuid("project_id").references(() => projects.id),
goalId: uuid("goal_id").references(() => goals.id),
heartbeatRunId: uuid("heartbeat_run_id").references(() => heartbeatRuns.id),
costEventId: uuid("cost_event_id").references(() => costEvents.id),
billingCode: text("billing_code"),
description: text("description"),
eventKind: text("event_kind").notNull(),
direction: text("direction").notNull().default("debit"),
biller: text("biller").notNull(),
provider: text("provider"),
executionAdapterType: text("execution_adapter_type"),
pricingTier: text("pricing_tier"),
region: text("region"),
model: text("model"),
quantity: integer("quantity"),
unit: text("unit"),
amountCents: integer("amount_cents").notNull(),
currency: text("currency").notNull().default("USD"),
estimated: boolean("estimated").notNull().default(false),
externalInvoiceId: text("external_invoice_id"),
metadataJson: jsonb("metadata_json").$type<Record<string, unknown> | null>(),
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
companyOccurredIdx: index("finance_events_company_occurred_idx").on(table.companyId, table.occurredAt),
companyBillerOccurredIdx: index("finance_events_company_biller_occurred_idx").on(
table.companyId,
table.biller,
table.occurredAt,
),
companyKindOccurredIdx: index("finance_events_company_kind_occurred_idx").on(
table.companyId,
table.eventKind,
table.occurredAt,
),
companyDirectionOccurredIdx: index("finance_events_company_direction_occurred_idx").on(
table.companyId,
table.direction,
table.occurredAt,
),
companyHeartbeatRunIdx: index("finance_events_company_heartbeat_run_idx").on(
table.companyId,
table.heartbeatRunId,
),
companyCostEventIdx: index("finance_events_company_cost_event_idx").on(
table.companyId,
table.costEventId,
),
}),
);
+4
View File
@@ -1,4 +1,5 @@
export { companies } from "./companies.js";
export { companyLogos } from "./company_logos.js";
export { authUsers, authSessions, authAccounts, authVerifications } from "./auth.js";
export { instanceUserRoles } from "./instance_user_roles.js";
export { agents } from "./agents.js";
@@ -6,6 +7,8 @@ export { companyMemberships } from "./company_memberships.js";
export { principalPermissionGrants } from "./principal_permission_grants.js";
export { invites } from "./invites.js";
export { joinRequests } from "./join_requests.js";
export { budgetPolicies } from "./budget_policies.js";
export { budgetIncidents } from "./budget_incidents.js";
export { agentConfigRevisions } from "./agent_config_revisions.js";
export { agentApiKeys } from "./agent_api_keys.js";
export { agentRuntimeState } from "./agent_runtime_state.js";
@@ -30,6 +33,7 @@ export { issueDocuments } from "./issue_documents.js";
export { heartbeatRuns } from "./heartbeat_runs.js";
export { heartbeatRunEvents } from "./heartbeat_run_events.js";
export { costEvents } from "./cost_events.js";
export { financeEvents } from "./finance_events.js";
export { approvals } from "./approvals.js";
export { approvalComments } from "./approval_comments.js";
export { activityLog } from "./activity_log.js";
+2
View File
@@ -15,6 +15,8 @@ export const projects = pgTable(
leadAgentId: uuid("lead_agent_id").references(() => agents.id),
targetDate: date("target_date"),
color: text("color"),
pauseReason: text("pause_reason"),
pausedAt: timestamp("paused_at", { withTimezone: true }),
executionWorkspacePolicy: jsonb("execution_workspace_policy").$type<Record<string, unknown>>(),
archivedAt: timestamp("archived_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),