@barekey/cli 0.5.3 → 0.5.6

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.
@@ -162,6 +162,7 @@ async function runWhoami(options) {
162
162
  console.log(await formatWhoamiWithAvatar({
163
163
  displayName: resolveDisplayName(session),
164
164
  email: session.email,
165
+ plan: session.plan,
165
166
  imageUrl: session.imageUrl,
166
167
  }));
167
168
  }
@@ -54,6 +54,7 @@ export declare const CliSessionResponseSchema: Schema.Struct<{
54
54
  displayName: Schema.NullOr<typeof Schema.String>;
55
55
  email: Schema.NullOr<typeof Schema.String>;
56
56
  imageUrl: Schema.NullOr<typeof Schema.String>;
57
+ plan: Schema.NullOr<Schema.Literal<["free", "pro", "max"]>>;
57
58
  orgId: typeof Schema.String;
58
59
  orgSlug: typeof Schema.String;
59
60
  source: Schema.Literal<["clerk", "cli"]>;
@@ -56,6 +56,7 @@ export const CliSessionResponseSchema = Schema.Struct({
56
56
  displayName: Schema.NullOr(Schema.String),
57
57
  email: Schema.NullOr(Schema.String),
58
58
  imageUrl: Schema.NullOr(Schema.String),
59
+ plan: Schema.NullOr(Schema.Literal("free", "pro", "max")),
59
60
  orgId: Schema.String,
60
61
  orgSlug: Schema.String,
61
62
  source: Schema.Literal("clerk", "cli"),
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
3
3
  *
4
- * @param input The display name, email, and optional avatar image URL.
4
+ * @param input The display name, email, plan, and optional avatar image URL.
5
5
  * @returns The formatted `whoami` string ready for stdout.
6
6
  * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
7
7
  * @lastModified 2026-03-19
@@ -10,5 +10,6 @@
10
10
  export declare function formatWhoamiWithAvatar(input: {
11
11
  displayName: string;
12
12
  email: string | null;
13
+ plan: "free" | "pro" | "max" | null;
13
14
  imageUrl: string | null;
14
15
  }): Promise<string>;
@@ -1,6 +1,7 @@
1
1
  import { Jimp } from "jimp";
2
+ import pc from "picocolors";
2
3
  const DEFAULT_AVATAR_COLUMNS = 10;
3
- const DEFAULT_AVATAR_ROWS = 8;
4
+ const DEFAULT_AVATAR_ROWS = 5;
4
5
  function supportsAnsiAvatar() {
5
6
  if (!process.stdout.isTTY) {
6
7
  return false;
@@ -66,7 +67,6 @@ async function renderAvatarLines(imageUrl) {
66
67
  w: DEFAULT_AVATAR_COLUMNS,
67
68
  h: bitmapRows,
68
69
  });
69
- image.circle();
70
70
  const { data, width, height } = image.bitmap;
71
71
  const lines = [];
72
72
  for (let y = 0; y < height; y += 2) {
@@ -78,38 +78,55 @@ async function renderAvatarLines(imageUrl) {
78
78
  }
79
79
  lines.push(`${line}\u001b[0m`);
80
80
  }
81
- while (lines.length > 0 && isBlankAvatarLine(lines[0] ?? "")) {
82
- lines.shift();
83
- }
84
- while (lines.length > 0 && isBlankAvatarLine(lines[lines.length - 1] ?? "")) {
85
- lines.pop();
86
- }
87
81
  return lines;
88
82
  }
89
- function isBlankAvatarLine(line) {
90
- return line.replace(/\u001b\[[0-9;]*m/g, "").trim().length === 0;
91
- }
92
83
  function joinAvatarAndText(avatarLines, textLines) {
93
84
  const totalRows = Math.max(avatarLines.length, textLines.length);
85
+ const textOffset = avatarLines.length > textLines.length
86
+ ? Math.floor((avatarLines.length - textLines.length) / 2)
87
+ : 0;
94
88
  const renderedRows = [];
95
89
  for (let index = 0; index < totalRows; index += 1) {
96
90
  const avatarLine = avatarLines[index] ?? " ".repeat(DEFAULT_AVATAR_COLUMNS);
97
- const textLine = textLines[index] ?? "";
91
+ const textIndex = index - textOffset;
92
+ const textLine = textIndex >= 0 && textIndex < textLines.length ? (textLines[textIndex] ?? "") : "";
98
93
  renderedRows.push(textLine.length > 0 ? `${avatarLine} ${textLine}` : avatarLine);
99
94
  }
100
95
  return renderedRows.join("\n");
101
96
  }
97
+ function formatPlanLabel(plan) {
98
+ if (plan === null) {
99
+ return "No plan";
100
+ }
101
+ if (plan === "free") {
102
+ return "Free";
103
+ }
104
+ if (plan === "pro") {
105
+ return "Pro";
106
+ }
107
+ return "Max";
108
+ }
102
109
  /**
103
110
  * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
104
111
  *
105
- * @param input The display name, email, and optional avatar image URL.
112
+ * @param input The display name, email, plan, and optional avatar image URL.
106
113
  * @returns The formatted `whoami` string ready for stdout.
107
114
  * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
108
115
  * @lastModified 2026-03-19
109
116
  * @author GPT-5.4
110
117
  */
111
118
  export async function formatWhoamiWithAvatar(input) {
112
- const textLines = [`Signed in as ${input.displayName}.`, input.email ?? ""].filter((line) => line.length > 0);
119
+ const firstLine = `Signed in as ${pc.bold(input.displayName)}`;
120
+ const secondSegments = [];
121
+ if (input.email !== null && input.email.length > 0) {
122
+ secondSegments.push(pc.gray(input.email));
123
+ }
124
+ const formattedPlan = pc.yellow(formatPlanLabel(input.plan));
125
+ if (secondSegments.length > 0) {
126
+ secondSegments.push(pc.dim("•"));
127
+ }
128
+ secondSegments.push(formattedPlan);
129
+ const textLines = [firstLine, secondSegments.join(" ")].filter((line) => line.length > 0);
113
130
  if (!supportsAnsiAvatar() || input.imageUrl === null) {
114
131
  return textLines.join("\n");
115
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barekey/cli",
3
- "version": "0.5.3",
3
+ "version": "0.5.6",
4
4
  "description": "Barekey command line interface",
5
5
  "type": "module",
6
6
  "bin": {
@@ -201,6 +201,7 @@ async function runWhoami(options: { json?: boolean }): Promise<void> {
201
201
  await formatWhoamiWithAvatar({
202
202
  displayName: resolveDisplayName(session),
203
203
  email: session.email,
204
+ plan: session.plan,
204
205
  imageUrl: session.imageUrl,
205
206
  }),
206
207
  );
@@ -78,6 +78,7 @@ export const CliSessionResponseSchema = Schema.Struct({
78
78
  displayName: Schema.NullOr(Schema.String),
79
79
  email: Schema.NullOr(Schema.String),
80
80
  imageUrl: Schema.NullOr(Schema.String),
81
+ plan: Schema.NullOr(Schema.Literal("free", "pro", "max")),
81
82
  orgId: Schema.String,
82
83
  orgSlug: Schema.String,
83
84
  source: Schema.Literal("clerk", "cli"),
@@ -1,7 +1,8 @@
1
1
  import { Jimp } from "jimp";
2
+ import pc from "picocolors";
2
3
 
3
4
  const DEFAULT_AVATAR_COLUMNS = 10;
4
- const DEFAULT_AVATAR_ROWS = 8;
5
+ const DEFAULT_AVATAR_ROWS = 5;
5
6
 
6
7
  type Rgba = {
7
8
  red: number;
@@ -92,7 +93,6 @@ async function renderAvatarLines(imageUrl: string): Promise<Array<string>> {
92
93
  w: DEFAULT_AVATAR_COLUMNS,
93
94
  h: bitmapRows,
94
95
  });
95
- image.circle();
96
96
 
97
97
  const { data, width, height } = image.bitmap;
98
98
  const lines: Array<string> = [];
@@ -107,40 +107,51 @@ async function renderAvatarLines(imageUrl: string): Promise<Array<string>> {
107
107
  lines.push(`${line}\u001b[0m`);
108
108
  }
109
109
 
110
- while (lines.length > 0 && isBlankAvatarLine(lines[0] ?? "")) {
111
- lines.shift();
112
- }
113
- while (lines.length > 0 && isBlankAvatarLine(lines[lines.length - 1] ?? "")) {
114
- lines.pop();
115
- }
116
-
117
110
  return lines;
118
111
  }
119
112
 
120
- function isBlankAvatarLine(line: string): boolean {
121
- return line.replace(/\u001b\[[0-9;]*m/g, "").trim().length === 0;
122
- }
123
-
124
113
  function joinAvatarAndText(
125
114
  avatarLines: Array<string>,
126
115
  textLines: Array<string>,
127
116
  ): string {
128
117
  const totalRows = Math.max(avatarLines.length, textLines.length);
118
+ const textOffset =
119
+ avatarLines.length > textLines.length
120
+ ? Math.floor((avatarLines.length - textLines.length) / 2)
121
+ : 0;
129
122
  const renderedRows: Array<string> = [];
130
123
 
131
124
  for (let index = 0; index < totalRows; index += 1) {
132
125
  const avatarLine = avatarLines[index] ?? " ".repeat(DEFAULT_AVATAR_COLUMNS);
133
- const textLine = textLines[index] ?? "";
126
+ const textIndex = index - textOffset;
127
+ const textLine =
128
+ textIndex >= 0 && textIndex < textLines.length ? (textLines[textIndex] ?? "") : "";
134
129
  renderedRows.push(textLine.length > 0 ? `${avatarLine} ${textLine}` : avatarLine);
135
130
  }
136
131
 
137
132
  return renderedRows.join("\n");
138
133
  }
139
134
 
135
+ function formatPlanLabel(plan: "free" | "pro" | "max" | null): string {
136
+ if (plan === null) {
137
+ return "No plan";
138
+ }
139
+
140
+ if (plan === "free") {
141
+ return "Free";
142
+ }
143
+
144
+ if (plan === "pro") {
145
+ return "Pro";
146
+ }
147
+
148
+ return "Max";
149
+ }
150
+
140
151
  /**
141
152
  * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
142
153
  *
143
- * @param input The display name, email, and optional avatar image URL.
154
+ * @param input The display name, email, plan, and optional avatar image URL.
144
155
  * @returns The formatted `whoami` string ready for stdout.
145
156
  * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
146
157
  * @lastModified 2026-03-19
@@ -149,9 +160,23 @@ function joinAvatarAndText(
149
160
  export async function formatWhoamiWithAvatar(input: {
150
161
  displayName: string;
151
162
  email: string | null;
163
+ plan: "free" | "pro" | "max" | null;
152
164
  imageUrl: string | null;
153
165
  }): Promise<string> {
154
- const textLines = [`Signed in as ${input.displayName}.`, input.email ?? ""].filter(
166
+ const firstLine = `Signed in as ${pc.bold(input.displayName)}`;
167
+ const secondSegments: Array<string> = [];
168
+
169
+ if (input.email !== null && input.email.length > 0) {
170
+ secondSegments.push(pc.gray(input.email));
171
+ }
172
+
173
+ const formattedPlan = pc.yellow(formatPlanLabel(input.plan));
174
+ if (secondSegments.length > 0) {
175
+ secondSegments.push(pc.dim("•"));
176
+ }
177
+ secondSegments.push(formattedPlan);
178
+
179
+ const textLines = [firstLine, secondSegments.join(" ")].filter(
155
180
  (line) => line.length > 0,
156
181
  );
157
182