Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ lfc --profile local builds list # one-off profile override

Config lives at `~/.config/lifecycle-cli/config.json`. Environment overrides: `LFC_PROFILE`, `LIFECYCLE_API_URL`, `LFC_JSON=1`, `LFC_CONFIG_DIR`.

The CLI reports anonymous usage telemetry to your own deployment's API (command and flag names, duration, and outcome — never arguments, values, error messages, or identity). Set `LFC_TELEMETRY_DISABLED=1` to turn it off.

## Builds (preview environments)

```bash
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lfc-cli",
"version": "0.2.2",
"version": "0.3.0",
"description": "Command-line interface for Lifecycle — preview environments, services, and static sites",
"license": "Apache-2.0",
"type": "module",
Expand Down
2 changes: 2 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface Profile {
export interface ConfigFile {
currentProfile: string;
profiles: Record<string, Profile>;
/** Anonymous telemetry install id — a random UUID, never tied to a user. */
installId?: string;
}

export const DEFAULT_PROFILE_NAME = 'default';
Expand Down
13 changes: 11 additions & 2 deletions src/lib/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import pc from 'picocolors';
import { ApiClient, ApiError } from './api.js';
import { AuthError } from './auth.js';
import { loadConfig, resolveProfile, type ConfigFile, type Profile } from './config.js';
import { reportInvocation, type TelemetryOutcome } from './telemetry.js';

export interface Ctx {
config: ConfigFile;
Expand Down Expand Up @@ -40,21 +41,29 @@ export function runAction<A extends unknown[]>(
return async (...args) => {
const cmd = args[args.length - 1] as Command;
const rest = args.slice(0, -1) as unknown as A;
let ctx: Ctx | undefined;
const startedAt = Date.now();
let outcome: TelemetryOutcome = { status: 'success', exitCode: 0 };
try {
const ctx = buildCtx(cmd);
ctx = buildCtx(cmd);
await fn(ctx, ...rest);
} catch (err) {
if (err instanceof AuthError) {
process.stderr.write(`${pc.red('auth error:')} ${err.message}\n`);
process.exitCode = 4;
outcome = { status: 'error', exitCode: 4, errorClass: 'AuthError' };
} else if (err instanceof ApiError) {
const reqId = err.requestId ? pc.dim(` (request_id: ${err.requestId})`) : '';
process.stderr.write(`${pc.red(`api error (${err.status}):`)} ${err.message}${reqId}\n`);
process.exitCode = err.status === 404 ? 3 : 1;
const exitCode = err.status === 404 ? 3 : 1;
process.exitCode = exitCode;
outcome = { status: 'error', exitCode, errorClass: 'ApiError', errorHttpStatus: err.status, errorCode: err.code };
} else {
process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\n`);
process.exitCode = 1;
outcome = { status: 'error', exitCode: 1, errorClass: 'Error' };
}
}
await reportInvocation(ctx, cmd, Date.now() - startedAt, outcome);
};
}
49 changes: 49 additions & 0 deletions src/lib/generated/createTelemetryEventBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { CreateTelemetryEventBodySource } from './createTelemetryEventBodySource.js';
import type { CreateTelemetryEventBodyAttributes } from './createTelemetryEventBodyAttributes.js';
import type { CreateTelemetryEventBodyStatus } from './createTelemetryEventBodyStatus.js';

export type CreateTelemetryEventBody = {
/** Which client type reported the event. */
source: CreateTelemetryEventBodySource;
/** Anonymous per-client identifier. */
clientId: string;
/**
* Event name. For the CLI this is the space-joined command path, e.g. "builds list".
* @maxLength 200
*/
event: string;
/** Arbitrary event attributes. Values limited to strings, numbers, booleans, or string arrays; at most 2KB serialized. */
attributes?: CreateTelemetryEventBodyAttributes;
/**
* @minimum 0
* @nullable
*/
durationMs?: number | null;
status: CreateTelemetryEventBodyStatus;
/**
* Process exit code (CLI-only).
* @nullable
*/
exitCode?: number | null;
/** @nullable */
errorClass?: string | null;
/** @nullable */
errorHttpStatus?: number | null;
/** @nullable */
errorCode?: string | null;
/** Version of the reporting client. */
clientVersion: string;
/** @nullable */
runtimeVersion?: string | null;
/** @nullable */
platform?: string | null;
/** @nullable */
arch?: string | null;
};
12 changes: 12 additions & 0 deletions src/lib/generated/createTelemetryEventBodyAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

/**
* Arbitrary event attributes. Values limited to strings, numbers, booleans, or string arrays; at most 2KB serialized.
*/
export type CreateTelemetryEventBodyAttributes = { [key: string]: unknown };
19 changes: 19 additions & 0 deletions src/lib/generated/createTelemetryEventBodySource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

/**
* Which client type reported the event.
*/
export type CreateTelemetryEventBodySource = typeof CreateTelemetryEventBodySource[keyof typeof CreateTelemetryEventBodySource];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const CreateTelemetryEventBodySource = {
cli: 'cli',
ui: 'ui',
} as const;
16 changes: 16 additions & 0 deletions src/lib/generated/createTelemetryEventBodyStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export type CreateTelemetryEventBodyStatus = typeof CreateTelemetryEventBodyStatus[keyof typeof CreateTelemetryEventBodyStatus];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const CreateTelemetryEventBodyStatus = {
success: 'success',
error: 'error',
} as const;
11 changes: 11 additions & 0 deletions src/lib/generated/createTelemetryEventSuccessResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { SuccessApiResponse } from './successApiResponse.js';
import type { CreateTelemetryEventSuccessResponseAllOf } from './createTelemetryEventSuccessResponseAllOf.js';

export type CreateTelemetryEventSuccessResponse = SuccessApiResponse & CreateTelemetryEventSuccessResponseAllOf;
12 changes: 12 additions & 0 deletions src/lib/generated/createTelemetryEventSuccessResponseAllOf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { CreateTelemetryEventSuccessResponseAllOfData } from './createTelemetryEventSuccessResponseAllOfData.js';

export type CreateTelemetryEventSuccessResponseAllOf = {
data: CreateTelemetryEventSuccessResponseAllOfData;
};
12 changes: 12 additions & 0 deletions src/lib/generated/createTelemetryEventSuccessResponseAllOfData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { CreateTelemetryEventSuccessResponseAllOfDataEvent } from './createTelemetryEventSuccessResponseAllOfDataEvent.js';

export type CreateTelemetryEventSuccessResponseAllOfData = {
event: CreateTelemetryEventSuccessResponseAllOfDataEvent;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export type CreateTelemetryEventSuccessResponseAllOfDataEvent = {
id: number;
createdAt: string;
};
16 changes: 16 additions & 0 deletions src/lib/generated/getTelemetryStatsInterval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export type GetTelemetryStatsInterval = typeof GetTelemetryStatsInterval[keyof typeof GetTelemetryStatsInterval];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetTelemetryStatsInterval = {
day: 'day',
week: 'week',
} as const;
28 changes: 28 additions & 0 deletions src/lib/generated/getTelemetryStatsParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { GetTelemetryStatsSource } from './getTelemetryStatsSource.js';
import type { GetTelemetryStatsInterval } from './getTelemetryStatsInterval.js';

export type GetTelemetryStatsParams = {
/**
* Which client type to aggregate.
*/
source: GetTelemetryStatsSource;
/**
* ISO date for the start of the range. Defaults to 30 days before now.
*/
from?: string;
/**
* ISO date for the end of the range. Defaults to now.
*/
to?: string;
/**
* Bucket size for time series.
*/
interval?: GetTelemetryStatsInterval;
};
16 changes: 16 additions & 0 deletions src/lib/generated/getTelemetryStatsSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export type GetTelemetryStatsSource = typeof GetTelemetryStatsSource[keyof typeof GetTelemetryStatsSource];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetTelemetryStatsSource = {
cli: 'cli',
ui: 'ui',
} as const;
11 changes: 11 additions & 0 deletions src/lib/generated/getTelemetryStatsSuccessResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { SuccessApiResponse } from './successApiResponse.js';
import type { GetTelemetryStatsSuccessResponseAllOf } from './getTelemetryStatsSuccessResponseAllOf.js';

export type GetTelemetryStatsSuccessResponse = SuccessApiResponse & GetTelemetryStatsSuccessResponseAllOf;
12 changes: 12 additions & 0 deletions src/lib/generated/getTelemetryStatsSuccessResponseAllOf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { GetTelemetryStatsSuccessResponseAllOfData } from './getTelemetryStatsSuccessResponseAllOfData.js';

export type GetTelemetryStatsSuccessResponseAllOf = {
data: GetTelemetryStatsSuccessResponseAllOfData;
};
14 changes: 14 additions & 0 deletions src/lib/generated/getTelemetryStatsSuccessResponseAllOfData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/
import type { TelemetryStatsRange } from './telemetryStatsRange.js';
import type { TelemetryStats } from './telemetryStats.js';

export type GetTelemetryStatsSuccessResponseAllOfData = {
range: TelemetryStatsRange;
stats: TelemetryStats;
};
23 changes: 23 additions & 0 deletions src/lib/generated/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,14 @@ export * from './createSandboxAgentSessionBodyOneOfFiveWorkspace.js';
export * from './createSandboxAgentSessionBodyOneOfService.js';
export * from './createSandboxAgentSessionBodyOneOfServiceOneOf.js';
export * from './createSandboxAgentSessionBodyOneOfWorkspace.js';
export * from './createTelemetryEventBody.js';
export * from './createTelemetryEventBodyAttributes.js';
export * from './createTelemetryEventBodySource.js';
export * from './createTelemetryEventBodyStatus.js';
export * from './createTelemetryEventSuccessResponse.js';
export * from './createTelemetryEventSuccessResponseAllOf.js';
export * from './createTelemetryEventSuccessResponseAllOfData.js';
export * from './createTelemetryEventSuccessResponseAllOfDataEvent.js';
export * from './creatorCapabilityAvailability.js';
export * from './creatorCapabilityAvailabilityMap.js';
export * from './customAgentCreationMode.js';
Expand Down Expand Up @@ -575,6 +583,12 @@ export * from './getSandboxServiceCandidates200DataServicesItem.js';
export * from './getSandboxServiceCandidates200DataStatus.js';
export * from './getSandboxServiceCandidates200Error.js';
export * from './getSandboxServiceCandidatesParams.js';
export * from './getTelemetryStatsInterval.js';
export * from './getTelemetryStatsParams.js';
export * from './getTelemetryStatsSource.js';
export * from './getTelemetryStatsSuccessResponse.js';
export * from './getTelemetryStatsSuccessResponseAllOf.js';
export * from './getTelemetryStatsSuccessResponseAllOfData.js';
export * from './getWebhooksSuccessResponse.js';
export * from './getWebhooksSuccessResponseAllOf.js';
export * from './installedRepository.js';
Expand Down Expand Up @@ -735,6 +749,15 @@ export * from './systemAgentDefinitionId.js';
export * from './tearDownBuildSuccessResponse.js';
export * from './tearDownBuildSuccessResponseAllOf.js';
export * from './tearDownBuildSuccessResponseAllOfData.js';
export * from './telemetryBucketCount.js';
export * from './telemetryEventStats.js';
export * from './telemetryStats.js';
export * from './telemetryStatsActiveClients.js';
export * from './telemetryStatsPlatformsItem.js';
export * from './telemetryStatsRange.js';
export * from './telemetryStatsRangeInterval.js';
export * from './telemetryStatsRangeSource.js';
export * from './telemetryStatsVersionsItem.js';
export * from './updateAdminAgentCapabilitiesParams.js';
export * from './updateAdminAgentCapabilitiesRequest.js';
export * from './updateAdminCustomAgentCreationPolicyRequest.js';
Expand Down
12 changes: 12 additions & 0 deletions src/lib/generated/telemetryBucketCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export interface TelemetryBucketCount {
bucket: string;
count: number;
}
18 changes: 18 additions & 0 deletions src/lib/generated/telemetryEventStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Generated by orval v7.4.1 🍺
* Do not edit manually.
* Lifecycle API
* API documentation for lifecycle
* OpenAPI spec version: 2.0.0
*/

export interface TelemetryEventStats {
event: string;
count: number;
errorCount: number;
errorRate: number;
/** @nullable */
p50DurationMs: number | null;
/** @nullable */
p95DurationMs: number | null;
}
Loading
Loading