@barekey/cli 0.5.1 → 0.5.2

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/bun.lock CHANGED
@@ -8,6 +8,7 @@
8
8
  "@clack/prompts": "^0.11.0",
9
9
  "commander": "^14.0.1",
10
10
  "effect": "3.19.19",
11
+ "jimp": "^1.6.0",
11
12
  "open": "^10.2.0",
12
13
  "picocolors": "^1.1.1",
13
14
  },
@@ -22,10 +23,80 @@
22
23
 
23
24
  "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
24
25
 
26
+ "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="],
27
+
28
+ "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="],
29
+
30
+ "@jimp/file-ops": ["@jimp/file-ops@1.6.0", "", {}, "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ=="],
31
+
32
+ "@jimp/js-bmp": ["@jimp/js-bmp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "bmp-ts": "^1.0.9" } }, "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw=="],
33
+
34
+ "@jimp/js-gif": ["@jimp/js-gif@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "gifwrap": "^0.10.1", "omggif": "^1.0.10" } }, "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g=="],
35
+
36
+ "@jimp/js-jpeg": ["@jimp/js-jpeg@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "jpeg-js": "^0.4.4" } }, "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA=="],
37
+
38
+ "@jimp/js-png": ["@jimp/js-png@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "pngjs": "^7.0.0" } }, "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg=="],
39
+
40
+ "@jimp/js-tiff": ["@jimp/js-tiff@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "utif2": "^4.1.0" } }, "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw=="],
41
+
42
+ "@jimp/plugin-blit": ["@jimp/plugin-blit@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA=="],
43
+
44
+ "@jimp/plugin-blur": ["@jimp/plugin-blur@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw=="],
45
+
46
+ "@jimp/plugin-circle": ["@jimp/plugin-circle@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw=="],
47
+
48
+ "@jimp/plugin-color": ["@jimp/plugin-color@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "tinycolor2": "^1.6.0", "zod": "^3.23.8" } }, "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA=="],
49
+
50
+ "@jimp/plugin-contain": ["@jimp/plugin-contain@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ=="],
51
+
52
+ "@jimp/plugin-cover": ["@jimp/plugin-cover@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA=="],
53
+
54
+ "@jimp/plugin-crop": ["@jimp/plugin-crop@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang=="],
55
+
56
+ "@jimp/plugin-displace": ["@jimp/plugin-displace@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q=="],
57
+
58
+ "@jimp/plugin-dither": ["@jimp/plugin-dither@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0" } }, "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ=="],
59
+
60
+ "@jimp/plugin-fisheye": ["@jimp/plugin-fisheye@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA=="],
61
+
62
+ "@jimp/plugin-flip": ["@jimp/plugin-flip@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg=="],
63
+
64
+ "@jimp/plugin-hash": ["@jimp/plugin-hash@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "any-base": "^1.1.0" } }, "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q=="],
65
+
66
+ "@jimp/plugin-mask": ["@jimp/plugin-mask@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA=="],
67
+
68
+ "@jimp/plugin-print": ["@jimp/plugin-print@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/types": "1.6.0", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", "simple-xml-to-json": "^1.2.2", "zod": "^3.23.8" } }, "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A=="],
69
+
70
+ "@jimp/plugin-quantize": ["@jimp/plugin-quantize@1.6.0", "", { "dependencies": { "image-q": "^4.0.0", "zod": "^3.23.8" } }, "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg=="],
71
+
72
+ "@jimp/plugin-resize": ["@jimp/plugin-resize@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/types": "1.6.0", "zod": "^3.23.8" } }, "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA=="],
73
+
74
+ "@jimp/plugin-rotate": ["@jimp/plugin-rotate@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw=="],
75
+
76
+ "@jimp/plugin-threshold": ["@jimp/plugin-threshold@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "zod": "^3.23.8" } }, "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w=="],
77
+
78
+ "@jimp/types": ["@jimp/types@1.6.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg=="],
79
+
80
+ "@jimp/utils": ["@jimp/utils@1.6.0", "", { "dependencies": { "@jimp/types": "1.6.0", "tinycolor2": "^1.6.0" } }, "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA=="],
81
+
25
82
  "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
26
83
 
84
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
85
+
27
86
  "@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
28
87
 
88
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
89
+
90
+ "any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="],
91
+
92
+ "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="],
93
+
94
+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
95
+
96
+ "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="],
97
+
98
+ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
99
+
29
100
  "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
30
101
 
31
102
  "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
@@ -38,28 +109,98 @@
38
109
 
39
110
  "effect": ["effect@3.19.19", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-Yc8U/SVXo2dHnaP7zNBlAo83h/nzSJpi7vph6Hzyl4ulgMBIgPmz3UzOjb9sBgpFE00gC0iETR244sfXDNLHRg=="],
40
111
 
112
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
113
+
114
+ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
115
+
116
+ "exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="],
117
+
41
118
  "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
42
119
 
120
+ "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
121
+
122
+ "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
123
+
124
+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
125
+
126
+ "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
127
+
43
128
  "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
44
129
 
45
130
  "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
46
131
 
47
132
  "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
48
133
 
134
+ "jimp": ["jimp@1.6.0", "", { "dependencies": { "@jimp/core": "1.6.0", "@jimp/diff": "1.6.0", "@jimp/js-bmp": "1.6.0", "@jimp/js-gif": "1.6.0", "@jimp/js-jpeg": "1.6.0", "@jimp/js-png": "1.6.0", "@jimp/js-tiff": "1.6.0", "@jimp/plugin-blit": "1.6.0", "@jimp/plugin-blur": "1.6.0", "@jimp/plugin-circle": "1.6.0", "@jimp/plugin-color": "1.6.0", "@jimp/plugin-contain": "1.6.0", "@jimp/plugin-cover": "1.6.0", "@jimp/plugin-crop": "1.6.0", "@jimp/plugin-displace": "1.6.0", "@jimp/plugin-dither": "1.6.0", "@jimp/plugin-fisheye": "1.6.0", "@jimp/plugin-flip": "1.6.0", "@jimp/plugin-hash": "1.6.0", "@jimp/plugin-mask": "1.6.0", "@jimp/plugin-print": "1.6.0", "@jimp/plugin-quantize": "1.6.0", "@jimp/plugin-resize": "1.6.0", "@jimp/plugin-rotate": "1.6.0", "@jimp/plugin-threshold": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0" } }, "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg=="],
135
+
136
+ "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
137
+
138
+ "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
139
+
140
+ "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
141
+
49
142
  "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
50
143
 
144
+ "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
145
+
146
+ "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
147
+
148
+ "parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="],
149
+
150
+ "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="],
151
+
152
+ "peek-readable": ["peek-readable@4.1.0", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="],
153
+
51
154
  "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
52
155
 
156
+ "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="],
157
+
158
+ "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="],
159
+
160
+ "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
161
+
53
162
  "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
54
163
 
164
+ "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
165
+
166
+ "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="],
167
+
55
168
  "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
56
169
 
170
+ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
171
+
172
+ "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
173
+
174
+ "simple-xml-to-json": ["simple-xml-to-json@1.2.4", "", {}, "sha512-3MY16e0ocMHL7N1ufpdObURGyX+lCo0T/A+y6VCwosLdH1HSda4QZl1Sdt/O+2qWp48WFi26XEp5rF0LoaL0Dg=="],
175
+
57
176
  "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
58
177
 
178
+ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
179
+
180
+ "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="],
181
+
182
+ "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
183
+
184
+ "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="],
185
+
59
186
  "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
60
187
 
61
188
  "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
62
189
 
190
+ "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
191
+
63
192
  "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
193
+
194
+ "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="],
195
+
196
+ "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
197
+
198
+ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
199
+
200
+ "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
201
+
202
+ "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="],
203
+
204
+ "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
64
205
  }
65
206
  }
@@ -8,6 +8,7 @@ import { buildSessionId } from "../context/session-id.js";
8
8
  import { clearConfig, deleteCredentials, saveConfig, saveCredentials, } from "../credentials-store.js";
9
9
  import { CliSessionResponseSchema, DevicePollResponseSchema, DeviceStartResponseSchema, RefreshResponseSchema, } from "../contracts/index.js";
10
10
  import { getJson, postJson } from "../http.js";
11
+ import { formatWhoamiWithAvatar } from "../output/avatar.js";
11
12
  import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
12
13
  function resolveClientName() {
13
14
  const configured = process.env.BAREKEY_CLIENT_NAME?.trim() ||
@@ -158,10 +159,11 @@ async function runWhoami(options) {
158
159
  toJsonOutput(true, session);
159
160
  return;
160
161
  }
161
- console.log(resolveDisplayName(session));
162
- if (session.email !== null) {
163
- console.log(session.email);
164
- }
162
+ console.log(await formatWhoamiWithAvatar({
163
+ displayName: resolveDisplayName(session),
164
+ email: session.email,
165
+ imageUrl: session.imageUrl,
166
+ }));
165
167
  }
166
168
  export function registerAuthCommands(program) {
167
169
  const auth = program.command("auth").description("Authentication commands");
@@ -7,11 +7,11 @@
7
7
  * @author GPT-5.4
8
8
  */
9
9
  export declare function listOrganizationsForCurrentUser(): Promise<readonly {
10
+ readonly imageUrl: string;
10
11
  readonly name: string;
11
12
  readonly id: string;
12
13
  readonly slug: string;
13
14
  readonly role: string;
14
- readonly imageUrl: string;
15
15
  }[]>;
16
16
  /**
17
17
  * Loads projects for one organization.
@@ -53,6 +53,7 @@ export declare const CliSessionResponseSchema: Schema.Struct<{
53
53
  clerkUserId: typeof Schema.String;
54
54
  displayName: Schema.NullOr<typeof Schema.String>;
55
55
  email: Schema.NullOr<typeof Schema.String>;
56
+ imageUrl: Schema.NullOr<typeof Schema.String>;
56
57
  orgId: typeof Schema.String;
57
58
  orgSlug: typeof Schema.String;
58
59
  source: Schema.Literal<["clerk", "cli"]>;
@@ -55,6 +55,7 @@ export const CliSessionResponseSchema = Schema.Struct({
55
55
  clerkUserId: Schema.String,
56
56
  displayName: Schema.NullOr(Schema.String),
57
57
  email: Schema.NullOr(Schema.String),
58
+ imageUrl: Schema.NullOr(Schema.String),
58
59
  orgId: Schema.String,
59
60
  orgSlug: Schema.String,
60
61
  source: Schema.Literal("clerk", "cli"),
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
3
+ *
4
+ * @param input The display name, email, and optional avatar image URL.
5
+ * @returns The formatted `whoami` string ready for stdout.
6
+ * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
7
+ * @lastModified 2026-03-19
8
+ * @author GPT-5.4
9
+ */
10
+ export declare function formatWhoamiWithAvatar(input: {
11
+ displayName: string;
12
+ email: string | null;
13
+ imageUrl: string | null;
14
+ }): Promise<string>;
@@ -0,0 +1,114 @@
1
+ import { Jimp } from "jimp";
2
+ const DEFAULT_AVATAR_COLUMNS = 10;
3
+ const DEFAULT_AVATAR_ROWS = 8;
4
+ function supportsAnsiAvatar() {
5
+ if (!process.stdout.isTTY) {
6
+ return false;
7
+ }
8
+ if ((process.env.NO_COLOR ?? "").trim().length > 0) {
9
+ return false;
10
+ }
11
+ const term = (process.env.TERM ?? "").toLowerCase();
12
+ if (term === "dumb") {
13
+ return false;
14
+ }
15
+ return true;
16
+ }
17
+ function pixelOffset(width, x, y) {
18
+ return (y * width + x) * 4;
19
+ }
20
+ function readPixel(data, width, x, y) {
21
+ const offset = pixelOffset(width, x, y);
22
+ return {
23
+ red: data[offset] ?? 0,
24
+ green: data[offset + 1] ?? 0,
25
+ blue: data[offset + 2] ?? 0,
26
+ alpha: data[offset + 3] ?? 0,
27
+ };
28
+ }
29
+ function isOpaque(pixel) {
30
+ return pixel.alpha >= 96;
31
+ }
32
+ function foregroundColor(pixel) {
33
+ return `\u001b[38;2;${pixel.red};${pixel.green};${pixel.blue}m`;
34
+ }
35
+ function backgroundColor(pixel) {
36
+ return `\u001b[48;2;${pixel.red};${pixel.green};${pixel.blue}m`;
37
+ }
38
+ function renderPixelPair(top, bottom) {
39
+ const topOpaque = isOpaque(top);
40
+ const bottomOpaque = isOpaque(bottom);
41
+ if (!topOpaque && !bottomOpaque) {
42
+ return " ";
43
+ }
44
+ if (topOpaque && bottomOpaque) {
45
+ return `${foregroundColor(top)}${backgroundColor(bottom)}▀`;
46
+ }
47
+ if (topOpaque) {
48
+ return `${foregroundColor(top)}▀`;
49
+ }
50
+ return `${foregroundColor(bottom)}▄`;
51
+ }
52
+ async function fetchAvatarBuffer(imageUrl) {
53
+ const response = await fetch(imageUrl, {
54
+ signal: AbortSignal.timeout(4_000),
55
+ });
56
+ if (!response.ok) {
57
+ throw new Error(`Avatar request failed with HTTP ${response.status}.`);
58
+ }
59
+ return Buffer.from(await response.arrayBuffer());
60
+ }
61
+ async function renderAvatarLines(imageUrl) {
62
+ const bitmapRows = DEFAULT_AVATAR_ROWS * 2;
63
+ const buffer = await fetchAvatarBuffer(imageUrl);
64
+ const image = await Jimp.read(buffer);
65
+ image.cover({
66
+ w: DEFAULT_AVATAR_COLUMNS,
67
+ h: bitmapRows,
68
+ });
69
+ image.circle();
70
+ const { data, width, height } = image.bitmap;
71
+ const lines = [];
72
+ for (let y = 0; y < height; y += 2) {
73
+ let line = "";
74
+ for (let x = 0; x < width; x += 1) {
75
+ const top = readPixel(data, width, x, y);
76
+ const bottom = readPixel(data, width, x, Math.min(y + 1, height - 1));
77
+ line += renderPixelPair(top, bottom);
78
+ }
79
+ lines.push(`${line}\u001b[0m`);
80
+ }
81
+ return lines;
82
+ }
83
+ function joinAvatarAndText(avatarLines, textLines) {
84
+ const totalRows = Math.max(avatarLines.length, textLines.length);
85
+ const renderedRows = [];
86
+ for (let index = 0; index < totalRows; index += 1) {
87
+ const avatarLine = avatarLines[index] ?? " ".repeat(DEFAULT_AVATAR_COLUMNS);
88
+ const textLine = textLines[index] ?? "";
89
+ renderedRows.push(textLine.length > 0 ? `${avatarLine} ${textLine}` : avatarLine);
90
+ }
91
+ return renderedRows.join("\n");
92
+ }
93
+ /**
94
+ * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
95
+ *
96
+ * @param input The display name, email, and optional avatar image URL.
97
+ * @returns The formatted `whoami` string ready for stdout.
98
+ * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
99
+ * @lastModified 2026-03-19
100
+ * @author GPT-5.4
101
+ */
102
+ export async function formatWhoamiWithAvatar(input) {
103
+ const textLines = [`Signed in as ${input.displayName}.`, input.email ?? ""].filter((line) => line.length > 0);
104
+ if (!supportsAnsiAvatar() || input.imageUrl === null) {
105
+ return textLines.join("\n");
106
+ }
107
+ try {
108
+ const avatarLines = await renderAvatarLines(input.imageUrl);
109
+ return joinAvatarAndText(avatarLines, textLines);
110
+ }
111
+ catch {
112
+ return textLines.join("\n");
113
+ }
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barekey/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Barekey command line interface",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,6 +17,7 @@
17
17
  "@clack/prompts": "^0.11.0",
18
18
  "commander": "^14.0.1",
19
19
  "effect": "3.19.19",
20
+ "jimp": "^1.6.0",
20
21
  "open": "^10.2.0",
21
22
  "picocolors": "^1.1.1"
22
23
  },
@@ -21,6 +21,7 @@ import {
21
21
  RefreshResponseSchema,
22
22
  } from "../contracts/index.js";
23
23
  import { getJson, postJson } from "../http.js";
24
+ import { formatWhoamiWithAvatar } from "../output/avatar.js";
24
25
  import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
25
26
 
26
27
  function resolveClientName(): string | undefined {
@@ -196,10 +197,13 @@ async function runWhoami(options: { json?: boolean }): Promise<void> {
196
197
  return;
197
198
  }
198
199
 
199
- console.log(resolveDisplayName(session));
200
- if (session.email !== null) {
201
- console.log(session.email);
202
- }
200
+ console.log(
201
+ await formatWhoamiWithAvatar({
202
+ displayName: resolveDisplayName(session),
203
+ email: session.email,
204
+ imageUrl: session.imageUrl,
205
+ }),
206
+ );
203
207
  }
204
208
 
205
209
  export function registerAuthCommands(program: Command): void {
@@ -77,6 +77,7 @@ export const CliSessionResponseSchema = Schema.Struct({
77
77
  clerkUserId: Schema.String,
78
78
  displayName: Schema.NullOr(Schema.String),
79
79
  email: Schema.NullOr(Schema.String),
80
+ imageUrl: Schema.NullOr(Schema.String),
80
81
  orgId: Schema.String,
81
82
  orgSlug: Schema.String,
82
83
  source: Schema.Literal("clerk", "cli"),
@@ -0,0 +1,157 @@
1
+ import { Jimp } from "jimp";
2
+
3
+ const DEFAULT_AVATAR_COLUMNS = 10;
4
+ const DEFAULT_AVATAR_ROWS = 8;
5
+
6
+ type Rgba = {
7
+ red: number;
8
+ green: number;
9
+ blue: number;
10
+ alpha: number;
11
+ };
12
+
13
+ function supportsAnsiAvatar(): boolean {
14
+ if (!process.stdout.isTTY) {
15
+ return false;
16
+ }
17
+
18
+ if ((process.env.NO_COLOR ?? "").trim().length > 0) {
19
+ return false;
20
+ }
21
+
22
+ const term = (process.env.TERM ?? "").toLowerCase();
23
+ if (term === "dumb") {
24
+ return false;
25
+ }
26
+
27
+ return true;
28
+ }
29
+
30
+ function pixelOffset(width: number, x: number, y: number): number {
31
+ return (y * width + x) * 4;
32
+ }
33
+
34
+ function readPixel(data: Buffer, width: number, x: number, y: number): Rgba {
35
+ const offset = pixelOffset(width, x, y);
36
+ return {
37
+ red: data[offset] ?? 0,
38
+ green: data[offset + 1] ?? 0,
39
+ blue: data[offset + 2] ?? 0,
40
+ alpha: data[offset + 3] ?? 0,
41
+ };
42
+ }
43
+
44
+ function isOpaque(pixel: Rgba): boolean {
45
+ return pixel.alpha >= 96;
46
+ }
47
+
48
+ function foregroundColor(pixel: Rgba): string {
49
+ return `\u001b[38;2;${pixel.red};${pixel.green};${pixel.blue}m`;
50
+ }
51
+
52
+ function backgroundColor(pixel: Rgba): string {
53
+ return `\u001b[48;2;${pixel.red};${pixel.green};${pixel.blue}m`;
54
+ }
55
+
56
+ function renderPixelPair(top: Rgba, bottom: Rgba): string {
57
+ const topOpaque = isOpaque(top);
58
+ const bottomOpaque = isOpaque(bottom);
59
+
60
+ if (!topOpaque && !bottomOpaque) {
61
+ return " ";
62
+ }
63
+
64
+ if (topOpaque && bottomOpaque) {
65
+ return `${foregroundColor(top)}${backgroundColor(bottom)}▀`;
66
+ }
67
+
68
+ if (topOpaque) {
69
+ return `${foregroundColor(top)}▀`;
70
+ }
71
+
72
+ return `${foregroundColor(bottom)}▄`;
73
+ }
74
+
75
+ async function fetchAvatarBuffer(imageUrl: string): Promise<Buffer> {
76
+ const response = await fetch(imageUrl, {
77
+ signal: AbortSignal.timeout(4_000),
78
+ });
79
+ if (!response.ok) {
80
+ throw new Error(`Avatar request failed with HTTP ${response.status}.`);
81
+ }
82
+
83
+ return Buffer.from(await response.arrayBuffer());
84
+ }
85
+
86
+ async function renderAvatarLines(imageUrl: string): Promise<Array<string>> {
87
+ const bitmapRows = DEFAULT_AVATAR_ROWS * 2;
88
+ const buffer = await fetchAvatarBuffer(imageUrl);
89
+ const image = await Jimp.read(buffer);
90
+
91
+ image.cover({
92
+ w: DEFAULT_AVATAR_COLUMNS,
93
+ h: bitmapRows,
94
+ });
95
+ image.circle();
96
+
97
+ const { data, width, height } = image.bitmap;
98
+ const lines: Array<string> = [];
99
+
100
+ for (let y = 0; y < height; y += 2) {
101
+ let line = "";
102
+ for (let x = 0; x < width; x += 1) {
103
+ const top = readPixel(data, width, x, y);
104
+ const bottom = readPixel(data, width, x, Math.min(y + 1, height - 1));
105
+ line += renderPixelPair(top, bottom);
106
+ }
107
+ lines.push(`${line}\u001b[0m`);
108
+ }
109
+
110
+ return lines;
111
+ }
112
+
113
+ function joinAvatarAndText(
114
+ avatarLines: Array<string>,
115
+ textLines: Array<string>,
116
+ ): string {
117
+ const totalRows = Math.max(avatarLines.length, textLines.length);
118
+ const renderedRows: Array<string> = [];
119
+
120
+ for (let index = 0; index < totalRows; index += 1) {
121
+ const avatarLine = avatarLines[index] ?? " ".repeat(DEFAULT_AVATAR_COLUMNS);
122
+ const textLine = textLines[index] ?? "";
123
+ renderedRows.push(textLine.length > 0 ? `${avatarLine} ${textLine}` : avatarLine);
124
+ }
125
+
126
+ return renderedRows.join("\n");
127
+ }
128
+
129
+ /**
130
+ * Builds the terminal `whoami` output with a tiny ANSI avatar when supported.
131
+ *
132
+ * @param input The display name, email, and optional avatar image URL.
133
+ * @returns The formatted `whoami` string ready for stdout.
134
+ * @remarks Non-TTY terminals and avatar failures fall back to plain text without throwing.
135
+ * @lastModified 2026-03-19
136
+ * @author GPT-5.4
137
+ */
138
+ export async function formatWhoamiWithAvatar(input: {
139
+ displayName: string;
140
+ email: string | null;
141
+ imageUrl: string | null;
142
+ }): Promise<string> {
143
+ const textLines = [`Signed in as ${input.displayName}.`, input.email ?? ""].filter(
144
+ (line) => line.length > 0,
145
+ );
146
+
147
+ if (!supportsAnsiAvatar() || input.imageUrl === null) {
148
+ return textLines.join("\n");
149
+ }
150
+
151
+ try {
152
+ const avatarLines = await renderAvatarLines(input.imageUrl);
153
+ return joinAvatarAndText(avatarLines, textLines);
154
+ } catch {
155
+ return textLines.join("\n");
156
+ }
157
+ }