UNPKG

@capgo/cli

Version:
489 lines (488 loc) • 22.7 kB
import type { InstallCommand, PackageManagerRunner, PackageManagerType } from '@capgo/find-package-manager'; import type { SemVer } from '@std/semver'; import type { Buffer } from 'node:buffer'; import type { UploadSpinner } from './bundle/reporter'; import type { CapacitorConfig, ExtConfigPairs } from './config'; import type { Compatibility, CompatibilityDetails, NativePackage } from './schemas/common'; import type { Organization } from './types/api'; import { trimTrailingSlashes } from './shared/trim-trailing-slashes'; export { trimTrailingSlashes }; export declare const baseKey = ".capgo_key"; export declare const baseKeyV2 = ".capgo_key_v2"; export declare const baseKeyPub = ".capgo_key.pub"; export declare const baseKeyPubV2 = ".capgo_key_v2.pub"; export declare const defaultHost = "https://capgo.app"; export declare const defaultFileHost = "https://files.capgo.app"; export declare const defaultApiHost = "https://api.capgo.app"; export declare const defaultHostWeb = "https://console.capgo.app"; /** Build a console web-app URL (settings, builds, connect, etc.). */ export declare function consoleWebUrl(path?: string): string; export declare const UPLOAD_TIMEOUT = 120000; export declare const ALERT_UPLOAD_SIZE_BYTES: number; export declare const MAX_UPLOAD_LENGTH_BYTES: number; export declare const MAX_CHUNK_SIZE_BYTES: number; export declare const TUS_UPLOAD_RETRY_DELAYS: number[]; export declare const MAX_MANIFEST_ENTRIES = 10000; /** User-facing error when a delta/manifest upload exceeds MAX_MANIFEST_ENTRIES. */ export declare function deltaManifestTooLargeMessage(fileCount: number): string; /** * Stable `name` for the error thrown when the standard (non-TUS) upload hits its * wall-clock timeout. Kept typed and constant so error tracking groups every * occurrence into a single issue instead of one duplicate per CLI version. */ export declare const UPLOAD_TIMEOUT_ERROR_NAME = "BundleUploadTimeoutError"; /** User-facing error when the standard HTTP PUT upload exceeds its wall-clock timeout. */ export declare function uploadTimeoutMessage(timeoutMs: number): string; export declare const PACKNAME = "package.json"; /** Stable PostHog fingerprint for incomplete Capgo server config before the API client is created. */ export type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never; export type { Organization }; export declare const regexSemver: RegExp; /** * Format an error for display. If it's a security policy error, * returns a human-readable message with actionable steps. */ export declare function formatError(error: any): string; /** * Render the safe, diagnostic part of an error chain for --verbose output. * * tus-js-client stores its underlying transport error in `causingError`, while * native fetch/Undici uses `cause`. Only known diagnostic fields are included; * request objects and headers are deliberately ignored, then the result passes * through the CLI's secret redactor as a final safeguard. */ export declare function formatVerboseError(error: unknown): string; type TagKey = Lowercase<string>; /** Tag Type */ type Tags = Record<TagKey, string | number | boolean>; /** * Options for publishing analytics events */ interface TrackOptions { /** * Channel name * example: "waitlist" */ channel: string; /** * Event name * example: "User Joined" */ event: string; /** * Event description * example: "joe@example.com joined waitlist" */ description?: string; /** * User ID * example: "user-123" */ user_id?: string; /** * Organization ID for actor-scoped tracking. */ org_id?: string; /** * Tracking payload contract version. */ tracking_version?: number; /** * Event tags * example: { username: "mattie" } */ tags?: Tags; /** * Event timestamp */ timestamp?: number | Date; } export declare function wait(ms: number): Promise<unknown>; interface PromptInteractivityOptions { silent?: boolean; stdinIsTTY?: boolean; stdoutIsTTY?: boolean; ci?: boolean; } export declare function canPromptInteractively({ silent, stdinIsTTY, stdoutIsTTY, ci, }?: PromptInteractivityOptions): boolean; interface CanOpenExternalUrlOptions { ci?: boolean; stdinIsTTY?: boolean; stdoutIsTTY?: boolean; display?: string | undefined; platform?: NodeJS.Platform; } /** Whether the CLI may spawn a desktop browser for upgrade/docs links. */ export declare function canOpenExternalUrl(options?: CanOpenExternalUrlOptions): boolean; /** Best-effort browser open; skipped in CI/headless and when xdg-open is missing. */ export declare function openExternalUrl(url: string): Promise<void>; export declare function projectIsMonorepo(dir: string): boolean; export declare function findRoot(dir: string): string; export declare function getPackageScripts(f?: string, file?: string | undefined): Record<string, string>; export declare function getBundleVersion(f?: string, file?: string | undefined): string; /** * Get the actual installed version of a package from node_modules (not from package.json) * Uses multiple resolution strategies to find the installed version: * 1. require.resolve - Works with all package managers * 2. Walk up node_modules - Handles hoisted dependencies in monorepos * 3. Native config files (iOS/Android) - For @capgo/capacitor-updater only * 4. Fallback to declared version in package.json * * @param packageName - The package name to check * @param rootDir - The root directory of the project * @param packageJsonPath - Optional custom package.json path provided by user (takes priority if provided) */ export declare function getInstalledVersion(packageName: string, rootDir?: string, packageJsonPath?: string): Promise<string | null>; export declare function getAllPackagesDependencies(f?: string, file?: string | undefined): Promise<Map<string, string>>; export declare function getDeclaredPackageVersionMap(f?: string, file?: string | undefined): Promise<Map<string, string>>; export declare const NO_CAPACITOR_CONFIG_MESSAGE = "No capacitor config file found, run `cap init` first"; export declare function getConfig(silent?: boolean): Promise<ExtConfigPairs>; /** Loads the source config that a subsequent mutation will write. */ export declare function getConfigForWrite(silent?: boolean): Promise<ExtConfigPairs>; export declare function updateConfigbyKey(key: string, newConfig: any): Promise<ExtConfigPairs>; export declare function updateConfigUpdater(newConfig: any): Promise<ExtConfigPairs>; export declare function getLocalConfig(silent?: boolean): Promise<{ host: string; hostWeb: string; hostFilesApi: string; hostApi: string; }>; interface CapgoHostOverride { apiHost?: string; filesHost?: string; } /** * Pin the Capgo API for this CLI process (--api-host / --files-host). Every host * lookup (HTTP calls, remote config, files config, TUS uploads) then uses it, so * tests and self-hosted backends never leak traffic to Capgo cloud. Library code * uses withCapgoHosts instead so concurrent clients never share a host. */ export declare function setCapgoHostOverride(hosts: CapgoHostOverride): void; /** * Run `action` with its own Capgo hosts. Empty hosts mean Capgo defaults (env, * capacitor config, cloud): a scope never inherits the CLI process pin. */ export declare function withCapgoHosts<T>(hosts: CapgoHostOverride, action: () => T): T; interface CapgoConfig { host: string; hostWeb: string; hostFilesApi: string; hostApi: string; minCliVersion?: string; minCliVersionReason?: string; } export declare function getRemoteConfig(silent?: boolean, signal?: AbortSignal, apikey?: string): Promise<{ host: string; hostWeb: string; hostFilesApi: string; hostApi: string; minCliVersion?: string; minCliVersionReason?: string; }>; export interface CapgoFilesConfig { partialUpload: boolean; partialUploadForced: boolean; TUSUpload: boolean; TUSUploadForced: boolean; maxUploadLength: number; maxChunkSize: number; alertUploadSize: number; } export declare function getRemoteFileConfig(apikey?: string): Promise<CapgoFilesConfig>; export declare function formatCapgoApiErrorBody(body: unknown): string; export declare function channelUpdatePackageCliError(error: unknown): Promise<string | null>; /** Options that select the Capgo API. */ export interface CapgoHostOptions { /** Capgo API base URL (`--api-host`): any Capgo backend, e.g. `https://api.example.com` or `http://127.0.0.1:8787`. */ apiHost?: string; /** Capgo files API base URL (`--files-host`); defaults to apiHost when apiHost is set. */ filesHost?: string; } /** Validate a Capgo API base URL: https (http only on loopback), no credentials/query. */ export declare function normalizeCapgoApiHost(host: string): string; export interface CapgoApiHosts { /** Capgo API base URL. */ apiHost: string; /** Capgo files API base URL (same as apiHost when self-hosting on Edge Functions). */ filesHost: string; } /** * Resolve Capgo API hosts: explicit apiHost/filesHost, then the process override, * env and capacitor config (see applyCapgoHostOverride), then Capgo cloud. */ export declare function resolveCapgoApiHosts(apiHost?: string, silent?: boolean, filesHost?: string): Promise<CapgoApiHosts>; export interface CapgoCliInvokeOptions { apikey: string; method?: string; body?: unknown; /** Capgo cloud files worker; same as the API host when self-hosting */ useFilesHost?: boolean; /** Capgo API base URL; defaults to capacitor config / Capgo cloud. */ apiHost?: string; signal?: AbortSignal; /** Set false for telemetry-internal calls so perf events never recurse. */ instrument?: boolean; } export declare function getCapgoCliHttpStatus(error: unknown): number | undefined; export declare function readCapgoCliApiErrorPayload(error: unknown): Promise<{ error?: string; message?: string; moreInfo?: Record<string, unknown>; } | null>; export declare function formatCapgoCliInvokeError(error: unknown): Promise<string>; /** * Call the Capgo HTTP API. Every request carries the API key and the `capgo_api` * version header (see buildCliRequestHeaders); the backend owns all data access. */ export declare function invokeCapgoCliApi<T = any>(path: string, options: CapgoCliInvokeOptions): Promise<{ data: T | null; error: Error | null; }>; export declare function resolveCapgoPublicApiHost(options?: { apiHost?: string; }, silent?: boolean): Promise<string>; /** * Connection to the Capgo HTTP API. The CLI never queries the database directly: * every read/write goes through Capgo HTTP endpoints via `invokeCapgoCliApi`. */ export interface CapgoClient extends CapgoApiHosts { apikey: string; } export declare function createCapgoClient(apikey: string, apiHost?: string, silent?: boolean, signal?: AbortSignal, filesHost?: string): Promise<CapgoClient>; export declare function fetchCliMembers2faStatus(apikey: string, orgId: string, httpOptions?: CliHttpOptions): Promise<Array<{ 'user_id': string; '2fa_enabled': boolean; }>>; export declare function fetchCliMembersPasswordPolicyStatus(apikey: string, orgId: string, httpOptions?: CliHttpOptions): Promise<Array<{ user_id: string; email: string; first_name: string; last_name: string; password_policy_compliant: boolean; }>>; export declare function findSavedKeySilent(): string | undefined; export declare function findSavedKey(quiet?: boolean, report?: (message: string) => void): string; export declare function getContentType(filename: string): string; export declare function findProjectType(options?: { quiet?: boolean; packageJsonPath?: string; }): Promise<string>; export declare function findMainFileForProjectType(projectType: string, isTypeScript: boolean, rootDir?: string): string | null; export declare function findBuildCommandForProjectType(projectType: string): Promise<"build" | "generate">; export declare function findMainFile(silent?: boolean, rootDir?: string): Promise<string>; export declare function uploadUrl(apikey: string, appId: string, name: string, options?: { apiHost?: string; }): Promise<string>; export declare const BROTLI_MIN_UPDATER_VERSION_V5 = "5.10.0"; export declare const BROTLI_MIN_UPDATER_VERSION_V6 = "6.25.0"; export declare const BROTLI_MIN_UPDATER_VERSION_V7 = "7.0.30"; export declare function isDeprecatedPluginVersion(parsedPluginVersion: SemVer, minFive?: string, minSix?: string, minSeven?: string): boolean; export declare function generateManifest(path: string): Promise<{ file: string; hash: string; }[]>; export type manifestType = Awaited<ReturnType<typeof generateManifest>>; export declare function zipFile(filePath: string): Promise<Buffer>; export declare function zipFileUnix(filePath: string): Buffer<ArrayBufferLike>; export declare function zipFileWindows(filePath: string): Promise<Buffer>; export declare function appAddHintMessage(appId: string): string; export declare function isAppNotFoundError(error: unknown): boolean; export declare function uploadTUS(apikey: string, data: Buffer, orgId: string, appId: string, name: string, spinner: UploadSpinner, localConfig: CapgoConfig, chunkSize: number): Promise<boolean>; export declare function deletedFailedVersion(apikey: string, appId: string, name: string, options?: { apiHost?: string; }): Promise<void>; export interface VersionManifestEntry { file_name: string; s3_path: string; file_hash: string; file_size_receipt?: string; } /** * Writes delta manifest rows through the backend (file_size stays 0 until R2 size lookup). * Prefer this over writing app_versions.manifest jsonb — old CLIs still use that legacy path. */ export declare function setVersionManifest(apikey: string, appId: string, name: string, manifest: VersionManifestEntry[], options?: { apiHost?: string; }): Promise<void>; type SendEventPayload = TrackOptions & { nonPersonTags?: Record<string, unknown>; } & ({ notifyConsole: true; icon?: string; } | { notifyConsole?: false; icon?: never; }); export declare function sendEvent(capgkey: string, payload: SendEventPayload, verbose?: boolean, signal?: AbortSignal, apiHost?: string, redirect?: RequestInit['redirect']): Promise<void>; export declare function show2FADeniedError(organizationName?: string): never; /** Capgo API selection for helpers that are not handed a CapgoClient. */ export interface CliHttpOptions { apiHost?: string; } /** API key plus optional host: a CapgoClient, or command options before a client exists. */ export interface CapgoApiTarget { apikey: string; apiHost?: string; filesHost?: string; } /** Call the Capgo API with a client's key and host. */ export declare function invokeCliHttpFromClient<T>(client: CapgoApiTarget, path: string, options?: { method?: string; body?: unknown; query?: Record<string, string | undefined>; useFilesHost?: boolean; signal?: AbortSignal; }): Promise<{ data: T | null; error: Error | null; }>; /** Host options matching a Capgo client, for helpers that take CliHttpOptions. */ export declare function hostOptionsFromClient(client: CapgoClient): CliHttpOptions; /** * Organizations visible to the API key. With `permission`, each row carries * `allowed`: whether the key holds that permission in the org. */ export declare function fetchOrganizations(apikey: string, httpOptions?: CliHttpOptions, permission?: string): Promise<Array<Organization & { allowed?: boolean; }>>; export interface CliOrganizationDetails { name: string; management_email?: string; created_by?: string; enforcing_2fa: boolean; password_policy_config: { enabled: boolean; min_length: number; require_uppercase: boolean; require_number: boolean; require_special: boolean; } | null; require_apikey_expiration: boolean; max_apikey_expiration_days: number | null; enforce_hashed_api_keys: boolean; } export declare function fetchCliOrganization(apikey: string, orgId: string, httpOptions?: CliHttpOptions): Promise<CliOrganizationDetails>; export declare function getOrganizationListWithPermission(client: CapgoClient, apikey: string, permissionKey: string, httpOptions?: CliHttpOptions): Promise<{ allOrganizations: Organization[]; allowedOrganizations: Organization[]; }>; export declare function getOrganizationWithPermission(client: CapgoClient, apikey: string, permissionKey: string): Promise<Organization>; export declare function resolveUserIdFromApiKey(client: CapgoClient, apikey: string, silent?: boolean, httpOptions?: CliHttpOptions): Promise<string>; interface CliPermissionScope { orgId?: string | null; appId?: string | null; channelId?: number | null; } /** Ask the backend which of these RBAC permissions the client key holds in one scope. */ export declare function fetchCliPermissions(client: CapgoClient, permissionKeys: string[], scope?: CliPermissionScope): Promise<Record<string, boolean>>; export declare function hasCliPermission(client: CapgoClient, _apikey: string, permissionKey: string, scope?: CliPermissionScope, silent?: boolean): Promise<boolean>; export declare function assertCliPermission(client: CapgoClient, apikey: string, permissionKey: string, scope?: CliPermissionScope, options?: { message?: string; silent?: boolean; }): Promise<void>; export declare function assertOrgPermission(client: CapgoClient, apikey: string, permissionKey: string, orgId: string, message: string, silent: boolean): Promise<void>; export declare function getOrganizationId(apikey: string, appId: string, options?: { apiHost?: string; }): Promise<string>; export declare function getHumanDate(createdA: string | null): string; export declare function getPMAndCommand(): { pm: PackageManagerType; command: InstallCommand; installCommand: string; runner: PackageManagerRunner; }; export declare function setPMAndCommand(next: { pm: PackageManagerType; command: InstallCommand; runner: PackageManagerRunner; }): void; export declare function getNativeProjectResetAdvice(platformRunner: string, nativePlatform: 'ios' | 'android'): { summary: string; command: string; }; export declare function getLocalDependencies(packageJsonPath: string | undefined, nodeModulesString: string | undefined): Promise<{ name: string; version: string; requested_version?: string; native: boolean; ios_checksum?: string; android_checksum?: string; }[]>; export declare function getRemoteDependencies(apikey: string, appId: string, channel: string, httpOptions?: CliHttpOptions): Promise<Map<string, NativePackage>>; export declare function getRemoteChecksums(apikey: string, appId: string, channel: string, httpOptions?: CliHttpOptions): Promise<string | null>; export type { NativePackage } from './schemas/common'; export declare function convertNativePackages(nativePackages: NativePackage[]): Map<string, NativePackage>; export type { Compatibility, CompatibilityDetails } from './schemas/common'; export declare function getAppId(appId: string | undefined, config: CapacitorConfig | undefined): any; /** * Check if a package is compatible and return detailed reasons if not */ export declare function getCompatibilityDetails(pkg: Compatibility): CompatibilityDetails; /** * Simple compatibility check (backward compatible) */ export declare function isCompatible(pkg: Compatibility): boolean; export declare function checkCompatibilityCloud(apikey: string, appId: string, channel: string, packageJsonPath: string | undefined, nodeModules: string | undefined, httpOptions?: CliHttpOptions): Promise<{ finalCompatibility: { name: string; localVersion?: string | undefined; remoteVersion?: string | undefined; localRequestedVersion?: string | undefined; remoteRequestedVersion?: string | undefined; localIosChecksum?: string | undefined; remoteIosChecksum?: string | undefined; localAndroidChecksum?: string | undefined; remoteAndroidChecksum?: string | undefined; }[]; localDependencies: { name: string; version: string; requested_version?: string; native: boolean; ios_checksum?: string; android_checksum?: string; }[]; }>; export declare function checkCompatibilityNativePackages(apikey: string, appId: string, channel: string, nativePackages: NativePackage[], httpOptions?: CliHttpOptions): Promise<{ finalCompatibility: { name: string; localVersion?: string | undefined; remoteVersion?: string | undefined; localRequestedVersion?: string | undefined; remoteRequestedVersion?: string | undefined; localIosChecksum?: string | undefined; remoteIosChecksum?: string | undefined; localAndroidChecksum?: string | undefined; remoteAndroidChecksum?: string | undefined; }[]; localDependencies: { name: string; version: string; requested_version?: string | undefined; ios_checksum?: string | undefined; android_checksum?: string | undefined; }[]; }>; export interface IosUpdaterSyncValidationResult { shouldCheck: boolean; valid: boolean; details: string[]; } /** * Validate whether the iOS native project is correctly synced for capacitor-updater. * * `shouldCheck` is `false` when no iOS project is present or no updater signals are detected * (no dependency declaration, installed package, or native references). `shouldCheck` is `true` * as soon as any signal indicates updater should be wired, then both dependency definitions * (`Podfile` or SPM `Package.swift`) and generated native outputs (`Podfile.lock`, * `capacitor.plugins.json`, or `ios/App/App/capacitor.config.json`) must include * updater markers for `valid` to be `true`. */ export declare function validateIosUpdaterSync(rootDir?: string, packageJsonPath?: string): IosUpdaterSyncValidationResult; interface PromptAndSyncOptions { validateIosUpdater?: boolean; packageJsonPath?: string; } export type IosUpdaterSyncValidation = ReturnType<typeof validateIosUpdaterSync>; export declare function throwIfIosUpdaterSyncInvalid(syncValidation: IosUpdaterSyncValidation, platformRunner: string, onFailure?: () => void): void; export declare function promptAndSyncCapacitor(isInit?: boolean, orgId?: string, apikey?: string, options?: PromptAndSyncOptions): Promise<void>;