pi-cliproxyapi 0.2.0 → 0.3.1

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
@@ -1,3 +1,12 @@
1
+ ```
2
+ ██████╗ ██╗ ██████╗██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗
3
+ ██╔══██╗██║ ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝
4
+ ██████╔╝██║ ██║ ██║ ██║██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝
5
+ ██╔═══╝ ██║ ██║ ██║ ██║██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝
6
+ ██║ ██║ ╚██████╗███████╗██║██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║
7
+ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
8
+ ```
9
+
1
10
  # pi-cliproxyapi
2
11
 
3
12
  [![npm](https://img.shields.io/npm/v/pi-cliproxyapi)](https://www.npmjs.com/package/pi-cliproxyapi)
@@ -7,39 +16,62 @@ Pi extension for corporate management of model providers via a single [CliProxyA
7
16
 
8
17
  One `(endpoint, apiKey)` pair — every provider and model inherits it automatically.
9
18
 
19
+ ![pi-cliproxyapi — the /cliproxy hub, Models tab](docs/pi-cliproxyapi-1.png)
20
+
10
21
  ## Features
11
22
 
23
+ - **Unified hub** — one `/cliproxy` overlay with **Models / Usage / Diagnostics** tabs (number hotkeys `1` `2` `3`) plus global actions: `r` refresh, `e` setup, `s` save
12
24
  - **Built-in provider routing** — whitelist which Anthropic / OpenAI / etc. models are available through the proxy
13
25
  - **Custom provider groups** — create named groups (e.g. `corp-glm`, `corp-gemini`) for proxy-only models with automatic metadata from [models.dev](https://models.dev)
14
- - **Exclusive model pool** — a model assigned to one group automatically disappears from others
15
- - **Per-account usage overlay** — colored quota bars, toggle disabled accounts, verbose errors no LLM call
26
+ - **Exclusive model pool** — a model assigned to one group automatically disappears from others, grouped by `owned_by` with type-to-filter (`/`)
27
+ - **Live save state** — the header shows `● unsaved` while you edit and `✓ settings saved` after `s`, no console noise
28
+ - **Per-account usage tab** — colored quota bars, toggle disabled accounts, verbose errors — no LLM call
16
29
  - **Setup wizard** — `/cliproxy-setup` configures endpoint, API key, provider prefix, and usage key interactively
17
30
 
18
31
  ## Commands
19
32
 
33
+ Two commands; everything else lives inside the hub as tabs and actions.
34
+
20
35
  | Command | Description |
21
36
  | --- | --- |
22
- | `/cliproxy` | Three-panel pickerproviders, assigned models, available pool |
37
+ | `/cliproxy` | Hub overlay**Models** / **Usage** / **Diagnostics** tabs plus global actions |
23
38
  | `/cliproxy-setup` | Configure endpoint, API key, provider prefix, usage key |
24
- | `/cliproxy-refresh` | Re-fetch upstream models, re-register providers |
25
- | `/cliproxy-usage` | Per-account quota windows with progress bars (`d` = show disabled, `v` = verbose) |
26
- | `/cliproxy-doctor` | Connectivity, key resolution, discovery diagnostics |
27
39
 
28
- ### `/cliproxy` picker
40
+ ### The `/cliproxy` hub
41
+
42
+ Global keys: `[` / `]` or `1` `2` `3` switch tabs · `r` refresh discovery + reapply · `e` setup · `s` save · `q` / `Esc` close.
29
43
 
30
- The picker has three panels you cycle through with `Tab` / arrow keys:
44
+ **Models tab** three panels cycled with `Tab` / arrows:
31
45
 
32
46
  - **left** — every provider (built-in + custom). `+ new custom group…` is the last row.
33
- - **right top** — models currently assigned to the focused provider. `Enter` / `Space` removes one.
34
- - **right bottom** — available model pool, grouped by upstream `owned_by`. `Enter` / `Space` attaches a model to the focused provider.
47
+ - **right top** — models assigned to the focused provider. `Enter` / `Space` removes one.
48
+ - **right bottom** — available pool, grouped by upstream `owned_by`. `Enter` / `Space` attaches. Press `/` to filter the pool by id/name. A `⚠` marks an API mismatch (attach still allowed).
49
+
50
+ Extra Models keys: `d` removes a custom group (with confirmation).
51
+
52
+ **Usage tab** — per-account quota bars; `d` shows disabled accounts, `v` shows verbose errors.
53
+
54
+ **Diagnostics tab** — connectivity, key resolution, and discovery shape.
55
+
56
+ ## Screenshots
57
+
58
+ **Models — custom group, pool grouped by owner (`/` filters the pool)**
59
+
60
+ ![Models tab: custom proxy group with grouped available pool](docs/pi-cliproxyapi-2.png)
61
+
62
+ **Usage — per-account quota windows**
63
+
64
+ ![Usage tab: per-account quota bars with reset windows](docs/pi-cliproxyapi-3.png)
65
+
66
+ **Diagnostics — connectivity, keys, discovery shape**
35
67
 
36
- Extra keys: `d` removes a custom group (with confirmation), `s` saves, `q` / `Esc` cancels. A `⚠` marker shows when a model's recommended API differs from the provider's API — attach is allowed anyway.
68
+ ![Diagnostics tab: endpoint, key resolution, discovery and conflicts](docs/pi-cliproxyapi-4.png)
37
69
 
38
70
  ## Prerequisites
39
71
 
40
72
  You need a running [CliProxyAPI](https://github.com/router-for-me/CLIProxyAPI) instance — this is the corporate LLM proxy that aggregates multiple providers behind a single OpenAI-compatible endpoint.
41
73
 
42
- For full functionality (`/cliproxy-usage`, enriched model metadata from [models.dev](https://models.dev)), also deploy the companion sidecar: **[pi-cliproxyapi-wellknown](https://github.com/abix5/pi-cliproxyapi-wellknown)**. See [Deploying the sidecar](#deploying-the-sidecar-service) below.
74
+ For full functionality (Usage tab, enriched model metadata from [models.dev](https://models.dev)), also deploy the companion sidecar: **[pi-cliproxyapi-wellknown](https://github.com/abix5/pi-cliproxyapi-wellknown)**. See [Deploying the sidecar](#deploying-the-sidecar-service) below.
43
75
 
44
76
  ## Install
45
77
 
@@ -85,18 +117,18 @@ The plugin tries `GET <endpoint-origin>/.well-known/pi` first (requires the side
85
117
  The **[pi-cliproxyapi-wellknown](https://github.com/abix5/pi-cliproxyapi-wellknown)** sidecar runs alongside CliProxyAPI and provides:
86
118
 
87
119
  - `/.well-known/pi` — model discovery with metadata from [models.dev](https://models.dev) (context windows, costs, reasoning flags)
88
- - `/api/usage` — per-account quota windows used by `/cliproxy-usage`
120
+ - `/api/usage` — per-account quota windows used by the hub Usage tab
89
121
 
90
122
  ```
91
123
  ┌──────────────┐ ┌───────────────────────────┐
92
- │ Pi + plugin │────▶│ CliProxyAPI (:8317)
93
- │ │ /v1/models, /v1/chat/...
94
- │ └───────────────────────────┘
95
- │ ┌───────────────────────────┐
96
- │────▶│ wellknown sidecar (:3458)
97
- │ │ /.well-known/pi
98
- │ │ /api/usage
99
- │ └───────────────────────────┘
124
+ │ Pi + plugin │────▶│ CliProxyAPI (:8317)
125
+ │ │ /v1/models, /v1/chat/...
126
+ │ └───────────────────────────┘
127
+ │ ┌───────────────────────────┐
128
+ │────▶│ wellknown sidecar (:3458)│
129
+ │ │ /.well-known/pi
130
+ │ │ /api/usage
131
+ │ └───────────────────────────┘
100
132
  └──────────────┘
101
133
  ```
102
134
 
@@ -143,16 +175,16 @@ Run `/cliproxy-setup` in Pi and enter:
143
175
  - **endpoint** — your public proxy URL ending with `/v1`
144
176
  - **apiKey** — CliProxyAPI bearer key
145
177
  - **providerPrefix** — short slug for custom provider names (e.g. `corp`, `myproxy`)
146
- - **usageKey** — same value as `PI_PLUGIN_USAGE_KEY` above (enables `/cliproxy-usage`)
178
+ - **usageKey** — same value as `PI_PLUGIN_USAGE_KEY` above (enables the Usage tab)
147
179
 
148
180
  The sidecar is **optional for basic usage** — without it the plugin falls back to raw `/v1/models` with local heuristics. What changes:
149
181
 
150
182
  | | With sidecar | Without sidecar |
151
183
  | --- | --- | --- |
152
184
  | Model discovery | Enriched from [models.dev](https://models.dev) (real context windows, costs, reasoning) | Defaults: `contextWindow=128k`, `maxTokens=16k`, `cost=0`, `reasoning=false` |
153
- | `/cliproxy-usage` | Works — per-account quota bars | **Does not work** (no `/api/usage` endpoint) |
185
+ | Usage tab | Works — per-account quota bars | **Does not work** (no `/api/usage` endpoint) |
154
186
  | Classification | Server-side, accurate | Local heuristics by `owned_by` |
155
- | `/cliproxy`, `/cliproxy-doctor` | Work | Work |
187
+ | `/cliproxy` hub | Works | Works (Usage tab shows an error) |
156
188
 
157
189
  ## Layout
158
190
 
@@ -160,26 +192,30 @@ The sidecar is **optional for basic usage** — without it the plugin falls back
160
192
  index.ts ExtensionFactory entry point
161
193
  src/
162
194
  config.ts ~/.config/pi-cliproxyapi/config.json
163
- commands.ts 5 slash commands
195
+ commands.ts 2 slash commands (hub + setup)
164
196
  apply.ts pi.registerProvider calls
165
197
  fetch-models.ts well-known + /v1/models fallback
166
198
  fetch-usage.ts /api/usage client with TTL cache
167
199
  compat.ts baseUrl derivation, model classification
168
200
  conflicts.ts read-only ~/.pi/{models,auth}.json scan
169
201
  ui-frame.ts single source of truth for overlay frames
170
- ui-overlay.ts scrollable overlay shell with toggles
171
202
  ui-setup.ts setup wizard
172
203
  ui-usage.ts ANSI-coloured usage renderer
173
- ui-picker/ three-panel /cliproxy overlay
174
- index.ts public runPicker entry
204
+ ui-hub/ the /cliproxy hub overlay
205
+ index.ts public runHub entry
206
+ hub.ts tabs, status header, global actions
207
+ types.ts HubView contract
208
+ shell.ts tab bar, status header, scroll/slice helpers
209
+ view-models.ts three-panel picker (single pool ordering + filter)
210
+ view-usage.ts usage tab (lazy fetch + d/v toggles)
211
+ view-diagnostics.ts diagnostics tab
212
+ ui-picker/ picker building blocks reused by the Models view
175
213
  types.ts shared TS types
176
214
  catalog.ts build a model lookup from discovery
177
215
  providers.ts resolve the providers shown in the left panel
178
- mutate.ts attach / detach / claim helpers + pool grouping
216
+ mutate.ts attach / detach / claim + pool grouping + display order
179
217
  render-text.ts ANSI-aware pad / truncate
180
218
  rows.ts per-row renderers for left / right panels
181
- picker.ts state + navigation, glues catalogue to UI
182
- picker-component.ts render + input dispatch
183
219
  prompt-confirm.ts remove-group confirmation
184
220
  prompt-name.ts new-group name prompt
185
221
  log.ts tagged logger
package/index.ts CHANGED
@@ -6,8 +6,8 @@
6
6
  * 1. load ~/.config/pi-cliproxyapi/config.json (defaults if missing)
7
7
  * 2. fetch discovery (well-known → fall back to /v1/models)
8
8
  * 3. call pi.registerProvider for each enabled built-in + custom provider
9
- * 4. register slash commands /cliproxy /cliproxy-setup /cliproxy-refresh
10
- * /cliproxy-usage /cliproxy-doctor
9
+ * 4. register slash commands /cliproxy and /cliproxy-setup
10
+ * (refresh, usage, and diagnostics are tabs/actions inside the hub)
11
11
  *
12
12
  * All discovery + apply errors are logged but never abort extension load —
13
13
  * a missing/broken proxy must not prevent Pi from starting.
@@ -43,7 +43,7 @@ export default async function cliproxyapi(pi: ExtensionAPI): Promise<void> {
43
43
  await applyAll(pi, cfg, discovery);
44
44
  } catch (err) {
45
45
  log.error("initial apply failed:", (err as Error).message);
46
- // Commands stay registered; user can /cliproxy-doctor or /cliproxy-refresh.
46
+ // Commands stay registered; user can open /cliproxy and press r to refresh.
47
47
  }
48
48
 
49
49
  if (cfg.refreshIntervalMinutes > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cliproxyapi",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "Pi extension for corporate management of model providers via a single CliProxyAPI endpoint",
6
6
  "license": "MIT",
package/src/commands.ts CHANGED
@@ -1,9 +1,8 @@
1
- // Slash commands.
2
- // /cliproxy — open the picker overlay
3
- // /cliproxy-setup — first-run / re-run setup wizard for endpoint+keys
4
- // /cliproxy-refresh — refetch discovery + re-apply
5
- // /cliproxy-usage — fetch /api/usage and render in overlay
6
- // /cliproxy-doctor — connectivity + key-resolution diagnostics
1
+ // Slash commands (2):
2
+ // /cliproxy — open the hub (Models / Usage / Diagnostics + actions)
3
+ // /cliproxy-setup — first-run / re-run wizard for endpoint+keys
4
+ //
5
+ // Refresh, usage, and diagnostics are now actions/tabs inside the hub.
7
6
 
8
7
  import type {
9
8
  ExtensionAPI,
@@ -11,20 +10,16 @@ import type {
11
10
  } from "@earendil-works/pi-coding-agent";
12
11
 
13
12
  import { applyAll } from "./apply.ts";
14
- import { loadConfig, resolveConfigValue, saveConfig } from "./config.ts";
15
- import { detectConflicts } from "./conflicts.ts";
16
- import { fetchDiscovery, PLUGIN_USER_AGENT } from "./fetch-models.ts";
17
- import { clearUsageCache, fetchUsage } from "./fetch-usage.ts";
18
- import { log } from "./log.ts";
19
- import { showOverlay } from "./ui-overlay.ts";
20
- import { runPicker } from "./ui-picker/index.ts";
13
+ import { loadConfig, resolveConfigValue } from "./config.ts";
14
+ import { fetchDiscovery } from "./fetch-models.ts";
15
+ import { clearUsageCache } from "./fetch-usage.ts";
16
+ import { runHub } from "./ui-hub/index.ts";
21
17
  import { runSetup } from "./ui-setup.ts";
22
- import { renderUsage } from "./ui-usage.ts";
23
18
 
24
19
  export function registerCommands(pi: ExtensionAPI): void {
25
20
  pi.registerCommand("cliproxy", {
26
21
  description:
27
- "Pick which models to expose via the CliProxyAPI corporate proxy",
22
+ "Manage proxy models, usage, and diagnostics in one hub overlay",
28
23
  handler: handleCliproxy.bind(null, pi),
29
24
  });
30
25
 
@@ -33,22 +28,6 @@ export function registerCommands(pi: ExtensionAPI): void {
33
28
  "Set endpoint, API key, and (optional) usage key for the proxy",
34
29
  handler: handleSetup.bind(null, pi),
35
30
  });
36
-
37
- pi.registerCommand("cliproxy-refresh", {
38
- description:
39
- "Re-fetch upstream model list and re-apply provider registrations",
40
- handler: handleRefresh.bind(null, pi),
41
- });
42
-
43
- pi.registerCommand("cliproxy-usage", {
44
- description: "Show per-account quota windows from the upstream",
45
- handler: handleUsage,
46
- });
47
-
48
- pi.registerCommand("cliproxy-doctor", {
49
- description: "Check connectivity, key resolution, and discovery shape",
50
- handler: handleDoctor,
51
- });
52
31
  }
53
32
 
54
33
  // --------------------------------------------------------------------------- /cliproxy
@@ -61,7 +40,7 @@ async function handleCliproxy(
61
40
  const cfg = loadConfig();
62
41
  if (!cfg.proxy.endpoint || !resolveConfigValue(cfg.proxy.apiKey)) {
63
42
  ctx.ui.notify(
64
- "endpoint or API key not set \u2014 launching /cliproxy-setup first",
43
+ "endpoint or API key not set \u2014 launching setup first",
65
44
  "info",
66
45
  );
67
46
  const ok = await runSetup(ctx, true);
@@ -76,17 +55,7 @@ async function handleCliproxy(
76
55
  ctx.ui.notify(`discovery failed: ${(err as Error).message}`, "error");
77
56
  return;
78
57
  }
79
- const updated = await runPicker(ctx, current, discovery);
80
- if (!updated) {
81
- ctx.ui.notify("changes discarded", "info");
82
- return;
83
- }
84
- saveConfig(updated);
85
- const rep = await applyAll(pi, updated, discovery);
86
- ctx.ui.notify(
87
- `saved \u00b7 ${rep.registered.length} providers registered, ${rep.skipped.length} skipped`,
88
- "info",
89
- );
58
+ await runHub(pi, ctx, current, discovery);
90
59
  }
91
60
 
92
61
  // --------------------------------------------------------------------------- /cliproxy-setup
@@ -118,103 +87,3 @@ async function handleSetup(
118
87
  );
119
88
  }
120
89
  }
121
-
122
- // --------------------------------------------------------------------------- /cliproxy-refresh
123
-
124
- async function handleRefresh(
125
- pi: ExtensionAPI,
126
- _args: string,
127
- ctx: ExtensionCommandContext,
128
- ): Promise<void> {
129
- const cfg = loadConfig();
130
- const resolvedKey = resolveConfigValue(cfg.proxy.apiKey);
131
- try {
132
- const discovery = await fetchDiscovery(cfg, resolvedKey);
133
- const rep = await applyAll(pi, cfg, discovery);
134
- clearUsageCache();
135
- ctx.ui.notify(
136
- `cliproxy: ${rep.registered.length} providers registered, ${rep.skipped.length} skipped (source=${discovery.source})`,
137
- "info",
138
- );
139
- } catch (err) {
140
- ctx.ui.notify(`refresh failed: ${(err as Error).message}`, "error");
141
- }
142
- }
143
-
144
- // --------------------------------------------------------------------------- /cliproxy-usage
145
-
146
- async function handleUsage(
147
- args: string,
148
- ctx: ExtensionCommandContext,
149
- ): Promise<void> {
150
- const force = /(^|\s)--refresh(\s|$)/.test(args);
151
- const cfg = loadConfig();
152
- const usageKey = resolveConfigValue(cfg.proxy.usageKey);
153
- let doc;
154
- try {
155
- doc = await fetchUsage(cfg, usageKey, { force });
156
- } catch (err) {
157
- ctx.ui.notify(`usage failed: ${(err as Error).message}`, "error");
158
- return;
159
- }
160
- await showOverlay(ctx, "cliproxy-usage", {
161
- render: (state) =>
162
- renderUsage(doc, {
163
- showDisabled: state["d"] === true,
164
- verbose: state["v"] === true,
165
- }).join("\n"),
166
- toggles: [
167
- { key: "d", hint: "d disabled" },
168
- { key: "v", hint: "v verbose" },
169
- ],
170
- });
171
- }
172
-
173
- // --------------------------------------------------------------------------- /cliproxy-doctor
174
-
175
- async function handleDoctor(
176
- _args: string,
177
- ctx: ExtensionCommandContext,
178
- ): Promise<void> {
179
- const cfg = loadConfig();
180
- const lines: string[] = [];
181
- lines.push(`endpoint: ${cfg.proxy.endpoint}`);
182
- lines.push(
183
- `apiKey resolves: ${resolveConfigValue(cfg.proxy.apiKey) ? "yes" : "NO (empty after resolution)"}`,
184
- );
185
- lines.push(
186
- `usageKey resolves: ${cfg.proxy.usageKey ? (resolveConfigValue(cfg.proxy.usageKey) ? "yes" : "NO") : "not configured"}`,
187
- );
188
- lines.push(`user-agent: ${PLUGIN_USER_AGENT}`);
189
-
190
- try {
191
- const discovery = await fetchDiscovery(
192
- cfg,
193
- resolveConfigValue(cfg.proxy.apiKey),
194
- );
195
- lines.push("");
196
- lines.push(`discovery source: ${discovery.source}`);
197
- lines.push(`upstream version: ${discovery.upstreamVersion ?? "(unknown)"}`);
198
- lines.push(`upstream total ids: ${discovery.upstreamTotal}`);
199
- lines.push(
200
- `built-in providers seen: ${discovery.builtinProviders.map((p) => `${p.name}=${p.models.length}`).join(", ") || "(none)"}`,
201
- );
202
- lines.push(`custom pool size: ${discovery.customPool.length}`);
203
- } catch (err) {
204
- lines.push("");
205
- lines.push(`discovery FAILED: ${(err as Error).message}`);
206
- }
207
-
208
- const conflicts = detectConflicts(cfg);
209
- if (conflicts.length > 0) {
210
- lines.push("");
211
- lines.push("conflicts:");
212
- for (const c of conflicts) lines.push(` [${c.kind}] ${c.detail}`);
213
- } else {
214
- lines.push("");
215
- lines.push("conflicts: none");
216
- }
217
-
218
- log.info("doctor:", lines.join(" | "));
219
- await showOverlay(ctx, "cliproxy-doctor", lines.join("\n"));
220
- }
@@ -20,7 +20,7 @@ import {
20
20
  import type { ProxyConfig } from "./config.ts";
21
21
  import { log } from "./log.ts";
22
22
 
23
- export const PLUGIN_USER_AGENT = "pi-cliproxyapi/0.1.0";
23
+ export const PLUGIN_USER_AGENT = "pi-cliproxyapi/0.3.1";
24
24
  const REQUEST_TIMEOUT_MS = 5_000;
25
25
 
26
26
  export interface DiscoveryModelEntry {
@@ -215,10 +215,7 @@ async function fetchRawModels(
215
215
  .filter((m) => m.id);
216
216
  }
217
217
 
218
- function classifyLocally(
219
- raw: RawUpstreamModel[],
220
- cfg: ProxyConfig,
221
- ): Discovery {
218
+ function classifyLocally(raw: RawUpstreamModel[], cfg: ProxyConfig): Discovery {
222
219
  const excludes = cfg.discoveryExcludes;
223
220
  const builtinByName = new Map<string, DiscoveryBuiltinProvider>();
224
221
  const customPool: DiscoveryCustomEntry[] = [];
package/src/log.ts CHANGED
@@ -1,19 +1,32 @@
1
1
  // Tiny logger that prefixes all messages with the extension tag.
2
2
  // Uses console.* directly — pi pipes stdout/stderr into its log sink.
3
+ //
4
+ // While an interactive overlay is open, console output corrupts the TUI (it
5
+ // prints over the box and forces a redraw below it — stacking headers). The
6
+ // overlay code wraps its lifetime in setLogQuiet(true) to mute us; user-facing
7
+ // results are surfaced inside the overlay instead.
3
8
 
4
9
  const TAG = "[pi-cliproxyapi]";
5
10
 
11
+ let quiet = false;
12
+
13
+ /** Mute/unmute all console output (used around interactive overlays). */
14
+ export function setLogQuiet(v: boolean): void {
15
+ quiet = v;
16
+ }
17
+
6
18
  export const log = {
7
19
  info(...args: unknown[]): void {
8
- console.log(TAG, ...args);
20
+ if (!quiet) console.log(TAG, ...args);
9
21
  },
10
22
  warn(...args: unknown[]): void {
11
- console.warn(TAG, ...args);
23
+ if (!quiet) console.warn(TAG, ...args);
12
24
  },
13
25
  error(...args: unknown[]): void {
14
- console.error(TAG, ...args);
26
+ if (!quiet) console.error(TAG, ...args);
15
27
  },
16
28
  debug(...args: unknown[]): void {
17
- if (process.env.PI_CLIPROXYAPI_DEBUG) console.log(TAG, "[debug]", ...args);
29
+ if (!quiet && process.env.PI_CLIPROXYAPI_DEBUG)
30
+ console.log(TAG, "[debug]", ...args);
18
31
  },
19
32
  };