@f5xc-salesdemos/xcsh 15.9.0 → 15.9.2
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "15.9.
|
|
4
|
+
"version": "15.9.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5xc-salesdemos/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
48
48
|
"@mozilla/readability": "^0.6",
|
|
49
|
-
"@f5xc-salesdemos/xcsh-stats": "15.9.
|
|
50
|
-
"@f5xc-salesdemos/pi-agent-core": "15.9.
|
|
51
|
-
"@f5xc-salesdemos/pi-ai": "15.9.
|
|
52
|
-
"@f5xc-salesdemos/pi-natives": "15.9.
|
|
53
|
-
"@f5xc-salesdemos/pi-tui": "15.9.
|
|
54
|
-
"@f5xc-salesdemos/pi-utils": "15.9.
|
|
49
|
+
"@f5xc-salesdemos/xcsh-stats": "15.9.2",
|
|
50
|
+
"@f5xc-salesdemos/pi-agent-core": "15.9.2",
|
|
51
|
+
"@f5xc-salesdemos/pi-ai": "15.9.2",
|
|
52
|
+
"@f5xc-salesdemos/pi-natives": "15.9.2",
|
|
53
|
+
"@f5xc-salesdemos/pi-tui": "15.9.2",
|
|
54
|
+
"@f5xc-salesdemos/pi-utils": "15.9.2",
|
|
55
55
|
"@sinclair/typebox": "^0.34",
|
|
56
56
|
"@xterm/headless": "^6.0",
|
|
57
57
|
"ajv": "^8.18",
|
|
@@ -458,6 +458,18 @@ export function startupHealthCheck(
|
|
|
458
458
|
|
|
459
459
|
const expectedUrl = `${envBaseUrl}/anthropic`;
|
|
460
460
|
if (anthropicConfig.baseUrl !== expectedUrl) {
|
|
461
|
+
// Only auto-fix configs that were generated by xcsh (they contain the
|
|
462
|
+
// literal string "apiKey: LITELLM_API_KEY"). User-written configs with a
|
|
463
|
+
// custom proxy URL must never be silently overwritten.
|
|
464
|
+
let isAutoGenerated = false;
|
|
465
|
+
try {
|
|
466
|
+
const content = fs.readFileSync(modelsPath, "utf-8");
|
|
467
|
+
isAutoGenerated = content.includes("apiKey: LITELLM_API_KEY");
|
|
468
|
+
} catch {
|
|
469
|
+
// File unreadable — skip, don't block startup
|
|
470
|
+
}
|
|
471
|
+
if (!isAutoGenerated) return false;
|
|
472
|
+
|
|
461
473
|
logger.warn("LiteLLM config drift detected — auto-fixing", {
|
|
462
474
|
configured: anthropicConfig.baseUrl,
|
|
463
475
|
expected: expectedUrl,
|
|
@@ -530,6 +542,12 @@ export async function probeAndUpgradeLiteLLMConfig(
|
|
|
530
542
|
return false;
|
|
531
543
|
}
|
|
532
544
|
|
|
545
|
+
// Only upgrade configs that were auto-generated by xcsh. User-written configs
|
|
546
|
+
// with custom proxy URLs must never be silently overwritten by LiteLLM probing.
|
|
547
|
+
if (!content.includes("apiKey: LITELLM_API_KEY")) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
|
|
533
551
|
// Probe the proxy to find the working API base path
|
|
534
552
|
const probe = await probeLiteLLMConnection(baseUrl, apiKey, { fetch: options?.fetch });
|
|
535
553
|
if (!probe.reachable) {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
unregisterCustomApis,
|
|
26
26
|
unregisterOAuthProviders,
|
|
27
27
|
} from "@f5xc-salesdemos/pi-ai";
|
|
28
|
-
import { isRecord, logger } from "@f5xc-salesdemos/pi-utils";
|
|
28
|
+
import { $env, isRecord, logger } from "@f5xc-salesdemos/pi-utils";
|
|
29
29
|
import { type Static, Type } from "@sinclair/typebox";
|
|
30
30
|
import { type ConfigError, ConfigFile } from "../config";
|
|
31
31
|
import { hasLiteLLMEnv, probeAndUpgradeLiteLLMConfig, startupHealthCheck } from "../config/auto-config";
|
|
@@ -1307,12 +1307,20 @@ export class ModelRegistry {
|
|
|
1307
1307
|
// Skip providers already handled by configured discovery (e.g. user-configured ollama with discovery.type)
|
|
1308
1308
|
const configuredDiscoveryProviders = new Set(this.#discoverableProviders.map(p => p.provider));
|
|
1309
1309
|
|
|
1310
|
-
// When a LiteLLM proxy is configured, providers
|
|
1311
|
-
// proxied through it. Their built-in discovery would query
|
|
1312
|
-
// listing endpoint, which may return model IDs the proxy
|
|
1313
|
-
// Skip them — the litellm discovery provider handles
|
|
1314
|
-
|
|
1315
|
-
|
|
1310
|
+
// When a LiteLLM proxy is configured, providers whose baseUrl points to the
|
|
1311
|
+
// LiteLLM proxy are proxied through it. Their built-in discovery would query
|
|
1312
|
+
// the proxy's model listing endpoint, which may return model IDs the proxy
|
|
1313
|
+
// can't serve for chat. Skip them — the litellm discovery provider handles
|
|
1314
|
+
// model listing instead. Providers with other custom baseUrls (not LiteLLM)
|
|
1315
|
+
// still need built-in discovery to discover new models at their endpoint.
|
|
1316
|
+
const liteLLMBaseUrl = hasLiteLLMEnv() ? $env.LITELLM_BASE_URL?.trim().replace(/\/+$/, "") : undefined;
|
|
1317
|
+
const proxiedProviders = liteLLMBaseUrl
|
|
1318
|
+
? new Set(
|
|
1319
|
+
[...this.#providerOverrides.keys()].filter(id => {
|
|
1320
|
+
const override = this.#providerOverrides.get(id);
|
|
1321
|
+
return override?.baseUrl?.startsWith(liteLLMBaseUrl);
|
|
1322
|
+
}),
|
|
1323
|
+
)
|
|
1316
1324
|
: new Set<string>();
|
|
1317
1325
|
|
|
1318
1326
|
const managerOptions = (await this.#collectBuiltInModelManagerOptions()).filter(opts => {
|
|
@@ -44,18 +44,23 @@ export interface TableOptions {
|
|
|
44
44
|
dividerBefore?: number; // insert ├──┤ divider before this row index
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// Measures the visible terminal column width of a string.
|
|
48
|
+
// Delegates to Bun.stringWidth() which strips ANSI escape sequences and handles
|
|
49
|
+
// Unicode wide characters — the same underlying function used by @f5xc-salesdemos/pi-tui.
|
|
50
|
+
const visibleWidth = (s: string): number => (s ? Bun.stringWidth(s) : 0);
|
|
51
|
+
|
|
47
52
|
export function renderF5XCTable(title: string, rows: TableRow[], options?: TableOptions): string {
|
|
48
|
-
// Calculate column widths
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const innerWidth = Math.max(maxKey + maxVal +
|
|
53
|
+
// Calculate column widths using visibleWidth (handles ANSI and Unicode)
|
|
54
|
+
const maxKey = Math.max(...rows.map(row => visibleWidth(row.key)), 0);
|
|
55
|
+
const maxVal = Math.max(...rows.map(row => visibleWidth(row.value)), 0);
|
|
56
|
+
// innerWidth = space + maxKey + 2-space separator + maxVal + space = maxKey + maxVal + 4
|
|
57
|
+
const innerWidth = Math.max(maxKey + maxVal + 4, visibleWidth(title) + 2, 40);
|
|
53
58
|
|
|
54
59
|
const lines: string[] = [];
|
|
55
60
|
|
|
56
61
|
// Top border: ╭─ title ──────╮
|
|
57
62
|
const titleText = ` ${title} `;
|
|
58
|
-
const titlePad = innerWidth -
|
|
63
|
+
const titlePad = innerWidth - visibleWidth(titleText) - 1;
|
|
59
64
|
lines.push(`${r(BOX.tl + BOX.h)}${BOLD}${titleText}${RESET}${r(BOX.h.repeat(Math.max(0, titlePad)) + BOX.tr)}`);
|
|
60
65
|
|
|
61
66
|
// Rows
|
|
@@ -63,13 +68,13 @@ export function renderF5XCTable(title: string, rows: TableRow[], options?: Table
|
|
|
63
68
|
// Optional divider
|
|
64
69
|
if (options?.dividerBefore === i) {
|
|
65
70
|
const divLabel = " Environment ";
|
|
66
|
-
const divPad = innerWidth -
|
|
71
|
+
const divPad = innerWidth - visibleWidth(divLabel) - 1;
|
|
67
72
|
lines.push(`${r(BOX.lt + BOX.h)}${BOLD}${divLabel}${RESET}${r(BOX.h.repeat(Math.max(0, divPad)) + BOX.rt)}`);
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
const { key, value } = rows[i];
|
|
71
|
-
const keyPad = maxKey -
|
|
72
|
-
const valPad = innerWidth - maxKey -
|
|
76
|
+
const keyPad = maxKey - visibleWidth(key);
|
|
77
|
+
const valPad = innerWidth - maxKey - visibleWidth(value) - 4;
|
|
73
78
|
lines.push(`${r(BOX.v)} ${key}${" ".repeat(keyPad)} ${value}${" ".repeat(Math.max(0, valPad))} ${r(BOX.v)}`);
|
|
74
79
|
}
|
|
75
80
|
|