/** * Request and response types for the `projects` namespace. * * These types map to the shapes of the run402 API endpoints exposed by * the gateway. They are intentionally not re-used from the gateway source * (private repo) — the SDK version is the canonical client-side contract. */ import type { ProjectKeys } from "../credentials.js"; import type { ExposeManifest, EffectiveAccessPreview } from "./deploy.types.js"; import type { OperationActorSnapshot } from "./identity-links.types.js"; export type ProjectTier = "prototype" | "hobby" | "team"; export interface ProvisionOptions { /** Tier determines price, lease length, storage, and API-call limits. Default: "prototype". */ tier?: ProjectTier; /** Optional display name. Auto-generated when omitted. */ name?: string; /** * Provision into an EXISTING org by id (v1.82). The caller must hold a * `developer`+ membership on that org (a project-scoped grant cannot authorize * creating a new project). Omit for the cold-start path — the wallet's billing * account is used or auto-created exactly as before. Note: tier is governed by * the org/organization, not the project — the gateway ignores a client tier. */ orgId?: string; /** * Idempotency key for safe retries (durable-side-effects doctrine). When set, * the SDK sends it as the `Idempotency-Key` header so a re-run with the same * key + payload returns the existing project instead of creating a duplicate. * Supply a stable key derived from the logical project identity (e.g. the app * name) so an agent's natural re-run after a crash cannot double-bill. The CLI * auto-derives one from `--name` when `--idempotency-key` is omitted. */ idempotencyKey?: string; } export interface ProvisionResult { project_id: string; anon_key: string; service_key: string; schema_slot: string; endpoints?: { rest_url?: string; static_base_url?: string; storage_base_url?: string; }; active_release_id?: string | null; capabilities?: unknown; } /** * Lifecycle state of the owning organization (gateway v1.57+). The state * machine moved from `internal.projects` to `internal.organizations`; every * project on the account inherits the same value. `purging` is an internal * transition state and is not exposed on the wire. */ export type OrganizationLifecycleState = "active" | "past_due" | "frozen" | "dormant" | "purged"; /** * Effective project status, derived from `(organization_lifecycle_state, * deleted_at, archived_at)`: * - `deleted_at` set → `"deleted"` * - `archived_at` set → `"archived"` * - otherwise → the organization's `lifecycle_state` * * Use this for serving / UX decisions instead of trying to combine the * underlying fields yourself. */ export type EffectiveProjectStatus = "active" | "past_due" | "frozen" | "dormant" | "archived" | "deleted"; /** * One project row from the named, domain-aware inventory (gateway * `project-findability`). Returned by `GET /projects/v1` (membership-scoped) * and `GET /agent/v1/me/projects` (every organization, `--all`) — both * share this shape. * * Tier and lifecycle live on the owning organization, not the project; the * row mirrors them for convenience but read `r.tier.status()` for the * authoritative account view. */ export interface ProjectSummary { id: string; name: string; /** Account-derived tier (mirror; authoritative source is `r.tier.status()`). */ tier?: string; /** * Primary public URL: the first claimed run402.com subdomain, else the first * custom domain, else null. Surfaced by the named inventory (`GET /projects/v1` * and the `--all` read). */ site_url?: string | null; /** * Every custom hostname mapped to this project (empty when none). The * run402.com subdomain is reflected in `site_url`, not here. */ custom_domains?: string[]; /** Derived effective status — see {@link EffectiveProjectStatus}. */ status?: EffectiveProjectStatus; /** Alias of `status` (gateway v1.57 canonical field). */ effective_status?: EffectiveProjectStatus; /** Owning organization's lifecycle state. */ organization_lifecycle_state?: OrganizationLifecycleState; /** Account-level lease-perpetual escape hatch (mirror). */ lease_perpetual?: boolean; /** * Owning org (organization) id — v1.77 org-owned control plane. A wallet * authenticates; the org owns the project. Surfaced as `org_id` in CLI/MCP * output. `null` for legacy rows; optional because the legacy wallet-scoped * list (`GET /wallets/v1/:address/projects`) omits it. */ org_id?: string | null; /** * Provisioning principal id — provenance for who created the project (v1.77). * Optional for the same reason as {@link ProjectSummary.org_id}. */ created_by?: string | null; /** Immutable creation-time public provenance; null on legacy projects. */ creator?: OperationActorSnapshot | null; created_at: string; deleted_at?: string | null; archived_at?: string | null; /** * Legacy wallet-scoped list (`GET /wallets/v1/:address/projects`) only — the * named inventory does not include per-project usage counters. Read * `r.projects.getUsage(id)` for live usage. */ api_calls?: number; /** See {@link ProjectSummary.api_calls}. */ storage_bytes?: number; } /** * Options for {@link Projects.list}. * * Membership-scoped by default (`GET /projects/v1` — every project owned by an * org the caller's principal is an active member of). The cold-start lone-agent * path is `list()` with no options. */ export interface ListProjectsOptions { /** * Narrow to projects owned by one org (organization) id. Authorize-before- * reveal: a non-member or guessed id returns the same 403 as a real-but- * unauthorized org; a non-UUID id is a clean 400. */ org?: string; /** * Read every project the caller can reach across all its organizations * (`GET /agent/v1/me/projects`) instead of the single membership-scoped page. * Supply `token` (a sign-in session) for the person's account; without it, * `all` uses the credential provider and a SIWX wallet reads its own slice. * Mutually exclusive with `org`. */ all?: boolean; /** * Sign-in session bearer token for the `all` read. When omitted, `all` uses * the credential provider. Ignored when `all` is not set. */ token?: string; /** Page size. Server default 50, max 200. Ignored for `all` (union, unpaged). */ limit?: number; /** Opaque pagination cursor from a previous response's `next_cursor`. */ cursor?: string; } export interface ListProjectsResult { projects: ProjectSummary[]; /** True when more pages remain (membership-scoped reads). */ has_more?: boolean; /** Cursor to fetch the next page, or null at the end. */ next_cursor?: string | null; /** `all` reads echo the resolved scope: `"principal"` (a sign-in session) or `"wallet"` (a wallet slice). */ scope?: string; } /** Result of {@link Projects.rename}. */ export interface RenameProjectResult { project_id: string; name: string; } /** Result of {@link Projects.setRepoName} (`POST /projects/v1/:id/repo-name`). */ export interface SetRepoNameResult { project_id: string; repo_name: string; /** The project's PRIOR address-form name, or `null` if it had none. */ previous_repo_name: string | null; } export type TenantPaymentStatus = "settling" | "settled" | "settle_failed" | "ambiguous"; export interface ListTenantPaymentsOptions { /** Page size. Server default 50, max 200. */ limit?: number; /** Opaque keyset cursor from a previous response's `next_cursor`. */ after?: string; /** Optional status filter. */ status?: TenantPaymentStatus; } /** * Redacted project-scoped tenant x402 payment record. Raw x402 authorization * headers, canonical authorization hashes, and internal metadata are never * returned by the gateway. */ export interface TenantPaymentRecord { payment_id: string; status: TenantPaymentStatus; org_id: string; project_id: string; release_id: string | null; route_pattern: string; route_method: string; route_target_function: string | null; amount_usd_micros: number; settled_amount_usd_micros: number | null; network: string; asset: string | null; asset_address: string | null; payer: string | null; pay_to: string; scheme: "x402" | string; scheme_version: string | null; facilitator: string | null; settlement_reference: string | null; settlement_tx_hash: string | null; request_id: string | null; host: string | null; path: string | null; operation_id: string | null; attempts: number; last_error_code: string | null; last_error_message: string | null; next_reconcile_at: string | null; last_reconciled_at: string | null; reconciliation_attempts: number; reuse_expires_at: string | null; last_seen_at: string | null; created_at: string; updated_at: string; settled_at: string | null; [key: string]: unknown; } export interface TenantPaymentListResult { project_id: string; payments: TenantPaymentRecord[]; has_more: boolean; next_cursor: string | null; } /** Active-release pointer on {@link ProjectDetail}. `null` when nothing is live. */ export interface ProjectLastDeploy { release_id: string; activated_at: string; } /** Usage counters paired with the owning account's tier limits. */ export interface ProjectUsageWithLimits { api_calls: number; storage_bytes: number; api_calls_limit: number; storage_bytes_limit: number; /** `storage_bytes_limit` as a human string ("250 MB"), derived from the tier constant. */ storage_limit: string; } /** * Authoritative server-side view of one project, from `GET /projects/v1/:project_id` * (gateway `project.read`). A superset of {@link ProjectSummary} that adds the * public id, the active-release pointer, active mailbox addresses, and usage vs. * tier limits. Carries NO key material — the endpoint never returns secrets; read * `r.projects.keys(id)` (local) for the anon/service keys. * * Authorize-before-reveal: a caller without `project.read` authority sees the same * `Unauthorized` for a real-but-forbidden project as for an absent one — never a * 404 that would confirm existence. */ export interface ProjectDetail { project_id: string; /** Short public identifier (distinct from the `prj_…` id). */ public_id: string; name: string; /** Owning org (organization) id — v1.77 org-owned control plane. */ org_id: string; /** Account-derived tier (authoritative source is `r.tier.status()`). */ tier: string; /** Derived effective status — see {@link EffectiveProjectStatus}. */ effective_status: EffectiveProjectStatus; /** Owning organization's lifecycle state. */ organization_lifecycle_state: OrganizationLifecycleState; /** Primary public URL, or `null` when none is claimed. */ site_url: string | null; /** Every custom hostname mapped to this project (empty when none). */ custom_domains: string[]; /** Active-release pointer, or `null` when nothing is deployed. */ last_deploy: ProjectLastDeploy | null; /** Active mailbox addresses (formatted, e.g. `hello@p-abc123.mail.run402.com`). */ mailbox: string[]; /** Usage counters paired with the owning account's tier limits. */ usage: ProjectUsageWithLimits; created_at: string; /** Legacy creating-principal field retained for compatibility. */ created_by: string | null; /** Immutable creation-time public provenance; null on legacy projects. */ creator: OperationActorSnapshot | null; /** Forward-compat: unknown future fields a newer gateway may add. */ [key: string]: unknown; } export interface UsageReport { project_id: string; tier: string; api_calls: number; api_calls_limit: number; storage_bytes: number; storage_limit_bytes: number; /** `storage_limit_bytes` as a human string ("250 MB"), derived from the tier constant. */ storage_limit: string; /** * Optional: the `/projects/v1/admin/:id/usage` endpoint does not currently * include the lease expiry. Read it from `tier.status()` if you need it. * `null` is reserved for unleased accounts. */ lease_expires_at?: string | null; /** Derived effective status — see {@link EffectiveProjectStatus}. */ effective_status: EffectiveProjectStatus; /** Owning organization's lifecycle state. */ organization_lifecycle_state: OrganizationLifecycleState; } export interface ColumnSchema { name: string; type: string; nullable: boolean; default_value: string | null; } export interface ConstraintSchema { name: string; type: string; definition: string; } export interface RlsPolicy { name: string; command: string; using_expression: string | null; check_expression: string | null; } export interface TableSchema { name: string; columns: ColumnSchema[]; constraints: ConstraintSchema[]; rls_enabled: boolean; /** * Planner row estimate (`reltuples`), refreshed by ANALYZE/VACUUM. Always an * estimate, never an exact count. `null` when the table was never analyzed * (so "unknown" stays distinguishable from a real zero); may be absent from * gateways predating the field. */ row_estimate?: number | null; policies: RlsPolicy[]; } export interface SchemaReport { schema: string; tables: TableSchema[]; } /** A warning on a SQL result. A warning never changes the result. */ export interface SqlWarning { /** `ROUTE_RETIRING` (a retiring route was called), `SCHEMA_CHANGE_OUTSIDE_MIGRATION` * (DDL on a project with a live release, which the release no longer * describes), or `MULTI_STATEMENT_TEXT_BODY` (several statements in one text * body; use {@link Projects.sqlBatch}). Future codes remain valid strings. */ code: "ROUTE_RETIRING" | "SCHEMA_CHANGE_OUTSIDE_MIGRATION" | "MULTI_STATEMENT_TEXT_BODY" | (string & {}); severity?: string; message: string; next_actions: Array>; [key: string]: unknown; } /** One result column: its name and Postgres type. */ export interface SqlField { name: string; type: string; } /** The result of {@link Projects.sql}, in the wire shape. */ export interface SqlResult { status: string; schema: string; /** The last statement's rows. */ rows: Array>; /** The last statement's row count (returned or affected). */ row_count: number; /** The last statement's columns. */ fields: SqlField[]; /** One entry per statement, in order; their sum is a batch's total. */ statements: Array<{ command: string | null; row_count: number; }>; warnings: SqlWarning[]; } /** One statement in a {@link Projects.sqlBatch} call: exactly one SQL statement. */ export interface SqlBatchStatement { sql: string; params?: unknown[]; } export interface SqlBatchOptions { /** `all` (default): one transaction; the first failure rolls back every * statement and throws `SQL_BATCH_STATEMENT_FAILED` naming its index. * `each`: every statement in its own transaction; failures are reported * per statement and later statements still run. */ transaction?: "all" | "each"; } export type SqlBatchStatementResult = { index: number; status: "ok"; command: string | null; rows: Array>; row_count: number; fields: SqlField[]; } | { index: number; status: "error"; message: string; }; /** The result of {@link Projects.sqlBatch}. `status` is `partial` when a * statement failed under `transaction: "each"`. */ export interface SqlBatchResult { status: "ok" | "partial"; schema: string; transaction: "all" | "each"; results: SqlBatchStatementResult[]; warnings: SqlWarning[]; } export type ProjectRestMethod = "GET" | "POST" | "PATCH" | "DELETE"; export type ProjectRestKeyType = "anon" | "service"; export interface ProjectRestOptions { /** HTTP method. Default: "GET". */ method?: ProjectRestMethod; /** Query string without leading "?", or key/value query parameters. */ query?: string | Record; /** JSON body for POST/PATCH/DELETE requests. */ body?: unknown; /** Key used for apikey + bearer auth. Default: "anon". */ keyType?: ProjectRestKeyType; } export interface ProjectRestResponse { status: number; body: T; } export type ExposeManifestValidationInput = ExposeManifest | string; export interface ValidateExposeOptions { /** Project id used for live-schema validation. Omit for projectless validation. */ project?: string; /** Alias for `project`, accepted for callers that already use gateway/MCP naming. */ project_id?: string; /** Migration SQL used as validation context only; it is not executed. */ migrationSql?: string; } export type ExposeManifestValidationIssueType = "missing-table" | "missing-column" | "missing-view-base" | "missing-rpc" | "ambiguous-rpc" | "unrestricted-ack-required" | "sensitive-column-public-write" | "grant-to-role-unknown" | "force-owner-without-owner-column" | "validation-inconclusive" | "schema-shape"; export type ExposeManifestValidationSeverity = "error" | "warning"; export interface ExposeManifestValidationIssue { type: ExposeManifestValidationIssueType; severity: ExposeManifestValidationSeverity; detail: string; fix?: string; } export interface ExposeManifestValidationResult { effective_access?: EffectiveAccessPreview[]; hasErrors: boolean; errors: ExposeManifestValidationIssue[]; warnings: ExposeManifestValidationIssue[]; } /** * One tier's pricing row from `GET /tiers/v1`. Byte quotas are decimal * (250 MB is 250,000,000 bytes); each byte count carries its human string * beside it under the same name with `_bytes` removed. */ export interface TierQuote { price: string; lease_days: number | null; /** App/project storage quota in bytes, pooled per organization. */ storage_bytes: number; /** `storage_bytes` as a human string ("250 MB"). */ storage: string; /** Vault (repos) quota in bytes — a separate pooled limit from `storage_bytes`. */ source_bytes: number; /** `source_bytes` as a human string ("1 GB"). */ source: string; api_calls: number; max_functions: number; description: string; } export interface QuoteResult { tiers: Record; auth?: Record; } export interface ProjectInfo extends ProjectKeys { project_id: string; } //# sourceMappingURL=projects.types.d.ts.map