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 +67 -31
- package/index.ts +3 -3
- package/package.json +1 -1
- package/src/commands.ts +12 -143
- package/src/fetch-models.ts +2 -5
- package/src/log.ts +17 -4
- package/src/ui-hub/hub.ts +264 -0
- package/src/ui-hub/index.ts +50 -0
- package/src/ui-hub/shell.ts +119 -0
- package/src/ui-hub/types.ts +16 -0
- package/src/ui-hub/view-diagnostics.ts +108 -0
- package/src/ui-hub/view-models.ts +515 -0
- package/src/ui-hub/view-usage.ts +131 -0
- package/src/ui-picker/mutate.ts +34 -0
- package/src/ui-setup.ts +1 -1
- package/src/ui-usage.ts +1 -1
- package/src/ui-overlay.ts +0 -235
- package/src/ui-picker/index.ts +0 -35
- package/src/ui-picker/picker-component.ts +0 -432
- package/src/ui-picker/picker.ts +0 -247
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
|
[](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
|
+

|
|
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
|
-
- **
|
|
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` |
|
|
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`
|
|
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
|
-
|
|
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
|
|
34
|
-
- **right bottom** — available
|
|
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
|
+

|
|
61
|
+
|
|
62
|
+
**Usage — per-account quota windows**
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
**Diagnostics — connectivity, keys, discovery shape**
|
|
35
67
|
|
|
36
|
-
|
|
68
|
+

|
|
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 (
|
|
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
|
|
120
|
+
- `/api/usage` — per-account quota windows used by the hub Usage tab
|
|
89
121
|
|
|
90
122
|
```
|
|
91
123
|
┌──────────────┐ ┌───────────────────────────┐
|
|
92
|
-
│ Pi + plugin
|
|
93
|
-
│
|
|
94
|
-
│
|
|
95
|
-
│
|
|
96
|
-
│
|
|
97
|
-
│
|
|
98
|
-
│
|
|
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
|
|
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
|
-
|
|
|
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
|
|
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
|
|
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-
|
|
174
|
-
index.ts public
|
|
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
|
|
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
|
|
10
|
-
*
|
|
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
|
|
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
package/src/commands.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// Slash commands
|
|
2
|
-
// /cliproxy
|
|
3
|
-
// /cliproxy-setup
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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
|
-
"
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
package/src/fetch-models.ts
CHANGED
|
@@ -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
|
|
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)
|
|
29
|
+
if (!quiet && process.env.PI_CLIPROXYAPI_DEBUG)
|
|
30
|
+
console.log(TAG, "[debug]", ...args);
|
|
18
31
|
},
|
|
19
32
|
};
|