diff --git a/src/auth-providers/apply.ts b/src/auth-providers/apply.ts index dba64c51..033a4df5 100644 --- a/src/auth-providers/apply.ts +++ b/src/auth-providers/apply.ts @@ -109,20 +109,24 @@ export function extractEnvPairs(content: string): Map { return out; } -// Refresh stale platform defaults in the user's .env.local. If the user's -// value matches the manifest's literal default AND the platform now has a -// real value (e.g., cloud DATABASE_URL), overwrite the line. User-customized -// values (anything that differs from the manifest default) are preserved. -// -// One extra case beyond "matches the default": the cloud's -// `database-connection-string` endpoint used to return a placeholder URL -// like `postgresql://postgres:********@host/db?sslmode=require` before -// 0.1.72 spliced the real password in. Users linked under that older CLI -// still have the masked URL in their .env.local. The masked value can -// never authenticate to Postgres, so it's clearly stale — detect any -// `:****@` (one or more `*`) password and refresh from the platform. -// Exported for unit testing. +// The cloud's `database-connection-string` endpoint used to return a +// placeholder URL like `postgresql://postgres:********@host/db?sslmode=require` +// before 0.1.72 spliced the real password in. Users linked under that older +// CLI still have the masked URL in their .env.local. The masked value can +// never authenticate to Postgres, so it's clearly stale — any `:****@` (one +// or more `*`) password is refreshed from the platform. const MASKED_PASSWORD_PATTERN = /:\*+@/; + +/** + * Refresh stale platform defaults in the user's .env.local. If the user's + * value matches the manifest's literal default AND the platform now has a + * real value (e.g., cloud DATABASE_URL), overwrite the line. User-customized + * values (anything that differs from the manifest default) are preserved. + * A masked-password URL (see `MASKED_PASSWORD_PATTERN`) is also treated as + * stale even when it differs from the default. + * + * Exported for unit testing. + */ export function refreshStaleEnvDefaults( existing: string, manifestDefaults: Map, @@ -364,13 +368,8 @@ export async function applyAuthProvider( await fs.writeFile(envLocalPath, filled + '\n'); result.envLocalWritten = true; } else { - // Two passes: - // 1. Refresh stale platform defaults in-place. If the user's existing - // value matches the manifest's literal default AND we now have a - // real platform value (e.g., they re-linked against a cloud project - // after the CLI started fetching DATABASE_URL), update the line. - // User-customized values are preserved untouched. - // 2. Append any genuinely new keys at the end. + // Two passes: refresh stale platform defaults in place (see + // refreshStaleEnvDefaults), then append any genuinely new keys. const manifestDefaults = extractEnvPairs(manifest.envExampleAppend); const platformValues = extractEnvPairs(filled); const { updated, refreshed } = refreshStaleEnvDefaults(existingLocal, manifestDefaults, platformValues); diff --git a/src/commands/branch/create.ts b/src/commands/branch/create.ts index 4d20c3a0..47f17842 100644 --- a/src/commands/branch/create.ts +++ b/src/commands/branch/create.ts @@ -24,7 +24,8 @@ const POLL_INTERVAL_MS = 3_000; // now covers the observed range with headroom. const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // Once the control plane says ready, wait for the data plane too. Until this -// passes, every subsequent command against the branch fails. +// passes, every subsequent command against the branch fails. The ready-to- +// serving measurements above come from InsForge/InsForge#1790. const HEALTH_TIMEOUT_MS = 10 * 60 * 1_000; const HEALTH_INTERVAL_MS = 5_000; // Tolerance for clock skew when deciding whether a branch is the one we just diff --git a/src/commands/config/apply.ts b/src/commands/config/apply.ts index 1c2dc1ff..58456344 100644 --- a/src/commands/config/apply.ts +++ b/src/commands/config/apply.ts @@ -1,4 +1,3 @@ -// CLI/src/commands/config/apply.ts import type { Command } from 'commander'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; diff --git a/src/commands/config/export.ts b/src/commands/config/export.ts index f2076426..60129c29 100644 --- a/src/commands/config/export.ts +++ b/src/commands/config/export.ts @@ -1,4 +1,3 @@ -// CLI/src/commands/config/export.ts import type { Command } from 'commander'; import { writeFileSync, existsSync } from 'node:fs'; import { resolve } from 'node:path'; diff --git a/src/commands/config/index.ts b/src/commands/config/index.ts index a669d4f5..ebf1c0fc 100644 --- a/src/commands/config/index.ts +++ b/src/commands/config/index.ts @@ -1,4 +1,3 @@ -// CLI/src/commands/config/index.ts import type { Command } from 'commander'; import { registerConfigExportCommand } from './export.js'; import { registerConfigPlanCommand } from './plan.js'; diff --git a/src/commands/config/plan.ts b/src/commands/config/plan.ts index a6b76064..a843eccd 100644 --- a/src/commands/config/plan.ts +++ b/src/commands/config/plan.ts @@ -1,4 +1,3 @@ -// CLI/src/commands/config/plan.ts import type { Command } from 'commander'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; diff --git a/src/commands/deployments/deploy.ts b/src/commands/deployments/deploy.ts index 60a10ad9..34a9e607 100644 --- a/src/commands/deployments/deploy.ts +++ b/src/commands/deployments/deploy.ts @@ -318,7 +318,7 @@ export async function pollDeployment( // responses other than the retryable ones are terminal. Gateway 5xx // responses on the status endpoint are transient — the deployment itself // may still succeed — so keep polling, same as network-level fetch - // failures, which `ossFetch` now tags as such. Anything left unclassified + // failures, which `ossFetch` tags as such. Anything left unclassified // (a malformed status body failing `.json()`, a missing `status` field) // is a bug, not an outage: it would fail identically on every retry, so // it surfaces now instead of at the end of the poll window. @@ -327,6 +327,9 @@ export async function pollDeployment( } // Transient: keep polling, but remember why the read failed so that an // outage lasting the whole window is not reported as a plain timeout. + // The `formatFetchError` arm is unreachable since fbffb74: `ossFetch` + // wraps raw fetch rejections into a CLIError, and `isTransientApiError` + // returns false for anything else (see errors.ts). lastTransientError = err instanceof CLIError ? err.message : formatFetchError(err, 'the deployment API'); } diff --git a/src/lib/api/platform.ts b/src/lib/api/platform.ts index cb449cfc..ea1891c1 100644 --- a/src/lib/api/platform.ts +++ b/src/lib/api/platform.ts @@ -85,7 +85,7 @@ export async function platformFetch( const fullUrl = `${baseUrl}${path}`; if (process.env.INSFORGE_DEBUG) { console.error(`[DEBUG] ${fetchOptions.method ?? 'GET'} ${fullUrl}`); - // Redact the bearer credential. For direct-key sessions it is now the + // Redact the bearer credential. For direct-key sessions it is the // long-lived, non-rotating uak_ (not a short-lived JWT), so never write it // in full — even behind the debug flag. Match the auth header in ANY case, // since a caller-provided lowercase `authorization` survives the spread. @@ -253,7 +253,6 @@ export type DiagnosticEventHandler = (event: DiagnosticSSEEvent) => void; /** * Stream diagnostic analysis via SSE. Calls `onEvent` for each SSE event. - * Returns the raw Response so the caller can handle errors before streaming. */ export async function streamDiagnosticAnalysis( payload: DiagnosticRequest, diff --git a/src/lib/command-telemetry.ts b/src/lib/command-telemetry.ts index b4cd09fa..68670e7a 100644 --- a/src/lib/command-telemetry.ts +++ b/src/lib/command-telemetry.ts @@ -21,9 +21,9 @@ import { CLIError } from './errors.js'; * Telemetry must never affect command behavior, so everything is wrapped in a * try/catch and failures are swallowed. * - * IMPORTANT: only pass non-sensitive metadata as `properties` — counts, - * booleans, enums, exit/status codes. Never SQL, file contents, credentials, - * resource names, or other user-entered free text. + * Only pass non-sensitive metadata as `properties` — counts, booleans, enums, + * exit/status codes. Never SQL, file contents, credentials, resource names, or + * other user-entered free text. */ export type CommandTelemetry = Record< string, diff --git a/src/lib/config-capabilities.ts b/src/lib/config-capabilities.ts index 752c6833..bce109da 100644 --- a/src/lib/config-capabilities.ts +++ b/src/lib/config-capabilities.ts @@ -1,5 +1,3 @@ -// CLI/src/lib/config-capabilities.ts -// // Capability detection by metadata-shape probing. // // InsForge backends evolve independently per project. A user's CLI is always @@ -13,9 +11,9 @@ // simply omit the key. The CLI infers support from presence/absence — no // version handshake, no new server endpoint. // -// IMPORTANT: probe the RAW JSON, never the Zod-parsed object. Zod's -// `.default([])` on the consumer schema would silently fill in absent fields -// and erase the signal we're checking for. +// Probe the RAW JSON, never the Zod-parsed object. Zod's `.default([])` on +// the consumer schema would silently fill in absent fields and erase the +// signal we're checking for. // // Server contract (documented in shared-schemas/metadata.schema.ts): when a // backend doesn't yet support a TOML-relevant field, its `/api/metadata` diff --git a/src/lib/config-metadata.ts b/src/lib/config-metadata.ts index 31f0ba02..6b6b4e73 100644 --- a/src/lib/config-metadata.ts +++ b/src/lib/config-metadata.ts @@ -1,5 +1,3 @@ -// CLI/src/lib/config-metadata.ts -// // Single source of truth for converting /api/metadata's raw JSON response // into the shapes the rest of the CLI consumes: // - liveFromMetadata → LiveConfig for the diff layer (apply, plan) diff --git a/src/lib/config-schema.ts b/src/lib/config-schema.ts index 0a0aec91..4bf2b30d 100644 --- a/src/lib/config-schema.ts +++ b/src/lib/config-schema.ts @@ -1,5 +1,3 @@ -// CLI/src/lib/config-schema.ts - import { validateSensitiveString } from './config-secrets.js'; /** diff --git a/src/lib/config-secrets.ts b/src/lib/config-secrets.ts index 24f51148..3a20344e 100644 --- a/src/lib/config-secrets.ts +++ b/src/lib/config-secrets.ts @@ -1,5 +1,3 @@ -// CLI/src/lib/config-secrets.ts -// // Sensitive-field validation for insforge.toml. // // Sensitive fields (OAuth client_secret, SMTP password, S3 secret key, etc.) diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 454ca917..f1f33719 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -51,10 +51,9 @@ export class ProjectNotLinkedError extends CLIError { * budget; * - anything that is not a CLIError at all. That is an UNCLASSIFIED throw — * a `res.json()` parse failure on a mangled body, or a plain bug — and a - * poll loop must not spin on it for its whole budget. `platformFetch` - * wraps every transport and HTTP failure into a CLIError, so nothing real - * is lost. `ossFetch` does NOT wrap raw fetch rejections, so its callers - * handle that case themselves (see the deployment poller). + * poll loop must not spin on it for its whole budget. `platformFetch` and + * `ossFetch` wrap every transport and HTTP failure into a CLIError, so + * nothing real is lost. */ export function isTransientApiError(err: unknown): boolean { if (!(err instanceof CLIError)) return false; diff --git a/src/lib/flyctl.ts b/src/lib/flyctl.ts index 8b6ddb55..762f7912 100644 --- a/src/lib/flyctl.ts +++ b/src/lib/flyctl.ts @@ -101,12 +101,12 @@ function ensureFlyTomlStub(opts: { dir: string; appId: string; region: string; p * and returns the resulting image ref. Does NOT launch a machine — the cloud * backend handles that step (it owns DB state and quota enforcement). * - * IMPORTANT: returns the **digest-pinned** ref `@sha256:`, not - * the tag-based `: