@f5xc-salesdemos/xcsh 19.28.2 → 19.29.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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "19.
|
|
4
|
+
"version": "19.29.1",
|
|
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",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
52
52
|
"@mozilla/readability": "^0.6",
|
|
53
|
-
"@f5xc-salesdemos/xcsh-stats": "19.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "19.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "19.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "19.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "19.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "19.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "19.29.1",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "19.29.1",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "19.29.1",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "19.29.1",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "19.29.1",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "19.29.1",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.20",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.29.1",
|
|
21
|
+
"commit": "87b69f04b6efe97f6f5d1f849b61ee0f01e448e0",
|
|
22
|
+
"shortCommit": "87b69f0",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.29.1",
|
|
25
|
+
"commitDate": "2026-06-12T18:13:55Z",
|
|
26
|
+
"buildDate": "2026-06-12T18:33:56.944Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5xc-salesdemos/xcsh",
|
|
30
30
|
"repoSlug": "f5xc-salesdemos/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/87b69f04b6efe97f6f5d1f849b61ee0f01e448e0",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.29.1"
|
|
33
33
|
};
|
|
@@ -666,3 +666,12 @@ export function addSection(
|
|
|
666
666
|
sections.push({ label: titled, lines });
|
|
667
667
|
}
|
|
668
668
|
}
|
|
669
|
+
|
|
670
|
+
export function humanizeResourceType(raw: string): string {
|
|
671
|
+
return raw
|
|
672
|
+
.replace(/^http_/, "")
|
|
673
|
+
.replace(/^tcp_/, "TCP ")
|
|
674
|
+
.replace(/_/g, " ")
|
|
675
|
+
.replace(/([a-z])(balancer|checker)/gi, "$1 $2")
|
|
676
|
+
.replace(/s$/, "");
|
|
677
|
+
}
|
|
@@ -8,6 +8,7 @@ import { CachedOutputBlock, F5_TOOL_BORDER_COLOR, renderStatusLine } from "../tu
|
|
|
8
8
|
import {
|
|
9
9
|
formatErrorMessage,
|
|
10
10
|
formatTimestamp,
|
|
11
|
+
humanizeResourceType,
|
|
11
12
|
addSection as pushSection,
|
|
12
13
|
replaceTabs,
|
|
13
14
|
stripEmpty,
|
|
@@ -91,7 +92,15 @@ function splitResultContent(textContent: string, isError: boolean): { json?: str
|
|
|
91
92
|
|
|
92
93
|
if (!isError) {
|
|
93
94
|
const pretty = tryPrettyJson(body);
|
|
94
|
-
|
|
95
|
+
if (pretty) return { json: pretty, raw: body };
|
|
96
|
+
// Body may contain "compactJSON\n\nAI guidance text" — split and discard guidance for display
|
|
97
|
+
const split = body.indexOf("\n\n");
|
|
98
|
+
if (split >= 0) {
|
|
99
|
+
const jsonPart = body.slice(0, split);
|
|
100
|
+
const prettyPart = tryPrettyJson(jsonPart);
|
|
101
|
+
if (prettyPart) return { json: prettyPart, raw: body };
|
|
102
|
+
}
|
|
103
|
+
return { raw: body };
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
// Error: body is "compactJSON\n\nguidanceText"
|
|
@@ -263,15 +272,10 @@ export const xcshApiToolRenderer = {
|
|
|
263
272
|
const emptyList = Array.isArray(parsed?.items) && (parsed!.items as unknown[]).length === 0;
|
|
264
273
|
|
|
265
274
|
if ((emptyBody || emptyList) && !guidance) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const rn = resourceName ? ` \u2018${resourceName}\u2019` : "";
|
|
269
|
-
if (!emptyList && status >= 200 && status < 300) {
|
|
270
|
-
if (method === "DELETE") successMessage = `Resource${rn} deleted successfully.`;
|
|
271
|
-
else if (method === "POST") successMessage = `Resource${rn} created successfully.`;
|
|
272
|
-
else if (method === "PUT" || method === "PATCH") successMessage = `Resource${rn} updated successfully.`;
|
|
275
|
+
if (emptyList) {
|
|
276
|
+
addSection("Response", [uiTheme.fg("dim", "No items found.")]);
|
|
273
277
|
}
|
|
274
|
-
|
|
278
|
+
// Empty mutation bodies ({}) are handled by the Result section below \u2014 no Response section needed
|
|
275
279
|
} else if (json && parsed) {
|
|
276
280
|
// Branch 1: List response with named items — compact summary
|
|
277
281
|
const items = parsed.items;
|
|
@@ -343,15 +347,38 @@ export const xcshApiToolRenderer = {
|
|
|
343
347
|
);
|
|
344
348
|
}
|
|
345
349
|
|
|
346
|
-
// Section
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
350
|
+
// Section: Result — human-readable glyph status at the bottom of the panel
|
|
351
|
+
{
|
|
352
|
+
const resultLines: string[] = [];
|
|
353
|
+
const infoIcon = uiTheme.styledSymbol("status.success", "chromeAccent");
|
|
354
|
+
const errorIcon = uiTheme.styledSymbol("status.error", "error");
|
|
355
|
+
|
|
356
|
+
if (isError) {
|
|
357
|
+
const apiMessage =
|
|
358
|
+
parsed && typeof parsed.message === "string" ? stripProtobufPrefix(parsed.message) : undefined;
|
|
359
|
+
const errorLabel = apiMessage ?? (status > 0 ? `Request failed (${status})` : "Request failed");
|
|
360
|
+
resultLines.push(`${errorIcon} ${uiTheme.fg("warning", errorLabel)}`);
|
|
361
|
+
if (guidance) resultLines.push(` ${uiTheme.fg("dim", `Try: ${guidance}`)}`);
|
|
362
|
+
} else if (details?.mutationVerb && details?.resourceLabel) {
|
|
363
|
+
resultLines.push(`${infoIcon} ${details.mutationVerb} ${details.resourceLabel}`);
|
|
364
|
+
} else if (method === "GET" && parsed) {
|
|
365
|
+
const items = parsed.items;
|
|
366
|
+
if (Array.isArray(items)) {
|
|
367
|
+
const rawType = pathParts.at(-1) ?? "items";
|
|
368
|
+
const typeLabel = humanizeResourceType(rawType);
|
|
369
|
+
const plural = items.length === 1 ? typeLabel : `${typeLabel}s`;
|
|
370
|
+
resultLines.push(`${infoIcon} ${items.length} ${plural}`);
|
|
371
|
+
} else {
|
|
372
|
+
const rawType = pathParts.at(-2) ?? "";
|
|
373
|
+
const name = resourceName ?? "";
|
|
374
|
+
const label = rawType && name ? `${humanizeResourceType(rawType)} '${name}'` : displayPath;
|
|
375
|
+
resultLines.push(`${infoIcon} Loaded ${label}`);
|
|
376
|
+
}
|
|
377
|
+
} else if (status >= 200 && status < 300) {
|
|
378
|
+
resultLines.push(`${infoIcon} OK`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (resultLines.length > 0) addSection("Result", resultLines);
|
|
355
382
|
}
|
|
356
383
|
|
|
357
384
|
// --- Render with CachedOutputBlock ---
|
package/src/tools/xcsh-api.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type Static, Type } from "@sinclair/typebox";
|
|
|
5
5
|
import xcshApiDescription from "../prompts/tools/xcsh-api.md" with { type: "text" };
|
|
6
6
|
import { type ContextEnv, createContextEnv } from "../services/context-env";
|
|
7
7
|
import type { ToolSession } from ".";
|
|
8
|
+
import { humanizeResourceType } from "./render-utils";
|
|
8
9
|
|
|
9
10
|
// Namespace filtering driven by x-f5xc-namespace-profile from enriched API specs.
|
|
10
11
|
|
|
@@ -108,6 +109,10 @@ export interface XcshApiToolDetails {
|
|
|
108
109
|
batchTotalItems?: number;
|
|
109
110
|
/** The resolved JSON body string sent to the API (after $F5XC_* expansion). */
|
|
110
111
|
resolvedPayload?: string;
|
|
112
|
+
/** Human-readable verb for mutation results. */
|
|
113
|
+
mutationVerb?: "Created" | "Updated" | "Deleted";
|
|
114
|
+
/** Human-readable resource label, e.g. "load balancer 'rm-headers'". */
|
|
115
|
+
resourceLabel?: string;
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
type XcshApiResult = AgentToolResult<XcshApiToolDetails> & { isError?: boolean };
|
|
@@ -638,6 +643,10 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
638
643
|
}
|
|
639
644
|
case 409:
|
|
640
645
|
return `Resource already exists${ctxHint}. Use PUT to replace the existing resource, or DELETE it first before creating a new one.`;
|
|
646
|
+
case 400:
|
|
647
|
+
return `Bad request${ctxHint}. Check the payload for missing required fields or invalid values.`;
|
|
648
|
+
case 422:
|
|
649
|
+
return `Validation failed${ctxHint}. The request payload has field-level errors — check the message above.`;
|
|
641
650
|
case 429:
|
|
642
651
|
return `API rate limit exceeded${ctxHint}. Wait briefly and retry the request.`;
|
|
643
652
|
default:
|
|
@@ -817,13 +826,6 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
817
826
|
// Label changes help the model echo type names in its response (Finding 13).
|
|
818
827
|
// INTERACTION EFFECT: this change + TCP protocol label are synergistic (Finding 31).
|
|
819
828
|
const pathSegs = resolvedPath.split("/").filter(Boolean);
|
|
820
|
-
const humanizeResourceType = (raw: string): string =>
|
|
821
|
-
raw
|
|
822
|
-
.replace(/^http_/, "")
|
|
823
|
-
.replace(/^tcp_/, "TCP ")
|
|
824
|
-
.replace(/_/g, " ")
|
|
825
|
-
.replace(/([a-z])(balancer|checker)/gi, "$1 $2")
|
|
826
|
-
.replace(/s$/, "");
|
|
827
829
|
let resourceLabel: string;
|
|
828
830
|
if (params.method === "POST") {
|
|
829
831
|
const rawType = pathSegs.at(-1) ?? "";
|
|
@@ -835,6 +837,9 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
835
837
|
const rawType = pathSegs.at(-2) ?? "";
|
|
836
838
|
resourceLabel = name && rawType ? `${humanizeResourceType(rawType)} ${name}` : resolvedPath;
|
|
837
839
|
}
|
|
840
|
+
detail.mutationVerb = verb as "Created" | "Updated" | "Deleted";
|
|
841
|
+
detail.resourceLabel = resourceLabel;
|
|
842
|
+
|
|
838
843
|
// POST returns the full resource; PUT/DELETE return {}.
|
|
839
844
|
// Only claim response contains the resource for POST to avoid misleading the model.
|
|
840
845
|
const resourceHint =
|