domsniper 0.1.0

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.
Files changed (63) hide show
  1. package/.env.example +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +246 -0
  4. package/package.json +72 -0
  5. package/src/app.tsx +2062 -0
  6. package/src/completions.ts +65 -0
  7. package/src/core/db.ts +1313 -0
  8. package/src/core/features/asn-lookup.ts +91 -0
  9. package/src/core/features/backlinks.ts +83 -0
  10. package/src/core/features/blacklist-check.ts +67 -0
  11. package/src/core/features/cert-transparency.ts +87 -0
  12. package/src/core/features/config.ts +81 -0
  13. package/src/core/features/cors-check.ts +90 -0
  14. package/src/core/features/dns-details.ts +27 -0
  15. package/src/core/features/domain-age.ts +33 -0
  16. package/src/core/features/domain-suggest.ts +87 -0
  17. package/src/core/features/drop-catch.ts +159 -0
  18. package/src/core/features/email-security.ts +112 -0
  19. package/src/core/features/expiring-feed.ts +160 -0
  20. package/src/core/features/export.ts +74 -0
  21. package/src/core/features/filter.ts +96 -0
  22. package/src/core/features/http-probe.ts +46 -0
  23. package/src/core/features/marketplace.ts +69 -0
  24. package/src/core/features/path-scanner.ts +123 -0
  25. package/src/core/features/port-scanner.ts +132 -0
  26. package/src/core/features/portfolio-bulk.ts +125 -0
  27. package/src/core/features/portfolio-monitor.ts +214 -0
  28. package/src/core/features/portfolio.ts +98 -0
  29. package/src/core/features/price-compare.ts +39 -0
  30. package/src/core/features/rdap.ts +128 -0
  31. package/src/core/features/reverse-ip.ts +73 -0
  32. package/src/core/features/s3-export.ts +99 -0
  33. package/src/core/features/scoring.ts +121 -0
  34. package/src/core/features/security-headers.ts +162 -0
  35. package/src/core/features/session.ts +74 -0
  36. package/src/core/features/snipe.ts +264 -0
  37. package/src/core/features/social-check.ts +81 -0
  38. package/src/core/features/ssl-check.ts +88 -0
  39. package/src/core/features/subdomain-discovery.ts +53 -0
  40. package/src/core/features/takeover-detect.ts +143 -0
  41. package/src/core/features/tech-stack.ts +135 -0
  42. package/src/core/features/tld-expand.ts +43 -0
  43. package/src/core/features/variations.ts +134 -0
  44. package/src/core/features/version-check.ts +58 -0
  45. package/src/core/features/waf-detect.ts +171 -0
  46. package/src/core/features/watch.ts +120 -0
  47. package/src/core/features/wayback.ts +64 -0
  48. package/src/core/features/webhooks.ts +126 -0
  49. package/src/core/features/whois-history.ts +99 -0
  50. package/src/core/features/zone-transfer.ts +75 -0
  51. package/src/core/index.ts +50 -0
  52. package/src/core/paths.ts +9 -0
  53. package/src/core/registrar.ts +413 -0
  54. package/src/core/theme.ts +140 -0
  55. package/src/core/types.ts +143 -0
  56. package/src/core/validate.ts +58 -0
  57. package/src/core/whois.ts +265 -0
  58. package/src/index.tsx +1888 -0
  59. package/src/market-client.ts +186 -0
  60. package/src/proxy/ca.ts +116 -0
  61. package/src/proxy/db.ts +175 -0
  62. package/src/proxy/server.ts +155 -0
  63. package/tsconfig.json +30 -0
@@ -0,0 +1,50 @@
1
+ // Core modules
2
+ export * from "./types.js";
3
+ export * from "./validate.js";
4
+ export * from "./db.js";
5
+ export * from "./whois.js";
6
+ export * from "./registrar.js";
7
+ export * from "./theme.js";
8
+ export * from "./paths.js";
9
+
10
+ // Features
11
+ export * from "./features/dns-details.js";
12
+ export * from "./features/http-probe.js";
13
+ export * from "./features/wayback.js";
14
+ export * from "./features/domain-age.js";
15
+ export * from "./features/scoring.js";
16
+ export * from "./features/filter.js";
17
+ export * from "./features/export.js";
18
+ export * from "./features/session.js";
19
+ export * from "./features/tld-expand.js";
20
+ export * from "./features/variations.js";
21
+ export * from "./features/watch.js";
22
+ export * from "./features/config.js";
23
+ export * from "./features/rdap.js";
24
+ export * from "./features/ssl-check.js";
25
+ export * from "./features/subdomain-discovery.js";
26
+ export * from "./features/marketplace.js";
27
+ export * from "./features/webhooks.js";
28
+ export * from "./features/domain-suggest.js";
29
+ export * from "./features/portfolio.js";
30
+ export * from "./features/portfolio-monitor.js";
31
+ export * from "./features/portfolio-bulk.js";
32
+ export * from "./features/price-compare.js";
33
+ export * from "./features/social-check.js";
34
+ export * from "./features/tech-stack.js";
35
+ export * from "./features/blacklist-check.js";
36
+ export * from "./features/backlinks.js";
37
+ export * from "./features/whois-history.js";
38
+ export * from "./features/drop-catch.js";
39
+ export * from "./features/expiring-feed.js";
40
+ export * from "./features/port-scanner.js";
41
+ export * from "./features/reverse-ip.js";
42
+ export * from "./features/asn-lookup.js";
43
+ export * from "./features/email-security.js";
44
+ export * from "./features/zone-transfer.js";
45
+ export * from "./features/cert-transparency.js";
46
+ export * from "./features/takeover-detect.js";
47
+ export * from "./features/security-headers.js";
48
+ export * from "./features/waf-detect.js";
49
+ export * from "./features/path-scanner.js";
50
+ export * from "./features/cors-check.js";
@@ -0,0 +1,9 @@
1
+ import { join } from "path";
2
+ import { homedir } from "os";
3
+
4
+ export const APP_DIR = join(homedir(), ".domain-sniper");
5
+ export const CONFIG_FILE = join(APP_DIR, "config.json");
6
+ export const PORTFOLIO_FILE = join(APP_DIR, "portfolio.json");
7
+ export const SESSION_DIR = join(APP_DIR, "sessions");
8
+ export const WHOIS_HISTORY_DIR = join(APP_DIR, "whois-history");
9
+ export const DB_FILE = join(APP_DIR, "domain-sniper.db");
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Domain registrar integrations
3
+ * Supports GoDaddy, Namecheap, and Cloudflare APIs
4
+ */
5
+
6
+ export type RegistrarProvider = "godaddy" | "namecheap" | "cloudflare";
7
+
8
+ interface GodaddyAvailabilityResponse {
9
+ available: boolean;
10
+ price?: number;
11
+ currency?: string;
12
+ }
13
+
14
+ interface GodaddyPurchaseResponse {
15
+ orderId?: number;
16
+ message?: string;
17
+ }
18
+
19
+ interface CloudflareResponse<T> {
20
+ success: boolean;
21
+ result?: T;
22
+ errors?: { message: string }[];
23
+ }
24
+
25
+ export interface RegistrarConfig {
26
+ provider: RegistrarProvider;
27
+ apiKey: string;
28
+ apiSecret?: string;
29
+ accountId?: string; // For Cloudflare
30
+ username?: string; // For Namecheap
31
+ clientIp?: string; // For Namecheap
32
+ }
33
+
34
+ export interface RegistrationResult {
35
+ success: boolean;
36
+ domain: string;
37
+ provider: RegistrarProvider;
38
+ message: string;
39
+ orderId?: string;
40
+ error?: string;
41
+ }
42
+
43
+ export interface AvailabilityCheckResult {
44
+ domain: string;
45
+ available: boolean;
46
+ price?: number;
47
+ currency?: string;
48
+ provider: RegistrarProvider;
49
+ error?: string;
50
+ }
51
+
52
+ // ─── GoDaddy ──────────────────────────────────────────────
53
+
54
+ async function godaddyCheckAvailability(
55
+ domain: string,
56
+ config: RegistrarConfig
57
+ ): Promise<AvailabilityCheckResult> {
58
+ try {
59
+ const resp = await fetch(
60
+ `https://api.godaddy.com/v1/domains/available?domain=${encodeURIComponent(domain)}`,
61
+ {
62
+ headers: {
63
+ Authorization: `sso-key ${config.apiKey}:${config.apiSecret}`,
64
+ "Content-Type": "application/json",
65
+ },
66
+ }
67
+ );
68
+ const data = (await resp.json()) as GodaddyAvailabilityResponse;
69
+ return {
70
+ domain,
71
+ available: data.available === true,
72
+ price: data.price ? data.price / 1000000 : undefined,
73
+ currency: data.currency || "USD",
74
+ provider: "godaddy" as const,
75
+ };
76
+ } catch (err: unknown) {
77
+ return {
78
+ domain,
79
+ available: false,
80
+ provider: "godaddy" as const,
81
+ error: err instanceof Error ? err.message : "Unknown error",
82
+ };
83
+ }
84
+ }
85
+
86
+ async function godaddyRegister(
87
+ domain: string,
88
+ config: RegistrarConfig
89
+ ): Promise<RegistrationResult> {
90
+ try {
91
+ const body = {
92
+ domain,
93
+ consent: {
94
+ agreedAt: new Date().toISOString(),
95
+ agreedBy: config.clientIp || "127.0.0.1",
96
+ agreementKeys: ["DNRA"],
97
+ },
98
+ period: 1,
99
+ renewAuto: false,
100
+ nameServers: [],
101
+ privacy: false,
102
+ };
103
+
104
+ const resp = await fetch("https://api.godaddy.com/v1/domains/purchase", {
105
+ method: "POST",
106
+ headers: {
107
+ Authorization: `sso-key ${config.apiKey}:${config.apiSecret}`,
108
+ "Content-Type": "application/json",
109
+ },
110
+ body: JSON.stringify(body),
111
+ });
112
+
113
+ const data = (await resp.json()) as GodaddyPurchaseResponse;
114
+ if (resp.ok) {
115
+ return {
116
+ success: true,
117
+ domain,
118
+ provider: "godaddy" as const,
119
+ message: `Domain ${domain} registered successfully!`,
120
+ orderId: data.orderId?.toString(),
121
+ };
122
+ }
123
+ return {
124
+ success: false,
125
+ domain,
126
+ provider: "godaddy" as const,
127
+ message: "Registration failed",
128
+ error: data.message || JSON.stringify(data),
129
+ };
130
+ } catch (err: unknown) {
131
+ return {
132
+ success: false,
133
+ domain,
134
+ provider: "godaddy" as const,
135
+ message: "Registration failed",
136
+ error: err instanceof Error ? err.message : "Unknown error",
137
+ };
138
+ }
139
+ }
140
+
141
+ // ─── Namecheap ────────────────────────────────────────────
142
+
143
+ function parseNamecheapDomain(domain: string): { sld: string; tld: string } {
144
+ const parts = domain.split(".");
145
+ const tld = parts.slice(1).join(".");
146
+ const sld = parts[0] || "";
147
+ return { sld, tld };
148
+ }
149
+
150
+ async function namecheapCheckAvailability(
151
+ domain: string,
152
+ config: RegistrarConfig
153
+ ): Promise<AvailabilityCheckResult> {
154
+ try {
155
+ const url = new URL("https://api.namecheap.com/xml.response");
156
+ url.searchParams.set("ApiUser", config.username || "");
157
+ url.searchParams.set("ApiKey", config.apiKey);
158
+ url.searchParams.set("UserName", config.username || "");
159
+ url.searchParams.set("ClientIp", config.clientIp || "127.0.0.1");
160
+ url.searchParams.set("Command", "namecheap.domains.check");
161
+ url.searchParams.set("DomainList", domain);
162
+
163
+ const resp = await fetch(url.toString());
164
+ const text = await resp.text();
165
+
166
+ const available = text.includes('Available="true"');
167
+ return {
168
+ domain,
169
+ available,
170
+ provider: "namecheap" as const,
171
+ };
172
+ } catch (err: unknown) {
173
+ return {
174
+ domain,
175
+ available: false,
176
+ provider: "namecheap" as const,
177
+ error: err instanceof Error ? err.message : "Unknown error",
178
+ };
179
+ }
180
+ }
181
+
182
+ async function namecheapRegister(
183
+ domain: string,
184
+ config: RegistrarConfig
185
+ ): Promise<RegistrationResult> {
186
+ try {
187
+ const { sld, tld } = parseNamecheapDomain(domain);
188
+ const url = new URL("https://api.namecheap.com/xml.response");
189
+ url.searchParams.set("ApiUser", config.username || "");
190
+ url.searchParams.set("ApiKey", config.apiKey);
191
+ url.searchParams.set("UserName", config.username || "");
192
+ url.searchParams.set("ClientIp", config.clientIp || "127.0.0.1");
193
+ url.searchParams.set("Command", "namecheap.domains.create");
194
+ url.searchParams.set("DomainName", domain);
195
+ url.searchParams.set("Years", "1");
196
+ // Registrant info would need to be configured
197
+ url.searchParams.set("AuxBillingFirstName", "Domain");
198
+ url.searchParams.set("AuxBillingLastName", "Sniper");
199
+
200
+ const resp = await fetch(url.toString());
201
+ const text = await resp.text();
202
+
203
+ if (text.includes('Status="OK"') || text.includes("DomainCreated")) {
204
+ return {
205
+ success: true,
206
+ domain,
207
+ provider: "namecheap" as const,
208
+ message: `Domain ${domain} registered via Namecheap!`,
209
+ };
210
+ }
211
+
212
+ return {
213
+ success: false,
214
+ domain,
215
+ provider: "namecheap" as const,
216
+ message: "Registration failed",
217
+ error: text.match(/<Error.*?>(.*?)<\/Error>/)?.[1] || "Unknown error",
218
+ };
219
+ } catch (err: unknown) {
220
+ return {
221
+ success: false,
222
+ domain,
223
+ provider: "namecheap" as const,
224
+ message: "Registration failed",
225
+ error: err instanceof Error ? err.message : "Unknown error",
226
+ };
227
+ }
228
+ }
229
+
230
+ // ─── Cloudflare ───────────────────────────────────────────
231
+
232
+ async function cloudflareCheckAvailability(
233
+ domain: string,
234
+ config: RegistrarConfig
235
+ ): Promise<AvailabilityCheckResult> {
236
+ try {
237
+ if (!config.accountId) {
238
+ return {
239
+ domain,
240
+ available: false,
241
+ provider: "cloudflare",
242
+ error: "Account ID required for Cloudflare",
243
+ };
244
+ }
245
+
246
+ const resp = await fetch(
247
+ `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/registrar/domains/${domain}`,
248
+ {
249
+ headers: {
250
+ Authorization: `Bearer ${config.apiKey}`,
251
+ "Content-Type": "application/json",
252
+ },
253
+ }
254
+ );
255
+ const data = (await resp.json()) as CloudflareResponse<{ available?: boolean }>;
256
+
257
+ if (!data.success) {
258
+ return {
259
+ domain,
260
+ available: false,
261
+ provider: "cloudflare" as const,
262
+ error: data.errors?.[0]?.message || "API request failed",
263
+ };
264
+ }
265
+
266
+ if (data.result?.available) {
267
+ return {
268
+ domain,
269
+ available: true,
270
+ provider: "cloudflare" as const,
271
+ };
272
+ }
273
+
274
+ return {
275
+ domain,
276
+ available: false,
277
+ provider: "cloudflare" as const,
278
+ };
279
+ } catch (err: unknown) {
280
+ return {
281
+ domain,
282
+ available: false,
283
+ provider: "cloudflare" as const,
284
+ error: err instanceof Error ? err.message : "Unknown error",
285
+ };
286
+ }
287
+ }
288
+
289
+ async function cloudflareRegister(
290
+ domain: string,
291
+ config: RegistrarConfig
292
+ ): Promise<RegistrationResult> {
293
+ try {
294
+ if (!config.accountId) {
295
+ return {
296
+ success: false,
297
+ domain,
298
+ provider: "cloudflare" as const,
299
+ message: "Registration failed",
300
+ error: "Account ID required for Cloudflare",
301
+ };
302
+ }
303
+
304
+ const resp = await fetch(
305
+ `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/registrar/domains/${domain}/register`,
306
+ {
307
+ method: "POST",
308
+ headers: {
309
+ Authorization: `Bearer ${config.apiKey}`,
310
+ "Content-Type": "application/json",
311
+ },
312
+ body: JSON.stringify({
313
+ auto_renew: false,
314
+ }),
315
+ }
316
+ );
317
+ const data = (await resp.json()) as CloudflareResponse<{ id?: string }>;
318
+
319
+ if (data.success) {
320
+ return {
321
+ success: true,
322
+ domain,
323
+ provider: "cloudflare" as const,
324
+ message: `Domain ${domain} registered via Cloudflare!`,
325
+ orderId: data.result?.id,
326
+ };
327
+ }
328
+
329
+ return {
330
+ success: false,
331
+ domain,
332
+ provider: "cloudflare" as const,
333
+ message: "Registration failed",
334
+ error: data.errors?.[0]?.message || "Unknown error",
335
+ };
336
+ } catch (err: unknown) {
337
+ return {
338
+ success: false,
339
+ domain,
340
+ provider: "cloudflare" as const,
341
+ message: "Registration failed",
342
+ error: err instanceof Error ? err.message : "Unknown error",
343
+ };
344
+ }
345
+ }
346
+
347
+ // ─── Unified API ──────────────────────────────────────────
348
+
349
+ export async function checkAvailabilityViaRegistrar(
350
+ domain: string,
351
+ config: RegistrarConfig
352
+ ): Promise<AvailabilityCheckResult> {
353
+ switch (config.provider) {
354
+ case "godaddy":
355
+ return godaddyCheckAvailability(domain, config);
356
+ case "namecheap":
357
+ return namecheapCheckAvailability(domain, config);
358
+ case "cloudflare":
359
+ return cloudflareCheckAvailability(domain, config);
360
+ default:
361
+ return {
362
+ domain,
363
+ available: false,
364
+ provider: config.provider,
365
+ error: `Unknown provider: ${config.provider}`,
366
+ };
367
+ }
368
+ }
369
+
370
+ export async function registerDomain(
371
+ domain: string,
372
+ config: RegistrarConfig
373
+ ): Promise<RegistrationResult> {
374
+ switch (config.provider) {
375
+ case "godaddy":
376
+ return godaddyRegister(domain, config);
377
+ case "namecheap":
378
+ return namecheapRegister(domain, config);
379
+ case "cloudflare":
380
+ return cloudflareRegister(domain, config);
381
+ default:
382
+ return {
383
+ success: false,
384
+ domain,
385
+ provider: config.provider,
386
+ message: "Unknown provider",
387
+ error: `Unknown provider: ${config.provider}`,
388
+ };
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Load registrar config from environment variables
394
+ */
395
+ export function loadConfigFromEnv(): RegistrarConfig | null {
396
+ const provider = (process.env.REGISTRAR_PROVIDER || "").toLowerCase() as RegistrarProvider;
397
+
398
+ if (!provider || !["godaddy", "namecheap", "cloudflare"].includes(provider)) {
399
+ return null;
400
+ }
401
+
402
+ const apiKey = process.env.REGISTRAR_API_KEY || "";
403
+ if (!apiKey) return null;
404
+
405
+ return {
406
+ provider,
407
+ apiKey,
408
+ apiSecret: process.env.REGISTRAR_API_SECRET || "",
409
+ accountId: process.env.CLOUDFLARE_ACCOUNT_ID || "",
410
+ username: process.env.NAMECHEAP_USERNAME || "",
411
+ clientIp: process.env.CLIENT_IP || "127.0.0.1",
412
+ };
413
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Theme system inspired by OpenCode's dark theme
3
+ * Uses a stepped grayscale palette with accent colors
4
+ */
5
+
6
+ export const palette = {
7
+ // Grayscale steps (dark to light)
8
+ step1: "#0a0a0a", // Deepest background
9
+ step2: "#141414", // Panel background
10
+ step3: "#1e1e1e", // Elevated surface
11
+ step4: "#282828", // Subtle borders
12
+ step5: "#323232", // Borders
13
+ step6: "#3c3c3c", // Active borders
14
+ step7: "#464646", // Muted elements
15
+ step8: "#505050", // Disabled text
16
+ step9: "#6e6e6e", // Placeholder text
17
+ step10: "#8c8c8c", // Muted text
18
+ step11: "#b0b0b0", // Secondary text
19
+ step12: "#eeeeee", // Primary text
20
+
21
+ // Accent colors
22
+ green: "#00e88f",
23
+ greenDim: "#0a3d2a",
24
+ blue: "#5c9cf5",
25
+ blueDim: "#1a2d4a",
26
+ yellow: "#f5c542",
27
+ yellowDim: "#3d3018",
28
+ red: "#f55c5c",
29
+ redDim: "#3d1a1a",
30
+ orange: "#f5955c",
31
+ orangeDim: "#3d2818",
32
+ purple: "#9d7cd8",
33
+ purpleDim: "#2a1f3d",
34
+ cyan: "#56d4dd",
35
+ cyanDim: "#1a3335",
36
+ } as const;
37
+
38
+ export const theme = {
39
+ // Backgrounds
40
+ background: palette.step1,
41
+ backgroundPanel: palette.step2,
42
+ backgroundElevated: palette.step3,
43
+
44
+ // Text
45
+ text: palette.step12,
46
+ textSecondary: palette.step11,
47
+ textMuted: palette.step10,
48
+ textPlaceholder: palette.step9,
49
+ textDisabled: palette.step8,
50
+
51
+ // Borders
52
+ border: palette.step5,
53
+ borderActive: palette.step6,
54
+ borderSubtle: palette.step4,
55
+
56
+ // Semantic
57
+ primary: palette.green,
58
+ primaryDim: palette.greenDim,
59
+ secondary: palette.blue,
60
+ secondaryDim: palette.blueDim,
61
+ warning: palette.yellow,
62
+ warningDim: palette.yellowDim,
63
+ error: palette.red,
64
+ errorDim: palette.redDim,
65
+ info: palette.cyan,
66
+ infoDim: palette.cyanDim,
67
+ accent: palette.purple,
68
+ accentDim: palette.purpleDim,
69
+ pending: palette.orange,
70
+ pendingDim: palette.orangeDim,
71
+ } as const;
72
+
73
+ // ─── Border characters ───────────────────────────────────
74
+
75
+ export const borders = {
76
+ rounded: {
77
+ topLeft: "╭",
78
+ topRight: "╮",
79
+ bottomLeft: "╰",
80
+ bottomRight: "╯",
81
+ horizontal: "─",
82
+ vertical: "│",
83
+ topT: "┬",
84
+ bottomT: "┴",
85
+ leftT: "├",
86
+ rightT: "┤",
87
+ cross: "┼",
88
+ },
89
+ heavy: {
90
+ topLeft: "┏",
91
+ topRight: "┓",
92
+ bottomLeft: "┗",
93
+ bottomRight: "┛",
94
+ horizontal: "━",
95
+ vertical: "┃",
96
+ topT: "┳",
97
+ bottomT: "┻",
98
+ leftT: "┣",
99
+ rightT: "┫",
100
+ cross: "╋",
101
+ },
102
+ splitLeft: {
103
+ topLeft: "",
104
+ topRight: "",
105
+ bottomLeft: "",
106
+ bottomRight: "",
107
+ horizontal: " ",
108
+ vertical: "┃",
109
+ topT: "",
110
+ bottomT: "",
111
+ leftT: "",
112
+ rightT: "",
113
+ cross: "",
114
+ },
115
+ } as const;
116
+
117
+ // ─── Status styling ──────────────────────────────────────
118
+
119
+ export type DomainStatus = "pending" | "checking" | "available" | "expired" | "taken" | "error" | "registered" | "registering";
120
+
121
+ export function statusStyle(status: DomainStatus) {
122
+ switch (status) {
123
+ case "pending":
124
+ return { icon: "○", fg: theme.textDisabled, label: "PENDING" };
125
+ case "checking":
126
+ return { icon: "◆", fg: theme.warning, label: "CHECKING" };
127
+ case "available":
128
+ return { icon: "●", fg: theme.primary, label: "AVAILABLE" };
129
+ case "expired":
130
+ return { icon: "◈", fg: theme.pending, label: "EXPIRED" };
131
+ case "taken":
132
+ return { icon: "✕", fg: theme.error, label: "TAKEN" };
133
+ case "registered":
134
+ return { icon: "◉", fg: theme.secondary, label: "REGISTERED" };
135
+ case "registering":
136
+ return { icon: "◌", fg: theme.info, label: "REGISTERING" };
137
+ case "error":
138
+ return { icon: "!", fg: theme.error, label: "ERROR" };
139
+ }
140
+ }