Build the agent-native compute CRM platform
CI / verify (push) Successful in 3m6s

This commit is contained in:
2026-08-13 01:39:01 -07:00
parent bfd2f8d95a
commit 853bde2265
160 changed files with 61812 additions and 483 deletions
+11 -32
View File
@@ -33,6 +33,13 @@ import {
timestamp,
uuid,
} from 'drizzle-orm/pg-core';
import { ALLOCATION_STATUSES, GUARANTEE_TYPES } from '@pig/core';
export {
ALLOCATION_STATUSES,
CONSUMING_ALLOCATION_STATUSES,
RESERVING_ALLOCATION_STATUSES,
} from '@pig/core';
export type { AllocationStatus } from '@pig/core';
import { capacityCommitments } from './supply';
import { demandDeals } from './demand';
import { users } from './identity';
@@ -95,7 +102,7 @@ export const allocations = pgTable(
* allocations are shown separately so a seller can see what the pipeline
* would do to utilisation without letting forecasts pollute the actuals.
*/
status: text('status').notNull().default('planned'),
status: text('status', { enum: ALLOCATION_STATUSES }).notNull().default('planned'),
/**
* A capacity HOLD with an expiry.
@@ -129,7 +136,9 @@ export const allocations = pgTable(
* system answer "can I actually sell this?" rather than "is something
* technically free?"
*/
guaranteeType: text('guarantee_type').notNull().default('committed'),
guaranteeType: text('guarantee_type', { enum: GUARANTEE_TYPES })
.notNull()
.default('committed'),
priority: integer('priority').notNull().default(100),
/**
@@ -160,35 +169,5 @@ export const allocations = pgTable(
],
);
/**
* Statuses that actually consume committed capacity.
*
* Kept here beside the column it describes so the definition cannot drift away
* from the schema. Utilisation and idle-capacity figures filter on this;
* including `planned` would let optimistic forecasting hide idle hardware.
*/
export const CONSUMING_ALLOCATION_STATUSES = ['committed', 'active', 'completed'] as const;
/**
* Statuses that block the capacity from being sold to somebody else.
*
* Deliberately WIDER than the set that counts toward utilisation and margin.
* A live hold must remove inventory from availability — otherwise two sellers
* promise the same GPUs — while not yet counting as sold, because it has not
* been. Conflating "cannot be offered to anyone else" with "earning revenue"
* is how a pipeline of optimistic holds comes to look like a full book.
*/
export const RESERVING_ALLOCATION_STATUSES = [
'planned',
...CONSUMING_ALLOCATION_STATUSES,
] as const;
export const ALLOCATION_STATUSES = [
'planned',
...CONSUMING_ALLOCATION_STATUSES,
'released',
] as const;
export type AllocationStatus = (typeof ALLOCATION_STATUSES)[number];
export type Allocation = typeof allocations.$inferSelect;
export type NewAllocation = typeof allocations.$inferInsert;
+20
View File
@@ -218,3 +218,23 @@ export const complianceArtifacts = pgTable(
export type ExportAuthorization = typeof exportAuthorizations.$inferSelect;
export type ComplianceDecision = typeof complianceDecisions.$inferSelect;
export type ComplianceArtifact = typeof complianceArtifacts.$inferSelect;
export type ComplianceMatchDecision = Pick<
ComplianceDecision,
'decision' | 'supersededAt'
>;
/**
* Whether a recorded export-control predicate permits a prospective match.
*
* `undefined` means no buyer-specific evaluation was requested, as in a generic
* inventory search. Once evaluation is requested, absence, review, block and a
* superseded allow all fail closed. Security classification is not a substitute
* for an export-control determination.
*/
export function complianceDecisionAllowsMatch(
decision: ComplianceMatchDecision | null | undefined,
): boolean {
if (decision === undefined) return true;
return decision?.decision === 'allow' && decision.supersededAt === null;
}
+11 -1
View File
@@ -214,15 +214,25 @@ export const channelLinks = pgTable(
id: uuid('id').primaryKey().defaultRandom(),
/** 'slack' | 'buzz' — kept as text so a new platform needs no migration. */
platform: text('platform').notNull(),
/** Slack channel ids are unique only inside a workspace. Buzz uses ''. */
workspaceId: text('workspace_id').notNull().default(''),
channelId: text('channel_id').notNull(),
channelName: text('channel_name'),
accountId: uuid('account_id').references(() => accounts.id, { onDelete: 'cascade' }),
/** Notify on stage changes, idle-capacity alerts, renewals due. */
notifyOn: jsonb('notify_on').$type<string[]>().notNull().default([]),
linkedByUserId: uuid('linked_by_user_id').references(() => users.id, {
onDelete: 'set null',
}),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
uniqueIndex('channel_links_platform_channel_key').on(t.platform, t.channelId),
uniqueIndex('channel_links_platform_workspace_channel_key').on(
t.platform,
t.workspaceId,
t.channelId,
),
index('channel_links_account_idx').on(t.accountId),
],
);
+44
View File
@@ -0,0 +1,44 @@
import { index, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { users } from './identity';
/** One least-privilege Google Sheets connection per PIG user. */
export const googleConnections = pgTable('google_connections', {
userId: uuid('user_id')
.primaryKey()
.references(() => users.id, { onDelete: 'cascade' }),
/** Purpose-bound AES-256-GCM envelopes. Tokens are never returned by the API. */
refreshTokenEncrypted: text('refresh_token_encrypted').notNull(),
accessTokenEncrypted: text('access_token_encrypted'),
accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
scopes: jsonb('scopes').$type<string[]>().notNull().default([]),
connectedAt: timestamp('connected_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
/**
* Short-lived OAuth proof material stays server-side. State is hashed and the
* PKCE verifier encrypted so a database read cannot complete an OAuth flow.
*/
export const googleOauthFlows = pgTable(
'google_oauth_flows',
{
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
stateHash: text('state_hash').notNull(),
browserBindingHash: text('browser_binding_hash').notNull(),
pkceVerifierEncrypted: text('pkce_verifier_encrypted').notNull(),
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
consumedAt: timestamp('consumed_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [
uniqueIndex('google_oauth_flows_state_hash_key').on(table.stateHash),
index('google_oauth_flows_expiry_idx').on(table.expiresAt),
index('google_oauth_flows_user_idx').on(table.userId),
],
);
export type GoogleConnection = typeof googleConnections.$inferSelect;
export type GoogleOauthFlow = typeof googleOauthFlows.$inferSelect;
+33
View File
@@ -0,0 +1,33 @@
import { index, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { users } from './identity';
/**
* Durable source identities make repeated imports updates rather than duplicate
* creates. Raw spreadsheet content stays out of the database; only the user's
* chosen stable key and the PIG record it resolved to are retained.
*/
export const importIdentities = pgTable(
'import_identities',
{
id: uuid('id').primaryKey().defaultRandom(),
entity: text('entity').notNull(),
keyColumn: text('key_column').notNull(),
keyValue: text('key_value').notNull(),
recordId: uuid('record_id').notNull(),
importedByUserId: uuid('imported_by_user_id').references(() => users.id, {
onDelete: 'set null',
}),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [
uniqueIndex('import_identities_source_key').on(
table.entity,
table.keyColumn,
table.keyValue,
),
index('import_identities_record_idx').on(table.entity, table.recordId),
],
);
export type ImportIdentity = typeof importIdentities.$inferSelect;
+5
View File
@@ -16,6 +16,7 @@
*/
export * from './enums';
export * from './identity';
export * from './settings';
export * from './crm';
export * from './supply';
export * from './demand';
@@ -24,3 +25,7 @@ export * from './contracts';
export * from './compliance';
export * from './agent';
export * from './fields';
export * from './integrations';
export * from './imports';
export * from './google';
export * from './notion';
+44
View File
@@ -0,0 +1,44 @@
import { index, integer, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { channelLinks } from './crm';
import { users } from './identity';
/**
* External delivery is a separate durable concern from the CRM transaction.
* A deal write only adds a row here; a provider-specific worker owns network
* failure, retry and completion without holding the request thread open.
*/
export const notificationOutbox = pgTable(
'notification_outbox',
{
id: uuid('id').primaryKey().defaultRandom(),
provider: text('provider').notNull(),
kind: text('kind').notNull(),
linkId: uuid('link_id').references(() => channelLinks.id, { onDelete: 'set null' }),
workspaceId: text('workspace_id'),
destination: text('destination').notNull(),
payload: jsonb('payload').$type<Record<string, unknown>>().notNull(),
idempotencyKey: text('idempotency_key').notNull(),
status: text('status').notNull().default('pending'),
attempts: integer('attempts').notNull().default(0),
maxAttempts: integer('max_attempts').notNull().default(5),
dueAt: timestamp('due_at', { withTimezone: true }).notNull().defaultNow(),
leasedUntil: timestamp('leased_until', { withTimezone: true }),
leasedBy: text('leased_by'),
deliveredAt: timestamp('delivered_at', { withTimezone: true }),
externalId: text('external_id'),
error: text('error'),
requestedByUserId: uuid('requested_by_user_id').references(() => users.id, {
onDelete: 'set null',
}),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
uniqueIndex('notification_outbox_idempotency_key').on(t.idempotencyKey),
index('notification_outbox_claimable_idx').on(t.provider, t.status, t.dueAt),
index('notification_outbox_link_idx').on(t.linkId),
],
);
export type NotificationOutboxItem = typeof notificationOutbox.$inferSelect;
+45
View File
@@ -0,0 +1,45 @@
import { index, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { users } from './identity';
/**
* A Notion grant belongs to the person who completed OAuth. Credentials are a
* single authenticated-encryption envelope so no token-shaped value is ever
* queryable, searchable, or accidentally selected as ordinary metadata.
*/
export const notionConnections = pgTable(
'notion_connections',
{
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
workspaceId: text('workspace_id').notNull(),
workspaceName: text('workspace_name'),
workspaceIcon: text('workspace_icon'),
botId: text('bot_id'),
credentialsEncrypted: text('credentials_encrypted').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [
uniqueIndex('notion_connections_user_workspace_key').on(table.userId, table.workspaceId),
index('notion_connections_user_idx').on(table.userId),
],
);
/**
* OAuth attempts survive multiple API processes but not replay. Only hashes
* are durable; the browser verifier is an HttpOnly, short-lived cookie.
*/
export const notionOauthStates = pgTable(
'notion_oauth_states',
{
stateHash: text('state_hash').primaryKey(),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
verifierHash: text('verifier_hash').notNull(),
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [index('notion_oauth_states_expiry_idx').on(table.expiresAt)],
);
export type NotionConnection = typeof notionConnections.$inferSelect;
export type NotionOauthState = typeof notionOauthStates.$inferSelect;
+42
View File
@@ -0,0 +1,42 @@
import {
boolean,
check,
integer,
pgTable,
text,
timestamp,
uuid,
} from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import { users } from './identity';
/** Workspace-wide controls. The check keeps this deliberately single-tenant. */
export const platformSettings = pgTable(
'platform_settings',
{
id: text('id').primaryKey().default('default'),
piggyModel: text('piggy_model').notNull().default('nvidia/nemotron-3-nano-30b-a3b'),
piggyInferenceBase: text('piggy_inference_base')
.notNull()
.default('https://api.pinference.ai/api/v1'),
piggyEnabled: boolean('piggy_enabled').notNull().default(false),
/** AES-256-GCM envelope. Its key lives outside the database. */
primeApiKeyEncrypted: text('prime_api_key_encrypted'),
primeApiKeyUpdatedAt: timestamp('prime_api_key_updated_at', { withTimezone: true }),
primeSyncEnabled: boolean('prime_sync_enabled').notNull().default(false),
primeSyncIntervalMinutes: integer('prime_sync_interval_minutes').notNull().default(30),
updatedByUserId: uuid('updated_by_user_id').references(() => users.id, {
onDelete: 'set null',
}),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
check('platform_settings_singleton_check', sql`${t.id} = 'default'`),
check(
'platform_settings_sync_interval_check',
sql`${t.primeSyncIntervalMinutes} BETWEEN 1 AND 1440`,
),
],
);
export type PlatformSettings = typeof platformSettings.$inferSelect;