Files
smart-org/packages/contracts/src/index.ts
T

1252 lines
29 KiB
TypeScript

/**
* Shared API contracts.
*
* These types mirror the FastAPI response schemas. They are intentionally
* hand-written and small: the backend is the source of truth, and a drift here
* surfaces immediately as a TypeScript error at the call site.
*/
export type Role = "owner" | "admin" | "manager" | "member" | "viewer";
export type MembershipStatus = "active" | "invited" | "suspended";
/**
* Platform-wide role — a different axis from the in-organization `Role`.
*
* - `owner` — the single owner of the whole system. Exactly one exists; only
* they may enter the admin console, create or demote managers, and move the
* owner seat.
* - `manager` — an account the owner creates. May build its own organizations
* and join other managers' organizations by invitation.
* - `user` — a plain account. Cannot create organizations; works only inside
* organizations it was invited to.
*/
export type PlatformRole = "owner" | "manager" | "user";
/** Roles that may create an organization. */
export function canCreateOrganization(role: PlatformRole | undefined | null): boolean {
return role === "owner" || role === "manager";
}
export const PLATFORM_ROLE_LABELS: Record<PlatformRole, string> = {
owner: "مالک سامانه",
manager: "مدیر",
user: "کاربر",
};
/**
* Provider kinds the AI Control Plane can talk to. agy (Google Antigravity)
* is the only one — the platform no longer has a provider picker.
*/
export type ProviderType = "agy";
/** agy reasoning efforts, cheapest first. Mirrors the backend's list. */
export type ReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
export const REASONING_EFFORTS: ReasoningEffort[] = [
"minimal",
"low",
"medium",
"high",
"xhigh",
];
const EFFORT_LABELS: Record<ReasoningEffort, string> = {
minimal: "حداقل",
low: "کم",
medium: "متوسط",
high: "زیاد",
xhigh: "بسیار زیاد",
};
/**
* Reasoning efforts a model may be configured with, or an empty list when the
* model cannot think — the backend rejects a thinking config everywhere else.
* Same marker rule as the agy adapter: thinking-capable ids contain one of
* `thinking` / `gemini-3` / `gemini-2.5-pro` / `gemini-2.5-flash`, image
* and claude models are excluded from explicit effort selection.
*/
export function effortsForModel(modelId: string): ReasoningEffort[] {
const lower = modelId.toLowerCase();
if (lower.includes("image")) return [];
if (lower.includes("claude")) return [];
if (lower.includes("gpt")) return ["medium"];
if (lower.includes("gemini-3.1-pro")) return ["low", "high"];
const canThink =
lower.includes("thinking") ||
lower.includes("gemini-3") ||
lower.includes("gemini-2.5-pro") ||
lower.includes("gemini-2.5-flash");
return canThink ? [...REASONING_EFFORTS] : [];
}
export function effortLabel(effort: string): string {
return EFFORT_LABELS[effort as ReasoningEffort] ?? effort;
}
/** A live model reported by an agy account's catalogue. */
export interface AgyAccountModel {
id: string;
display_name: string;
supports_thinking: boolean;
supported_efforts: ReasoningEffort[];
default_effort?: ReasoningEffort | null;
supports_images?: boolean;
}
/** Rotation-pool lifecycle of one agy account. */
export type AgyAccountStatus = "active" | "cooldown" | "error";
/**
* One agy account (Google identity). Deliberately credential-free: the console
* manages accounts, never tokens.
*/
export interface AgyAccount {
id: string;
provider_id: string;
name: string;
email: string;
project_id: string;
status: AgyAccountStatus;
cooldown_until: string | null;
is_active: boolean;
last_error: string;
last_used_at: string | null;
created_at: string;
}
/** What a model can do. Routing picks models by required capability. */
export type Capability = "text" | "vision" | "tools" | "reasoning" | "long_context" | "code";
/** Routing intents. Customers never see these names — only the platform owner does. */
export type RoutingMode = "fast" | "cheap" | "balanced" | "reasoning" | "vision" | "coding";
export type TaskStatus =
| "new"
| "triaged"
| "waiting_approval"
| "running"
| "done"
| "failed"
| "cancelled";
export type RunStatus =
| "queued"
| "planning"
| "waiting_approval"
| "running"
| "tool_calling"
| "sandbox_running"
| "completed"
| "failed"
| "cancelled"
| "timed_out";
export type RoleKey =
| "manager"
| "planner"
| "developer"
| "reviewer"
| "tester"
| "researcher"
| "content_creator"
| "support";
export type EmployeeStatus = "active" | "paused" | "archived";
export type PreviewDomainStatus = "active" | "disabled" | "failed";
export type RateUnit =
| "model_input_tokens"
| "model_output_tokens"
| "sandbox_cpu_second"
| "sandbox_memory_gb_second"
| "storage_gb_day"
| "tool_call"
| "premium_employee_run";
export interface ApiErrorBody {
error: {
code: string;
message: string;
details?: unknown;
/**
* Correlates this response with the row the API wrote to its error console
* (and with the `X-Request-ID` response header). Surfacing it lets a user
* quote one string that points at exactly one server-side record.
*/
request_id?: string;
};
}
export interface User {
id: string;
email: string;
name: string;
avatar_url?: string | null;
/** Platform-wide role. Exactly one account in the system is `owner`. */
platform_role: PlatformRole;
/** True for the manager tier (may create organizations). */
is_manager: boolean;
is_platform_owner: boolean;
}
export interface Organization {
id: string;
name: string;
slug: string;
owner_user_id: string;
}
export interface OrganizationWithRole extends Organization {
role: Role;
}
export interface Member {
id: string;
user_id: string;
email: string;
name: string;
role: Role;
status: MembershipStatus;
created_at: string;
}
export interface Invite {
id: string;
email: string;
role: Role;
expires_at: string;
invite_token: string;
invite_url: string;
}
/**
* What the accept-invite screen shows before the invitee commits.
*
* Two kinds share one endpoint: a `platform` invite mints a new manager account
* (v2 D12) and has no organization yet, while an `organization` invite joins an
* existing one. The screen shows a different title for each — "ساخت حساب مدیر"
* versus joining a team — so the caller has to be able to tell them apart.
*/
export interface InvitePreview {
kind: "platform" | "organization";
organization_id: string;
organization_name: string;
email: string;
role: Role;
expires_at: string;
/** True when the signed-in user is already a member of that organization. */
already_member: boolean;
/**
* True when an account exists for the invited email. The invitee then logs
* in first; setting a password anonymously is only for brand-new accounts.
*/
has_account: boolean;
/** For a platform invite: the role the redeemed account will receive. */
platform_role: PlatformRole;
}
export interface AcceptInviteResult {
kind: "platform" | "organization";
organization_id: string;
organization_name: string;
role: Role;
}
export interface TokenResponse {
access_token: string;
token_type: string;
user: User;
}
export interface MeResponse {
user: User;
organizations: OrganizationWithRole[];
/**
* True for a manager who belongs to no organization yet — they arrived on the
* platform owner's invitation link and have built nothing, so the only useful
* thing the UI can do is open the creation wizard. The owner is excluded.
*/
needs_onboarding: boolean;
}
/** An invitation the platform owner issued, as it appears in the console. */
export interface PlatformInvite {
id: string;
email: string | null;
platform_role: string;
note: string;
expires_at: string;
created_at: string;
created_by_email: string;
}
export interface IssuedPlatformInvite {
invite: PlatformInvite;
/** The raw URL. Shown exactly once, at creation. */
invite_url: string;
}
export interface Project {
id: string;
organization_id: string;
name: string;
slug: string;
description: string;
default_branch: string;
created_at: string;
}
export interface RepoStatus {
exists: boolean;
private: boolean;
default_branch: string;
branches: string[];
open_pull_requests: number;
}
export interface Repository {
id: string;
provider: string;
owner: string;
repo_name: string;
clone_url: string;
web_url: string;
default_branch: string;
private: boolean;
}
export interface AdminModel {
id: string;
provider_id: string;
internal_name: string;
display_name_for_admin: string;
capabilities: string[];
input_price_per_1m: number;
output_price_per_1m: number;
/** agy reasoning effort ("" = provider default). Owner-only metadata. */
reasoning_effort: string;
is_active: boolean;
}
export interface AdminRoutingPolicy {
id: string;
name: string;
description: string;
mode: string;
is_default: boolean;
is_active: boolean;
}
export interface AdminRateCard {
id: string;
name: string;
unit: RateUnit;
customer_price: number;
internal_cost_estimate: number;
is_active: boolean;
}
export interface AdminOrganization {
id: string;
name: string;
slug: string;
member_count: number;
created_at: string;
}
/** Result of a routing dry-run: which model a mode would pick, and why. */
export interface ModelChoice {
mode: string;
model_id: string;
internal_name: string;
capabilities: string[];
estimated_input_price_per_1m: number;
estimated_output_price_per_1m: number;
}
export interface AdminUsageRow {
organization_id: string;
quantity: number;
total_price: number;
}
/* Account administration (platform owner only) */
/** One row in the owner's account list. */
export interface AdminUser {
id: string;
email: string;
name: string;
platform_role: PlatformRole;
is_manager: boolean;
is_platform_owner: boolean;
is_active: boolean;
created_at: string;
/** How many organizations this account owns — what moves if it is demoted. */
owned_organizations: number;
}
/**
* A created account (or a password reset). `temp_password` is shown exactly
* once and never retrievable again.
*/
export interface AdminUserCreated {
user: AdminUser;
temp_password: string;
}
export interface CreateUserRequest {
email: string;
name?: string;
/** `owner` is refused: the seat moves only through transfer-ownership. */
platform_role: "manager" | "user";
}
export interface UpdateUserRequest {
platform_role?: "manager" | "user";
is_active?: boolean;
}
export interface OrganizationTransfer {
organization_id: string;
organization_name: string;
}
export interface DeleteUserResult {
deleted_user_id: string;
/** Organizations that moved to the platform owner. */
transferred_organizations: OrganizationTransfer[];
}
export interface PreviewDomain {
id: string;
host: string;
port: number;
status: PreviewDomainStatus;
protected: boolean;
url: string;
}
export interface Employee {
id: string;
organization_id: string;
project_id?: string | null;
template_id?: string | null;
name: string;
role_key: RoleKey;
status: EmployeeStatus;
routing_policy_id?: string | null;
created_at: string;
}
export interface Task {
id: string;
organization_id: string;
project_id?: string | null;
thread_id?: string | null;
title: string;
description: string;
status: TaskStatus;
assigned_employee_id?: string | null;
created_at: string;
updated_at: string;
}
export interface Run {
id: string;
task_id: string;
employee_id?: string | null;
status: RunStatus;
error?: string | null;
started_at?: string | null;
finished_at?: string | null;
created_at: string;
}
export interface Thread {
id: string;
organization_id: string;
project_id?: string | null;
title: string;
created_at: string;
}
export interface Message {
id: string;
thread_id: string;
role: "user" | "assistant" | "system";
content: string;
employee_id?: string | null;
created_at: string;
}
export interface ThreadDetail extends Thread {
messages: Message[];
}
export interface RunStep {
id: string;
index: number;
kind: string;
title: string;
payload_json: Record<string, unknown>;
created_at: string;
}
export interface ToolCall {
id: string;
tool_key: string;
status: string;
requires_approval: boolean;
arguments_json: Record<string, unknown>;
result_json: Record<string, unknown>;
created_at: string;
}
export interface ArtifactDetail extends Artifact {
content_ref: string;
run_id?: string | null;
}
export interface RunDetail extends Run {
steps: RunStep[];
tool_calls: ToolCall[];
artifacts: ArtifactDetail[];
}
export interface TaskDetail extends Task {
runs: Run[];
}
export interface AgentTemplate {
id: string;
name: string;
role_key: RoleKey;
description: string;
default_tools_json: string[];
}
/** A routing policy as a customer may see it — no mode, no model hints. */
export interface RoutingChoice {
id: string;
name: string;
description: string;
}
export interface PromptVersion {
id: string;
version: number;
content: string;
created_at: string;
}
export interface ToolPermission {
tool_key: string;
allowed: boolean;
}
/** One entry in the tool catalog an employee can be granted. */
export interface ToolSpec {
key: string;
label: string;
description: string;
requires_approval: boolean;
destructive: boolean;
category: string;
}
export interface EmployeeDetail extends Employee {
prompt_versions: PromptVersion[];
tool_permissions: ToolPermission[];
open_task_count: number;
}
export interface PostMessageResponse {
user_message: Message;
assistant_message: Message;
}
export interface Artifact {
id: string;
name: string;
kind: string;
size_bytes: number;
created_at: string;
}
export interface BillingSummary {
balance: number;
currency: string;
low_balance: boolean;
low_balance_threshold: number;
total_spend: number;
by_unit: { unit: RateUnit; quantity: number; total_price: number }[];
by_employee: Record<string, number>;
by_project: Record<string, number>;
}
export interface UsageEvent {
id: string;
unit: RateUnit;
quantity: number;
unit_price: number;
total_price: number;
project_id?: string | null;
employee_id?: string | null;
run_id?: string | null;
created_at: string;
}
export interface Invoice {
id: string;
amount: number;
currency: string;
status: string;
period_start?: string | null;
period_end?: string | null;
created_at: string;
}
export interface Payment {
id: string;
amount: number;
currency: string;
status: string;
provider: string;
created_at: string;
}
export interface Wallet {
balance: number;
currency: string;
low_balance: boolean;
}
export interface AuditEvent {
id: string;
action: string;
actor_user_id?: string | null;
target_type: string;
target_id: string;
created_at: string;
}
// --- sandbox ----------------------------------------------------------------
export type NetworkPolicy = "deny_all" | "allowlist" | "open";
export interface SandboxTemplate {
id: string;
name: string;
provider: string;
image: string;
description: string;
default_cpu: number;
default_memory_mb: number;
default_disk_mb: number;
}
export interface SandboxWorkspace {
id: string;
project_id: string;
template_id: string;
provider: string;
status: string;
created_at: string;
}
export interface SandboxSession {
id: string;
workspace_id: string;
status: string;
started_at: string;
last_active_at: string;
}
export interface SandboxCommandRun {
id: string;
command: string;
exit_code: number | null;
stdout: string;
stderr: string;
duration_ms: number;
truncated: boolean;
created_at: string;
}
export interface SandboxFileRead {
path: string;
content: string;
}
export interface SandboxSnapshot {
id: string;
name: string;
created_at: string;
}
export interface SandboxPreviewUrl {
port: number;
url: string;
}
/**
* Effective limits are resolved server-side (project -> organization ->
* platform) and clamped. The UI only ever displays them.
*/
export interface SandboxResourceLimit {
cpu: number;
memory_mb: number;
disk_mb: number;
timeout_seconds: number;
max_output_bytes: number;
network_policy: NetworkPolicy;
}
export interface HealthResponse {
status: string;
app: string;
env: string;
}
export interface ReadinessResponse {
status: string;
checks: {
database: string;
model_provider: string;
adapters: Record<string, string>;
};
}
/* -------------------------------------------------------------------------- */
/* Error console (platform owner) */
/* -------------------------------------------------------------------------- */
export type ErrorSeverity = "warning" | "error" | "critical";
export type ErrorSource = "http" | "worker" | "startup";
export type ErrorStatusFilter = "open" | "resolved";
/**
* One persisted failure. Deliberately wide: a record that lacks the request
* path or the exception class forces whoever is debugging to reproduce the bug
* just to learn what it was.
*/
export interface ErrorEvent {
id: string;
kind: string;
message: string;
stack: string;
severity: ErrorSeverity;
source: ErrorSource;
error_type: string;
fingerprint: string;
status_code: number | null;
method: string;
path: string;
organization_id: string | null;
user_id: string | null;
run_id: string | null;
request_id: string;
ip: string;
user_agent: string;
metadata_json: Record<string, unknown>;
resolved_at: string | null;
resolved_note: string;
created_at: string;
}
/** Every occurrence of one distinct bug, collapsed into a single row. */
export interface ErrorGroup {
fingerprint: string;
occurrences: number;
open_count: number;
first_seen: string;
last_seen: string;
kind: string;
error_type: string;
severity: ErrorSeverity;
source: ErrorSource;
path: string;
sample_message: string;
}
export interface ErrorSummary {
open_by_severity: Record<string, number>;
total_open: number;
}
export interface ErrorListResponse {
items: ErrorEvent[];
total: number;
limit: number;
offset: number;
}
export interface ResolveErrorResponse {
fingerprint: string;
resolved: number;
}
export interface ErrorQuery {
status?: ErrorStatusFilter;
severity?: ErrorSeverity;
source?: ErrorSource;
organization_id?: string;
search?: string;
limit?: number;
offset?: number;
}
// ---------------------------------------------------------------------------
// Realtime (WebSocket)
//
// The live feed is the same product surface as the REST API: one envelope
// shape, mirrored key-for-key from ``app/application/ports/realtime.py``.
// A page that switches on ``WsEventType`` never needs to know which part of
// the backend produced the event.
//
// Channel naming is what a page subscribes to — ``org:{id}`` for the tenant
// feed, ``task:{id}`` for one task, and so on. The server authorizes every
// subscribe against the caller's memberships, so a channel the user may not
// see simply comes back rejected.
// ---------------------------------------------------------------------------
export type WsEventType =
| "run.step"
| "run.status"
| "task.status"
| "task.created"
| "chat.pending"
| "message.created"
| "approval.pending"
| "artifact.created"
| "sandbox.command"
| "sandbox.session"
| "sandbox.reaped"
| "usage.recorded"
| "billing.wallet"
| "error.recorded"
| "audit.recorded"
| "worker.status"
// v2 delivery: CI/CD runs, releases and production deploys (org channel).
| "pipeline.queued"
| "pipeline.step"
| "pipeline.log"
| "pipeline.status"
| "release.live"
| "deploy.status";
/** One pushed event, as the browser receives it. */
export interface WsEvent<T = Record<string, unknown>> {
id: string;
type: WsEventType;
data: T;
organization_id: string | null;
/** ISO-8601 UTC; render with ``toLocaleString`` like every other timestamp. */
at: string;
}
/** Every frame the server sends. Control frames and events share ``type``. */
export type WsServerFrame =
| { type: "hello"; user_id: string | null; server_time: string }
| { type: "subscribed"; accepted: string[]; rejected: string[] }
| { type: "unsubscribed"; channels: string[] }
| { type: "pong" }
| { type: "error"; message: string }
| { type: "event"; event: WsEvent }
/** Reply to a client ``rpc`` frame: the HTTP status and parsed JSON body of the
* internal dispatch, correlating on ``id``. */
| { type: "rpc_result"; id: string; status: number; body: unknown };
/** Every frame the client may send. */
export type WsClientFrame =
| { action: "subscribe"; channels: string[] }
| { action: "unsubscribe"; channels: string[] }
| { action: "ping" }
/** A request/response call into the server. ``id`` correlates the
* ``rpc_result``; ``path`` is the full mounted path including any query
* string. Every application call travels over this frame. */
| {
action: "rpc";
id: string;
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
path: string;
body: unknown;
};
/** Live run-step payload — what the task timeline renders as it arrives. */
export interface WsRunStepData {
run_id: string;
task_id: string;
step: {
id: string;
index: number;
kind: string;
title: string;
payload: Record<string, unknown>;
};
}
/** Live sandbox command payload — the terminal panel's stream. */
export interface WsSandboxCommandData {
session_id: string;
run_id: string;
command: string;
status: string;
exit_code: number | null;
stdout: string;
stderr: string;
duration_ms: number;
truncated: boolean;
}
/* -------------------------------------------------------------------------- */
/* v2 delivery (CI/CD, demo runtime, production deploy) */
/* -------------------------------------------------------------------------- */
export type PipelineRunStatus =
| "queued"
| "running"
| "succeeded"
| "failed"
| "cancelled"
| "superseded";
export type PipelineStepStatus = "pending" | "running" | "succeeded" | "failed" | "skipped";
/** The eight steps of a run, in the order the server executes them. */
export const PIPELINE_STEP_NAMES = [
"checkout",
"setup",
"build",
"test",
"package",
"deploy_demo",
"healthcheck",
"route",
] as const;
export interface PipelineStep {
id: string;
index: number;
name: string;
status: PipelineStepStatus;
exit_code: number | null;
log_tail: string;
duration_ms: number;
}
export interface PipelineRun {
id: string;
project_id: string;
branch: string;
sha: string;
trigger: "engine" | "poll" | "manual";
status: PipelineRunStatus;
error_summary: string;
started_at: string | null;
finished_at: string | null;
created_at: string;
}
export interface PipelineRunDetail extends PipelineRun {
steps: PipelineStep[];
}
export interface Release {
id: string;
project_id: string;
run_id: string;
sha: string;
is_demo_live: boolean;
created_at: string;
}
export interface AppRuntime {
project_id: string;
demo_host: string;
demo_url: string;
host_port: number | null;
status: "provisioning" | "live" | "degraded" | "stopped";
current_release_id: string | null;
updated_at: string;
}
export type ProductionDeployStatus =
| "queued"
| "running"
| "succeeded"
| "failed"
| "rolled_back";
/** The production target as a *user* sees it: never a secret, only a hint. */
export interface ProductionTarget {
kind: "ssh" | "sftp" | "ftp" | "ftps";
host: string;
port: number;
username: string;
auth_type: "password" | "key";
remote_path: string;
has_secret: boolean;
host_fingerprint: string | null;
last_tested_at: string | null;
last_test_ok: boolean | null;
last_test_message: string | null;
}
export interface ProductionDeploy {
id: string;
project_id: string;
kind: string;
host: string;
status: ProductionDeployStatus;
message: string;
log_tail: string;
release_id: string | null;
rolled_back_from: string | null;
started_at: string | null;
finished_at: string | null;
created_at: string;
}
export interface ProductionOverview {
target: ProductionTarget | null;
deploys: ProductionDeploy[];
}
/** Owner console: one managed preview route. */
export interface DomainRoute {
id: string;
host: string;
port: number;
status: string;
protected: boolean;
url: string;
organization_id: string;
project_id: string;
project_name: string;
created_at: string | null;
}
export interface SuggestedRoute {
label: string;
host: string;
port: number;
description: string;
enabled: boolean;
}
export interface DomainSyncResult {
ok: boolean;
message: string;
routes: number;
changed: boolean;
}
/* -- live event payloads (channel `org:<id>`) ------------------------------ */
export interface WsPipelineLogData {
run_id: string;
step: string;
lines: string[];
}
export interface WsPipelineStepData {
run_id: string;
step: string;
index: number;
status: PipelineStepStatus;
}
export interface WsPipelineStatusData {
run_id: string;
project_id: string;
status: PipelineRunStatus;
error?: string;
}
export interface WsPipelineQueuedData {
run_id: string;
project_id: string;
sha: string;
trigger: string;
superseded: number;
}
export interface WsReleaseLiveData {
project_id: string;
run_id?: string;
release_id?: string;
sha?: string;
demo_host?: string;
demo_url?: string;
rolled_back?: boolean;
status?: string;
}
export interface WsDeployStatusData {
project_id: string;
deploy_id: string;
kind: string;
status: ProductionDeployStatus;
message: string;
}
/* -- org wizard (v2 A1-2) ------------------------------------------------- */
export interface CatalogPayload {
app_types: string[];
app_type_labels: Record<string, string>;
git_sources: string[];
languages: CatalogLanguage[];
}
export interface CatalogLanguage {
key: string;
display: string;
frameworks: CatalogFramework[];
}
export interface CatalogFramework {
key: string;
display: string;
app_type: string;
defaults: CatalogDefaults;
}
export interface CatalogDefaults {
runtime_version: string;
package_manager: string;
setup_command: string;
build_command: string | null;
test_command: string | null;
start_command: string;
app_port: number;
healthcheck_path: string;
docker_image: string;
}
export interface WizardCompleteResult {
organization: { id: string; name: string; slug: string };
project: { id: string; name: string; slug: string };
deploy_target_id: string | null;
}
export interface AppProfile {
project_id: string;
app_type: string | null;
language: string | null;
framework: string | null;
runtime_version: string | null;
package_manager: string | null;
setup_command: string | null;
build_command: string | null;
test_command: string | null;
start_command: string | null;
app_port: number | null;
healthcheck_path: string;
git_source: string;
external_repo_url: string | null;
default_branch: string;
wizard_completed: boolean;
}
export interface DeployTargetSummary {
id: string;
kind: string;
host: string;
port: number;
username: string;
auth_type: string;
remote_path: string;
has_secret: boolean;
last_test_ok: boolean | null;
last_test_message: string | null;
}
/* -- org wizard request bodies (v2 A1-2) -------------------------------- */
/** Persisted wizard draft. Secrets are never stored here — only in the final submit. */
export interface SetupDraft {
step: number;
data: Record<string, unknown>;
}
export interface CheckGitBody {
url: string;
auth_type?: string;
username?: string | null;
secret?: string | null;
}
export interface CheckDeployBody {
kind: string;
host: string;
port?: number;
username: string;
auth_type?: string;
secret?: string | null;
remote_path?: string;
}
/** Final wizard submission. Mirrors the backend `WizardCompleteRequest`. */
export interface WizardCompleteBody {
org_name: string;
slug: string;
app_name: string;
app_type: string;
language: string;
framework: string;
runtime_version?: string | null;
package_manager?: string | null;
setup_command?: string | null;
build_command?: string | null;
test_command?: string | null;
start_command?: string | null;
app_port?: number | null;
healthcheck_path?: string;
git_source?: string;
external_repo_url?: string | null;
external_git_auth_type?: string;
external_git_username?: string | null;
external_git_secret?: string | null;
deploy_target_kind?: string | null;
deploy_target_host?: string | null;
deploy_target_port?: number;
deploy_target_username?: string | null;
deploy_target_auth_type?: string;
deploy_target_secret?: string | null;
deploy_target_remote_path?: string;
}
export type DeployTargetPutBody = CheckDeployBody;
export interface GitCredentialsPutBody {
auth_type?: string;
username?: string | null;
secret?: string | null;
}
/** Updatable app-profile fields (org owner/admin only). */
export type AppProfileUpdate = Partial<
Pick<
AppProfile,
| "app_type"
| "language"
| "framework"
| "runtime_version"
| "package_manager"
| "setup_command"
| "build_command"
| "test_command"
| "start_command"
| "app_port"
| "healthcheck_path"
| "git_source"
| "external_repo_url"
| "default_branch"
>
>;