@barekey/cli 0.5.8 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/audit.js +3 -2
- package/dist/commands/auth.js +5 -4
- package/dist/commands/billing.js +6 -5
- package/dist/commands/env.js +11 -8
- package/dist/commands/init.js +2 -1
- package/dist/commands/org.js +4 -3
- package/dist/commands/project.js +5 -4
- package/dist/commands/stage.js +6 -5
- package/dist/commands/typegen.js +9 -9
- package/dist/index.js +6 -2
- package/dist/output/theme.d.ts +112 -0
- package/dist/output/theme.js +149 -0
- package/package.json +1 -1
- package/src/commands/audit.ts +5 -2
- package/src/commands/auth.ts +5 -4
- package/src/commands/billing.ts +10 -5
- package/src/commands/env.ts +11 -8
- package/src/commands/init.ts +2 -1
- package/src/commands/org.ts +8 -3
- package/src/commands/project.ts +11 -4
- package/src/commands/stage.ts +12 -5
- package/src/commands/typegen.ts +9 -9
- package/src/index.ts +6 -2
- package/src/output/theme.ts +166 -0
package/dist/commands/audit.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
2
2
|
import { requireLocalSession, toJsonOutput } from "../command-utils.js";
|
|
3
3
|
import { AuditListResponseSchema } from "../contracts/index.js";
|
|
4
4
|
import { postJson } from "../http.js";
|
|
5
|
+
import { info, muted, subtle, strong } from "../output/theme.js";
|
|
5
6
|
import { promptForOrganizationSlug } from "./target-prompts.js";
|
|
6
7
|
async function runAuditList(options) {
|
|
7
8
|
const local = await requireLocalSession();
|
|
@@ -25,11 +26,11 @@ async function runAuditList(options) {
|
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
if (response.items.length === 0) {
|
|
28
|
-
console.log("No audit events found.");
|
|
29
|
+
console.log(subtle("No audit events found."));
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
for (const item of response.items) {
|
|
32
|
-
console.log(`${new Date(item.occurredAtMs).toISOString()} ${item.category} ${item.title}`);
|
|
33
|
+
console.log(`${muted(new Date(item.occurredAtMs).toISOString())} ${info(item.category)} ${strong(item.title)}`);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
export function registerAuditCommands(program) {
|
package/dist/commands/auth.js
CHANGED
|
@@ -9,6 +9,7 @@ import { clearConfig, deleteCredentials, saveConfig, saveCredentials, } from "..
|
|
|
9
9
|
import { CliSessionResponseSchema, DevicePollResponseSchema, DeviceStartResponseSchema, RefreshResponseSchema, } from "../contracts/index.js";
|
|
10
10
|
import { getJson, postJson } from "../http.js";
|
|
11
11
|
import { formatWhoamiWithAvatar } from "../output/avatar.js";
|
|
12
|
+
import { accent, muted, strong, success } from "../output/theme.js";
|
|
12
13
|
import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
|
|
13
14
|
function resolveClientName() {
|
|
14
15
|
const configured = process.env.BAREKEY_CLIENT_NAME?.trim() ||
|
|
@@ -60,8 +61,8 @@ async function runLogin(options) {
|
|
|
60
61
|
const verificationUri = resolveVerificationUri(baseUrl, started.verificationUri);
|
|
61
62
|
loading.stop("Authorization initialized");
|
|
62
63
|
loadingActive = false;
|
|
63
|
-
console.log(`${
|
|
64
|
-
console.log(`${
|
|
64
|
+
console.log(`${accent("Open")} ${muted(verificationUri)}`);
|
|
65
|
+
console.log(`${accent("Code")} ${strong(started.userCode)}`);
|
|
65
66
|
try {
|
|
66
67
|
await open(verificationUri);
|
|
67
68
|
}
|
|
@@ -115,7 +116,7 @@ async function runLogin(options) {
|
|
|
115
116
|
});
|
|
116
117
|
pollSpinner.stop("Login approved");
|
|
117
118
|
pollActive = false;
|
|
118
|
-
outro(
|
|
119
|
+
outro(`${success("Signed in as")} ${pc.bold(resolveDisplayName(refreshed))}.`);
|
|
119
120
|
return;
|
|
120
121
|
}
|
|
121
122
|
pollSpinner.stop("Timed out");
|
|
@@ -143,7 +144,7 @@ async function runLogout() {
|
|
|
143
144
|
});
|
|
144
145
|
await deleteCredentials(local.accountId);
|
|
145
146
|
await clearConfig();
|
|
146
|
-
console.log("Logged out.");
|
|
147
|
+
console.log(success("Logged out."));
|
|
147
148
|
}
|
|
148
149
|
async function runWhoami(options) {
|
|
149
150
|
const local = await requireLocalSession();
|
package/dist/commands/billing.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
2
2
|
import { requireLocalSession, toJsonOutput } from "../command-utils.js";
|
|
3
3
|
import { BillingCatalogResponseSchema, BillingStatusResponseSchema } from "../contracts/index.js";
|
|
4
4
|
import { getJson, postJson } from "../http.js";
|
|
5
|
+
import { accent, info, muted, strong, subtle, warning } from "../output/theme.js";
|
|
5
6
|
import { promptForOrganizationSlug } from "./target-prompts.js";
|
|
6
7
|
async function runBillingCatalog(options) {
|
|
7
8
|
const local = await requireLocalSession();
|
|
@@ -14,8 +15,8 @@ async function runBillingCatalog(options) {
|
|
|
14
15
|
toJsonOutput(true, response);
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
17
|
-
console.log(
|
|
18
|
-
console.log(
|
|
18
|
+
console.log(`${subtle("Plans")} ${strong(String(response.variants.length))}`);
|
|
19
|
+
console.log(`${subtle("Metered features")} ${info(Object.values(response.featureIds).join(", "))}`);
|
|
19
20
|
}
|
|
20
21
|
async function runBillingStatus(options) {
|
|
21
22
|
const local = await requireLocalSession();
|
|
@@ -35,9 +36,9 @@ async function runBillingStatus(options) {
|
|
|
35
36
|
toJsonOutput(true, response);
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
|
-
console.log(
|
|
39
|
-
console.log(
|
|
40
|
-
console.log(
|
|
39
|
+
console.log(`${subtle("Tier")} ${warning(response.currentTier ?? "none")}`);
|
|
40
|
+
console.log(`${subtle("Product")} ${muted(response.currentProductId ?? "none")}`);
|
|
41
|
+
console.log(`${subtle("Can manage billing")} ${response.canManageBilling ? accent("yes") : muted("no")}`);
|
|
41
42
|
}
|
|
42
43
|
export function registerBillingCommands(program) {
|
|
43
44
|
const billing = program.command("billing").description("Billing information");
|
package/dist/commands/env.js
CHANGED
|
@@ -5,6 +5,7 @@ import { addTargetOptions, dotenvEscape, parseChance, requireLocalSession, resol
|
|
|
5
5
|
import { EnvEvaluateBatchResponseSchema, EnvEvaluateResponseSchema, EnvListResponseSchema, EnvPullResponseSchema, EnvWriteResponseSchema, } from "../contracts/index.js";
|
|
6
6
|
import { postJson } from "../http.js";
|
|
7
7
|
import { collectOptionValues, parseRolloutFunction, parseRolloutMilestones, parseVisibility, } from "./env-helpers.js";
|
|
8
|
+
import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
|
|
8
9
|
async function resolveEnvAccess(options) {
|
|
9
10
|
const local = await requireLocalSession();
|
|
10
11
|
const target = await resolveTarget(options, local);
|
|
@@ -98,7 +99,7 @@ async function runEnvGet(name, options) {
|
|
|
98
99
|
toJsonOutput(true, value);
|
|
99
100
|
return;
|
|
100
101
|
}
|
|
101
|
-
console.log(renderScalar(value.value));
|
|
102
|
+
console.log(`${accent(resolvedName)} ${subtle("=")} ${strong(renderScalar(value.value))}`);
|
|
102
103
|
}
|
|
103
104
|
async function runEnvGetMany(options) {
|
|
104
105
|
const namesCsv = await promptForRequiredText(options.names, "Which variable names do you want, comma-separated?");
|
|
@@ -126,7 +127,7 @@ async function runEnvGetMany(options) {
|
|
|
126
127
|
return;
|
|
127
128
|
}
|
|
128
129
|
for (const value of [...response.values].sort((left, right) => left.name.localeCompare(right.name))) {
|
|
129
|
-
console.log(`${value.name}
|
|
130
|
+
console.log(`${accent(value.name)}${subtle("=")}${strong(renderScalar(value.value))}`);
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
async function runEnvList(options) {
|
|
@@ -147,7 +148,7 @@ async function runEnvList(options) {
|
|
|
147
148
|
return;
|
|
148
149
|
}
|
|
149
150
|
if (response.variables.length === 0) {
|
|
150
|
-
console.log("No variables found.");
|
|
151
|
+
console.log(subtle("No variables found."));
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
154
|
for (const row of response.variables) {
|
|
@@ -155,7 +156,9 @@ async function runEnvList(options) {
|
|
|
155
156
|
const rolloutSuffix = row.kind === "rollout"
|
|
156
157
|
? ` ${row.rolloutFunction ?? "linear"}(${row.rolloutMilestones?.length ?? 0} milestones)`
|
|
157
158
|
: "";
|
|
158
|
-
|
|
159
|
+
const formattedChanceSuffix = chanceSuffix.length > 0 ? muted(chanceSuffix) : "";
|
|
160
|
+
const formattedRolloutSuffix = rolloutSuffix.length > 0 ? muted(rolloutSuffix) : "";
|
|
161
|
+
console.log(`${strong(row.name)} ${muted(row.visibility)} ${info(row.kind)} ${accent(row.declaredType)}${formattedChanceSuffix}${formattedRolloutSuffix}`);
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
async function runEnvWrite(operation, name, value, options) {
|
|
@@ -223,7 +226,7 @@ async function runEnvWrite(operation, name, value, options) {
|
|
|
223
226
|
toJsonOutput(true, result);
|
|
224
227
|
return;
|
|
225
228
|
}
|
|
226
|
-
console.log(
|
|
229
|
+
console.log(`${success("Created")} ${strong(String(result.createdCount))} ${subtle("•")} ${warning("Updated")} ${strong(String(result.updatedCount))} ${subtle("•")} ${muted("Deleted")} ${strong(String(result.deletedCount))}`);
|
|
227
230
|
}
|
|
228
231
|
async function runEnvDelete(name, options) {
|
|
229
232
|
const resolvedName = await promptForRequiredText(name, "What's the name of the variable to delete?");
|
|
@@ -279,7 +282,7 @@ async function runEnvDelete(name, options) {
|
|
|
279
282
|
toJsonOutput(true, result);
|
|
280
283
|
return;
|
|
281
284
|
}
|
|
282
|
-
console.log(
|
|
285
|
+
console.log(`${success("Deleted")} ${strong(String(result.deletedCount))}`);
|
|
283
286
|
}
|
|
284
287
|
async function runEnvPull(options) {
|
|
285
288
|
const { local, target, accessToken } = await resolveEnvAccess(options);
|
|
@@ -303,11 +306,11 @@ async function runEnvPull(options) {
|
|
|
303
306
|
: `${sortedKeys.map((keyName) => `${keyName}=${dotenvEscape(response.byName[keyName] ?? "")}`).join("\n")}\n`;
|
|
304
307
|
if (options.out) {
|
|
305
308
|
await writeFile(options.out, output, "utf8");
|
|
306
|
-
console.log(
|
|
309
|
+
console.log(`${success("Wrote")} ${muted(options.out)}`);
|
|
307
310
|
return;
|
|
308
311
|
}
|
|
309
312
|
if (options.redact && !process.stdout.isTTY) {
|
|
310
|
-
console.log(
|
|
313
|
+
console.log(`${success("Pulled")} ${strong(String(response.values.length))} ${muted("variables.")}`);
|
|
311
314
|
return;
|
|
312
315
|
}
|
|
313
316
|
process.stdout.write(output);
|
package/dist/commands/init.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { promptForOrganizationSlug, promptForProjectSlug, promptForStageSlug, } from "./target-prompts.js";
|
|
4
|
+
import { muted, success } from "../output/theme.js";
|
|
4
5
|
async function runInit(options) {
|
|
5
6
|
const orgSlug = await promptForOrganizationSlug(options.org);
|
|
6
7
|
const projectSlug = await promptForProjectSlug(orgSlug, options.project);
|
|
@@ -16,7 +17,7 @@ async function runInit(options) {
|
|
|
16
17
|
},
|
|
17
18
|
}, null, 2)}\n`;
|
|
18
19
|
await writeFile(configPath, contents, "utf8");
|
|
19
|
-
console.log(
|
|
20
|
+
console.log(`${success("Wrote")} ${muted(configPath)}`);
|
|
20
21
|
}
|
|
21
22
|
export function registerInitCommand(program) {
|
|
22
23
|
program
|
package/dist/commands/org.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
3
3
|
import { requireLocalSession, toJsonOutput } from "../command-utils.js";
|
|
4
4
|
import { OrganizationsResponseSchema } from "../contracts/index.js";
|
|
5
5
|
import { getJson, postJson } from "../http.js";
|
|
6
|
+
import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
|
|
6
7
|
async function promptForName(name) {
|
|
7
8
|
const existing = name?.trim();
|
|
8
9
|
if (existing && existing.length > 0) {
|
|
@@ -36,11 +37,11 @@ async function runOrgList(options) {
|
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
if (response.organizations.length === 0) {
|
|
39
|
-
console.log("No organizations found.");
|
|
40
|
+
console.log(subtle("No organizations found."));
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
for (const organization of response.organizations) {
|
|
43
|
-
console.log(`${organization.name} (${organization.slug}) ${organization.role}`);
|
|
44
|
+
console.log(`${strong(organization.name)} ${muted(`(${organization.slug})`)} ${info(organization.role)}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
async function runOrgCreate(name, options) {
|
|
@@ -62,7 +63,7 @@ async function runOrgCreate(name, options) {
|
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
const organization = response.organization;
|
|
65
|
-
console.log(
|
|
66
|
+
console.log(`${success("Created")} ${accent("organization")} ${strong(organization.name)} ${muted(`(${organization.slug})`)}`);
|
|
66
67
|
}
|
|
67
68
|
export function registerOrgCommands(program) {
|
|
68
69
|
const org = program.command("org").description("Organization management");
|
package/dist/commands/project.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel, text } from "@clack/prompts";
|
|
2
2
|
import { toJsonOutput } from "../command-utils.js";
|
|
3
3
|
import { createProjectForOrganization, deleteProjectForOrganization, listProjectsForOrganization, promptForOrganizationSlug, promptForProjectSlug, } from "./target-prompts.js";
|
|
4
|
+
import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
|
|
4
5
|
async function promptForProjectName(name) {
|
|
5
6
|
const existing = name?.trim();
|
|
6
7
|
if (existing && existing.length > 0) {
|
|
@@ -27,11 +28,11 @@ async function runProjectList(options) {
|
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
if (projects.length === 0) {
|
|
30
|
-
console.log("No projects found.");
|
|
31
|
+
console.log(subtle("No projects found."));
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
for (const project of projects) {
|
|
34
|
-
console.log(`${project.name} (${project.slug}) secrets=${project.secretCount}`);
|
|
35
|
+
console.log(`${strong(project.name)} ${muted(`(${project.slug})`)} ${info(`secrets=${project.secretCount}`)}`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
async function runProjectCreate(name, options) {
|
|
@@ -42,7 +43,7 @@ async function runProjectCreate(name, options) {
|
|
|
42
43
|
toJsonOutput(true, project);
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
console.log(
|
|
46
|
+
console.log(`${success("Created")} ${accent("project")} ${strong(project.name)} ${muted(`(${project.slug})`)}`);
|
|
46
47
|
}
|
|
47
48
|
export function registerProjectCommands(program) {
|
|
48
49
|
const project = program.command("project").description("Project management");
|
|
@@ -94,6 +95,6 @@ export function registerProjectCommands(program) {
|
|
|
94
95
|
toJsonOutput(true, response);
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
97
|
-
console.log(
|
|
98
|
+
console.log(`${success("Deleted")} ${accent("project")} ${muted(response.deletedProjectSlug)}`);
|
|
98
99
|
});
|
|
99
100
|
}
|
package/dist/commands/stage.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel, text } from "@clack/prompts";
|
|
2
2
|
import { toJsonOutput } from "../command-utils.js";
|
|
3
3
|
import { createStageForProject, deleteStageForProject, listStagesForProject, promptForOrganizationSlug, promptForProjectSlug, promptForStageSlug, renameStageForProject, } from "./target-prompts.js";
|
|
4
|
+
import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
|
|
4
5
|
async function promptForStageName(name) {
|
|
5
6
|
const existing = name?.trim();
|
|
6
7
|
if (existing && existing.length > 0) {
|
|
@@ -28,11 +29,11 @@ async function runStageList(options) {
|
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
if (stages.length === 0) {
|
|
31
|
-
console.log("No stages found.");
|
|
32
|
+
console.log(subtle("No stages found."));
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
for (const stage of stages) {
|
|
35
|
-
console.log(`${stage.name} (${stage.slug}) variables=${stage.variableCount}`);
|
|
36
|
+
console.log(`${strong(stage.name)} ${muted(`(${stage.slug})`)} ${info(`variables=${stage.variableCount}`)}`);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
async function runStageCreate(name, options) {
|
|
@@ -44,7 +45,7 @@ async function runStageCreate(name, options) {
|
|
|
44
45
|
toJsonOutput(true, stage);
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
|
-
console.log(
|
|
48
|
+
console.log(`${success("Created")} ${accent("stage")} ${strong(stage.name)} ${muted(`(${stage.slug})`)}`);
|
|
48
49
|
}
|
|
49
50
|
export function registerStageCommands(program) {
|
|
50
51
|
const stage = program.command("stage").description("Stage management");
|
|
@@ -85,7 +86,7 @@ export function registerStageCommands(program) {
|
|
|
85
86
|
toJsonOutput(true, stage);
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
|
-
console.log(
|
|
89
|
+
console.log(`${warning("Renamed")} ${accent("stage")} ${muted(stage.slug)} ${subtle("→")} ${strong(stage.name)}`);
|
|
89
90
|
});
|
|
90
91
|
stage
|
|
91
92
|
.command("delete")
|
|
@@ -120,6 +121,6 @@ export function registerStageCommands(program) {
|
|
|
120
121
|
toJsonOutput(true, response);
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
console.log(
|
|
124
|
+
console.log(`${success("Deleted")} ${accent("stage")} ${muted(response.deletedStageSlug)}`);
|
|
124
125
|
});
|
|
125
126
|
}
|
package/dist/commands/typegen.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import pc from "picocolors";
|
|
2
1
|
import { createCliAuthProvider } from "../auth-provider.js";
|
|
3
2
|
import { addTargetOptions, requireLocalSession, resolveTarget, } from "../command-utils.js";
|
|
4
3
|
import { TypegenManifestSchema } from "../contracts/index.js";
|
|
5
4
|
import { getJson } from "../http.js";
|
|
6
5
|
import { loadRuntimeConfig } from "../runtime-config.js";
|
|
7
6
|
import { writeInstalledSdkGeneratedTypes, } from "../typegen/core.js";
|
|
7
|
+
import { accent, info, muted, strong, success, warning } from "../output/theme.js";
|
|
8
8
|
const DEFAULT_TYPEGEN_WATCH_INTERVAL_MS = 5_000;
|
|
9
9
|
export function formatTypegenResultMessage(result) {
|
|
10
10
|
const title = result.written
|
|
11
|
-
?
|
|
12
|
-
:
|
|
11
|
+
? success(strong("Typegen complete"))
|
|
12
|
+
: info(strong("Typegen already up to date"));
|
|
13
13
|
const detail = result.written
|
|
14
14
|
? "Fresh SDK types are ready in your installed @barekey/sdk package."
|
|
15
15
|
: "Your installed @barekey/sdk package already has the latest generated types.";
|
|
16
16
|
return [
|
|
17
17
|
title,
|
|
18
|
-
detail,
|
|
19
|
-
`${
|
|
20
|
-
`${
|
|
18
|
+
muted(detail),
|
|
19
|
+
`${accent("Server types")} ${muted(result.serverPath)}`,
|
|
20
|
+
`${accent("Public types")} ${muted(result.publicPath)}`,
|
|
21
21
|
].join("\n");
|
|
22
22
|
}
|
|
23
23
|
export function formatTypegenWatchStartedMessage(input) {
|
|
24
24
|
return [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
info(strong("Watching Barekey typegen")),
|
|
26
|
+
`${accent("Target")} ${strong(`${input.organization}/${input.project}@${input.environment}`)}`,
|
|
27
|
+
`${warning("Polling every")} ${strong(`${input.intervalMs}ms`)}${muted(". Press Ctrl+C to stop.")}`,
|
|
28
28
|
].join("\n");
|
|
29
29
|
}
|
|
30
30
|
export function parseTypegenWatchInterval(value) {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import pc from "picocolors";
|
|
4
3
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
5
4
|
import { registerAuditCommands } from "./commands/audit.js";
|
|
6
5
|
import { registerBillingCommands } from "./commands/billing.js";
|
|
@@ -11,8 +10,13 @@ import { registerProjectCommands } from "./commands/project.js";
|
|
|
11
10
|
import { registerStageCommands } from "./commands/stage.js";
|
|
12
11
|
import { registerTypegenCommand } from "./commands/typegen.js";
|
|
13
12
|
import { CLI_DESCRIPTION, CLI_NAME, CLI_VERSION } from "./constants.js";
|
|
13
|
+
import { danger } from "./output/theme.js";
|
|
14
14
|
const program = new Command();
|
|
15
15
|
program.name(CLI_NAME).description(CLI_DESCRIPTION).version(CLI_VERSION);
|
|
16
|
+
program.configureOutput({
|
|
17
|
+
writeErr: (message) => process.stderr.write(message),
|
|
18
|
+
outputError: (message, write) => write(danger(message)),
|
|
19
|
+
});
|
|
16
20
|
registerAuthCommands(program);
|
|
17
21
|
registerAuditCommands(program);
|
|
18
22
|
registerBillingCommands(program);
|
|
@@ -24,6 +28,6 @@ registerEnvCommands(program);
|
|
|
24
28
|
registerTypegenCommand(program);
|
|
25
29
|
program.parseAsync(process.argv).catch((error) => {
|
|
26
30
|
const message = error instanceof Error ? error.message : "Command failed.";
|
|
27
|
-
console.error(
|
|
31
|
+
console.error(danger(message));
|
|
28
32
|
process.exitCode = 1;
|
|
29
33
|
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
declare const MINECRAFT_HEX: {
|
|
2
|
+
readonly "1": "#0000AA";
|
|
3
|
+
readonly "6": "#FFAA00";
|
|
4
|
+
readonly "7": "#AAAAAA";
|
|
5
|
+
readonly "8": "#555555";
|
|
6
|
+
readonly a: "#55FF55";
|
|
7
|
+
readonly b: "#55FFFF";
|
|
8
|
+
readonly c: "#FF5555";
|
|
9
|
+
readonly e: "#FFFF55";
|
|
10
|
+
};
|
|
11
|
+
type MinecraftColorCode = keyof typeof MINECRAFT_HEX;
|
|
12
|
+
/**
|
|
13
|
+
* Returns whether styled terminal output should be emitted.
|
|
14
|
+
*
|
|
15
|
+
* @returns `true` when stdout is interactive and color has not been disabled.
|
|
16
|
+
* @remarks This keeps human output colorful while preserving clean automation output in dumb/no-color terminals.
|
|
17
|
+
* @lastModified 2026-03-19
|
|
18
|
+
* @author GPT-5.4
|
|
19
|
+
*/
|
|
20
|
+
export declare function supportsCliColor(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Applies one Minecraft-style truecolor swatch to a string.
|
|
23
|
+
*
|
|
24
|
+
* @param code The legacy Minecraft color code to emulate.
|
|
25
|
+
* @param value The text to colorize.
|
|
26
|
+
* @returns The styled string, or the original text when colors are disabled.
|
|
27
|
+
* @remarks This uses truecolor ANSI so the palette stays stable across modern terminals.
|
|
28
|
+
* @lastModified 2026-03-19
|
|
29
|
+
* @author GPT-5.4
|
|
30
|
+
*/
|
|
31
|
+
export declare function mc(code: MinecraftColorCode, value: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Styles one primary CLI label.
|
|
34
|
+
*
|
|
35
|
+
* @param value The label text.
|
|
36
|
+
* @returns The highlighted label.
|
|
37
|
+
* @remarks Gold is used as the main CLI accent color.
|
|
38
|
+
* @lastModified 2026-03-19
|
|
39
|
+
* @author GPT-5.4
|
|
40
|
+
*/
|
|
41
|
+
export declare function accent(value: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Styles one positive/success string.
|
|
44
|
+
*
|
|
45
|
+
* @param value The text to colorize.
|
|
46
|
+
* @returns The success-colored string.
|
|
47
|
+
* @remarks Green is used for successful actions and positive counts.
|
|
48
|
+
* @lastModified 2026-03-19
|
|
49
|
+
* @author GPT-5.4
|
|
50
|
+
*/
|
|
51
|
+
export declare function success(value: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Styles one informational string.
|
|
54
|
+
*
|
|
55
|
+
* @param value The text to colorize.
|
|
56
|
+
* @returns The info-colored string.
|
|
57
|
+
* @remarks Aqua is used for list metadata and secondary highlights.
|
|
58
|
+
* @lastModified 2026-03-19
|
|
59
|
+
* @author GPT-5.4
|
|
60
|
+
*/
|
|
61
|
+
export declare function info(value: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Styles one warning string.
|
|
64
|
+
*
|
|
65
|
+
* @param value The text to colorize.
|
|
66
|
+
* @returns The warning-colored string.
|
|
67
|
+
* @remarks Bright yellow is used for cautionary or count summary output.
|
|
68
|
+
* @lastModified 2026-03-19
|
|
69
|
+
* @author GPT-5.4
|
|
70
|
+
*/
|
|
71
|
+
export declare function warning(value: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Styles one muted metadata string.
|
|
74
|
+
*
|
|
75
|
+
* @param value The text to colorize.
|
|
76
|
+
* @returns The muted string.
|
|
77
|
+
* @remarks Gray is used for low-emphasis metadata like slugs and file paths.
|
|
78
|
+
* @lastModified 2026-03-19
|
|
79
|
+
* @author GPT-5.4
|
|
80
|
+
*/
|
|
81
|
+
export declare function muted(value: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Styles one subtle low-contrast string.
|
|
84
|
+
*
|
|
85
|
+
* @param value The text to colorize.
|
|
86
|
+
* @returns The subtle string.
|
|
87
|
+
* @remarks Dark gray is used for separators, labels, and empty-state output.
|
|
88
|
+
* @lastModified 2026-03-19
|
|
89
|
+
* @author GPT-5.4
|
|
90
|
+
*/
|
|
91
|
+
export declare function subtle(value: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Styles one failure/error string.
|
|
94
|
+
*
|
|
95
|
+
* @param value The text to colorize.
|
|
96
|
+
* @returns The danger-colored string.
|
|
97
|
+
* @remarks Red is reserved for errors and destructive outcomes.
|
|
98
|
+
* @lastModified 2026-03-19
|
|
99
|
+
* @author GPT-5.4
|
|
100
|
+
*/
|
|
101
|
+
export declare function danger(value: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Styles one prominent value with bold emphasis.
|
|
104
|
+
*
|
|
105
|
+
* @param value The text to emphasize.
|
|
106
|
+
* @returns The bold string.
|
|
107
|
+
* @remarks This intentionally leaves color choice to the caller.
|
|
108
|
+
* @lastModified 2026-03-19
|
|
109
|
+
* @author GPT-5.4
|
|
110
|
+
*/
|
|
111
|
+
export declare function strong(value: string): string;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
const MINECRAFT_HEX = {
|
|
3
|
+
"1": "#0000AA",
|
|
4
|
+
"6": "#FFAA00",
|
|
5
|
+
"7": "#AAAAAA",
|
|
6
|
+
"8": "#555555",
|
|
7
|
+
"a": "#55FF55",
|
|
8
|
+
"b": "#55FFFF",
|
|
9
|
+
"c": "#FF5555",
|
|
10
|
+
"e": "#FFFF55",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Returns whether styled terminal output should be emitted.
|
|
14
|
+
*
|
|
15
|
+
* @returns `true` when stdout is interactive and color has not been disabled.
|
|
16
|
+
* @remarks This keeps human output colorful while preserving clean automation output in dumb/no-color terminals.
|
|
17
|
+
* @lastModified 2026-03-19
|
|
18
|
+
* @author GPT-5.4
|
|
19
|
+
*/
|
|
20
|
+
export function supportsCliColor() {
|
|
21
|
+
if (!process.stdout.isTTY) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if ((process.env.NO_COLOR ?? "").trim().length > 0) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return (process.env.TERM ?? "").toLowerCase() !== "dumb";
|
|
28
|
+
}
|
|
29
|
+
function hexToRgb(hex) {
|
|
30
|
+
const normalized = hex.replace(/^#/, "");
|
|
31
|
+
return {
|
|
32
|
+
red: Number.parseInt(normalized.slice(0, 2), 16),
|
|
33
|
+
green: Number.parseInt(normalized.slice(2, 4), 16),
|
|
34
|
+
blue: Number.parseInt(normalized.slice(4, 6), 16),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Applies one Minecraft-style truecolor swatch to a string.
|
|
39
|
+
*
|
|
40
|
+
* @param code The legacy Minecraft color code to emulate.
|
|
41
|
+
* @param value The text to colorize.
|
|
42
|
+
* @returns The styled string, or the original text when colors are disabled.
|
|
43
|
+
* @remarks This uses truecolor ANSI so the palette stays stable across modern terminals.
|
|
44
|
+
* @lastModified 2026-03-19
|
|
45
|
+
* @author GPT-5.4
|
|
46
|
+
*/
|
|
47
|
+
export function mc(code, value) {
|
|
48
|
+
if (!supportsCliColor()) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
const { red, green, blue } = hexToRgb(MINECRAFT_HEX[code]);
|
|
52
|
+
return `\u001b[38;2;${red};${green};${blue}m${value}\u001b[39m`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Styles one primary CLI label.
|
|
56
|
+
*
|
|
57
|
+
* @param value The label text.
|
|
58
|
+
* @returns The highlighted label.
|
|
59
|
+
* @remarks Gold is used as the main CLI accent color.
|
|
60
|
+
* @lastModified 2026-03-19
|
|
61
|
+
* @author GPT-5.4
|
|
62
|
+
*/
|
|
63
|
+
export function accent(value) {
|
|
64
|
+
return mc("6", value);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Styles one positive/success string.
|
|
68
|
+
*
|
|
69
|
+
* @param value The text to colorize.
|
|
70
|
+
* @returns The success-colored string.
|
|
71
|
+
* @remarks Green is used for successful actions and positive counts.
|
|
72
|
+
* @lastModified 2026-03-19
|
|
73
|
+
* @author GPT-5.4
|
|
74
|
+
*/
|
|
75
|
+
export function success(value) {
|
|
76
|
+
return mc("a", value);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Styles one informational string.
|
|
80
|
+
*
|
|
81
|
+
* @param value The text to colorize.
|
|
82
|
+
* @returns The info-colored string.
|
|
83
|
+
* @remarks Aqua is used for list metadata and secondary highlights.
|
|
84
|
+
* @lastModified 2026-03-19
|
|
85
|
+
* @author GPT-5.4
|
|
86
|
+
*/
|
|
87
|
+
export function info(value) {
|
|
88
|
+
return mc("b", value);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Styles one warning string.
|
|
92
|
+
*
|
|
93
|
+
* @param value The text to colorize.
|
|
94
|
+
* @returns The warning-colored string.
|
|
95
|
+
* @remarks Bright yellow is used for cautionary or count summary output.
|
|
96
|
+
* @lastModified 2026-03-19
|
|
97
|
+
* @author GPT-5.4
|
|
98
|
+
*/
|
|
99
|
+
export function warning(value) {
|
|
100
|
+
return mc("e", value);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Styles one muted metadata string.
|
|
104
|
+
*
|
|
105
|
+
* @param value The text to colorize.
|
|
106
|
+
* @returns The muted string.
|
|
107
|
+
* @remarks Gray is used for low-emphasis metadata like slugs and file paths.
|
|
108
|
+
* @lastModified 2026-03-19
|
|
109
|
+
* @author GPT-5.4
|
|
110
|
+
*/
|
|
111
|
+
export function muted(value) {
|
|
112
|
+
return mc("7", value);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Styles one subtle low-contrast string.
|
|
116
|
+
*
|
|
117
|
+
* @param value The text to colorize.
|
|
118
|
+
* @returns The subtle string.
|
|
119
|
+
* @remarks Dark gray is used for separators, labels, and empty-state output.
|
|
120
|
+
* @lastModified 2026-03-19
|
|
121
|
+
* @author GPT-5.4
|
|
122
|
+
*/
|
|
123
|
+
export function subtle(value) {
|
|
124
|
+
return mc("8", value);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Styles one failure/error string.
|
|
128
|
+
*
|
|
129
|
+
* @param value The text to colorize.
|
|
130
|
+
* @returns The danger-colored string.
|
|
131
|
+
* @remarks Red is reserved for errors and destructive outcomes.
|
|
132
|
+
* @lastModified 2026-03-19
|
|
133
|
+
* @author GPT-5.4
|
|
134
|
+
*/
|
|
135
|
+
export function danger(value) {
|
|
136
|
+
return mc("c", value);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Styles one prominent value with bold emphasis.
|
|
140
|
+
*
|
|
141
|
+
* @param value The text to emphasize.
|
|
142
|
+
* @returns The bold string.
|
|
143
|
+
* @remarks This intentionally leaves color choice to the caller.
|
|
144
|
+
* @lastModified 2026-03-19
|
|
145
|
+
* @author GPT-5.4
|
|
146
|
+
*/
|
|
147
|
+
export function strong(value) {
|
|
148
|
+
return pc.bold(value);
|
|
149
|
+
}
|
package/package.json
CHANGED
package/src/commands/audit.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
4
4
|
import { requireLocalSession, toJsonOutput, type EnvTargetOptions } from "../command-utils.js";
|
|
5
5
|
import { AuditListResponseSchema } from "../contracts/index.js";
|
|
6
6
|
import { postJson } from "../http.js";
|
|
7
|
+
import { info, muted, subtle, strong } from "../output/theme.js";
|
|
7
8
|
import { promptForOrganizationSlug } from "./target-prompts.js";
|
|
8
9
|
|
|
9
10
|
async function runAuditList(
|
|
@@ -36,12 +37,14 @@ async function runAuditList(
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
if (response.items.length === 0) {
|
|
39
|
-
console.log("No audit events found.");
|
|
40
|
+
console.log(subtle("No audit events found."));
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
for (const item of response.items) {
|
|
44
|
-
console.log(
|
|
45
|
+
console.log(
|
|
46
|
+
`${muted(new Date(item.occurredAtMs).toISOString())} ${info(item.category)} ${strong(item.title)}`,
|
|
47
|
+
);
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
package/src/commands/auth.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "../contracts/index.js";
|
|
23
23
|
import { getJson, postJson } from "../http.js";
|
|
24
24
|
import { formatWhoamiWithAvatar } from "../output/avatar.js";
|
|
25
|
+
import { accent, muted, strong, success } from "../output/theme.js";
|
|
25
26
|
import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
|
|
26
27
|
|
|
27
28
|
function resolveClientName(): string | undefined {
|
|
@@ -88,8 +89,8 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
88
89
|
|
|
89
90
|
loading.stop("Authorization initialized");
|
|
90
91
|
loadingActive = false;
|
|
91
|
-
console.log(`${
|
|
92
|
-
console.log(`${
|
|
92
|
+
console.log(`${accent("Open")} ${muted(verificationUri)}`);
|
|
93
|
+
console.log(`${accent("Code")} ${strong(started.userCode)}`);
|
|
93
94
|
|
|
94
95
|
try {
|
|
95
96
|
await open(verificationUri);
|
|
@@ -149,7 +150,7 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
149
150
|
|
|
150
151
|
pollSpinner.stop("Login approved");
|
|
151
152
|
pollActive = false;
|
|
152
|
-
outro(
|
|
153
|
+
outro(`${success("Signed in as")} ${pc.bold(resolveDisplayName(refreshed))}.`);
|
|
153
154
|
return;
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -178,7 +179,7 @@ async function runLogout(): Promise<void> {
|
|
|
178
179
|
});
|
|
179
180
|
await deleteCredentials(local.accountId);
|
|
180
181
|
await clearConfig();
|
|
181
|
-
console.log("Logged out.");
|
|
182
|
+
console.log(success("Logged out."));
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
async function runWhoami(options: { json?: boolean }): Promise<void> {
|
package/src/commands/billing.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
4
4
|
import { requireLocalSession, toJsonOutput, type EnvTargetOptions } from "../command-utils.js";
|
|
5
5
|
import { BillingCatalogResponseSchema, BillingStatusResponseSchema } from "../contracts/index.js";
|
|
6
6
|
import { getJson, postJson } from "../http.js";
|
|
7
|
+
import { accent, info, muted, strong, subtle, warning } from "../output/theme.js";
|
|
7
8
|
import { promptForOrganizationSlug } from "./target-prompts.js";
|
|
8
9
|
|
|
9
10
|
async function runBillingCatalog(options: { json?: boolean }) {
|
|
@@ -19,8 +20,10 @@ async function runBillingCatalog(options: { json?: boolean }) {
|
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
console.log(
|
|
23
|
-
console.log(
|
|
23
|
+
console.log(`${subtle("Plans")} ${strong(String(response.variants.length))}`);
|
|
24
|
+
console.log(
|
|
25
|
+
`${subtle("Metered features")} ${info(Object.values(response.featureIds).join(", "))}`,
|
|
26
|
+
);
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
async function runBillingStatus(options: EnvTargetOptions & { json?: boolean }) {
|
|
@@ -43,9 +46,11 @@ async function runBillingStatus(options: EnvTargetOptions & { json?: boolean })
|
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
console.log(
|
|
47
|
-
console.log(
|
|
48
|
-
console.log(
|
|
49
|
+
console.log(`${subtle("Tier")} ${warning(response.currentTier ?? "none")}`);
|
|
50
|
+
console.log(`${subtle("Product")} ${muted(response.currentProductId ?? "none")}`);
|
|
51
|
+
console.log(
|
|
52
|
+
`${subtle("Can manage billing")} ${response.canManageBilling ? accent("yes") : muted("no")}`,
|
|
53
|
+
);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export function registerBillingCommands(program: Command): void {
|
package/src/commands/env.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
type CliRolloutFunction,
|
|
30
30
|
type CliVisibility,
|
|
31
31
|
} from "./env-helpers.js";
|
|
32
|
+
import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
|
|
32
33
|
|
|
33
34
|
type EnvWriteOptions = EnvTargetOptions & {
|
|
34
35
|
ab?: string;
|
|
@@ -158,7 +159,7 @@ async function runEnvGet(
|
|
|
158
159
|
return;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
console.log(renderScalar(value.value));
|
|
162
|
+
console.log(`${accent(resolvedName)} ${subtle("=")} ${strong(renderScalar(value.value))}`);
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
async function runEnvGetMany(
|
|
@@ -201,7 +202,7 @@ async function runEnvGetMany(
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
for (const value of [...response.values].sort((left, right) => left.name.localeCompare(right.name))) {
|
|
204
|
-
console.log(`${value.name}
|
|
205
|
+
console.log(`${accent(value.name)}${subtle("=")}${strong(renderScalar(value.value))}`);
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -225,7 +226,7 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
if (response.variables.length === 0) {
|
|
228
|
-
console.log("No variables found.");
|
|
229
|
+
console.log(subtle("No variables found."));
|
|
229
230
|
return;
|
|
230
231
|
}
|
|
231
232
|
|
|
@@ -235,8 +236,10 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
|
|
|
235
236
|
row.kind === "rollout"
|
|
236
237
|
? ` ${row.rolloutFunction ?? "linear"}(${row.rolloutMilestones?.length ?? 0} milestones)`
|
|
237
238
|
: "";
|
|
239
|
+
const formattedChanceSuffix = chanceSuffix.length > 0 ? muted(chanceSuffix) : "";
|
|
240
|
+
const formattedRolloutSuffix = rolloutSuffix.length > 0 ? muted(rolloutSuffix) : "";
|
|
238
241
|
console.log(
|
|
239
|
-
`${row.name} ${row.visibility} ${row.kind} ${row.declaredType}${
|
|
242
|
+
`${strong(row.name)} ${muted(row.visibility)} ${info(row.kind)} ${accent(row.declaredType)}${formattedChanceSuffix}${formattedRolloutSuffix}`,
|
|
240
243
|
);
|
|
241
244
|
}
|
|
242
245
|
}
|
|
@@ -320,7 +323,7 @@ async function runEnvWrite(
|
|
|
320
323
|
}
|
|
321
324
|
|
|
322
325
|
console.log(
|
|
323
|
-
|
|
326
|
+
`${success("Created")} ${strong(String(result.createdCount))} ${subtle("•")} ${warning("Updated")} ${strong(String(result.updatedCount))} ${subtle("•")} ${muted("Deleted")} ${strong(String(result.deletedCount))}`,
|
|
324
327
|
);
|
|
325
328
|
}
|
|
326
329
|
|
|
@@ -390,7 +393,7 @@ async function runEnvDelete(
|
|
|
390
393
|
return;
|
|
391
394
|
}
|
|
392
395
|
|
|
393
|
-
console.log(
|
|
396
|
+
console.log(`${success("Deleted")} ${strong(String(result.deletedCount))}`);
|
|
394
397
|
}
|
|
395
398
|
|
|
396
399
|
async function runEnvPull(
|
|
@@ -426,12 +429,12 @@ async function runEnvPull(
|
|
|
426
429
|
|
|
427
430
|
if (options.out) {
|
|
428
431
|
await writeFile(options.out, output, "utf8");
|
|
429
|
-
console.log(
|
|
432
|
+
console.log(`${success("Wrote")} ${muted(options.out)}`);
|
|
430
433
|
return;
|
|
431
434
|
}
|
|
432
435
|
|
|
433
436
|
if (options.redact && !process.stdout.isTTY) {
|
|
434
|
-
console.log(
|
|
437
|
+
console.log(`${success("Pulled")} ${strong(String(response.values.length))} ${muted("variables.")}`);
|
|
435
438
|
return;
|
|
436
439
|
}
|
|
437
440
|
|
package/src/commands/init.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
promptForProjectSlug,
|
|
10
10
|
promptForStageSlug,
|
|
11
11
|
} from "./target-prompts.js";
|
|
12
|
+
import { muted, success } from "../output/theme.js";
|
|
12
13
|
|
|
13
14
|
async function runInit(options: EnvTargetOptions & { path?: string }) {
|
|
14
15
|
const orgSlug = await promptForOrganizationSlug(options.org);
|
|
@@ -30,7 +31,7 @@ async function runInit(options: EnvTargetOptions & { path?: string }) {
|
|
|
30
31
|
)}\n`;
|
|
31
32
|
|
|
32
33
|
await writeFile(configPath, contents, "utf8");
|
|
33
|
-
console.log(
|
|
34
|
+
console.log(`${success("Wrote")} ${muted(configPath)}`);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function registerInitCommand(program: Command): void {
|
package/src/commands/org.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { createCliAuthProvider } from "../auth-provider.js";
|
|
|
5
5
|
import { requireLocalSession, toJsonOutput } from "../command-utils.js";
|
|
6
6
|
import { OrganizationsResponseSchema } from "../contracts/index.js";
|
|
7
7
|
import { getJson, postJson } from "../http.js";
|
|
8
|
+
import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
|
|
8
9
|
|
|
9
10
|
async function promptForName(name: string | undefined): Promise<string> {
|
|
10
11
|
const existing = name?.trim();
|
|
@@ -42,12 +43,14 @@ async function runOrgList(options: { json?: boolean }) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (response.organizations.length === 0) {
|
|
45
|
-
console.log("No organizations found.");
|
|
46
|
+
console.log(subtle("No organizations found."));
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
for (const organization of response.organizations) {
|
|
50
|
-
console.log(
|
|
51
|
+
console.log(
|
|
52
|
+
`${strong(organization.name)} ${muted(`(${organization.slug})`)} ${info(organization.role)}`,
|
|
53
|
+
);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -78,7 +81,9 @@ async function runOrgCreate(
|
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
const organization = (response as { organization: { name: string; slug: string } }).organization;
|
|
81
|
-
console.log(
|
|
84
|
+
console.log(
|
|
85
|
+
`${success("Created")} ${accent("organization")} ${strong(organization.name)} ${muted(`(${organization.slug})`)}`,
|
|
86
|
+
);
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
export function registerOrgCommands(program: Command): void {
|
package/src/commands/project.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
promptForOrganizationSlug,
|
|
10
10
|
promptForProjectSlug,
|
|
11
11
|
} from "./target-prompts.js";
|
|
12
|
+
import { accent, info, muted, strong, subtle, success } from "../output/theme.js";
|
|
12
13
|
|
|
13
14
|
async function promptForProjectName(name: string | undefined): Promise<string> {
|
|
14
15
|
const existing = name?.trim();
|
|
@@ -39,12 +40,14 @@ async function runProjectList(options: EnvTargetOptions & { json?: boolean }) {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
if (projects.length === 0) {
|
|
42
|
-
console.log("No projects found.");
|
|
43
|
+
console.log(subtle("No projects found."));
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
for (const project of projects) {
|
|
47
|
-
console.log(
|
|
48
|
+
console.log(
|
|
49
|
+
`${strong(project.name)} ${muted(`(${project.slug})`)} ${info(`secrets=${project.secretCount}`)}`,
|
|
50
|
+
);
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -61,7 +64,9 @@ async function runProjectCreate(
|
|
|
61
64
|
return;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
console.log(
|
|
67
|
+
console.log(
|
|
68
|
+
`${success("Created")} ${accent("project")} ${strong(project.name)} ${muted(`(${project.slug})`)}`,
|
|
69
|
+
);
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
export function registerProjectCommands(program: Command): void {
|
|
@@ -124,7 +129,9 @@ export function registerProjectCommands(program: Command): void {
|
|
|
124
129
|
return;
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
console.log(
|
|
132
|
+
console.log(
|
|
133
|
+
`${success("Deleted")} ${accent("project")} ${muted(response.deletedProjectSlug)}`,
|
|
134
|
+
);
|
|
128
135
|
},
|
|
129
136
|
);
|
|
130
137
|
}
|
package/src/commands/stage.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
promptForStageSlug,
|
|
12
12
|
renameStageForProject,
|
|
13
13
|
} from "./target-prompts.js";
|
|
14
|
+
import { accent, info, muted, strong, subtle, success, warning } from "../output/theme.js";
|
|
14
15
|
|
|
15
16
|
async function promptForStageName(name: string | undefined): Promise<string> {
|
|
16
17
|
const existing = name?.trim();
|
|
@@ -42,12 +43,14 @@ async function runStageList(options: EnvTargetOptions & { json?: boolean }) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (stages.length === 0) {
|
|
45
|
-
console.log("No stages found.");
|
|
46
|
+
console.log(subtle("No stages found."));
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
for (const stage of stages) {
|
|
50
|
-
console.log(
|
|
51
|
+
console.log(
|
|
52
|
+
`${strong(stage.name)} ${muted(`(${stage.slug})`)} ${info(`variables=${stage.variableCount}`)}`,
|
|
53
|
+
);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -65,7 +68,9 @@ async function runStageCreate(
|
|
|
65
68
|
return;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
console.log(
|
|
71
|
+
console.log(
|
|
72
|
+
`${success("Created")} ${accent("stage")} ${strong(stage.name)} ${muted(`(${stage.slug})`)}`,
|
|
73
|
+
);
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
export function registerStageCommands(program: Command): void {
|
|
@@ -117,7 +122,9 @@ export function registerStageCommands(program: Command): void {
|
|
|
117
122
|
return;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
console.log(
|
|
125
|
+
console.log(
|
|
126
|
+
`${warning("Renamed")} ${accent("stage")} ${muted(stage.slug)} ${subtle("→")} ${strong(stage.name)}`,
|
|
127
|
+
);
|
|
121
128
|
},
|
|
122
129
|
);
|
|
123
130
|
|
|
@@ -161,7 +168,7 @@ export function registerStageCommands(program: Command): void {
|
|
|
161
168
|
return;
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
console.log(
|
|
171
|
+
console.log(`${success("Deleted")} ${accent("stage")} ${muted(response.deletedStageSlug)}`);
|
|
165
172
|
},
|
|
166
173
|
);
|
|
167
174
|
}
|
package/src/commands/typegen.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import pc from "picocolors";
|
|
3
2
|
|
|
4
3
|
import { createCliAuthProvider } from "../auth-provider.js";
|
|
5
4
|
import {
|
|
@@ -15,22 +14,23 @@ import {
|
|
|
15
14
|
type CliTypegenResult,
|
|
16
15
|
writeInstalledSdkGeneratedTypes,
|
|
17
16
|
} from "../typegen/core.js";
|
|
17
|
+
import { accent, info, muted, strong, success, warning } from "../output/theme.js";
|
|
18
18
|
|
|
19
19
|
const DEFAULT_TYPEGEN_WATCH_INTERVAL_MS = 5_000;
|
|
20
20
|
|
|
21
21
|
export function formatTypegenResultMessage(result: CliTypegenResult): string {
|
|
22
22
|
const title = result.written
|
|
23
|
-
?
|
|
24
|
-
:
|
|
23
|
+
? success(strong("Typegen complete"))
|
|
24
|
+
: info(strong("Typegen already up to date"));
|
|
25
25
|
const detail = result.written
|
|
26
26
|
? "Fresh SDK types are ready in your installed @barekey/sdk package."
|
|
27
27
|
: "Your installed @barekey/sdk package already has the latest generated types.";
|
|
28
28
|
|
|
29
29
|
return [
|
|
30
30
|
title,
|
|
31
|
-
detail,
|
|
32
|
-
`${
|
|
33
|
-
`${
|
|
31
|
+
muted(detail),
|
|
32
|
+
`${accent("Server types")} ${muted(result.serverPath)}`,
|
|
33
|
+
`${accent("Public types")} ${muted(result.publicPath)}`,
|
|
34
34
|
].join("\n");
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -41,9 +41,9 @@ export function formatTypegenWatchStartedMessage(input: {
|
|
|
41
41
|
intervalMs: number;
|
|
42
42
|
}): string {
|
|
43
43
|
return [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
info(strong("Watching Barekey typegen")),
|
|
45
|
+
`${accent("Target")} ${strong(`${input.organization}/${input.project}@${input.environment}`)}`,
|
|
46
|
+
`${warning("Polling every")} ${strong(`${input.intervalMs}ms`)}${muted(". Press Ctrl+C to stop.")}`,
|
|
47
47
|
].join("\n");
|
|
48
48
|
}
|
|
49
49
|
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import pc from "picocolors";
|
|
4
3
|
|
|
5
4
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
6
5
|
import { registerAuditCommands } from "./commands/audit.js";
|
|
@@ -12,9 +11,14 @@ import { registerProjectCommands } from "./commands/project.js";
|
|
|
12
11
|
import { registerStageCommands } from "./commands/stage.js";
|
|
13
12
|
import { registerTypegenCommand } from "./commands/typegen.js";
|
|
14
13
|
import { CLI_DESCRIPTION, CLI_NAME, CLI_VERSION } from "./constants.js";
|
|
14
|
+
import { danger } from "./output/theme.js";
|
|
15
15
|
|
|
16
16
|
const program = new Command();
|
|
17
17
|
program.name(CLI_NAME).description(CLI_DESCRIPTION).version(CLI_VERSION);
|
|
18
|
+
program.configureOutput({
|
|
19
|
+
writeErr: (message) => process.stderr.write(message),
|
|
20
|
+
outputError: (message, write) => write(danger(message)),
|
|
21
|
+
});
|
|
18
22
|
|
|
19
23
|
registerAuthCommands(program);
|
|
20
24
|
registerAuditCommands(program);
|
|
@@ -28,6 +32,6 @@ registerTypegenCommand(program);
|
|
|
28
32
|
|
|
29
33
|
program.parseAsync(process.argv).catch((error: unknown) => {
|
|
30
34
|
const message = error instanceof Error ? error.message : "Command failed.";
|
|
31
|
-
console.error(
|
|
35
|
+
console.error(danger(message));
|
|
32
36
|
process.exitCode = 1;
|
|
33
37
|
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
|
|
3
|
+
const MINECRAFT_HEX = {
|
|
4
|
+
"1": "#0000AA",
|
|
5
|
+
"6": "#FFAA00",
|
|
6
|
+
"7": "#AAAAAA",
|
|
7
|
+
"8": "#555555",
|
|
8
|
+
"a": "#55FF55",
|
|
9
|
+
"b": "#55FFFF",
|
|
10
|
+
"c": "#FF5555",
|
|
11
|
+
"e": "#FFFF55",
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
type MinecraftColorCode = keyof typeof MINECRAFT_HEX;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns whether styled terminal output should be emitted.
|
|
18
|
+
*
|
|
19
|
+
* @returns `true` when stdout is interactive and color has not been disabled.
|
|
20
|
+
* @remarks This keeps human output colorful while preserving clean automation output in dumb/no-color terminals.
|
|
21
|
+
* @lastModified 2026-03-19
|
|
22
|
+
* @author GPT-5.4
|
|
23
|
+
*/
|
|
24
|
+
export function supportsCliColor(): boolean {
|
|
25
|
+
if (!process.stdout.isTTY) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ((process.env.NO_COLOR ?? "").trim().length > 0) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (process.env.TERM ?? "").toLowerCase() !== "dumb";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hexToRgb(hex: string): { red: number; green: number; blue: number } {
|
|
37
|
+
const normalized = hex.replace(/^#/, "");
|
|
38
|
+
return {
|
|
39
|
+
red: Number.parseInt(normalized.slice(0, 2), 16),
|
|
40
|
+
green: Number.parseInt(normalized.slice(2, 4), 16),
|
|
41
|
+
blue: Number.parseInt(normalized.slice(4, 6), 16),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Applies one Minecraft-style truecolor swatch to a string.
|
|
47
|
+
*
|
|
48
|
+
* @param code The legacy Minecraft color code to emulate.
|
|
49
|
+
* @param value The text to colorize.
|
|
50
|
+
* @returns The styled string, or the original text when colors are disabled.
|
|
51
|
+
* @remarks This uses truecolor ANSI so the palette stays stable across modern terminals.
|
|
52
|
+
* @lastModified 2026-03-19
|
|
53
|
+
* @author GPT-5.4
|
|
54
|
+
*/
|
|
55
|
+
export function mc(code: MinecraftColorCode, value: string): string {
|
|
56
|
+
if (!supportsCliColor()) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { red, green, blue } = hexToRgb(MINECRAFT_HEX[code]);
|
|
61
|
+
return `\u001b[38;2;${red};${green};${blue}m${value}\u001b[39m`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Styles one primary CLI label.
|
|
66
|
+
*
|
|
67
|
+
* @param value The label text.
|
|
68
|
+
* @returns The highlighted label.
|
|
69
|
+
* @remarks Gold is used as the main CLI accent color.
|
|
70
|
+
* @lastModified 2026-03-19
|
|
71
|
+
* @author GPT-5.4
|
|
72
|
+
*/
|
|
73
|
+
export function accent(value: string): string {
|
|
74
|
+
return mc("6", value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Styles one positive/success string.
|
|
79
|
+
*
|
|
80
|
+
* @param value The text to colorize.
|
|
81
|
+
* @returns The success-colored string.
|
|
82
|
+
* @remarks Green is used for successful actions and positive counts.
|
|
83
|
+
* @lastModified 2026-03-19
|
|
84
|
+
* @author GPT-5.4
|
|
85
|
+
*/
|
|
86
|
+
export function success(value: string): string {
|
|
87
|
+
return mc("a", value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Styles one informational string.
|
|
92
|
+
*
|
|
93
|
+
* @param value The text to colorize.
|
|
94
|
+
* @returns The info-colored string.
|
|
95
|
+
* @remarks Aqua is used for list metadata and secondary highlights.
|
|
96
|
+
* @lastModified 2026-03-19
|
|
97
|
+
* @author GPT-5.4
|
|
98
|
+
*/
|
|
99
|
+
export function info(value: string): string {
|
|
100
|
+
return mc("b", value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Styles one warning string.
|
|
105
|
+
*
|
|
106
|
+
* @param value The text to colorize.
|
|
107
|
+
* @returns The warning-colored string.
|
|
108
|
+
* @remarks Bright yellow is used for cautionary or count summary output.
|
|
109
|
+
* @lastModified 2026-03-19
|
|
110
|
+
* @author GPT-5.4
|
|
111
|
+
*/
|
|
112
|
+
export function warning(value: string): string {
|
|
113
|
+
return mc("e", value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Styles one muted metadata string.
|
|
118
|
+
*
|
|
119
|
+
* @param value The text to colorize.
|
|
120
|
+
* @returns The muted string.
|
|
121
|
+
* @remarks Gray is used for low-emphasis metadata like slugs and file paths.
|
|
122
|
+
* @lastModified 2026-03-19
|
|
123
|
+
* @author GPT-5.4
|
|
124
|
+
*/
|
|
125
|
+
export function muted(value: string): string {
|
|
126
|
+
return mc("7", value);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Styles one subtle low-contrast string.
|
|
131
|
+
*
|
|
132
|
+
* @param value The text to colorize.
|
|
133
|
+
* @returns The subtle string.
|
|
134
|
+
* @remarks Dark gray is used for separators, labels, and empty-state output.
|
|
135
|
+
* @lastModified 2026-03-19
|
|
136
|
+
* @author GPT-5.4
|
|
137
|
+
*/
|
|
138
|
+
export function subtle(value: string): string {
|
|
139
|
+
return mc("8", value);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Styles one failure/error string.
|
|
144
|
+
*
|
|
145
|
+
* @param value The text to colorize.
|
|
146
|
+
* @returns The danger-colored string.
|
|
147
|
+
* @remarks Red is reserved for errors and destructive outcomes.
|
|
148
|
+
* @lastModified 2026-03-19
|
|
149
|
+
* @author GPT-5.4
|
|
150
|
+
*/
|
|
151
|
+
export function danger(value: string): string {
|
|
152
|
+
return mc("c", value);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Styles one prominent value with bold emphasis.
|
|
157
|
+
*
|
|
158
|
+
* @param value The text to emphasize.
|
|
159
|
+
* @returns The bold string.
|
|
160
|
+
* @remarks This intentionally leaves color choice to the caller.
|
|
161
|
+
* @lastModified 2026-03-19
|
|
162
|
+
* @author GPT-5.4
|
|
163
|
+
*/
|
|
164
|
+
export function strong(value: string): string {
|
|
165
|
+
return pc.bold(value);
|
|
166
|
+
}
|