Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ declarations at `dist/index.d.ts`. Local builds (outside CI) also emit

Source maps are intentionally excluded from npm releases because they increase
package size and are not required for normal CLI usage. The `files` field in
`package.json` whitelists only `dist/index.js` and `dist/index.d.ts`, so
`dist/index.js.map` is never published. If you add new build outputs under
`package.json` whitelists only `dist/index.js`, `dist/index.d.ts`, and the
copied assets (`dist/assets/forger.json`, `dist/assets/local/cli-overlay.yml`),
so `dist/index.js.map` is never published. If you add new build outputs under
`dist/`, update `package.json` `files` accordingly.

Inspect what would be published with:
Expand Down
11 changes: 5 additions & 6 deletions src/commands/branch/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,11 @@ async function waitUntilServing(
/**
* Poll the control plane until the branch reaches a terminal state.
*
* A failed READ is not a failed branch. The control plane returning 502 once
* mid-poll used to end the command on the spot, while the backend went on to
* mark the branch ready ~15s later — leaving a real, billing branch behind a
* non-zero exit (agent-e2e runs 31832239687 and 32055449431). Transient
* failures therefore consume a poll interval and nothing more; only a real
* rejection (auth, 404, a terminal branch state) ends the loop early.
* A failed READ is not a failed branch: a 502 mid-poll must not end the command
* while the backend goes on to mark the branch ready, leaving a real, billing
* branch behind a non-zero exit. Transient failures therefore consume a poll
* interval and nothing more; only a real rejection (auth, 404, a terminal
* branch state) ends the loop early.
*/
async function pollUntilReady(
branchId: string,
Expand Down
6 changes: 4 additions & 2 deletions src/commands/branch/reset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import type { Branch } from '../../types.js';

const POLL_INTERVAL_MS = 3_000;
// Reset re-runs pg_restore in-place, plus (for schema-only) the truncate
// finalize. Same order of magnitude as create — minutes for a small DB,
// longer for a populated one. Match create's 5-min budget.
// finalize. Minutes for a small DB, longer for a populated one. Note this
// does NOT match create's budget: create uses 15 min because slow regions
// were measured at 2–11.5 min (see create.ts); reset has not been
// re-measured, so its 5-min budget stands until it is.
const POLL_TIMEOUT_MS = 5 * 60 * 1_000;
// Retries for the post-timeout read that decides the verdict. Inside the loop
// the interval is the retry; this one has no second chance.
Expand Down
3 changes: 0 additions & 3 deletions src/commands/local/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,3 @@ export function registerLocalStartCommand(localCmd: Command): void {
}
});
}

/** Exported for tests. */
export const __testing = { parsePort, portOverrides, resolveStorage };
7 changes: 3 additions & 4 deletions src/commands/local/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,9 @@ export function registerLocalStopCommand(localCmd: Command): void {
}
}

// Before the success event, and before the --json early return. Both
// used to run first, so a partial delete was recorded as a success and
// exited 0 — and the catch below could not correct it, since telemetry
// only reports once per process.
// Must precede the success event and the --json early return: telemetry
// reports once per process, so a partial delete recorded as a success
// cannot be corrected by the catch below.
if (remainingVolumes.length > 0) {
throw new CLIError(
`The containers are stopped, but ${remainingVolumes.length} volume` +
Expand Down
27 changes: 3 additions & 24 deletions src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,15 @@ export class ProjectNotLinkedError extends CLIError {
}
}

export class NotFoundError extends CLIError {
constructor(resource: string) {
super(`${resource} not found.`, 4, 'NOT_FOUND');
}
}

export class PermissionError extends CLIError {
constructor(message: string = 'Permission denied.') {
super(message, 5, 'PERMISSION_DENIED');
}
}

/**
* True when a CLASSIFIED API failure says nothing about the operation being
* polled, so a caller in a poll loop should read again rather than give up.
*
* Gateway 5xx is the case this exists for: `insforge branch create` polls the
* control plane every 3s for up to 15 minutes, and a SINGLE 502 anywhere in
* that window used to abort a branch that the backend went on to finish
* creating seconds later (observed twice in agent-e2e: CLI out at ~90s and
* ~97s, branch ready at ~108s and ~113s). The branch still exists and still
* bills, so exiting is both wrong and expensive.
* that window must not abort a branch the backend goes on to finish creating.
* The branch still exists and still bills, so exiting is both wrong and
* expensive.
*
* Terminal by design:
* - any CLIError with no `statusCode` — a locally raised error (a failed job,
Expand Down Expand Up @@ -151,14 +138,6 @@ export function handleError(err: unknown, json: boolean): never {
process.exit(1);
}

export function getJsonFlag(cmd: Command): boolean {
let root: Command = cmd;
while (root.parent) {
root = root.parent;
}
return root.opts().json ?? false;
}

export function getRootOpts(cmd: Command): { json: boolean; apiUrl?: string; yes: boolean } {
let root: Command = cmd;
while (root.parent) {
Expand Down
2 changes: 0 additions & 2 deletions src/lib/guard/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ export function audit(entry: AuditEntry): void {
/* auditing must never block the guard */
}
}

export { AUDIT_FILE };
9 changes: 3 additions & 6 deletions src/lib/local/checkout.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
/**
* Fetch and run InsForge's own setup.sh to populate `.insforge/checkout/`.
*
* The CLI used to carry a 266-line copy of the compose file plus the four files
* it inlines. That copy drifted within a day of being written — the connection
* pool alignment added upstream never reached it. So the stack definition comes
* from the repository now, through the script the repository already ships for
* exactly this: it owns the file list, the layout the compose file's relative
* mounts expect, and the secret generation.
* The stack definition is never vendored here: a copy drifts from upstream, and
* setup.sh owns the file list, the layout the compose file's relative mounts
* expect, and the secret generation.
*
* INSFORGE_NO_GIT=1 keeps `docker` the only thing a developer needs installed;
* the script falls back to fetching each file over HTTPS.
Expand Down
40 changes: 1 addition & 39 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ export interface LoginResponse {
user: User;
}

export interface RefreshResponse {
token: string;
}

export interface Organization {
id: string;
name: string;
Expand All @@ -25,11 +21,6 @@ export interface Organization {
updated_at: string;
}

export interface OrgMembership {
organization: Organization;
role: string;
}

export interface Project {
id: string;
organization_id: string;
Expand All @@ -46,32 +37,10 @@ export interface Project {
updated_at: string;
}

export interface ProjectAuthResponse {
code: string;
expires_in: number;
type: string;
}

export interface ApiKeyResponse {
access_api_key: string;
}

export interface DatabasePasswordResponse {
databasePassword: string;
}

export interface ConnectionStringResponse {
connectionURL: string;
parameters: {
host: string;
port: number;
database: string;
user: string;
password: string;
sslmode: string;
};
}

// Stored credentials
export interface StoredCredentials {
access_token: string;
Expand Down Expand Up @@ -196,13 +165,6 @@ export interface MergeConflictResponse {
diff: DiffResult;
}

// API Error
export interface ApiError {
code?: string;
error: string;
requestId?: string;
}

// OSS API types

export type { ListFunctionsResponse, StorageBucketSchema, ListDeploymentsResponse,
Expand All @@ -211,7 +173,7 @@ export type { ListFunctionsResponse, StorageBucketSchema, ListDeploymentsRespons
CreateScheduleResponse, ListSchedulesResponse, GetScheduleResponse, ListExecutionLogsResponse,
ListSecretsResponse, GetSecretValueResponse, CreateSecretResponse, DeleteSecretResponse, UpdateSecretResponse,
CreateDeploymentResponse, CreateDirectDeploymentRequest, CreateDirectDeploymentResponse,
DeploymentManifestFileEntry, DeploymentManifestFile, UploadDeploymentFileResponse,
DeploymentManifestFileEntry, DeploymentManifestFile,
StartDeploymentRequest, DeploymentSchema, DeploymentMetadataResponse
} from '@insforge/shared-schemas';

Expand Down
Loading