@dianshuv/copilot-api 0.10.1 → 0.11.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 +16 -10
- package/dist/main.mjs +119 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ make down
|
|
|
57
57
|
| Option | Description | Default |
|
|
58
58
|
|--------|-------------|---------|
|
|
59
59
|
| `--port`, `-p` | Port to listen on | 4141 |
|
|
60
|
-
| `--host`, `-H` | Host/interface to bind to
|
|
60
|
+
| `--host`, `-H` | Host/interface to bind to (`0.0.0.0` = all interfaces) | `127.0.0.1` |
|
|
61
61
|
| `--verbose`, `-v` | Enable verbose logging | false |
|
|
62
62
|
| `--account-type`, `-a` | Account type (individual, business, enterprise) | individual |
|
|
63
63
|
| `--manual` | Manual request approval mode | false |
|
|
@@ -200,17 +200,23 @@ The startup banner prints whether auth is on/off and, when on, the source
|
|
|
200
200
|
|
|
201
201
|
### Network binding
|
|
202
202
|
|
|
203
|
-
The proxy **binds
|
|
204
|
-
instance is reachable from
|
|
205
|
-
|
|
203
|
+
The proxy **binds loopback (`127.0.0.1`) by default**, so an unconfigured
|
|
204
|
+
instance is reachable only from the local machine. To expose it on your network
|
|
205
|
+
— behind auth, a tunnel, or inside a container — opt into an all-interfaces
|
|
206
|
+
bind explicitly:
|
|
206
207
|
|
|
207
208
|
```sh
|
|
208
|
-
copilot-api start --host
|
|
209
|
+
copilot-api start --host 0.0.0.0
|
|
209
210
|
```
|
|
210
211
|
|
|
211
|
-
The
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
The bind host resolves with **`--host` flag > `HOST` env > `127.0.0.1`**. Note
|
|
213
|
+
that a `HOST` env var — inherited from your shell or a container — also widens
|
|
214
|
+
the bind: `HOST=0.0.0.0` opens all interfaces just like the flag. A *blank*
|
|
215
|
+
`HOST` (`HOST=`, or `HOST=$UNSET`) is ignored and falls back to the loopback
|
|
216
|
+
default, so an accidentally-empty env var can't silently expose the proxy. The
|
|
217
|
+
startup banner always reports the **real** bind address (`127.0.0.1` for the
|
|
218
|
+
loopback default, `0.0.0.0` for an all-interfaces bind, or the narrowed host you
|
|
219
|
+
passed), so a wide-open bind is never hidden.
|
|
214
220
|
|
|
215
221
|
### Choosing a key
|
|
216
222
|
|
|
@@ -277,8 +283,8 @@ a custom header, so when auth is on they stop working:
|
|
|
277
283
|
|
|
278
284
|
Workarounds:
|
|
279
285
|
|
|
280
|
-
- Run a **separate local, unauthenticated instance** (`--
|
|
281
|
-
|
|
286
|
+
- Run a **separate local, unauthenticated instance** (no `--api-key`; the
|
|
287
|
+
default `127.0.0.1` bind keeps it local) for the browser UI, or
|
|
282
288
|
- Hit the history JSON API directly with a key, e.g.
|
|
283
289
|
`curl -H "Authorization: Bearer $COPILOT_API_KEY" http://127.0.0.1:4141/history/api/entries`.
|
|
284
290
|
|
package/dist/main.mjs
CHANGED
|
@@ -1348,7 +1348,7 @@ const patchClaude = defineCommand({
|
|
|
1348
1348
|
|
|
1349
1349
|
//#endregion
|
|
1350
1350
|
//#region package.json
|
|
1351
|
-
var version = "0.
|
|
1351
|
+
var version = "0.11.1";
|
|
1352
1352
|
|
|
1353
1353
|
//#endregion
|
|
1354
1354
|
//#region src/lib/adaptive-rate-limiter.ts
|
|
@@ -1762,6 +1762,44 @@ function resolveProxyApiKey(sources) {
|
|
|
1762
1762
|
};
|
|
1763
1763
|
}
|
|
1764
1764
|
/**
|
|
1765
|
+
* Resolve the hostname the server will *actually* bind to, decided at the CLI
|
|
1766
|
+
* edge with flag-over-env precedence and a **safe loopback default** — the same
|
|
1767
|
+
* flag-over-env shape this codebase already uses to reconcile a CLI flag with
|
|
1768
|
+
* its env twin (`--api-key`/`COPILOT_API_KEY`, `--github-token`/`GH_TOKEN`).
|
|
1769
|
+
*
|
|
1770
|
+
* - `--host` flag wins when present; otherwise the `HOST` env; otherwise the
|
|
1771
|
+
* default `127.0.0.1`.
|
|
1772
|
+
* - The default is **loopback, not all-interfaces**: an unconfigured instance
|
|
1773
|
+
* must not expose `/token` (which echoes the plaintext Copilot token) and the
|
|
1774
|
+
* otherwise-unauthenticated API to the whole network. Binding every interface
|
|
1775
|
+
* is now an explicit opt-in — pass `--host 0.0.0.0` (or `HOST=0.0.0.0`).
|
|
1776
|
+
* - **flag vs env asymmetry on a blank value** (the security-critical part): an
|
|
1777
|
+
* explicit `--host` flag is the operator's deliberate choice, so a blank flag
|
|
1778
|
+
* (`--host ""` / whitespace) is taken as the wildcard-bind escape hatch and
|
|
1779
|
+
* canonicalized to an explicit `0.0.0.0` (rather than left as `""` to lean on
|
|
1780
|
+
* srvx's undocumented empty-string handling). But a *set-but-blank* `HOST` env
|
|
1781
|
+
* (`HOST=`, or `HOST=$UNSET` in a shell / compose where the var is unset →
|
|
1782
|
+
* empty — NOT a deliberate keystroke) is accidental plumbing, so it is treated
|
|
1783
|
+
* as **not provided** and falls through to the loopback default. This mirrors
|
|
1784
|
+
* `resolveProxyApiKey` trimming `""` to not-provided, so an empty `HOST` can't
|
|
1785
|
+
* silently reopen the all-interfaces-unauthenticated exposure the loopback
|
|
1786
|
+
* default exists to prevent.
|
|
1787
|
+
* - **Both sources are trimmed**: a padded `--host " 10.0.0.5 "` or
|
|
1788
|
+
* `HOST=" 10.0.0.5 "` would otherwise reach the socket bind verbatim and fail
|
|
1789
|
+
* with `ENOTFOUND`. Trimming also decides blank-ness for the rules above.
|
|
1790
|
+
*
|
|
1791
|
+
* `env` is passed in (not read here) to keep the function pure and unit-testable.
|
|
1792
|
+
*/
|
|
1793
|
+
function resolveBindHost(flag, env) {
|
|
1794
|
+
if (flag !== void 0) {
|
|
1795
|
+
const trimmed = flag.trim();
|
|
1796
|
+
return trimmed === "" ? "0.0.0.0" : trimmed;
|
|
1797
|
+
}
|
|
1798
|
+
const envTrimmed = env?.trim() ?? "";
|
|
1799
|
+
if (envTrimmed !== "") return envTrimmed;
|
|
1800
|
+
return "127.0.0.1";
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1765
1803
|
* Resolve the address the server will *actually* bind to for the startup banner
|
|
1766
1804
|
* (Issue 04).
|
|
1767
1805
|
*
|
|
@@ -8014,10 +8052,11 @@ function translateErrorToAnthropicErrorEvent(error) {
|
|
|
8014
8052
|
//#endregion
|
|
8015
8053
|
//#region src/routes/messages/tool-call-recovery.ts
|
|
8016
8054
|
const ENVELOPE = String.raw`(?:<(?:antml:)?function_calls>|call)`;
|
|
8017
|
-
const INVOKE_BODY = String.raw`<(?:antml:)?invoke\s+name="[^"]+">[\s\S]*?</(?:antml:)?invoke>`;
|
|
8018
|
-
const LEAKED_REGION_RE = new RegExp(String.raw`(?:^|\n)[ \t]
|
|
8019
|
-
const
|
|
8020
|
-
const
|
|
8055
|
+
const INVOKE_BODY = String.raw`<(?:antml:)?invoke\s+name="[^"]+">(?:(?!<(?:antml:)?invoke\b)[\s\S])*?</(?:antml:)?invoke>`;
|
|
8056
|
+
const LEAKED_REGION_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*(?:` + INVOKE_BODY + String.raw`\s*)+(?:</(?:antml:)?function_calls>)?|` + INVOKE_BODY + String.raw`)`, "g");
|
|
8057
|
+
const STARTS_WITH_ENVELOPE_RE = new RegExp(String.raw`^[ \t\n]*` + ENVELOPE);
|
|
8058
|
+
const INVOKE_OPENER_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*)?<(?:antml:)?invoke\b[^\n>]*>?`, "g");
|
|
8059
|
+
const LEAK_OPEN_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*)?<(?:antml:)?invoke\s+name="`, "g");
|
|
8021
8060
|
const INVOKE_RE = /<(?:antml:)?invoke\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?invoke>/g;
|
|
8022
8061
|
const PARAMETER_RE = /<(?:antml:)?parameter\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?parameter>/g;
|
|
8023
8062
|
function coerceParamValue(raw) {
|
|
@@ -8043,19 +8082,43 @@ function parseRegionInvokes(region, knownTools) {
|
|
|
8043
8082
|
}
|
|
8044
8083
|
return calls;
|
|
8045
8084
|
}
|
|
8085
|
+
const FENCE_DELIM_RE = /(?:^|\n)[ \t]*```/g;
|
|
8086
|
+
function insideFence(text, pos) {
|
|
8087
|
+
let openAt = -1;
|
|
8088
|
+
for (const m of text.matchAll(FENCE_DELIM_RE)) if (openAt === -1) {
|
|
8089
|
+
if (m.index >= pos) break;
|
|
8090
|
+
openAt = m.index;
|
|
8091
|
+
} else if (m.index > pos) return true;
|
|
8092
|
+
else openAt = -1;
|
|
8093
|
+
return false;
|
|
8094
|
+
}
|
|
8095
|
+
function regionIsLeak(region, offset, fullText, knownTools) {
|
|
8096
|
+
if (insideFence(fullText, offset)) return false;
|
|
8097
|
+
if (STARTS_WITH_ENVELOPE_RE.test(region)) return true;
|
|
8098
|
+
if (!knownTools) return false;
|
|
8099
|
+
return parseRegionInvokes(region, knownTools).length > 0;
|
|
8100
|
+
}
|
|
8046
8101
|
/**
|
|
8047
8102
|
* Split assistant text into ordered segments — dropping leaked envelope markup
|
|
8048
8103
|
* (and undeclared-tool invokes) while preserving the natural-language on either
|
|
8049
8104
|
* side and the pre/tool/post ordering. Returns a single text segment when there
|
|
8050
8105
|
* is no leak. Shared by both response recovery paths so they cannot diverge.
|
|
8106
|
+
*
|
|
8107
|
+
* `from` restricts the EMITTED window to `text.slice(from)` while still
|
|
8108
|
+
* classifying against the FULL `text` — so a streaming capture that started just
|
|
8109
|
+
* after an opening ``` fence still sees that fence (and the now-arrived closing
|
|
8110
|
+
* one) when deciding whether the region is documentation.
|
|
8051
8111
|
*/
|
|
8052
|
-
function recoverSegments(text, knownTools) {
|
|
8112
|
+
function recoverSegments(text, knownTools, from = 0) {
|
|
8053
8113
|
const segments = [];
|
|
8054
|
-
let cursor =
|
|
8114
|
+
let cursor = from;
|
|
8055
8115
|
let sawRegion = false;
|
|
8056
8116
|
for (const region of text.matchAll(LEAKED_REGION_RE)) {
|
|
8117
|
+
const idx = region.index;
|
|
8118
|
+
if (idx < from) continue;
|
|
8119
|
+
if (!regionIsLeak(region[0], idx, text, knownTools)) continue;
|
|
8057
8120
|
sawRegion = true;
|
|
8058
|
-
const pre = text.slice(cursor,
|
|
8121
|
+
const pre = text.slice(cursor, idx);
|
|
8059
8122
|
if (pre.trim() !== "") segments.push({
|
|
8060
8123
|
kind: "text",
|
|
8061
8124
|
text: pre
|
|
@@ -8064,11 +8127,11 @@ function recoverSegments(text, knownTools) {
|
|
|
8064
8127
|
kind: "tool",
|
|
8065
8128
|
call
|
|
8066
8129
|
});
|
|
8067
|
-
cursor =
|
|
8130
|
+
cursor = idx + region[0].length;
|
|
8068
8131
|
}
|
|
8069
8132
|
if (!sawRegion) return [{
|
|
8070
8133
|
kind: "text",
|
|
8071
|
-
text
|
|
8134
|
+
text: text.slice(from)
|
|
8072
8135
|
}];
|
|
8073
8136
|
const post = text.slice(cursor);
|
|
8074
8137
|
if (post.trim() !== "") segments.push({
|
|
@@ -8077,19 +8140,20 @@ function recoverSegments(text, knownTools) {
|
|
|
8077
8140
|
});
|
|
8078
8141
|
return segments;
|
|
8079
8142
|
}
|
|
8080
|
-
/** True when `text` contains at least one
|
|
8081
|
-
function containsLeakedToolCall(text) {
|
|
8143
|
+
/** True when `text` contains at least one real leak (see `regionIsLeak`). */
|
|
8144
|
+
function containsLeakedToolCall(text, knownTools) {
|
|
8082
8145
|
if (!text.includes("invoke")) return false;
|
|
8083
|
-
for (const
|
|
8146
|
+
for (const region of text.matchAll(LEAKED_REGION_RE)) if (regionIsLeak(region[0], region.index, text, knownTools)) return true;
|
|
8084
8147
|
return false;
|
|
8085
8148
|
}
|
|
8086
8149
|
/**
|
|
8087
|
-
* Remove
|
|
8088
|
-
* surrounding natural-language on both sides.
|
|
8150
|
+
* Remove real leaked tool-call regions from assistant text, preserving the
|
|
8151
|
+
* surrounding natural-language on both sides. Regions that are not leaks (an
|
|
8152
|
+
* envelope-less <invoke> that names no declared tool) round-trip untouched.
|
|
8089
8153
|
*/
|
|
8090
|
-
function stripLeakedToolCalls(text) {
|
|
8154
|
+
function stripLeakedToolCalls(text, knownTools) {
|
|
8091
8155
|
if (!text.includes("invoke")) return text;
|
|
8092
|
-
const stripped = text.replaceAll(LEAKED_REGION_RE, "");
|
|
8156
|
+
const stripped = text.replaceAll(LEAKED_REGION_RE, (match, offset) => regionIsLeak(match, offset, text, knownTools) ? "" : match);
|
|
8093
8157
|
if (stripped === text) return text;
|
|
8094
8158
|
return stripped.replace(/[ \t\n]+$/, "");
|
|
8095
8159
|
}
|
|
@@ -8103,14 +8167,27 @@ function toolNameSet(tools) {
|
|
|
8103
8167
|
return new Set((tools ?? []).filter((tool) => !isServerToolType(tool.type)).map((tool) => tool.name));
|
|
8104
8168
|
}
|
|
8105
8169
|
const EMPTIED_ASSISTANT_PLACEHOLDER = "[malformed tool call removed by proxy]";
|
|
8106
|
-
|
|
8170
|
+
const INVOKE_NAME_RE = /<(?:antml:)?invoke\s+name="([^"]+)"/;
|
|
8171
|
+
function findIncompleteLeakTail(text, knownTools) {
|
|
8172
|
+
let last;
|
|
8173
|
+
for (const m of text.matchAll(INVOKE_OPENER_RE)) last = m;
|
|
8174
|
+
if (!last || last.index === void 0) return -1;
|
|
8175
|
+
if (text.includes("</invoke>", last.index + last[0].length)) return -1;
|
|
8176
|
+
if (insideFence(text, last.index)) return -1;
|
|
8177
|
+
if (STARTS_WITH_ENVELOPE_RE.test(last[0])) return last.index;
|
|
8178
|
+
if (!knownTools) return -1;
|
|
8179
|
+
const name = INVOKE_NAME_RE.exec(last[0]);
|
|
8180
|
+
return name && knownTools.has(name[1]) ? last.index : -1;
|
|
8181
|
+
}
|
|
8182
|
+
function historyHasLeak(text, knownTools) {
|
|
8107
8183
|
if (!text.includes("invoke")) return false;
|
|
8108
|
-
return containsLeakedToolCall(text) ||
|
|
8184
|
+
return containsLeakedToolCall(text, knownTools) || findIncompleteLeakTail(text, knownTools) !== -1;
|
|
8109
8185
|
}
|
|
8110
|
-
function scrubHistoryText(text) {
|
|
8111
|
-
const stripped = stripLeakedToolCalls(text);
|
|
8112
|
-
const
|
|
8113
|
-
|
|
8186
|
+
function scrubHistoryText(text, knownTools) {
|
|
8187
|
+
const stripped = stripLeakedToolCalls(text, knownTools);
|
|
8188
|
+
const tailStart = findIncompleteLeakTail(stripped, knownTools);
|
|
8189
|
+
if (tailStart === -1) return stripped;
|
|
8190
|
+
return stripped.slice(0, tailStart).replace(/[ \t\n]+$/, "");
|
|
8114
8191
|
}
|
|
8115
8192
|
/**
|
|
8116
8193
|
* Request-side de-poison. Strips leaked tool-call markup (complete and truncated)
|
|
@@ -8121,6 +8198,7 @@ function scrubHistoryText(text) {
|
|
|
8121
8198
|
*/
|
|
8122
8199
|
function dePoisonAssistantMessages(payload) {
|
|
8123
8200
|
if (!payload.messages.some((m) => m.role === "assistant" && (typeof m.content === "string" ? m.content.includes("invoke") : m.content.some((b) => b.type === "text" && b.text.includes("invoke"))))) return payload;
|
|
8201
|
+
const knownTools = toolNameSet(payload.tools);
|
|
8124
8202
|
let changed = false;
|
|
8125
8203
|
const messages = [];
|
|
8126
8204
|
for (const msg of payload.messages) {
|
|
@@ -8129,26 +8207,26 @@ function dePoisonAssistantMessages(payload) {
|
|
|
8129
8207
|
continue;
|
|
8130
8208
|
}
|
|
8131
8209
|
if (typeof msg.content === "string") {
|
|
8132
|
-
if (!historyHasLeak(msg.content)) {
|
|
8210
|
+
if (!historyHasLeak(msg.content, knownTools)) {
|
|
8133
8211
|
messages.push(msg);
|
|
8134
8212
|
continue;
|
|
8135
8213
|
}
|
|
8136
8214
|
changed = true;
|
|
8137
|
-
const cleaned = scrubHistoryText(msg.content);
|
|
8215
|
+
const cleaned = scrubHistoryText(msg.content, knownTools);
|
|
8138
8216
|
messages.push({
|
|
8139
8217
|
...msg,
|
|
8140
8218
|
content: cleaned.trim() === "" ? EMPTIED_ASSISTANT_PLACEHOLDER : cleaned
|
|
8141
8219
|
});
|
|
8142
8220
|
continue;
|
|
8143
8221
|
}
|
|
8144
|
-
if (!msg.content.some((b) => b.type === "text" && historyHasLeak(b.text))) {
|
|
8222
|
+
if (!msg.content.some((b) => b.type === "text" && historyHasLeak(b.text, knownTools))) {
|
|
8145
8223
|
messages.push(msg);
|
|
8146
8224
|
continue;
|
|
8147
8225
|
}
|
|
8148
8226
|
changed = true;
|
|
8149
8227
|
const content = msg.content.flatMap((b) => {
|
|
8150
|
-
if (b.type !== "text" || !historyHasLeak(b.text)) return [b];
|
|
8151
|
-
const cleaned = scrubHistoryText(b.text);
|
|
8228
|
+
if (b.type !== "text" || !historyHasLeak(b.text, knownTools)) return [b];
|
|
8229
|
+
const cleaned = scrubHistoryText(b.text, knownTools);
|
|
8152
8230
|
return cleaned.trim() === "" ? [] : [{
|
|
8153
8231
|
...b,
|
|
8154
8232
|
text: cleaned
|
|
@@ -8184,7 +8262,7 @@ function recoverLeakedToolCallsInResponse(response, knownTools) {
|
|
|
8184
8262
|
let recoveredCall = false;
|
|
8185
8263
|
const content = [];
|
|
8186
8264
|
for (const block of response.content) {
|
|
8187
|
-
if (block.type !== "text" || !containsLeakedToolCall(block.text)) {
|
|
8265
|
+
if (block.type !== "text" || !containsLeakedToolCall(block.text, knownTools)) {
|
|
8188
8266
|
content.push(block);
|
|
8189
8267
|
continue;
|
|
8190
8268
|
}
|
|
@@ -8290,12 +8368,16 @@ var LeakedToolCallStreamRecovery = class {
|
|
|
8290
8368
|
if (t.buffer.length - t.captureStart > MAX_CAPTURE) return this.abandonCapture(t);
|
|
8291
8369
|
return [];
|
|
8292
8370
|
}
|
|
8293
|
-
if (!t.abandoned) {
|
|
8294
|
-
const open
|
|
8295
|
-
if (open) return this.beginCapture(t, open.index);
|
|
8371
|
+
if (!t.abandoned && t.buffer.includes("invoke")) {
|
|
8372
|
+
for (const open of t.buffer.matchAll(LEAK_OPEN_RE)) if (this.shouldArmCapture(open[0], t.buffer, open.index)) return this.beginCapture(t, open.index);
|
|
8296
8373
|
}
|
|
8297
8374
|
return this.flushSafe(t);
|
|
8298
8375
|
}
|
|
8376
|
+
shouldArmCapture(opener, buffer, openerIndex) {
|
|
8377
|
+
if (insideFence(buffer, openerIndex)) return false;
|
|
8378
|
+
if (STARTS_WITH_ENVELOPE_RE.test(opener)) return true;
|
|
8379
|
+
return this.knownTools !== void 0 && this.knownTools.size > 0;
|
|
8380
|
+
}
|
|
8299
8381
|
onBlockStop(event, rawData) {
|
|
8300
8382
|
const t = this.text;
|
|
8301
8383
|
if (!t || event.index !== t.upstreamIndex) return [this.reindexed(event, rawData)];
|
|
@@ -8441,7 +8523,7 @@ var LeakedToolCallStreamRecovery = class {
|
|
|
8441
8523
|
return events;
|
|
8442
8524
|
}
|
|
8443
8525
|
finishCapture(t) {
|
|
8444
|
-
const segments = recoverSegments(t.buffer
|
|
8526
|
+
const segments = recoverSegments(t.buffer, this.knownTools, t.captureStart);
|
|
8445
8527
|
const hasTool = segments.some((s) => s.kind === "tool");
|
|
8446
8528
|
const base = t.upstreamIndex + this.extraBlocks;
|
|
8447
8529
|
const events = [];
|
|
@@ -9756,7 +9838,7 @@ async function runServer(options) {
|
|
|
9756
9838
|
const visibleModels = allModels.filter((m) => !isHiddenModel(m.id, state.showAllModels));
|
|
9757
9839
|
if (visibleModels.length === 0) consola.warn("All upstream models are filtered by the hardcoded blacklist. /v1/models will return an empty list, but explicit POSTs with a hidden id still pass through to upstream. Restart with --show-all-models to see the full catalogue.");
|
|
9758
9840
|
else consola.info(`Available models:\n${visibleModels.map((m) => formatModelInfo(m)).join("\n")}`);
|
|
9759
|
-
const serverUrl = `http://${resolveClientHost(options.host,
|
|
9841
|
+
const serverUrl = `http://${resolveClientHost(options.host, void 0)}:${options.port}`;
|
|
9760
9842
|
if (options.claudeCode) {
|
|
9761
9843
|
if (visibleModels.length === 0) {
|
|
9762
9844
|
consola.error("--claude-code interactive setup needs at least one visible model. Restart with --show-all-models or update src/lib/hidden-models.ts.");
|
|
@@ -9792,7 +9874,7 @@ async function runServer(options) {
|
|
|
9792
9874
|
consola.box(`🌐 Usage Viewer: https://ericc-ch.github.io/copilot-api?endpoint=${serverUrl}/usage${options.history ? `\n📜 History UI: ${serverUrl}/history` : ""}`);
|
|
9793
9875
|
for (const line of buildStartupAuthLines({
|
|
9794
9876
|
source: options.apiKeySource,
|
|
9795
|
-
bindAddress: resolveBindAddress(options.host,
|
|
9877
|
+
bindAddress: resolveBindAddress(options.host, void 0)
|
|
9796
9878
|
})) process.stdout.write(`${line}\n`);
|
|
9797
9879
|
setupShutdownHandlers();
|
|
9798
9880
|
setServerInstance(serve({
|
|
@@ -9842,7 +9924,7 @@ const start = defineCommand({
|
|
|
9842
9924
|
host: {
|
|
9843
9925
|
alias: "H",
|
|
9844
9926
|
type: "string",
|
|
9845
|
-
description: "Host/interface to bind to
|
|
9927
|
+
description: "Host/interface to bind to. Default: 127.0.0.1 (loopback only); pass 0.0.0.0 to bind all interfaces. Falls back to the HOST env var when the flag is omitted."
|
|
9846
9928
|
},
|
|
9847
9929
|
verbose: {
|
|
9848
9930
|
alias: "v",
|
|
@@ -9968,7 +10050,7 @@ const start = defineCommand({
|
|
|
9968
10050
|
});
|
|
9969
10051
|
return runServer({
|
|
9970
10052
|
port: Number.parseInt(args.port, 10),
|
|
9971
|
-
host: args.host,
|
|
10053
|
+
host: resolveBindHost(args.host, process.env.HOST),
|
|
9972
10054
|
verbose: args.verbose,
|
|
9973
10055
|
accountType: args["account-type"],
|
|
9974
10056
|
manual: args.manual,
|