AI tracker: owner-only Setup tab (per-provider connections, skills, instructions, plugins, routines, agents, plan/key labels, notes; ai.provider_setup_providers + provider_setup_entries, secrets refused)
Some checks failed
Deploy production / deploy (push) Failing after 1m3s

This commit is contained in:
2026-10-26 09:09:32 +00:00
parent 9e89a615c6
commit 8d2da984e0
9 changed files with 43668 additions and 6 deletions

View File

@@ -1,5 +1,13 @@
import { sql } from 'drizzle-orm';
import { check, date, doublePrecision, index, text, uniqueIndex } from 'drizzle-orm/pg-core';
import {
check,
date,
doublePrecision,
index,
integer,
text,
uniqueIndex
} from 'drizzle-orm/pg-core';
import { aiSchema } from './schemas';
import { id, timestamps, timestamptz } from './helpers';
@@ -82,3 +90,84 @@ export const aiUsageEntries = aiSchema.table(
check('usage_entries_metric_check', sql`length(btrim(${t.metric})) > 0`)
]
);
/**
* Setup tab (owner-only): how each AI provider is set up. One row per provider card; entries
* are grouped into sections. Plan/API keys are labels only: the app refuses values that look
* like a secret, and nothing here is ever a key value.
*/
export const AI_SETUP_SECTIONS = [
'connections',
'skills',
'instructions',
'plugins',
'routines',
'agents',
'plan',
'notes'
] as const;
export type AiSetupSection = (typeof AI_SETUP_SECTIONS)[number];
export const AI_SETUP_SCOPES = ['global', 'per-repo', 'app'] as const;
export type AiSetupScope = (typeof AI_SETUP_SCOPES)[number];
export const AI_SETUP_COLORS = [
'orange',
'green',
'blue',
'violet',
'pink',
'sky',
'amber',
'zinc'
] as const;
export const aiSetupProviders = aiSchema.table(
'provider_setup_providers',
{
id: id(),
name: text('name').notNull(),
/** The products this card covers, e.g. "claude.ai + Claude Code". */
products: text('products'),
color: text('color').notNull().default('zinc'),
sortOrder: integer('sort_order').notNull().default(0),
...timestamps
},
(t) => [
uniqueIndex('provider_setup_providers_name_key').on(t.name),
check('provider_setup_providers_name_check', sql`length(btrim(${t.name})) > 0`),
check(
'provider_setup_providers_color_check',
sql`${t.color} IN ('orange','green','blue','violet','pink','sky','amber','zinc')`
)
]
);
export const aiSetupEntries = aiSchema.table(
'provider_setup_entries',
{
id: id(),
providerId: integer('provider_id')
.notNull()
.references(() => aiSetupProviders.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
section: text('section').notNull(),
name: text('name').notNull(),
note: text('note'),
scope: text('scope'),
link: text('link'),
/** Free markdown text (custom instructions, longer notes). */
body: text('body'),
sortOrder: integer('sort_order').notNull().default(0),
...timestamps
},
(t) => [
index('provider_setup_entries_provider_idx').on(t.providerId, t.section, t.sortOrder),
check(
'provider_setup_entries_section_check',
sql`${t.section} IN ('connections','skills','instructions','plugins','routines','agents','plan','notes')`
),
check(
'provider_setup_entries_scope_check',
sql`${t.scope} IS NULL OR ${t.scope} IN ('global','per-repo','app')`
),
check('provider_setup_entries_name_check', sql`length(btrim(${t.name})) > 0`)
]
);