forked from farhoodlabs/paperclip
Merge public-gh/master into paperclip-company-import-export
This commit is contained in:
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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';
|
||||
@@ -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": "",
|
||||
|
||||
+1052
-3
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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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'`),
|
||||
}),
|
||||
);
|
||||
@@ -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,
|
||||
),
|
||||
}),
|
||||
);
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
}),
|
||||
);
|
||||
@@ -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,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
}),
|
||||
);
|
||||
@@ -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";
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user