@qverisai/cli 0.1.0 → 0.3.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.
package/README.md CHANGED
@@ -57,7 +57,7 @@ tool: weather_gov... · id: 7ebcaf9d-...
57
57
  **One-liner (recommended):**
58
58
 
59
59
  ```bash
60
- curl -fsSL https://qveris.ai/install | bash
60
+ curl -fsSL https://qveris.ai/cli/install | bash
61
61
  ```
62
62
 
63
63
  **Or via npm:**
@@ -242,6 +242,28 @@ qveris discover "weather" --api-key "sk-1_..."
242
242
 
243
243
  **Resolution order:** `--api-key` flag > `QVERIS_API_KEY` env > config file
244
244
 
245
+ ### Region
246
+
247
+ The API region is auto-detected from your key prefix:
248
+
249
+ | Key prefix | Region | Base URL |
250
+ |------------|--------|----------|
251
+ | `sk-xxx` | Global | `https://qveris.ai/api/v1` |
252
+ | `sk-cn-xxx` | China | `https://qveris.cn/api/v1` |
253
+
254
+ No extra configuration needed. To override manually:
255
+
256
+ ```bash
257
+ # Via environment variable
258
+ export QVERIS_REGION=cn
259
+
260
+ # Or set a custom base URL
261
+ export QVERIS_BASE_URL=https://custom.endpoint/api/v1
262
+
263
+ # Or per-command
264
+ qveris discover "weather" --base-url https://qveris.cn/api/v1
265
+ ```
266
+
245
267
  ### Config File
246
268
 
247
269
  Located at `~/.config/qveris/config.json` (respects `XDG_CONFIG_HOME`).
@@ -313,9 +335,9 @@ QVeris CLI uses only Node.js built-in APIs. No `chalk`, no `commander`, no `yarg
313
335
 
314
336
  ## Links
315
337
 
316
- - Website: https://qveris.ai
338
+ - Website: https://qveris.ai (global) / https://qveris.cn (China)
317
339
  - API Docs: https://qveris.ai/docs
318
- - Get API Key: https://qveris.ai/account?page=api-keys
340
+ - Get API Key: https://qveris.ai/account?page=api-keys (global) / https://qveris.cn/account?page=api-keys (China)
319
341
  - GitHub: https://github.com/QVerisAI/QVerisAI
320
342
 
321
343
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qverisai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "QVeris CLI -- discover, inspect, and call 10,000+ capabilities",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # QVeris CLI Installer
3
- # Usage: curl -fsSL https://qveris.ai/install | bash
3
+ # Usage: curl -fsSL https://qveris.ai/cli/install | bash
4
4
  set -euo pipefail
5
5
 
6
6
  BOLD='\033[1m'
@@ -1,8 +1,8 @@
1
- import { resolve } from "../config/resolve.mjs";
1
+ import { resolveBaseUrl } from "../config/region.mjs";
2
2
  import { CliError } from "../errors/handler.mjs";
3
3
 
4
- function getBaseUrl(flagValue) {
5
- return resolve("base_url", flagValue).value;
4
+ function getBaseUrl(baseUrlFlag, apiKey) {
5
+ return resolveBaseUrl({ baseUrlFlag, apiKey }).baseUrl;
6
6
  }
7
7
 
8
8
  async function requestJson(path, { method = "POST", query = {}, body, timeoutMs = 30000, apiKey, baseUrl }) {
@@ -10,7 +10,7 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
10
10
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
11
11
 
12
12
  try {
13
- const url = new URL(baseUrl.replace(/\/$/, "") + path);
13
+ const url = new URL(baseUrl.replace(/\/+$/, "") + path);
14
14
  for (const [key, value] of Object.entries(query)) {
15
15
  if (value !== undefined && value !== null) {
16
16
  url.searchParams.set(key, String(value));
@@ -35,8 +35,6 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
35
35
  jsonBody = JSON.parse(rawText);
36
36
  errorDetail = jsonBody.error_message || jsonBody.message || null;
37
37
  } catch { /* not JSON */ }
38
- // For known error codes, pass errorDetail only if it's a meaningful message
39
- // (not raw JSON); otherwise let the CliError template message apply
40
38
  if (status === 401 || status === 403) throw new CliError("AUTH_INVALID_KEY", errorDetail);
41
39
  if (status === 402) throw new CliError("CREDITS_INSUFFICIENT", errorDetail);
42
40
  if (status === 429) throw new CliError("RATE_LIMITED", errorDetail);
@@ -60,12 +58,12 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
60
58
  }
61
59
 
62
60
  export async function discoverTools({ apiKey, baseUrl: baseUrlFlag, query, limit = 5, timeoutMs = 30000 }) {
63
- const baseUrl = getBaseUrl(baseUrlFlag);
61
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
64
62
  return requestJson("/search", { apiKey, baseUrl, body: { query, limit }, timeoutMs });
65
63
  }
66
64
 
67
65
  export async function inspectToolsByIds({ apiKey, baseUrl: baseUrlFlag, toolIds, discoveryId, timeoutMs = 30000 }) {
68
- const baseUrl = getBaseUrl(baseUrlFlag);
66
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
69
67
  const body = { tool_ids: toolIds };
70
68
  if (discoveryId) body.search_id = discoveryId;
71
69
  return requestJson("/tools/by-ids", { apiKey, baseUrl, body, timeoutMs });
@@ -80,7 +78,7 @@ export async function callTool({
80
78
  maxResponseSize = 102400,
81
79
  timeoutMs = 120000,
82
80
  }) {
83
- const baseUrl = getBaseUrl(baseUrlFlag);
81
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
84
82
  return requestJson("/tools/execute", {
85
83
  apiKey,
86
84
  baseUrl,
@@ -93,4 +91,3 @@ export async function callTool({
93
91
  timeoutMs,
94
92
  });
95
93
  }
96
-
@@ -5,7 +5,9 @@ import { resolveParams } from "../utils/params.mjs";
5
5
  import { formatDiscoverResult, formatInspectResult, formatCallResult } from "../output/formatter.mjs";
6
6
  import { generateSnippet } from "../output/codegen.mjs";
7
7
  import { writeSession } from "../session/session.mjs";
8
+ import { VERSION } from "../config/defaults.mjs";
8
9
  import { bold, dim, cyan } from "../output/colors.mjs";
10
+ import { printWelcomeBanner } from "../output/banner.mjs";
9
11
  import { handleError } from "../errors/handler.mjs";
10
12
  import { createSpinner } from "../output/spinner.mjs";
11
13
 
@@ -22,7 +24,11 @@ export async function runInteractive(flags) {
22
24
  lastCallContext: null,
23
25
  };
24
26
 
25
- console.log(`\n ${bold("QVeris Interactive Mode")}`);
27
+ if (!flags.json) {
28
+ printWelcomeBanner({ version: VERSION, noColor: flags.noColor, compact: true });
29
+ }
30
+
31
+ console.log(` ${bold("QVeris Interactive Mode")}`);
26
32
  console.log(` ${dim("Type 'help' for commands, 'exit' to quit.")}\n`);
27
33
 
28
34
  const rl = createInterface({
@@ -4,7 +4,9 @@ import { platform } from "node:os";
4
4
  import { resolve } from "../config/resolve.mjs";
5
5
  import { setConfigValue, deleteConfigValue } from "../config/store.mjs";
6
6
  import { discoverTools } from "../client/api.mjs";
7
+ import { VERSION } from "../config/defaults.mjs";
7
8
  import { bold, green, red, dim, cyan } from "../output/colors.mjs";
9
+ import { printLoginBanner } from "../output/banner.mjs";
8
10
 
9
11
  const ACCOUNT_URL = "https://qveris.ai/account?page=api-keys";
10
12
 
@@ -91,7 +93,11 @@ export async function runLogin(flags) {
91
93
  return;
92
94
  }
93
95
 
94
- console.log(`\n Get your API key at: ${cyan(ACCOUNT_URL)}\n`);
96
+ if (!flags.json) {
97
+ printLoginBanner({ version: VERSION, noColor: flags.noColor });
98
+ }
99
+
100
+ console.log(` Get your API key at: ${cyan(ACCOUNT_URL)}\n`);
95
101
 
96
102
  if (!flags.noBrowser) {
97
103
  openBrowser(ACCOUNT_URL);
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Region resolution for QVeris API.
3
+ *
4
+ * Priority: --base-url flag > QVERIS_BASE_URL env > QVERIS_REGION env > key prefix auto-detect > default (global)
5
+ *
6
+ * Key prefix convention:
7
+ * sk-cn-xxx → cn region (qveris.cn)
8
+ * sk-xxx → global (qveris.ai)
9
+ */
10
+
11
+ const REGION_URLS = {
12
+ global: "https://qveris.ai/api/v1",
13
+ cn: "https://qveris.cn/api/v1",
14
+ };
15
+
16
+ /**
17
+ * Detect region from API key prefix.
18
+ * @param {string} apiKey
19
+ * @returns {"cn"|"global"}
20
+ */
21
+ export function detectRegionFromKey(apiKey) {
22
+ if (typeof apiKey === "string" && apiKey.startsWith("sk-cn-")) return "cn";
23
+ return "global";
24
+ }
25
+
26
+ /**
27
+ * Resolve the base URL for the QVeris API.
28
+ *
29
+ * Priority:
30
+ * 1. Explicit base URL (--base-url flag or QVERIS_BASE_URL env)
31
+ * 2. Explicit region (QVERIS_REGION env or config)
32
+ * 3. Auto-detect from API key prefix
33
+ * 4. Default: global (qveris.ai)
34
+ *
35
+ * @param {{ baseUrlFlag?: string, apiKey?: string }} options
36
+ * @returns {{ baseUrl: string, region: string, source: string }}
37
+ */
38
+ export function resolveBaseUrl({ baseUrlFlag, apiKey } = {}) {
39
+ // 1. Explicit base URL flag or env var
40
+ if (baseUrlFlag) {
41
+ return { baseUrl: baseUrlFlag.replace(/\/+$/, ""), region: "custom", source: "flag" };
42
+ }
43
+ if (process.env.QVERIS_BASE_URL) {
44
+ return { baseUrl: process.env.QVERIS_BASE_URL.replace(/\/+$/, ""), region: "custom", source: "env (QVERIS_BASE_URL)" };
45
+ }
46
+
47
+ // 2. Explicit region env var
48
+ if (process.env.QVERIS_REGION) {
49
+ const region = process.env.QVERIS_REGION.toLowerCase();
50
+ const url = REGION_URLS[region] || REGION_URLS.global;
51
+ return { baseUrl: url, region, source: "env (QVERIS_REGION)" };
52
+ }
53
+
54
+ // 3. Auto-detect from API key prefix
55
+ if (apiKey) {
56
+ const region = detectRegionFromKey(apiKey);
57
+ return { baseUrl: REGION_URLS[region], region, source: "auto (key prefix)" };
58
+ }
59
+
60
+ // 4. Default
61
+ return { baseUrl: REGION_URLS.global, region: "global", source: "default" };
62
+ }
63
+
64
+ export { REGION_URLS };
package/src/main.mjs CHANGED
@@ -2,6 +2,7 @@ import { normalizeLegacyArgs } from "./compat/aliases.mjs";
2
2
  import { handleError } from "./errors/handler.mjs";
3
3
  import { VERSION } from "./config/defaults.mjs";
4
4
  import { bold, dim, cyan } from "./output/colors.mjs";
5
+ import { printWelcomeBanner } from "./output/banner.mjs";
5
6
 
6
7
  export async function main(argv) {
7
8
  const rawArgs = argv.slice(2);
@@ -12,6 +13,10 @@ export async function main(argv) {
12
13
  }
13
14
 
14
15
  const flags = extractGlobalFlags(args);
16
+ if (flags.noColor) {
17
+ process.env.NO_COLOR = "1";
18
+ }
19
+
15
20
  const positional = flags._positional;
16
21
  const command = positional[0];
17
22
  const rest = positional.slice(1);
@@ -22,7 +27,7 @@ export async function main(argv) {
22
27
  }
23
28
 
24
29
  if (!command || flags.help) {
25
- printUsage();
30
+ printUsage(flags);
26
31
  return;
27
32
  }
28
33
 
@@ -202,9 +207,13 @@ function extractGlobalFlags(args) {
202
207
  return flags;
203
208
  }
204
209
 
205
- function printUsage() {
210
+ function printUsage(flags = {}) {
211
+ if (!flags.json) {
212
+ printWelcomeBanner({ version: VERSION, noColor: flags.noColor, compact: false });
213
+ }
214
+
206
215
  console.log(`
207
- ${bold("QVeris CLI")} ${dim(`v${VERSION}`)} -- discover, inspect, and call 10,000+ capabilities
216
+ ${bold("QVeris CLI")} ${dim("discover, inspect, and call 10,000+ capabilities")}
208
217
 
209
218
  ${bold("Usage:")}
210
219
  qveris <command> [args] [flags]
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Welcome / login banners: gradient on solid block letters (brand cyan → indigo).
3
+ * Figlet font "Banner3" (filled #), mapped to █ for stronger contrast.
4
+ */
5
+
6
+ import { homedir } from "node:os";
7
+ import { cwd as processCwd } from "node:process";
8
+ import { dim, cyan, isColorEnabled } from "./colors.mjs";
9
+
10
+ /** @type {readonly [number, number, number][]} brand stops: highlight → mid → shadow */
11
+ const BRAND_STOPS = [
12
+ [0, 242, 255], // #00F2FF
13
+ [0, 153, 255], // #0099FF
14
+ [26, 0, 255], // #1A00FF
15
+ ];
16
+
17
+ const RESET = "\x1b[0m";
18
+
19
+ /** Figlet -f Banner3 QVERIS (53 cols × 7 rows). # → █ at render. */
20
+ const ASCII_QVERIS_HASH = [
21
+ " ####### ## ## ######## ######## #### ###### ",
22
+ "## ## ## ## ## ## ## ## ## ## ",
23
+ "## ## ## ## ## ## ## ## ## ",
24
+ "## ## ## ## ###### ######## ## ###### ",
25
+ "## ## ## ## ## ## ## ## ## ## ",
26
+ "## ## ## ## ## ## ## ## ## ## ",
27
+ " ##### ## ### ######## ## ## #### ###### ",
28
+ ].join("\n");
29
+
30
+ function solidBlockLines() {
31
+ return ASCII_QVERIS_HASH.split("\n").map((line) => line.replace(/#/g, "█"));
32
+ }
33
+
34
+ function colorDepth() {
35
+ try {
36
+ return typeof process.stdout.getColorDepth === "function"
37
+ ? process.stdout.getColorDepth()
38
+ : 8;
39
+ } catch {
40
+ return 8;
41
+ }
42
+ }
43
+
44
+ function lerp(a, b, t) {
45
+ return Math.round(a + (b - a) * t);
46
+ }
47
+
48
+ /** RGB along multi-stop gradient, t in [0,1]. */
49
+ function rgbAt(t) {
50
+ const n = BRAND_STOPS.length - 1;
51
+ const x = Math.min(1, Math.max(0, t)) * n;
52
+ const i = Math.min(Math.floor(x), n - 1);
53
+ const f = x - i;
54
+ const [r1, g1, b1] = BRAND_STOPS[i];
55
+ const [r2, g2, b2] = BRAND_STOPS[i + 1];
56
+ return [lerp(r1, r2, f), lerp(g1, g2, f), lerp(b1, b2, f)];
57
+ }
58
+
59
+ function truecolorFg(r, g, b) {
60
+ return `\x1b[38;2;${r};${g};${b}m`;
61
+ }
62
+
63
+ /**
64
+ * Map column index to gradient color (horizontal sweep across the banner).
65
+ * Spaces stay uncolored. Consecutive non-space chars with identical RGB share one SGR span.
66
+ */
67
+ function paintLineTruecolor(line, maxCols) {
68
+ if (!line.length) return "";
69
+ const denom = Math.max(1, maxCols - 1);
70
+ let out = "";
71
+ let i = 0;
72
+ while (i < line.length) {
73
+ const ch = line[i];
74
+ if (ch === " ") {
75
+ out += ch;
76
+ i++;
77
+ continue;
78
+ }
79
+ const t0 = i / denom;
80
+ const [r0, g0, b0] = rgbAt(t0);
81
+ let j = i + 1;
82
+ while (j < line.length) {
83
+ const chj = line[j];
84
+ if (chj === " ") break;
85
+ const t = j / denom;
86
+ const [r, g, b] = rgbAt(t);
87
+ if (r !== r0 || g !== g0 || b !== b0) break;
88
+ j++;
89
+ }
90
+ out += truecolorFg(r0, g0, b0) + line.slice(i, j) + RESET;
91
+ i = j;
92
+ }
93
+ return out;
94
+ }
95
+
96
+ /** 16-color fallback: batched SGR per band (cyan / bold cyan / dim cyan). */
97
+ function paintLineFallback(line, maxCols) {
98
+ if (!isColorEnabled()) return line;
99
+ const denom = Math.max(1, maxCols - 1);
100
+ let out = "";
101
+ let i = 0;
102
+ while (i < line.length) {
103
+ const ch = line[i];
104
+ if (ch === " ") {
105
+ out += ch;
106
+ i++;
107
+ continue;
108
+ }
109
+ const band0 = Math.min(2, Math.floor((i / denom) * 3));
110
+ let j = i + 1;
111
+ while (j < line.length) {
112
+ const chj = line[j];
113
+ if (chj === " ") break;
114
+ const band = Math.min(2, Math.floor((j / denom) * 3));
115
+ if (band !== band0) break;
116
+ j++;
117
+ }
118
+ const chunk = line.slice(i, j);
119
+ if (band0 === 0) out += `\x1b[36m${chunk}\x1b[0m`;
120
+ else if (band0 === 1) out += `\x1b[1;36m${chunk}\x1b[0m`;
121
+ else out += `\x1b[2;36m${chunk}\x1b[0m`;
122
+ i = j;
123
+ }
124
+ return out;
125
+ }
126
+
127
+ export function bannerColorAllowed(noColorFlag) {
128
+ if (noColorFlag) return false;
129
+ return isColorEnabled();
130
+ }
131
+
132
+ function shortenPath(p) {
133
+ const home = homedir();
134
+ if (p === home) return "~";
135
+ if (p.startsWith(home + "/")) return "~" + p.slice(home.length);
136
+ return p;
137
+ }
138
+
139
+ /** Strip SGR ANSI sequences (truecolor / 16-color). */
140
+ function stripAnsi(s) {
141
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
142
+ }
143
+
144
+ const RE_HAN = /\p{Script=Han}/u;
145
+ const RE_HANGUL = /\p{Script=Hangul}/u;
146
+
147
+ /** Terminal display width: CJK/Hangul/fullwidth = 2 cols (matches typical monospace). */
148
+ function displayWidth(str) {
149
+ let w = 0;
150
+ for (const ch of str) {
151
+ const cp = ch.codePointAt(0);
152
+ if (RE_HAN.test(ch) || RE_HANGUL.test(ch)) w += 2;
153
+ else if (cp >= 0xff01 && cp <= 0xff60) w += 2; // fullwidth forms
154
+ else w += 1;
155
+ }
156
+ return w;
157
+ }
158
+
159
+ function centerBlock(lines, termCols) {
160
+ const widths = lines.map((l) => displayWidth(stripAnsi(l)));
161
+ const maxW = Math.max(...widths, 1);
162
+ const pad = termCols >= maxW + 4 ? Math.max(0, Math.floor((termCols - maxW) / 2)) : 2;
163
+ const prefix = " ".repeat(pad);
164
+ return lines.map((l) => prefix + l);
165
+ }
166
+
167
+ /**
168
+ * @param {object} opts
169
+ * @param {string} opts.version
170
+ * @param {boolean} [opts.noColor]
171
+ * @param {boolean} [opts.compact] — fewer blank lines (e.g. interactive)
172
+ */
173
+ export function printWelcomeBanner(opts) {
174
+ const { version, noColor = false, compact = false } = opts;
175
+ if (!bannerColorAllowed(noColor)) {
176
+ printPlainBanner({ version, compact });
177
+ return;
178
+ }
179
+
180
+ const rawLines = solidBlockLines();
181
+ const maxW = Math.max(...rawLines.map((l) => l.length));
182
+ const termCols = process.stdout.columns || 80;
183
+ const useTc = colorDepth() >= 24;
184
+
185
+ const painted = rawLines.map((line) => {
186
+ const padded = line.padEnd(maxW);
187
+ return useTc ? paintLineTruecolor(padded, maxW) : paintLineFallback(padded, maxW);
188
+ });
189
+ const centered = centerBlock(painted, termCols);
190
+
191
+ const nl = compact ? "\n" : "\n\n";
192
+ console.log(nl + centered.join("\n"));
193
+
194
+ const tag = "✦ Discover · Inspect · Call · 10,000+ capabilities · intelligent orchestration ✦";
195
+ const meta = dim(`v${version} · ${shortenPath(processCwd())}`);
196
+ const tagLine = centerLine(dim(cyan(tag)), termCols);
197
+ const metaLine = centerLine(meta, termCols);
198
+
199
+ console.log("\n" + tagLine + "\n" + metaLine + (compact ? "\n" : "\n\n"));
200
+ }
201
+
202
+ function centerLine(text, termCols) {
203
+ const len = displayWidth(stripAnsi(text));
204
+ const pad = termCols >= len + 4 ? Math.max(0, Math.floor((termCols - len) / 2)) : 2;
205
+ return " ".repeat(pad) + text;
206
+ }
207
+
208
+ function printPlainBanner({ version, compact }) {
209
+ const rawLines = solidBlockLines();
210
+ const termCols = process.stdout.columns || 80;
211
+ const centered = centerBlock(rawLines, termCols);
212
+ const nl = compact ? "\n" : "\n\n";
213
+ console.log(nl + centered.join("\n"));
214
+ const tag = "✦ Discover · Inspect · Call · 10,000+ capabilities · intelligent orchestration ✦";
215
+ const meta = `v${version} · ${shortenPath(processCwd())}`;
216
+ const tagLine = centerLine(dim(tag), termCols);
217
+ const metaLine = centerLine(dim(meta), termCols);
218
+ console.log("\n" + tagLine + "\n" + metaLine + (compact ? "\n" : "\n\n"));
219
+ }
220
+
221
+ /**
222
+ * Login screen: banner + framed hint (cyan border).
223
+ */
224
+ export function printLoginBanner(opts) {
225
+ const { version, noColor = false } = opts;
226
+ printWelcomeBanner({ version, noColor, compact: true });
227
+
228
+ if (!bannerColorAllowed(noColor)) {
229
+ console.log(dim(" ─ Secure login · paste your API key below"));
230
+ return;
231
+ }
232
+
233
+ const termCols = process.stdout.columns || 80;
234
+ const inner = " Secure login · API key ";
235
+ const maxInner = Math.min(inner.length + 4, termCols - 4);
236
+ const bar = "─".repeat(Math.max(8, maxInner));
237
+ const top = centerLine(cyan("╭" + bar + "╮"), termCols);
238
+ const mid = centerLine(cyan("│") + dim(inner) + cyan("│"), termCols);
239
+ const bot = centerLine(cyan("╰" + bar + "╯"), termCols);
240
+ console.log(top + "\n" + mid + "\n" + bot + "\n");
241
+ }
@@ -1,9 +1,13 @@
1
- const enabled =
2
- !process.env.NO_COLOR &&
3
- (process.env.FORCE_COLOR === "1" || (process.stdout.isTTY && process.stderr.isTTY));
1
+ let memoizedEnabled;
2
+ export const isColorEnabled = () => {
3
+ if (memoizedEnabled !== undefined) return memoizedEnabled;
4
+ memoizedEnabled = !process.env.NO_COLOR &&
5
+ (process.env.FORCE_COLOR === "1" || (process.stdout.isTTY && process.stderr.isTTY));
6
+ return memoizedEnabled;
7
+ };
4
8
 
5
- const code = (open, close) =>
6
- enabled ? (s) => `\x1b[${open}m${s}\x1b[${close}m` : (s) => s;
9
+ const code = (open, close) => (s) =>
10
+ isColorEnabled() ? `\x1b[${open}m${s}\x1b[${close}m` : s;
7
11
 
8
12
  export const bold = code("1", "22");
9
13
  export const dim = code("2", "22");
@@ -12,5 +16,3 @@ export const green = code("32", "39");
12
16
  export const yellow = code("33", "39");
13
17
  export const cyan = code("36", "39");
14
18
  export const gray = code("90", "39");
15
-
16
- export const isColorEnabled = () => enabled;