CREATE TABLE IF NOT EXISTS hwlab_billing_plans ( id TEXT PRIMARY KEY, display_name TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'active', description TEXT NOT NULL DEFAULT '', metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (status IN ('active', 'disabled')) ); INSERT INTO hwlab_billing_plans (id, display_name, status, description, metadata) VALUES ('default', 'Default', 'active', 'Default HWLAB v0.3 resource plan', '{"source":"hwlab-v03","limitSemantics":"zero-means-unlimited"}'::jsonb) ON CONFLICT (id) DO UPDATE SET display_name = EXCLUDED.display_name, status = EXCLUDED.status, description = EXCLUDED.description, metadata = hwlab_billing_plans.metadata || EXCLUDED.metadata, updated_at = now(); CREATE TABLE IF NOT EXISTS hwlab_resource_entitlements ( id TEXT PRIMARY KEY, plan_id TEXT NOT NULL REFERENCES hwlab_billing_plans(id) ON DELETE CASCADE, resource_type TEXT NOT NULL, enabled BOOLEAN NOT NULL DEFAULT TRUE, monthly_quota_credits BIGINT NOT NULL DEFAULT 0, max_concurrent_reservations INTEGER NOT NULL DEFAULT 0, rpm_limit INTEGER NOT NULL DEFAULT 0, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (plan_id, resource_type), CHECK (resource_type <> ''), CHECK (monthly_quota_credits >= 0), CHECK (max_concurrent_reservations >= 0), CHECK (rpm_limit >= 0) ); CREATE INDEX IF NOT EXISTS hwlab_resource_entitlements_plan_idx ON hwlab_resource_entitlements(plan_id); CREATE INDEX IF NOT EXISTS hwlab_resource_entitlements_resource_idx ON hwlab_resource_entitlements(resource_type); INSERT INTO hwlab_resource_entitlements (id, plan_id, resource_type, enabled, monthly_quota_credits, max_concurrent_reservations, rpm_limit, metadata) VALUES ('ent_default_code_agent', 'default', 'code_agent', TRUE, 0, 0, 0, '{"serviceIds":["hwlab-code-agent"],"limitSemantics":"zero-means-unlimited"}'::jsonb), ('ent_default_aipod', 'default', 'aipod', TRUE, 0, 0, 0, '{"serviceIds":["hwlab-aipod"],"limitSemantics":"zero-means-unlimited"}'::jsonb), ('ent_default_hwpod', 'default', 'hwpod', TRUE, 0, 0, 0, '{"serviceIds":["hwlab-hwpod"],"limitSemantics":"zero-means-unlimited"}'::jsonb) ON CONFLICT (plan_id, resource_type) DO UPDATE SET enabled = EXCLUDED.enabled, monthly_quota_credits = EXCLUDED.monthly_quota_credits, max_concurrent_reservations = EXCLUDED.max_concurrent_reservations, rpm_limit = EXCLUDED.rpm_limit, metadata = hwlab_resource_entitlements.metadata || EXCLUDED.metadata, updated_at = now(); ALTER TABLE hwlab_billing_reservations ADD COLUMN IF NOT EXISTS resource_type TEXT NOT NULL DEFAULT 'generic'; ALTER TABLE hwlab_usage_records ADD COLUMN IF NOT EXISTS resource_type TEXT NOT NULL DEFAULT 'generic'; UPDATE hwlab_billing_reservations SET resource_type = CASE WHEN lower(service_id) LIKE '%code-agent%' OR lower(service_id) LIKE '%code_agent%' THEN 'code_agent' WHEN lower(service_id) LIKE '%aipod%' THEN 'aipod' WHEN lower(service_id) LIKE '%hwpod%' THEN 'hwpod' ELSE resource_type END WHERE resource_type = 'generic'; UPDATE hwlab_usage_records SET resource_type = CASE WHEN lower(service_id) LIKE '%code-agent%' OR lower(service_id) LIKE '%code_agent%' THEN 'code_agent' WHEN lower(service_id) LIKE '%aipod%' THEN 'aipod' WHEN lower(service_id) LIKE '%hwpod%' THEN 'hwpod' ELSE resource_type END WHERE resource_type = 'generic'; CREATE INDEX IF NOT EXISTS hwlab_billing_reservations_user_resource_idx ON hwlab_billing_reservations(user_id, resource_type, status, expires_at); CREATE INDEX IF NOT EXISTS hwlab_billing_reservations_user_resource_created_idx ON hwlab_billing_reservations(user_id, resource_type, created_at); CREATE INDEX IF NOT EXISTS hwlab_usage_records_user_resource_created_idx ON hwlab_usage_records(user_id, resource_type, created_at);