@robinmordasiewicz/f5xc-xcsh 6.43.0 → 6.45.0
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/dist/index.js +1985 -1680
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45354,8 +45354,8 @@ function getLogoModeFromEnv(envPrefix) {
|
|
|
45354
45354
|
var CLI_NAME = "xcsh";
|
|
45355
45355
|
var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
|
|
45356
45356
|
function getVersion() {
|
|
45357
|
-
if ("6.
|
|
45358
|
-
return "6.
|
|
45357
|
+
if ("6.45.0") {
|
|
45358
|
+
return "6.45.0";
|
|
45359
45359
|
}
|
|
45360
45360
|
if (process.env.XCSH_VERSION) {
|
|
45361
45361
|
return process.env.XCSH_VERSION;
|
|
@@ -46540,1819 +46540,2113 @@ function formatAPIError(statusCode, body, operation) {
|
|
|
46540
46540
|
return lines.join("\n");
|
|
46541
46541
|
}
|
|
46542
46542
|
|
|
46543
|
-
// src/
|
|
46544
|
-
|
|
46545
|
-
|
|
46546
|
-
|
|
46547
|
-
description:
|
|
46548
|
-
|
|
46549
|
-
|
|
46550
|
-
|
|
46551
|
-
|
|
46552
|
-
|
|
46553
|
-
|
|
46554
|
-
|
|
46543
|
+
// src/config/envvars.ts
|
|
46544
|
+
var EnvVarRegistry = [
|
|
46545
|
+
{
|
|
46546
|
+
name: `${ENV_PREFIX}_API_URL`,
|
|
46547
|
+
description: "API endpoint URL",
|
|
46548
|
+
relatedFlag: "",
|
|
46549
|
+
required: true
|
|
46550
|
+
},
|
|
46551
|
+
{
|
|
46552
|
+
name: `${ENV_PREFIX}_API_TOKEN`,
|
|
46553
|
+
description: "API authentication token",
|
|
46554
|
+
relatedFlag: "",
|
|
46555
|
+
required: true
|
|
46556
|
+
},
|
|
46557
|
+
{
|
|
46558
|
+
name: `${ENV_PREFIX}_NAMESPACE`,
|
|
46559
|
+
description: "Default namespace",
|
|
46560
|
+
relatedFlag: "-ns"
|
|
46561
|
+
},
|
|
46562
|
+
{
|
|
46563
|
+
name: `${ENV_PREFIX}_OUTPUT_FORMAT`,
|
|
46564
|
+
description: "Output format (json, yaml, table)",
|
|
46565
|
+
relatedFlag: "-o"
|
|
46566
|
+
},
|
|
46567
|
+
{
|
|
46568
|
+
name: `${ENV_PREFIX}_LOGO`,
|
|
46569
|
+
description: "Logo display mode (auto, image, ascii, both, none)",
|
|
46570
|
+
relatedFlag: "--logo"
|
|
46571
|
+
},
|
|
46572
|
+
{
|
|
46573
|
+
name: "NO_COLOR",
|
|
46574
|
+
description: "Disable color output",
|
|
46575
|
+
relatedFlag: "--no-color"
|
|
46555
46576
|
}
|
|
46556
|
-
|
|
46557
|
-
|
|
46577
|
+
];
|
|
46578
|
+
function formatEnvVarsSection() {
|
|
46579
|
+
const maxLen = Math.max(...EnvVarRegistry.map((e) => e.name.length));
|
|
46580
|
+
const lines = ["ENVIRONMENT VARIABLES"];
|
|
46581
|
+
for (const env3 of EnvVarRegistry) {
|
|
46582
|
+
const padding = " ".repeat(maxLen - env3.name.length + 3);
|
|
46583
|
+
const flagNote = env3.relatedFlag ? ` [${env3.relatedFlag}]` : "";
|
|
46584
|
+
lines.push(` ${env3.name}${padding}${env3.description}${flagNote}`);
|
|
46558
46585
|
}
|
|
46559
|
-
return
|
|
46586
|
+
return lines;
|
|
46560
46587
|
}
|
|
46561
|
-
function
|
|
46562
|
-
return
|
|
46588
|
+
function formatConfigSection() {
|
|
46589
|
+
return [
|
|
46590
|
+
"CONFIGURATION",
|
|
46591
|
+
` Config file: ~/${CONFIG_FILE_NAME}`,
|
|
46592
|
+
" Priority: CLI flags > environment variables > config file > defaults",
|
|
46593
|
+
"",
|
|
46594
|
+
"DOCUMENTATION",
|
|
46595
|
+
` ${DOCS_URL}`
|
|
46596
|
+
];
|
|
46563
46597
|
}
|
|
46564
|
-
|
|
46565
|
-
|
|
46566
|
-
|
|
46567
|
-
|
|
46568
|
-
|
|
46569
|
-
|
|
46570
|
-
|
|
46571
|
-
|
|
46572
|
-
|
|
46573
|
-
|
|
46574
|
-
|
|
46575
|
-
|
|
46576
|
-
|
|
46577
|
-
|
|
46578
|
-
|
|
46579
|
-
|
|
46580
|
-
|
|
46581
|
-
|
|
46582
|
-
|
|
46583
|
-
{
|
|
46584
|
-
command: "xcsh cloudstatus status --quiet && echo 'All systems operational'",
|
|
46585
|
-
description: "Use in scripts for health checks"
|
|
46586
|
-
},
|
|
46587
|
-
{
|
|
46588
|
-
command: "xcsh cloudstatus status --output json",
|
|
46589
|
-
description: "Get status as JSON for automation"
|
|
46590
|
-
}
|
|
46591
|
-
],
|
|
46592
|
-
category: "cloudstatus",
|
|
46593
|
-
related: [
|
|
46594
|
-
"cloudstatus summary",
|
|
46595
|
-
"cloudstatus components",
|
|
46596
|
-
"cloudstatus incidents"
|
|
46597
|
-
]
|
|
46598
|
-
}),
|
|
46599
|
-
summary: buildCommandSpec({
|
|
46600
|
-
command: "cloudstatus summary",
|
|
46601
|
-
description: "Get complete status summary including overall health, component status, and active incidents.",
|
|
46602
|
-
usage: "xcsh cloudstatus summary",
|
|
46603
|
-
examples: [
|
|
46604
|
-
{
|
|
46605
|
-
command: "xcsh cloudstatus summary",
|
|
46606
|
-
description: "View full infrastructure health overview"
|
|
46607
|
-
},
|
|
46608
|
-
{
|
|
46609
|
-
command: "xcsh cloudstatus summary --output json",
|
|
46610
|
-
description: "Get complete summary as JSON"
|
|
46611
|
-
}
|
|
46612
|
-
],
|
|
46613
|
-
category: "cloudstatus",
|
|
46614
|
-
related: ["cloudstatus status", "cloudstatus components"]
|
|
46615
|
-
}),
|
|
46616
|
-
components: buildCommandSpec({
|
|
46617
|
-
command: "cloudstatus components",
|
|
46618
|
-
description: "List all infrastructure components and their current operational status.",
|
|
46619
|
-
usage: "xcsh cloudstatus components",
|
|
46620
|
-
examples: [
|
|
46621
|
-
{
|
|
46622
|
-
command: "xcsh cloudstatus components",
|
|
46623
|
-
description: "List all components with status"
|
|
46624
|
-
},
|
|
46625
|
-
{
|
|
46626
|
-
command: "xcsh cloudstatus components --output json",
|
|
46627
|
-
description: "Get components as JSON for monitoring integration"
|
|
46628
|
-
}
|
|
46629
|
-
],
|
|
46630
|
-
category: "cloudstatus",
|
|
46631
|
-
related: ["cloudstatus status", "cloudstatus summary"]
|
|
46632
|
-
}),
|
|
46633
|
-
incidents: buildCommandSpec({
|
|
46634
|
-
command: "cloudstatus incidents",
|
|
46635
|
-
description: "List active and recent incidents affecting F5 Distributed Cloud services.",
|
|
46636
|
-
usage: "xcsh cloudstatus incidents",
|
|
46637
|
-
examples: [
|
|
46638
|
-
{
|
|
46639
|
-
command: "xcsh cloudstatus incidents",
|
|
46640
|
-
description: "View active incidents"
|
|
46641
|
-
},
|
|
46642
|
-
{
|
|
46643
|
-
command: "xcsh cloudstatus incidents --output json",
|
|
46644
|
-
description: "Get incidents as JSON for alerting systems"
|
|
46645
|
-
}
|
|
46646
|
-
],
|
|
46647
|
-
category: "cloudstatus",
|
|
46648
|
-
related: ["cloudstatus status", "cloudstatus maintenance"]
|
|
46649
|
-
}),
|
|
46650
|
-
maintenance: buildCommandSpec({
|
|
46651
|
-
command: "cloudstatus maintenance",
|
|
46652
|
-
description: "List scheduled maintenance windows for F5 Distributed Cloud services.",
|
|
46653
|
-
usage: "xcsh cloudstatus maintenance",
|
|
46654
|
-
examples: [
|
|
46655
|
-
{
|
|
46656
|
-
command: "xcsh cloudstatus maintenance",
|
|
46657
|
-
description: "View upcoming maintenance windows"
|
|
46658
|
-
},
|
|
46659
|
-
{
|
|
46660
|
-
command: "xcsh cloudstatus maintenance --output json",
|
|
46661
|
-
description: "Get maintenance schedule as JSON"
|
|
46662
|
-
}
|
|
46663
|
-
],
|
|
46664
|
-
category: "cloudstatus",
|
|
46665
|
-
related: ["cloudstatus status", "cloudstatus incidents"]
|
|
46666
|
-
})
|
|
46667
|
-
};
|
|
46598
|
+
|
|
46599
|
+
// src/repl/help.ts
|
|
46600
|
+
function wrapText3(text, width, indent) {
|
|
46601
|
+
const prefix = " ".repeat(indent);
|
|
46602
|
+
const words = text.split(/\s+/);
|
|
46603
|
+
const lines = [];
|
|
46604
|
+
let currentLine = prefix;
|
|
46605
|
+
for (const word of words) {
|
|
46606
|
+
if (currentLine.length + word.length + 1 > width && currentLine !== prefix) {
|
|
46607
|
+
lines.push(currentLine);
|
|
46608
|
+
currentLine = prefix + word;
|
|
46609
|
+
} else {
|
|
46610
|
+
currentLine += (currentLine === prefix ? "" : " ") + word;
|
|
46611
|
+
}
|
|
46612
|
+
}
|
|
46613
|
+
if (currentLine.trim()) {
|
|
46614
|
+
lines.push(currentLine);
|
|
46615
|
+
}
|
|
46616
|
+
return lines;
|
|
46668
46617
|
}
|
|
46669
|
-
function
|
|
46670
|
-
return
|
|
46671
|
-
|
|
46672
|
-
|
|
46673
|
-
|
|
46674
|
-
|
|
46675
|
-
|
|
46676
|
-
|
|
46677
|
-
|
|
46678
|
-
|
|
46679
|
-
|
|
46680
|
-
|
|
46681
|
-
|
|
46682
|
-
|
|
46683
|
-
}
|
|
46684
|
-
|
|
46685
|
-
|
|
46686
|
-
|
|
46687
|
-
|
|
46688
|
-
|
|
46689
|
-
|
|
46690
|
-
|
|
46691
|
-
|
|
46692
|
-
|
|
46693
|
-
|
|
46694
|
-
|
|
46695
|
-
|
|
46696
|
-
|
|
46697
|
-
|
|
46698
|
-
|
|
46699
|
-
|
|
46700
|
-
|
|
46701
|
-
|
|
46702
|
-
|
|
46703
|
-
|
|
46704
|
-
|
|
46705
|
-
"
|
|
46706
|
-
|
|
46707
|
-
|
|
46708
|
-
|
|
46709
|
-
|
|
46710
|
-
|
|
46711
|
-
name: "name",
|
|
46712
|
-
description: "Profile name to show (optional, defaults to active)",
|
|
46713
|
-
type: "string"
|
|
46714
|
-
}
|
|
46715
|
-
],
|
|
46716
|
-
examples: [
|
|
46717
|
-
{
|
|
46718
|
-
command: "xcsh login profile show",
|
|
46719
|
-
description: "Show active profile"
|
|
46720
|
-
},
|
|
46721
|
-
{
|
|
46722
|
-
command: "xcsh login profile show production",
|
|
46723
|
-
description: "Show specific profile"
|
|
46724
|
-
}
|
|
46725
|
-
],
|
|
46726
|
-
category: "login",
|
|
46727
|
-
related: ["login profile list", "login profile use"]
|
|
46728
|
-
}),
|
|
46729
|
-
"profile create": buildCommandSpec({
|
|
46730
|
-
command: "login profile create",
|
|
46731
|
-
description: "Create a new connection profile with URL and credentials.",
|
|
46732
|
-
usage: "xcsh login profile create <name>",
|
|
46733
|
-
flags: [
|
|
46734
|
-
{
|
|
46735
|
-
name: "name",
|
|
46736
|
-
description: "Profile name",
|
|
46737
|
-
type: "string",
|
|
46738
|
-
required: true
|
|
46739
|
-
}
|
|
46740
|
-
],
|
|
46741
|
-
examples: [
|
|
46742
|
-
{
|
|
46743
|
-
command: "xcsh login profile create production",
|
|
46744
|
-
description: "Create a new profile named 'production'"
|
|
46745
|
-
}
|
|
46746
|
-
],
|
|
46747
|
-
category: "login",
|
|
46748
|
-
related: ["login profile list", "login profile use"]
|
|
46749
|
-
}),
|
|
46750
|
-
"profile use": buildCommandSpec({
|
|
46751
|
-
command: "login profile use",
|
|
46752
|
-
description: "Switch to a different connection profile.",
|
|
46753
|
-
usage: "xcsh login profile use <name>",
|
|
46754
|
-
flags: [
|
|
46755
|
-
{
|
|
46756
|
-
name: "name",
|
|
46757
|
-
description: "Profile name to activate",
|
|
46758
|
-
type: "string",
|
|
46759
|
-
required: true
|
|
46760
|
-
}
|
|
46761
|
-
],
|
|
46762
|
-
examples: [
|
|
46763
|
-
{
|
|
46764
|
-
command: "xcsh login profile use staging",
|
|
46765
|
-
description: "Switch to staging profile"
|
|
46766
|
-
}
|
|
46767
|
-
],
|
|
46768
|
-
category: "login",
|
|
46769
|
-
related: ["login profile list", "login profile show"]
|
|
46770
|
-
}),
|
|
46771
|
-
"context show": buildCommandSpec({
|
|
46772
|
-
command: "login context show",
|
|
46773
|
-
description: "Show the current default namespace context.",
|
|
46774
|
-
usage: "xcsh login context show",
|
|
46775
|
-
examples: [
|
|
46776
|
-
{
|
|
46777
|
-
command: "xcsh login context show",
|
|
46778
|
-
description: "Display current namespace"
|
|
46779
|
-
}
|
|
46780
|
-
],
|
|
46781
|
-
category: "login",
|
|
46782
|
-
related: ["login context set", "login context list"]
|
|
46783
|
-
}),
|
|
46784
|
-
"context set": buildCommandSpec({
|
|
46785
|
-
command: "login context set",
|
|
46786
|
-
description: "Set the default namespace for subsequent operations.",
|
|
46787
|
-
usage: "xcsh login context set <namespace>",
|
|
46788
|
-
flags: [
|
|
46789
|
-
{
|
|
46790
|
-
name: "namespace",
|
|
46791
|
-
description: "Namespace to set as default",
|
|
46792
|
-
type: "string",
|
|
46793
|
-
required: true
|
|
46794
|
-
}
|
|
46795
|
-
],
|
|
46796
|
-
examples: [
|
|
46797
|
-
{
|
|
46798
|
-
command: "xcsh login context set production",
|
|
46799
|
-
description: "Set production as default namespace"
|
|
46800
|
-
}
|
|
46801
|
-
],
|
|
46802
|
-
category: "login",
|
|
46803
|
-
related: ["login context show", "login context list"]
|
|
46804
|
-
})
|
|
46805
|
-
};
|
|
46618
|
+
function formatRootHelp() {
|
|
46619
|
+
return [
|
|
46620
|
+
"",
|
|
46621
|
+
colorBoldWhite(`${CLI_NAME} - ${CLI_FULL_NAME} v${CLI_VERSION}`),
|
|
46622
|
+
"",
|
|
46623
|
+
"DESCRIPTION",
|
|
46624
|
+
...wrapText3(CLI_DESCRIPTION_LONG, 80, 2),
|
|
46625
|
+
"",
|
|
46626
|
+
"USAGE",
|
|
46627
|
+
` ${CLI_NAME} Enter interactive REPL mode`,
|
|
46628
|
+
` ${CLI_NAME} <domain> <action> Execute command non-interactively`,
|
|
46629
|
+
` ${CLI_NAME} help [topic] Show help for a topic`,
|
|
46630
|
+
"",
|
|
46631
|
+
"EXAMPLES",
|
|
46632
|
+
` ${CLI_NAME} tenant_and_identity list namespace List all namespaces`,
|
|
46633
|
+
` ${CLI_NAME} virtual get http_loadbalancer Get a specific load balancer`,
|
|
46634
|
+
` ${CLI_NAME} dns list List DNS zones`,
|
|
46635
|
+
` ${CLI_NAME} waf list -ns prod List WAF policies in prod`,
|
|
46636
|
+
"",
|
|
46637
|
+
...formatDomainsSection(),
|
|
46638
|
+
"",
|
|
46639
|
+
...formatGlobalFlags(),
|
|
46640
|
+
"",
|
|
46641
|
+
...formatEnvVarsSection(),
|
|
46642
|
+
"",
|
|
46643
|
+
...formatConfigSection(),
|
|
46644
|
+
"",
|
|
46645
|
+
"NAVIGATION (Interactive Mode)",
|
|
46646
|
+
" <domain> Navigate into a domain (e.g., 'dns', 'lb')",
|
|
46647
|
+
" /domain Navigate directly to domain from anywhere",
|
|
46648
|
+
" .. Go up one level",
|
|
46649
|
+
" / Return to root",
|
|
46650
|
+
" context Show current navigation context",
|
|
46651
|
+
"",
|
|
46652
|
+
"BUILTINS",
|
|
46653
|
+
" help Show this help",
|
|
46654
|
+
" domains List all available domains",
|
|
46655
|
+
" clear Clear the screen",
|
|
46656
|
+
" history Show command history",
|
|
46657
|
+
" quit, exit Exit the shell",
|
|
46658
|
+
""
|
|
46659
|
+
];
|
|
46806
46660
|
}
|
|
46807
|
-
function
|
|
46808
|
-
|
|
46809
|
-
|
|
46810
|
-
|
|
46811
|
-
|
|
46812
|
-
|
|
46813
|
-
|
|
46661
|
+
function formatGlobalFlags() {
|
|
46662
|
+
return [
|
|
46663
|
+
"GLOBAL FLAGS",
|
|
46664
|
+
" -v, --version Show version number",
|
|
46665
|
+
" -h, --help Show this help",
|
|
46666
|
+
" --no-color Disable color output",
|
|
46667
|
+
" -o, --output <fmt> Output format (json, yaml, table)",
|
|
46668
|
+
" -ns, --namespace <ns> Target namespace",
|
|
46669
|
+
" --spec Output command specification as JSON (for AI)"
|
|
46670
|
+
];
|
|
46671
|
+
}
|
|
46672
|
+
function formatEnvironmentVariables() {
|
|
46673
|
+
return formatEnvVarsSection();
|
|
46674
|
+
}
|
|
46675
|
+
function formatDomainHelp(domain) {
|
|
46676
|
+
const output = ["", colorBoldWhite(`${domain.displayName}`), ""];
|
|
46677
|
+
output.push(` ${domain.description}`);
|
|
46678
|
+
output.push("");
|
|
46679
|
+
if (domain.category || domain.complexity) {
|
|
46680
|
+
const meta = [];
|
|
46681
|
+
if (domain.category) meta.push(`Category: ${domain.category}`);
|
|
46682
|
+
if (domain.complexity) meta.push(`Complexity: ${domain.complexity}`);
|
|
46683
|
+
output.push(colorDim(` ${meta.join(" | ")}`));
|
|
46684
|
+
output.push("");
|
|
46814
46685
|
}
|
|
46815
|
-
|
|
46816
|
-
|
|
46817
|
-
|
|
46686
|
+
output.push("USAGE");
|
|
46687
|
+
output.push(` ${CLI_NAME} ${domain.name} <action> [options]`);
|
|
46688
|
+
output.push("");
|
|
46689
|
+
output.push("ACTIONS");
|
|
46690
|
+
const actionDescriptions2 = {
|
|
46691
|
+
list: "List resources",
|
|
46692
|
+
get: "Get a specific resource by name",
|
|
46693
|
+
create: "Create a new resource",
|
|
46694
|
+
delete: "Delete a resource",
|
|
46695
|
+
replace: "Replace a resource configuration",
|
|
46696
|
+
apply: "Apply configuration from file",
|
|
46697
|
+
status: "Get resource status",
|
|
46698
|
+
patch: "Patch a resource",
|
|
46699
|
+
"add-labels": "Add labels to a resource",
|
|
46700
|
+
"remove-labels": "Remove labels from a resource"
|
|
46701
|
+
};
|
|
46702
|
+
for (const action of validActions) {
|
|
46703
|
+
const desc = actionDescriptions2[action] ?? action;
|
|
46704
|
+
output.push(` ${action.padEnd(16)} ${desc}`);
|
|
46818
46705
|
}
|
|
46819
|
-
|
|
46820
|
-
|
|
46821
|
-
|
|
46822
|
-
|
|
46823
|
-
|
|
46824
|
-
|
|
46825
|
-
|
|
46826
|
-
|
|
46827
|
-
|
|
46828
|
-
|
|
46829
|
-
|
|
46830
|
-
|
|
46831
|
-
|
|
46832
|
-
_serverUrl = "";
|
|
46833
|
-
_apiToken = "";
|
|
46834
|
-
_apiClient = null;
|
|
46835
|
-
_outputFormat = "table";
|
|
46836
|
-
_debug = false;
|
|
46837
|
-
_profileManager;
|
|
46838
|
-
_activeProfile = null;
|
|
46839
|
-
_activeProfileName = null;
|
|
46840
|
-
_namespaceCache = [];
|
|
46841
|
-
_namespaceCacheTime = 0;
|
|
46842
|
-
// Token validation state
|
|
46843
|
-
_tokenValidated = false;
|
|
46844
|
-
_validationError = null;
|
|
46845
|
-
constructor(config = {}) {
|
|
46846
|
-
this._namespace = config.namespace ?? this.getDefaultNamespace();
|
|
46847
|
-
this._contextPath = new ContextPath();
|
|
46848
|
-
this._validator = new ContextValidator();
|
|
46849
|
-
this._profileManager = getProfileManager();
|
|
46850
|
-
this._serverUrl = config.serverUrl ?? process.env[`${ENV_PREFIX}_API_URL`] ?? "";
|
|
46851
|
-
this._apiToken = config.apiToken ?? process.env[`${ENV_PREFIX}_API_TOKEN`] ?? "";
|
|
46852
|
-
this._outputFormat = config.outputFormat ?? getOutputFormatFromEnv() ?? "table";
|
|
46853
|
-
this._debug = config.debug ?? process.env[`${ENV_PREFIX}_DEBUG`] === "true";
|
|
46854
|
-
if (this._serverUrl) {
|
|
46855
|
-
this._tenant = this.extractTenant(this._serverUrl);
|
|
46856
|
-
}
|
|
46857
|
-
if (this._serverUrl) {
|
|
46858
|
-
this._apiClient = new APIClient({
|
|
46859
|
-
serverUrl: this._serverUrl,
|
|
46860
|
-
apiToken: this._apiToken,
|
|
46861
|
-
debug: this._debug
|
|
46862
|
-
});
|
|
46706
|
+
output.push("");
|
|
46707
|
+
output.push("EXAMPLES");
|
|
46708
|
+
output.push(` ${CLI_NAME} ${domain.name} list`);
|
|
46709
|
+
output.push(` ${CLI_NAME} ${domain.name} get my-resource`);
|
|
46710
|
+
output.push(
|
|
46711
|
+
` ${CLI_NAME} ${domain.name} create my-resource -f config.yaml`
|
|
46712
|
+
);
|
|
46713
|
+
output.push(` ${CLI_NAME} ${domain.name} delete my-resource`);
|
|
46714
|
+
output.push("");
|
|
46715
|
+
if (domain.useCases && domain.useCases.length > 0) {
|
|
46716
|
+
output.push("USE CASES");
|
|
46717
|
+
for (const useCase of domain.useCases.slice(0, 5)) {
|
|
46718
|
+
output.push(` - ${useCase}`);
|
|
46863
46719
|
}
|
|
46720
|
+
output.push("");
|
|
46864
46721
|
}
|
|
46865
|
-
|
|
46866
|
-
|
|
46867
|
-
|
|
46868
|
-
|
|
46869
|
-
try {
|
|
46870
|
-
this._history = await HistoryManager.create(
|
|
46871
|
-
getHistoryFilePath(),
|
|
46872
|
-
1e3
|
|
46873
|
-
);
|
|
46874
|
-
} catch (error) {
|
|
46875
|
-
console.error("Warning: could not initialize history:", error);
|
|
46876
|
-
this._history = new HistoryManager(getHistoryFilePath(), 1e3);
|
|
46877
|
-
}
|
|
46878
|
-
await this.loadActiveProfile();
|
|
46879
|
-
if (this._apiClient?.isAuthenticated()) {
|
|
46880
|
-
const result = await this._apiClient.validateToken();
|
|
46881
|
-
this._tokenValidated = result.valid;
|
|
46882
|
-
this._validationError = result.error ?? null;
|
|
46883
|
-
if (result.valid) {
|
|
46884
|
-
await this.fetchUserInfo();
|
|
46885
|
-
}
|
|
46886
|
-
}
|
|
46722
|
+
if (domain.relatedDomains && domain.relatedDomains.length > 0) {
|
|
46723
|
+
output.push("RELATED DOMAINS");
|
|
46724
|
+
output.push(` ${domain.relatedDomains.join(", ")}`);
|
|
46725
|
+
output.push("");
|
|
46887
46726
|
}
|
|
46888
|
-
|
|
46889
|
-
|
|
46890
|
-
|
|
46891
|
-
|
|
46892
|
-
if (!this._apiClient) return;
|
|
46893
|
-
try {
|
|
46894
|
-
const response = await this._apiClient.get("/api/web/custom/user/info");
|
|
46895
|
-
if (response.ok && response.data) {
|
|
46896
|
-
this._username = response.data.email || response.data.name || response.data.username || "";
|
|
46897
|
-
}
|
|
46898
|
-
} catch {
|
|
46899
|
-
}
|
|
46727
|
+
if (domain.aliases && domain.aliases.length > 0) {
|
|
46728
|
+
output.push("ALIASES");
|
|
46729
|
+
output.push(` ${domain.aliases.join(", ")}`);
|
|
46730
|
+
output.push("");
|
|
46900
46731
|
}
|
|
46901
|
-
|
|
46902
|
-
|
|
46903
|
-
|
|
46904
|
-
|
|
46905
|
-
|
|
46906
|
-
|
|
46907
|
-
|
|
46908
|
-
|
|
46909
|
-
|
|
46910
|
-
|
|
46911
|
-
|
|
46912
|
-
|
|
46913
|
-
|
|
46914
|
-
|
|
46915
|
-
|
|
46916
|
-
|
|
46917
|
-
|
|
46918
|
-
|
|
46919
|
-
|
|
46920
|
-
|
|
46921
|
-
|
|
46922
|
-
|
|
46923
|
-
|
|
46924
|
-
|
|
46925
|
-
|
|
46926
|
-
|
|
46927
|
-
|
|
46928
|
-
|
|
46929
|
-
|
|
46930
|
-
|
|
46931
|
-
|
|
46932
|
-
|
|
46933
|
-
|
|
46934
|
-
|
|
46935
|
-
|
|
46936
|
-
|
|
46937
|
-
|
|
46938
|
-
|
|
46939
|
-
|
|
46940
|
-
|
|
46941
|
-
|
|
46942
|
-
|
|
46943
|
-
}
|
|
46944
|
-
}
|
|
46732
|
+
output.push(colorDim(`For global options, run: ${CLI_NAME} --help`));
|
|
46733
|
+
output.push("");
|
|
46734
|
+
return output;
|
|
46735
|
+
}
|
|
46736
|
+
function formatActionHelp(domainName, action) {
|
|
46737
|
+
const domain = getDomainInfo(domainName);
|
|
46738
|
+
const displayDomain = domain?.displayName ?? domainName;
|
|
46739
|
+
const actionDescriptions2 = {
|
|
46740
|
+
list: {
|
|
46741
|
+
desc: "List all resources in the namespace",
|
|
46742
|
+
usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
|
|
46743
|
+
},
|
|
46744
|
+
get: {
|
|
46745
|
+
desc: "Get a specific resource by name",
|
|
46746
|
+
usage: `${CLI_NAME} ${domainName} get <name> [-o json|yaml|table]`
|
|
46747
|
+
},
|
|
46748
|
+
create: {
|
|
46749
|
+
desc: "Create a new resource",
|
|
46750
|
+
usage: `${CLI_NAME} ${domainName} create <name> -f <file.yaml>`
|
|
46751
|
+
},
|
|
46752
|
+
delete: {
|
|
46753
|
+
desc: "Delete a resource by name",
|
|
46754
|
+
usage: `${CLI_NAME} ${domainName} delete <name>`
|
|
46755
|
+
},
|
|
46756
|
+
replace: {
|
|
46757
|
+
desc: "Replace an existing resource configuration",
|
|
46758
|
+
usage: `${CLI_NAME} ${domainName} replace <name> -f <file.yaml>`
|
|
46759
|
+
},
|
|
46760
|
+
apply: {
|
|
46761
|
+
desc: "Apply configuration from a file (create or update)",
|
|
46762
|
+
usage: `${CLI_NAME} ${domainName} apply -f <file.yaml>`
|
|
46763
|
+
},
|
|
46764
|
+
status: {
|
|
46765
|
+
desc: "Get the current status of a resource",
|
|
46766
|
+
usage: `${CLI_NAME} ${domainName} status <name>`
|
|
46767
|
+
},
|
|
46768
|
+
patch: {
|
|
46769
|
+
desc: "Patch specific fields of a resource",
|
|
46770
|
+
usage: `${CLI_NAME} ${domainName} patch <name> -f <patch.yaml>`
|
|
46771
|
+
},
|
|
46772
|
+
"add-labels": {
|
|
46773
|
+
desc: "Add labels to a resource",
|
|
46774
|
+
usage: `${CLI_NAME} ${domainName} add-labels <name> key=value`
|
|
46775
|
+
},
|
|
46776
|
+
"remove-labels": {
|
|
46777
|
+
desc: "Remove labels from a resource",
|
|
46778
|
+
usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
|
|
46945
46779
|
}
|
|
46780
|
+
};
|
|
46781
|
+
const actionInfo = actionDescriptions2[action] ?? {
|
|
46782
|
+
desc: `Execute ${action} operation`,
|
|
46783
|
+
usage: `${CLI_NAME} ${domainName} ${action} [options]`
|
|
46784
|
+
};
|
|
46785
|
+
return [
|
|
46786
|
+
"",
|
|
46787
|
+
colorBoldWhite(`${displayDomain} - ${action}`),
|
|
46788
|
+
"",
|
|
46789
|
+
` ${actionInfo.desc}`,
|
|
46790
|
+
"",
|
|
46791
|
+
"USAGE",
|
|
46792
|
+
` ${actionInfo.usage}`,
|
|
46793
|
+
"",
|
|
46794
|
+
"OPTIONS",
|
|
46795
|
+
" -n, --name <name> Resource name",
|
|
46796
|
+
" -ns, --namespace <ns> Target namespace",
|
|
46797
|
+
" -o, --output <fmt> Output format (json, yaml, table)",
|
|
46798
|
+
" -f, --file <path> Configuration file",
|
|
46799
|
+
"",
|
|
46800
|
+
colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`),
|
|
46801
|
+
""
|
|
46802
|
+
];
|
|
46803
|
+
}
|
|
46804
|
+
function formatTopicHelp(topic) {
|
|
46805
|
+
const lowerTopic = topic.toLowerCase();
|
|
46806
|
+
const domainInfo = getDomainInfo(lowerTopic);
|
|
46807
|
+
if (domainInfo) {
|
|
46808
|
+
return formatDomainHelp(domainInfo);
|
|
46946
46809
|
}
|
|
46947
|
-
|
|
46948
|
-
|
|
46949
|
-
|
|
46950
|
-
|
|
46951
|
-
|
|
46810
|
+
switch (lowerTopic) {
|
|
46811
|
+
case "domains":
|
|
46812
|
+
return formatDomainsHelp();
|
|
46813
|
+
case "actions":
|
|
46814
|
+
return formatActionsHelp();
|
|
46815
|
+
case "navigation":
|
|
46816
|
+
case "nav":
|
|
46817
|
+
return formatNavigationHelp();
|
|
46818
|
+
case "env":
|
|
46819
|
+
case "environment":
|
|
46820
|
+
return ["", ...formatEnvironmentVariables(), ""];
|
|
46821
|
+
case "flags":
|
|
46822
|
+
return ["", ...formatGlobalFlags(), ""];
|
|
46823
|
+
default:
|
|
46824
|
+
return [
|
|
46825
|
+
"",
|
|
46826
|
+
`Unknown help topic: ${topic}`,
|
|
46827
|
+
"",
|
|
46828
|
+
"Available topics:",
|
|
46829
|
+
" domains List all available domains",
|
|
46830
|
+
" actions List available actions",
|
|
46831
|
+
" navigation Navigation commands",
|
|
46832
|
+
" env Environment variables",
|
|
46833
|
+
" flags Global flags",
|
|
46834
|
+
" <domain> Help for a specific domain (e.g., 'help dns')",
|
|
46835
|
+
""
|
|
46836
|
+
];
|
|
46952
46837
|
}
|
|
46953
|
-
|
|
46954
|
-
|
|
46955
|
-
|
|
46956
|
-
|
|
46957
|
-
|
|
46958
|
-
|
|
46959
|
-
|
|
46960
|
-
|
|
46961
|
-
if (parts.length > 0 && parts[0]) {
|
|
46962
|
-
return parts[0];
|
|
46963
|
-
}
|
|
46964
|
-
return hostname;
|
|
46965
|
-
} catch {
|
|
46966
|
-
return "";
|
|
46838
|
+
}
|
|
46839
|
+
function formatDomainsHelp() {
|
|
46840
|
+
const output = ["", colorBoldWhite("Available Domains"), ""];
|
|
46841
|
+
const categories = /* @__PURE__ */ new Map();
|
|
46842
|
+
for (const domain of domainRegistry.values()) {
|
|
46843
|
+
const category = domain.category ?? "Other";
|
|
46844
|
+
if (!categories.has(category)) {
|
|
46845
|
+
categories.set(category, []);
|
|
46967
46846
|
}
|
|
46847
|
+
categories.get(category)?.push(domain);
|
|
46968
46848
|
}
|
|
46969
|
-
|
|
46970
|
-
|
|
46971
|
-
|
|
46972
|
-
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
|
|
46979
|
-
|
|
46980
|
-
|
|
46981
|
-
|
|
46982
|
-
|
|
46983
|
-
|
|
46984
|
-
|
|
46985
|
-
return this._lastExitCode;
|
|
46986
|
-
}
|
|
46987
|
-
/**
|
|
46988
|
-
* Set the exit code of the last command
|
|
46989
|
-
*/
|
|
46990
|
-
setLastExitCode(code) {
|
|
46991
|
-
this._lastExitCode = code;
|
|
46992
|
-
}
|
|
46993
|
-
/**
|
|
46994
|
-
* Get the current navigation context
|
|
46995
|
-
*/
|
|
46996
|
-
getContextPath() {
|
|
46997
|
-
return this._contextPath;
|
|
46998
|
-
}
|
|
46999
|
-
/**
|
|
47000
|
-
* Get the current tenant name
|
|
47001
|
-
*/
|
|
47002
|
-
getTenant() {
|
|
47003
|
-
return this._tenant;
|
|
47004
|
-
}
|
|
47005
|
-
/**
|
|
47006
|
-
* Get the logged-in user's name/email
|
|
47007
|
-
*/
|
|
47008
|
-
getUsername() {
|
|
47009
|
-
return this._username;
|
|
47010
|
-
}
|
|
47011
|
-
/**
|
|
47012
|
-
* Set the username (used when fetched from API)
|
|
47013
|
-
*/
|
|
47014
|
-
setUsername(username) {
|
|
47015
|
-
this._username = username;
|
|
47016
|
-
}
|
|
47017
|
-
/**
|
|
47018
|
-
* Get the context validator
|
|
47019
|
-
*/
|
|
47020
|
-
getValidator() {
|
|
47021
|
-
return this._validator;
|
|
46849
|
+
const sortedCategories = Array.from(categories.keys()).sort();
|
|
46850
|
+
for (const category of sortedCategories) {
|
|
46851
|
+
const domains = categories.get(category) ?? [];
|
|
46852
|
+
output.push(colorBoldWhite(` ${category}`));
|
|
46853
|
+
for (const domain of domains.sort(
|
|
46854
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
46855
|
+
)) {
|
|
46856
|
+
const aliases = domain.aliases.length > 0 ? colorDim(` (${domain.aliases.join(", ")})`) : "";
|
|
46857
|
+
output.push(
|
|
46858
|
+
` ${domain.name.padEnd(24)} ${domain.descriptionShort}`
|
|
46859
|
+
);
|
|
46860
|
+
if (aliases) {
|
|
46861
|
+
output.push(` ${"".padEnd(24)} Aliases:${aliases}`);
|
|
46862
|
+
}
|
|
46863
|
+
}
|
|
46864
|
+
output.push("");
|
|
47022
46865
|
}
|
|
47023
|
-
|
|
47024
|
-
|
|
47025
|
-
|
|
47026
|
-
|
|
47027
|
-
|
|
47028
|
-
|
|
47029
|
-
|
|
47030
|
-
|
|
47031
|
-
|
|
47032
|
-
|
|
47033
|
-
|
|
47034
|
-
|
|
47035
|
-
|
|
47036
|
-
|
|
47037
|
-
|
|
47038
|
-
|
|
47039
|
-
|
|
47040
|
-
|
|
47041
|
-
|
|
47042
|
-
|
|
47043
|
-
|
|
47044
|
-
|
|
47045
|
-
|
|
47046
|
-
}
|
|
47047
|
-
|
|
47048
|
-
|
|
47049
|
-
|
|
47050
|
-
|
|
47051
|
-
|
|
46866
|
+
return output;
|
|
46867
|
+
}
|
|
46868
|
+
function formatActionsHelp() {
|
|
46869
|
+
return [
|
|
46870
|
+
"",
|
|
46871
|
+
colorBoldWhite("Available Actions"),
|
|
46872
|
+
"",
|
|
46873
|
+
" list List all resources in the namespace",
|
|
46874
|
+
" get Get a specific resource by name",
|
|
46875
|
+
" create Create a new resource from a file",
|
|
46876
|
+
" delete Delete a resource by name",
|
|
46877
|
+
" replace Replace a resource configuration",
|
|
46878
|
+
" apply Apply configuration (create or update)",
|
|
46879
|
+
" status Get resource status",
|
|
46880
|
+
" patch Patch specific fields of a resource",
|
|
46881
|
+
" add-labels Add labels to a resource",
|
|
46882
|
+
" remove-labels Remove labels from a resource",
|
|
46883
|
+
"",
|
|
46884
|
+
"USAGE",
|
|
46885
|
+
` ${CLI_NAME} <domain> <action> [options]`,
|
|
46886
|
+
"",
|
|
46887
|
+
"EXAMPLES",
|
|
46888
|
+
` ${CLI_NAME} dns list`,
|
|
46889
|
+
` ${CLI_NAME} lb get my-loadbalancer`,
|
|
46890
|
+
` ${CLI_NAME} waf create my-policy -f policy.yaml`,
|
|
46891
|
+
""
|
|
46892
|
+
];
|
|
46893
|
+
}
|
|
46894
|
+
function formatNavigationHelp() {
|
|
46895
|
+
return [
|
|
46896
|
+
"",
|
|
46897
|
+
colorBoldWhite("Navigation Commands"),
|
|
46898
|
+
"",
|
|
46899
|
+
" <domain> Navigate into a domain context",
|
|
46900
|
+
" /<domain> Navigate directly to domain from anywhere",
|
|
46901
|
+
" .. Go up one level in context",
|
|
46902
|
+
" / Return to root context",
|
|
46903
|
+
" back Go up one level (same as ..)",
|
|
46904
|
+
" root Return to root (same as /)",
|
|
46905
|
+
"",
|
|
46906
|
+
"CONTEXT DISPLAY",
|
|
46907
|
+
" context Show current navigation context",
|
|
46908
|
+
" ctx Alias for context",
|
|
46909
|
+
"",
|
|
46910
|
+
"EXAMPLES",
|
|
46911
|
+
" xcsh> dns # Enter dns domain",
|
|
46912
|
+
" dns> list # Execute list in dns context",
|
|
46913
|
+
" dns> .. # Return to root",
|
|
46914
|
+
" xcsh> /waf # Jump directly to waf",
|
|
46915
|
+
" waf> /dns # Jump from waf to dns",
|
|
46916
|
+
""
|
|
46917
|
+
];
|
|
46918
|
+
}
|
|
46919
|
+
function formatDomainsSection() {
|
|
46920
|
+
const output = ["DOMAINS"];
|
|
46921
|
+
const domains = Array.from(domainRegistry.values()).sort(
|
|
46922
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
46923
|
+
);
|
|
46924
|
+
const maxNameLen = Math.max(...domains.map((d) => d.name.length));
|
|
46925
|
+
for (const domain of domains) {
|
|
46926
|
+
const padding = " ".repeat(maxNameLen - domain.name.length + 2);
|
|
46927
|
+
output.push(` ${domain.name}${padding}${domain.descriptionShort}`);
|
|
47052
46928
|
}
|
|
47053
|
-
|
|
47054
|
-
|
|
47055
|
-
|
|
47056
|
-
|
|
47057
|
-
|
|
46929
|
+
return output;
|
|
46930
|
+
}
|
|
46931
|
+
function formatCustomDomainHelp(domain) {
|
|
46932
|
+
const output = ["", colorBoldWhite(domain.name), ""];
|
|
46933
|
+
output.push("DESCRIPTION");
|
|
46934
|
+
output.push(...wrapText3(domain.description, 80, 2));
|
|
46935
|
+
output.push("");
|
|
46936
|
+
output.push("USAGE");
|
|
46937
|
+
output.push(` ${CLI_NAME} ${domain.name} <command> [options]`);
|
|
46938
|
+
output.push("");
|
|
46939
|
+
if (domain.subcommands.size > 0) {
|
|
46940
|
+
output.push("SUBCOMMANDS");
|
|
46941
|
+
for (const [name, group] of domain.subcommands) {
|
|
46942
|
+
output.push(` ${name.padEnd(16)} ${group.descriptionShort}`);
|
|
46943
|
+
}
|
|
46944
|
+
output.push("");
|
|
47058
46945
|
}
|
|
47059
|
-
|
|
47060
|
-
|
|
47061
|
-
|
|
47062
|
-
|
|
47063
|
-
|
|
46946
|
+
if (domain.commands.size > 0) {
|
|
46947
|
+
output.push("COMMANDS");
|
|
46948
|
+
for (const [name, cmd] of domain.commands) {
|
|
46949
|
+
output.push(` ${name.padEnd(16)} ${cmd.descriptionShort}`);
|
|
46950
|
+
}
|
|
46951
|
+
output.push("");
|
|
47064
46952
|
}
|
|
47065
|
-
|
|
47066
|
-
|
|
47067
|
-
|
|
47068
|
-
|
|
47069
|
-
|
|
46953
|
+
output.push(colorDim(`For global options, run: ${CLI_NAME} --help`));
|
|
46954
|
+
output.push("");
|
|
46955
|
+
return output;
|
|
46956
|
+
}
|
|
46957
|
+
function formatSubcommandHelp(domainName, subcommand) {
|
|
46958
|
+
const output = [
|
|
46959
|
+
"",
|
|
46960
|
+
colorBoldWhite(`${domainName} ${subcommand.name}`),
|
|
46961
|
+
""
|
|
46962
|
+
];
|
|
46963
|
+
output.push("DESCRIPTION");
|
|
46964
|
+
output.push(...wrapText3(subcommand.description, 80, 2));
|
|
46965
|
+
output.push("");
|
|
46966
|
+
output.push("USAGE");
|
|
46967
|
+
output.push(
|
|
46968
|
+
` ${CLI_NAME} ${domainName} ${subcommand.name} <command> [options]`
|
|
46969
|
+
);
|
|
46970
|
+
output.push("");
|
|
46971
|
+
if (subcommand.commands.size > 0) {
|
|
46972
|
+
output.push("COMMANDS");
|
|
46973
|
+
for (const [name, cmd] of subcommand.commands) {
|
|
46974
|
+
const usage = cmd.usage ? ` ${cmd.usage}` : "";
|
|
46975
|
+
output.push(
|
|
46976
|
+
` ${name}${usage.padEnd(16 - name.length)} ${cmd.descriptionShort}`
|
|
46977
|
+
);
|
|
46978
|
+
}
|
|
46979
|
+
output.push("");
|
|
47070
46980
|
}
|
|
46981
|
+
output.push(
|
|
46982
|
+
colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`)
|
|
46983
|
+
);
|
|
46984
|
+
output.push("");
|
|
46985
|
+
return output;
|
|
46986
|
+
}
|
|
46987
|
+
|
|
46988
|
+
// src/domains/registry.ts
|
|
46989
|
+
var DomainRegistry = class {
|
|
46990
|
+
domains = /* @__PURE__ */ new Map();
|
|
47071
46991
|
/**
|
|
47072
|
-
*
|
|
46992
|
+
* Register a custom domain
|
|
47073
46993
|
*/
|
|
47074
|
-
|
|
47075
|
-
this.
|
|
46994
|
+
register(domain) {
|
|
46995
|
+
this.domains.set(domain.name, domain);
|
|
47076
46996
|
}
|
|
47077
46997
|
/**
|
|
47078
|
-
*
|
|
46998
|
+
* Check if a domain is registered
|
|
47079
46999
|
*/
|
|
47080
|
-
|
|
47081
|
-
return this.
|
|
47000
|
+
has(name) {
|
|
47001
|
+
return this.domains.has(name);
|
|
47082
47002
|
}
|
|
47083
47003
|
/**
|
|
47084
|
-
* Get
|
|
47004
|
+
* Get a domain by name
|
|
47085
47005
|
*/
|
|
47086
|
-
|
|
47087
|
-
return this.
|
|
47006
|
+
get(name) {
|
|
47007
|
+
return this.domains.get(name);
|
|
47088
47008
|
}
|
|
47089
47009
|
/**
|
|
47090
|
-
* Get
|
|
47010
|
+
* Get all registered domain names
|
|
47091
47011
|
*/
|
|
47092
|
-
|
|
47093
|
-
return this.
|
|
47012
|
+
list() {
|
|
47013
|
+
return Array.from(this.domains.keys());
|
|
47094
47014
|
}
|
|
47095
47015
|
/**
|
|
47096
|
-
* Get
|
|
47016
|
+
* Get all domains
|
|
47097
47017
|
*/
|
|
47098
|
-
|
|
47099
|
-
return this.
|
|
47018
|
+
all() {
|
|
47019
|
+
return Array.from(this.domains.values());
|
|
47100
47020
|
}
|
|
47101
47021
|
/**
|
|
47102
|
-
*
|
|
47022
|
+
* Execute a command within a domain
|
|
47023
|
+
* Handles routing through subcommand groups
|
|
47024
|
+
*
|
|
47025
|
+
* @param domainName - Name of the domain (e.g., "login")
|
|
47026
|
+
* @param args - Command arguments (e.g., ["profile", "list"])
|
|
47027
|
+
* @param session - REPL session
|
|
47103
47028
|
*/
|
|
47104
|
-
async
|
|
47105
|
-
const
|
|
47106
|
-
if (!
|
|
47107
|
-
return
|
|
47108
|
-
|
|
47109
|
-
|
|
47110
|
-
|
|
47111
|
-
|
|
47112
|
-
|
|
47113
|
-
|
|
47114
|
-
this._tokenValidated = false;
|
|
47115
|
-
this._validationError = null;
|
|
47116
|
-
this._activeProfileName = profileName;
|
|
47117
|
-
this._activeProfile = profile;
|
|
47118
|
-
if (profile.apiUrl) {
|
|
47119
|
-
this._serverUrl = profile.apiUrl;
|
|
47120
|
-
this._tenant = this.extractTenant(profile.apiUrl);
|
|
47121
|
-
}
|
|
47122
|
-
if (profile.apiToken) {
|
|
47123
|
-
this._apiToken = profile.apiToken;
|
|
47124
|
-
}
|
|
47125
|
-
if (profile.defaultNamespace) {
|
|
47126
|
-
this._namespace = profile.defaultNamespace;
|
|
47029
|
+
async execute(domainName, args, session) {
|
|
47030
|
+
const domain = this.domains.get(domainName);
|
|
47031
|
+
if (!domain) {
|
|
47032
|
+
return {
|
|
47033
|
+
output: [`Unknown domain: ${domainName}`],
|
|
47034
|
+
shouldExit: false,
|
|
47035
|
+
shouldClear: false,
|
|
47036
|
+
contextChanged: false,
|
|
47037
|
+
error: "Unknown domain"
|
|
47038
|
+
};
|
|
47127
47039
|
}
|
|
47128
|
-
if (
|
|
47129
|
-
|
|
47130
|
-
|
|
47131
|
-
apiToken: this._apiToken,
|
|
47132
|
-
debug: this._debug
|
|
47133
|
-
});
|
|
47134
|
-
if (this._apiClient.isAuthenticated()) {
|
|
47135
|
-
const validationResult = await this._apiClient.validateToken();
|
|
47136
|
-
this._tokenValidated = validationResult.valid;
|
|
47137
|
-
this._validationError = validationResult.error ?? null;
|
|
47040
|
+
if (args.length === 0) {
|
|
47041
|
+
if (domain.defaultCommand) {
|
|
47042
|
+
return domain.defaultCommand.execute([], session);
|
|
47138
47043
|
}
|
|
47139
|
-
|
|
47140
|
-
this._apiClient = null;
|
|
47044
|
+
return this.showDomainHelp(domain);
|
|
47141
47045
|
}
|
|
47142
|
-
|
|
47143
|
-
|
|
47144
|
-
|
|
47145
|
-
|
|
47146
|
-
*/
|
|
47147
|
-
clearActiveProfile() {
|
|
47148
|
-
this._activeProfileName = null;
|
|
47149
|
-
this._activeProfile = null;
|
|
47150
|
-
}
|
|
47151
|
-
/**
|
|
47152
|
-
* Get cached namespaces (returns empty array if cache is stale/empty)
|
|
47153
|
-
*/
|
|
47154
|
-
getNamespaceCache() {
|
|
47155
|
-
const now = Date.now();
|
|
47156
|
-
if (this._namespaceCache.length > 0 && now - this._namespaceCacheTime < NAMESPACE_CACHE_TTL) {
|
|
47157
|
-
return this._namespaceCache;
|
|
47046
|
+
const firstArg = args[0]?.toLowerCase() ?? "";
|
|
47047
|
+
const restArgs = args.slice(1);
|
|
47048
|
+
if (firstArg === "--help" || firstArg === "-h" || firstArg === "help") {
|
|
47049
|
+
return this.showDomainHelp(domain);
|
|
47158
47050
|
}
|
|
47159
|
-
|
|
47160
|
-
|
|
47161
|
-
|
|
47162
|
-
|
|
47163
|
-
|
|
47164
|
-
|
|
47165
|
-
|
|
47166
|
-
|
|
47167
|
-
|
|
47168
|
-
|
|
47169
|
-
|
|
47170
|
-
|
|
47171
|
-
|
|
47172
|
-
|
|
47173
|
-
|
|
47051
|
+
const subgroup = domain.subcommands.get(firstArg);
|
|
47052
|
+
if (subgroup) {
|
|
47053
|
+
if (restArgs.length === 0) {
|
|
47054
|
+
if (subgroup.defaultCommand) {
|
|
47055
|
+
return subgroup.defaultCommand.execute([], session);
|
|
47056
|
+
}
|
|
47057
|
+
return this.showSubcommandHelp(domain, subgroup);
|
|
47058
|
+
}
|
|
47059
|
+
const cmdName = restArgs[0]?.toLowerCase() ?? "";
|
|
47060
|
+
if (cmdName === "--help" || cmdName === "-h" || cmdName === "help") {
|
|
47061
|
+
return this.showSubcommandHelp(domain, subgroup);
|
|
47062
|
+
}
|
|
47063
|
+
const cmdArgs = restArgs.slice(1);
|
|
47064
|
+
const cmd2 = subgroup.commands.get(cmdName);
|
|
47065
|
+
if (cmd2) {
|
|
47066
|
+
return cmd2.execute(cmdArgs, session);
|
|
47067
|
+
}
|
|
47068
|
+
for (const [, command] of subgroup.commands) {
|
|
47069
|
+
if (command.aliases?.includes(cmdName)) {
|
|
47070
|
+
return command.execute(cmdArgs, session);
|
|
47071
|
+
}
|
|
47072
|
+
}
|
|
47073
|
+
return {
|
|
47074
|
+
output: [
|
|
47075
|
+
`Unknown command: ${domainName} ${firstArg} ${cmdName}`,
|
|
47076
|
+
``,
|
|
47077
|
+
`Run '${domainName} ${firstArg}' for available commands.`
|
|
47078
|
+
],
|
|
47079
|
+
shouldExit: false,
|
|
47080
|
+
shouldClear: false,
|
|
47081
|
+
contextChanged: false,
|
|
47082
|
+
error: "Unknown command"
|
|
47083
|
+
};
|
|
47084
|
+
}
|
|
47085
|
+
const cmd = domain.commands.get(firstArg);
|
|
47086
|
+
if (cmd) {
|
|
47087
|
+
return cmd.execute(restArgs, session);
|
|
47088
|
+
}
|
|
47089
|
+
for (const [, command] of domain.commands) {
|
|
47090
|
+
if (command.aliases?.includes(firstArg)) {
|
|
47091
|
+
return command.execute(restArgs, session);
|
|
47092
|
+
}
|
|
47093
|
+
}
|
|
47094
|
+
return {
|
|
47095
|
+
output: [
|
|
47096
|
+
`Unknown command: ${domainName} ${firstArg}`,
|
|
47097
|
+
``,
|
|
47098
|
+
`Run '${domainName}' for available commands.`
|
|
47099
|
+
],
|
|
47100
|
+
shouldExit: false,
|
|
47101
|
+
shouldClear: false,
|
|
47102
|
+
contextChanged: false,
|
|
47103
|
+
error: "Unknown command"
|
|
47104
|
+
};
|
|
47174
47105
|
}
|
|
47175
47106
|
/**
|
|
47176
|
-
*
|
|
47107
|
+
* Get completions for a domain command
|
|
47177
47108
|
*/
|
|
47178
|
-
|
|
47179
|
-
this.
|
|
47109
|
+
async getCompletions(domainName, args, partial, session) {
|
|
47110
|
+
const domain = this.domains.get(domainName);
|
|
47111
|
+
if (!domain) {
|
|
47112
|
+
return [];
|
|
47113
|
+
}
|
|
47114
|
+
const suggestions = [];
|
|
47115
|
+
if (args.length === 0) {
|
|
47116
|
+
for (const [name, group] of domain.subcommands) {
|
|
47117
|
+
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47118
|
+
suggestions.push({
|
|
47119
|
+
text: name,
|
|
47120
|
+
description: group.descriptionShort,
|
|
47121
|
+
category: "subcommand"
|
|
47122
|
+
});
|
|
47123
|
+
}
|
|
47124
|
+
}
|
|
47125
|
+
for (const [name, cmd] of domain.commands) {
|
|
47126
|
+
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47127
|
+
suggestions.push({
|
|
47128
|
+
text: name,
|
|
47129
|
+
description: cmd.descriptionShort,
|
|
47130
|
+
category: "command"
|
|
47131
|
+
});
|
|
47132
|
+
}
|
|
47133
|
+
}
|
|
47134
|
+
return suggestions;
|
|
47135
|
+
}
|
|
47136
|
+
const firstArg = args[0]?.toLowerCase() ?? "";
|
|
47137
|
+
const subgroup = domain.subcommands.get(firstArg);
|
|
47138
|
+
if (subgroup && args.length === 1) {
|
|
47139
|
+
for (const [name, cmd] of subgroup.commands) {
|
|
47140
|
+
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47141
|
+
suggestions.push({
|
|
47142
|
+
text: name,
|
|
47143
|
+
description: cmd.descriptionShort,
|
|
47144
|
+
category: "command"
|
|
47145
|
+
});
|
|
47146
|
+
}
|
|
47147
|
+
}
|
|
47148
|
+
return suggestions;
|
|
47149
|
+
}
|
|
47150
|
+
if (subgroup && args.length >= 2) {
|
|
47151
|
+
const cmdName = args[1]?.toLowerCase() ?? "";
|
|
47152
|
+
const cmd = subgroup.commands.get(cmdName);
|
|
47153
|
+
if (cmd?.completion) {
|
|
47154
|
+
const completions = await cmd.completion(
|
|
47155
|
+
partial,
|
|
47156
|
+
args.slice(2),
|
|
47157
|
+
session
|
|
47158
|
+
);
|
|
47159
|
+
return completions.map((text) => ({
|
|
47160
|
+
text,
|
|
47161
|
+
description: "",
|
|
47162
|
+
category: "argument"
|
|
47163
|
+
}));
|
|
47164
|
+
}
|
|
47165
|
+
}
|
|
47166
|
+
return suggestions;
|
|
47180
47167
|
}
|
|
47181
47168
|
/**
|
|
47182
|
-
*
|
|
47169
|
+
* Show help for a domain using the unified help formatter.
|
|
47170
|
+
* This ensures consistent professional formatting across all domains.
|
|
47183
47171
|
*/
|
|
47184
|
-
|
|
47185
|
-
|
|
47172
|
+
showDomainHelp(domain) {
|
|
47173
|
+
return {
|
|
47174
|
+
output: formatCustomDomainHelp(domain),
|
|
47175
|
+
shouldExit: false,
|
|
47176
|
+
shouldClear: false,
|
|
47177
|
+
contextChanged: false
|
|
47178
|
+
};
|
|
47186
47179
|
}
|
|
47187
47180
|
/**
|
|
47188
|
-
*
|
|
47181
|
+
* Show help for a subcommand group using the unified help formatter.
|
|
47182
|
+
* This ensures consistent professional formatting across all subcommands.
|
|
47189
47183
|
*/
|
|
47190
|
-
|
|
47191
|
-
|
|
47184
|
+
showSubcommandHelp(domain, subgroup) {
|
|
47185
|
+
return {
|
|
47186
|
+
output: formatSubcommandHelp(domain.name, subgroup),
|
|
47187
|
+
shouldExit: false,
|
|
47188
|
+
shouldClear: false,
|
|
47189
|
+
contextChanged: false
|
|
47190
|
+
};
|
|
47192
47191
|
}
|
|
47193
47192
|
};
|
|
47194
|
-
|
|
47195
|
-
|
|
47196
|
-
|
|
47197
|
-
|
|
47193
|
+
var customDomains = new DomainRegistry();
|
|
47194
|
+
function successResult(output, contextChanged = false) {
|
|
47195
|
+
return {
|
|
47196
|
+
output,
|
|
47197
|
+
shouldExit: false,
|
|
47198
|
+
shouldClear: false,
|
|
47199
|
+
contextChanged
|
|
47200
|
+
};
|
|
47201
|
+
}
|
|
47202
|
+
function errorResult(message) {
|
|
47203
|
+
return {
|
|
47204
|
+
output: [message],
|
|
47205
|
+
shouldExit: false,
|
|
47206
|
+
shouldClear: false,
|
|
47207
|
+
contextChanged: false,
|
|
47208
|
+
error: message
|
|
47209
|
+
};
|
|
47210
|
+
}
|
|
47211
|
+
function rawStdoutResult(content) {
|
|
47212
|
+
return {
|
|
47213
|
+
output: [],
|
|
47214
|
+
// No regular output - rawStdout is used instead
|
|
47215
|
+
shouldExit: false,
|
|
47216
|
+
shouldClear: false,
|
|
47217
|
+
contextChanged: false,
|
|
47218
|
+
rawStdout: content
|
|
47219
|
+
};
|
|
47198
47220
|
}
|
|
47199
47221
|
|
|
47200
|
-
// src/
|
|
47201
|
-
|
|
47202
|
-
|
|
47203
|
-
|
|
47204
|
-
|
|
47205
|
-
|
|
47206
|
-
|
|
47207
|
-
|
|
47208
|
-
|
|
47209
|
-
|
|
47210
|
-
|
|
47211
|
-
|
|
47212
|
-
});
|
|
47213
|
-
const { cursorOffset, cursorWidth } = state;
|
|
47214
|
-
(0, import_react22.useEffect)(() => {
|
|
47215
|
-
setState((previousState) => {
|
|
47216
|
-
if (!focus || !showCursor) {
|
|
47217
|
-
return previousState;
|
|
47218
|
-
}
|
|
47219
|
-
const newValue = originalValue || "";
|
|
47220
|
-
if (previousState.cursorOffset > newValue.length - 1) {
|
|
47221
|
-
return {
|
|
47222
|
-
cursorOffset: newValue.length,
|
|
47223
|
-
cursorWidth: 0
|
|
47224
|
-
};
|
|
47225
|
-
}
|
|
47226
|
-
return previousState;
|
|
47227
|
-
});
|
|
47228
|
-
}, [originalValue, focus, showCursor]);
|
|
47229
|
-
const cursorActualWidth = highlightPastedText ? cursorWidth : 0;
|
|
47230
|
-
const value = mask ? mask.repeat(originalValue.length) : originalValue;
|
|
47231
|
-
let renderedValue = value;
|
|
47232
|
-
let renderedPlaceholder = placeholder ? source_default.grey(placeholder) : void 0;
|
|
47233
|
-
if (showCursor && focus) {
|
|
47234
|
-
renderedPlaceholder = placeholder.length > 0 ? source_default.inverse(placeholder[0]) + source_default.grey(placeholder.slice(1)) : source_default.inverse(" ");
|
|
47235
|
-
renderedValue = value.length > 0 ? "" : source_default.inverse(" ");
|
|
47236
|
-
let i = 0;
|
|
47237
|
-
for (const char of value) {
|
|
47238
|
-
renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? source_default.inverse(char) : char;
|
|
47239
|
-
i++;
|
|
47240
|
-
}
|
|
47241
|
-
if (value.length > 0 && cursorOffset === value.length) {
|
|
47242
|
-
renderedValue += source_default.inverse(" ");
|
|
47243
|
-
}
|
|
47222
|
+
// src/output/spec.ts
|
|
47223
|
+
function buildCommandSpec(options) {
|
|
47224
|
+
const spec = {
|
|
47225
|
+
command: options.command,
|
|
47226
|
+
description: options.description,
|
|
47227
|
+
usage: options.usage ?? `xcsh ${options.command} [options]`,
|
|
47228
|
+
flags: options.flags ?? [],
|
|
47229
|
+
examples: options.examples ?? [],
|
|
47230
|
+
outputFormats: options.outputFormats ?? ["table", "json", "yaml"]
|
|
47231
|
+
};
|
|
47232
|
+
if (options.related !== void 0) {
|
|
47233
|
+
spec.related = options.related;
|
|
47244
47234
|
}
|
|
47245
|
-
|
|
47246
|
-
|
|
47247
|
-
|
|
47248
|
-
|
|
47249
|
-
if (key.return) {
|
|
47250
|
-
if (onSubmit) {
|
|
47251
|
-
onSubmit(originalValue);
|
|
47252
|
-
}
|
|
47253
|
-
return;
|
|
47254
|
-
}
|
|
47255
|
-
let nextCursorOffset = cursorOffset;
|
|
47256
|
-
let nextValue = originalValue;
|
|
47257
|
-
let nextCursorWidth = 0;
|
|
47258
|
-
if (key.leftArrow) {
|
|
47259
|
-
if (showCursor) {
|
|
47260
|
-
nextCursorOffset--;
|
|
47261
|
-
}
|
|
47262
|
-
} else if (key.rightArrow) {
|
|
47263
|
-
if (showCursor) {
|
|
47264
|
-
nextCursorOffset++;
|
|
47265
|
-
}
|
|
47266
|
-
} else if (key.backspace || key.delete) {
|
|
47267
|
-
if (cursorOffset > 0) {
|
|
47268
|
-
nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset, originalValue.length);
|
|
47269
|
-
nextCursorOffset--;
|
|
47270
|
-
}
|
|
47271
|
-
} else {
|
|
47272
|
-
nextValue = originalValue.slice(0, cursorOffset) + input + originalValue.slice(cursorOffset, originalValue.length);
|
|
47273
|
-
nextCursorOffset += input.length;
|
|
47274
|
-
if (input.length > 1) {
|
|
47275
|
-
nextCursorWidth = input.length;
|
|
47276
|
-
}
|
|
47277
|
-
}
|
|
47278
|
-
if (cursorOffset < 0) {
|
|
47279
|
-
nextCursorOffset = 0;
|
|
47280
|
-
}
|
|
47281
|
-
if (cursorOffset > originalValue.length) {
|
|
47282
|
-
nextCursorOffset = originalValue.length;
|
|
47283
|
-
}
|
|
47284
|
-
setState({
|
|
47285
|
-
cursorOffset: nextCursorOffset,
|
|
47286
|
-
cursorWidth: nextCursorWidth
|
|
47287
|
-
});
|
|
47288
|
-
if (nextValue !== originalValue) {
|
|
47289
|
-
onChange(nextValue);
|
|
47290
|
-
}
|
|
47291
|
-
}, { isActive: focus });
|
|
47292
|
-
return import_react22.default.createElement(Text, null, placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue);
|
|
47235
|
+
if (options.category !== void 0) {
|
|
47236
|
+
spec.category = options.category;
|
|
47237
|
+
}
|
|
47238
|
+
return spec;
|
|
47293
47239
|
}
|
|
47294
|
-
|
|
47295
|
-
|
|
47296
|
-
// src/repl/components/InputBox.tsx
|
|
47297
|
-
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
47298
|
-
function HorizontalRule({ width }) {
|
|
47299
|
-
const rule = "\u2500".repeat(Math.max(width, 1));
|
|
47300
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "#CA260A", children: rule });
|
|
47240
|
+
function formatSpec(spec) {
|
|
47241
|
+
return JSON.stringify(spec, null, 2);
|
|
47301
47242
|
}
|
|
47302
|
-
|
|
47303
|
-
|
|
47304
|
-
|
|
47305
|
-
|
|
47306
|
-
|
|
47307
|
-
|
|
47308
|
-
|
|
47309
|
-
|
|
47310
|
-
}
|
|
47311
|
-
|
|
47312
|
-
|
|
47313
|
-
|
|
47314
|
-
|
|
47315
|
-
|
|
47316
|
-
|
|
47243
|
+
var GLOBAL_FLAGS = [
|
|
47244
|
+
{
|
|
47245
|
+
name: "--output",
|
|
47246
|
+
alias: "-o",
|
|
47247
|
+
description: "Output format",
|
|
47248
|
+
type: "string",
|
|
47249
|
+
default: "table",
|
|
47250
|
+
choices: ["table", "json", "yaml", "tsv"]
|
|
47251
|
+
},
|
|
47252
|
+
{
|
|
47253
|
+
name: "--namespace",
|
|
47254
|
+
alias: "-ns",
|
|
47255
|
+
description: "Namespace to use for the operation",
|
|
47256
|
+
type: "string"
|
|
47257
|
+
},
|
|
47258
|
+
{
|
|
47259
|
+
name: "--no-color",
|
|
47260
|
+
description: "Disable colored output",
|
|
47261
|
+
type: "boolean",
|
|
47262
|
+
default: "false"
|
|
47263
|
+
},
|
|
47264
|
+
{
|
|
47265
|
+
name: "--spec",
|
|
47266
|
+
description: "Output command specification as JSON (for AI assistants)",
|
|
47267
|
+
type: "boolean"
|
|
47268
|
+
},
|
|
47269
|
+
{
|
|
47270
|
+
name: "--help",
|
|
47271
|
+
alias: "-h",
|
|
47272
|
+
description: "Show help information",
|
|
47273
|
+
type: "boolean"
|
|
47274
|
+
},
|
|
47275
|
+
{
|
|
47276
|
+
name: "--version",
|
|
47277
|
+
alias: "-v",
|
|
47278
|
+
description: "Show version number",
|
|
47279
|
+
type: "boolean"
|
|
47280
|
+
}
|
|
47281
|
+
];
|
|
47282
|
+
function buildCloudstatusSpecs() {
|
|
47283
|
+
return {
|
|
47284
|
+
status: buildCommandSpec({
|
|
47285
|
+
command: "cloudstatus status",
|
|
47286
|
+
description: "Get the overall health indicator for F5 Distributed Cloud services. Returns status level (operational, degraded, major outage) with description.",
|
|
47287
|
+
usage: "xcsh cloudstatus status [--quiet]",
|
|
47288
|
+
flags: [
|
|
47317
47289
|
{
|
|
47318
|
-
|
|
47319
|
-
|
|
47320
|
-
|
|
47321
|
-
|
|
47290
|
+
name: "--quiet",
|
|
47291
|
+
alias: "-q",
|
|
47292
|
+
description: "Return exit code only (0=operational, 1=degraded, 2=outage)",
|
|
47293
|
+
type: "boolean"
|
|
47294
|
+
}
|
|
47295
|
+
],
|
|
47296
|
+
examples: [
|
|
47297
|
+
{
|
|
47298
|
+
command: "xcsh cloudstatus status",
|
|
47299
|
+
description: "Check current F5 XC service status"
|
|
47322
47300
|
},
|
|
47323
|
-
|
|
47324
|
-
|
|
47325
|
-
|
|
47326
|
-
|
|
47327
|
-
|
|
47328
|
-
|
|
47329
|
-
|
|
47330
|
-
|
|
47331
|
-
|
|
47332
|
-
|
|
47333
|
-
|
|
47334
|
-
|
|
47335
|
-
|
|
47336
|
-
|
|
47337
|
-
|
|
47338
|
-
|
|
47339
|
-
|
|
47340
|
-
|
|
47341
|
-
|
|
47342
|
-
|
|
47343
|
-
|
|
47344
|
-
|
|
47345
|
-
|
|
47346
|
-
|
|
47347
|
-
}
|
|
47348
|
-
|
|
47349
|
-
|
|
47350
|
-
|
|
47351
|
-
|
|
47352
|
-
|
|
47353
|
-
|
|
47354
|
-
|
|
47355
|
-
|
|
47356
|
-
|
|
47357
|
-
|
|
47358
|
-
|
|
47359
|
-
|
|
47360
|
-
|
|
47361
|
-
|
|
47362
|
-
|
|
47363
|
-
|
|
47364
|
-
|
|
47365
|
-
|
|
47366
|
-
|
|
47367
|
-
|
|
47368
|
-
|
|
47369
|
-
|
|
47370
|
-
|
|
47371
|
-
|
|
47372
|
-
|
|
47373
|
-
|
|
47374
|
-
|
|
47375
|
-
|
|
47376
|
-
|
|
47377
|
-
|
|
47301
|
+
{
|
|
47302
|
+
command: "xcsh cloudstatus status --quiet && echo 'All systems operational'",
|
|
47303
|
+
description: "Use in scripts for health checks"
|
|
47304
|
+
},
|
|
47305
|
+
{
|
|
47306
|
+
command: "xcsh cloudstatus status --output json",
|
|
47307
|
+
description: "Get status as JSON for automation"
|
|
47308
|
+
}
|
|
47309
|
+
],
|
|
47310
|
+
category: "cloudstatus",
|
|
47311
|
+
related: [
|
|
47312
|
+
"cloudstatus summary",
|
|
47313
|
+
"cloudstatus components",
|
|
47314
|
+
"cloudstatus incidents"
|
|
47315
|
+
]
|
|
47316
|
+
}),
|
|
47317
|
+
summary: buildCommandSpec({
|
|
47318
|
+
command: "cloudstatus summary",
|
|
47319
|
+
description: "Get complete status summary including overall health, component status, and active incidents.",
|
|
47320
|
+
usage: "xcsh cloudstatus summary",
|
|
47321
|
+
examples: [
|
|
47322
|
+
{
|
|
47323
|
+
command: "xcsh cloudstatus summary",
|
|
47324
|
+
description: "View full infrastructure health overview"
|
|
47325
|
+
},
|
|
47326
|
+
{
|
|
47327
|
+
command: "xcsh cloudstatus summary --output json",
|
|
47328
|
+
description: "Get complete summary as JSON"
|
|
47329
|
+
}
|
|
47330
|
+
],
|
|
47331
|
+
category: "cloudstatus",
|
|
47332
|
+
related: ["cloudstatus status", "cloudstatus components"]
|
|
47333
|
+
}),
|
|
47334
|
+
components: buildCommandSpec({
|
|
47335
|
+
command: "cloudstatus components",
|
|
47336
|
+
description: "List all infrastructure components and their current operational status.",
|
|
47337
|
+
usage: "xcsh cloudstatus components",
|
|
47338
|
+
examples: [
|
|
47339
|
+
{
|
|
47340
|
+
command: "xcsh cloudstatus components",
|
|
47341
|
+
description: "List all components with status"
|
|
47342
|
+
},
|
|
47343
|
+
{
|
|
47344
|
+
command: "xcsh cloudstatus components --output json",
|
|
47345
|
+
description: "Get components as JSON for monitoring integration"
|
|
47346
|
+
}
|
|
47347
|
+
],
|
|
47348
|
+
category: "cloudstatus",
|
|
47349
|
+
related: ["cloudstatus status", "cloudstatus summary"]
|
|
47350
|
+
}),
|
|
47351
|
+
incidents: buildCommandSpec({
|
|
47352
|
+
command: "cloudstatus incidents",
|
|
47353
|
+
description: "List active and recent incidents affecting F5 Distributed Cloud services.",
|
|
47354
|
+
usage: "xcsh cloudstatus incidents",
|
|
47355
|
+
examples: [
|
|
47356
|
+
{
|
|
47357
|
+
command: "xcsh cloudstatus incidents",
|
|
47358
|
+
description: "View active incidents"
|
|
47359
|
+
},
|
|
47360
|
+
{
|
|
47361
|
+
command: "xcsh cloudstatus incidents --output json",
|
|
47362
|
+
description: "Get incidents as JSON for alerting systems"
|
|
47363
|
+
}
|
|
47364
|
+
],
|
|
47365
|
+
category: "cloudstatus",
|
|
47366
|
+
related: ["cloudstatus status", "cloudstatus maintenance"]
|
|
47367
|
+
}),
|
|
47368
|
+
maintenance: buildCommandSpec({
|
|
47369
|
+
command: "cloudstatus maintenance",
|
|
47370
|
+
description: "List scheduled maintenance windows for F5 Distributed Cloud services.",
|
|
47371
|
+
usage: "xcsh cloudstatus maintenance",
|
|
47372
|
+
examples: [
|
|
47373
|
+
{
|
|
47374
|
+
command: "xcsh cloudstatus maintenance",
|
|
47375
|
+
description: "View upcoming maintenance windows"
|
|
47376
|
+
},
|
|
47377
|
+
{
|
|
47378
|
+
command: "xcsh cloudstatus maintenance --output json",
|
|
47379
|
+
description: "Get maintenance schedule as JSON"
|
|
47380
|
+
}
|
|
47381
|
+
],
|
|
47382
|
+
category: "cloudstatus",
|
|
47383
|
+
related: ["cloudstatus status", "cloudstatus incidents"]
|
|
47384
|
+
})
|
|
47378
47385
|
};
|
|
47379
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { width, paddingX: 1, justifyContent: "space-between", children: [
|
|
47380
|
-
renderLeft(),
|
|
47381
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Spacer, {}),
|
|
47382
|
-
renderRight()
|
|
47383
|
-
] });
|
|
47384
47386
|
}
|
|
47385
|
-
function
|
|
47386
|
-
|
|
47387
|
-
|
|
47388
|
-
|
|
47389
|
-
|
|
47390
|
-
|
|
47391
|
-
|
|
47392
|
-
|
|
47393
|
-
|
|
47394
|
-
|
|
47395
|
-
|
|
47396
|
-
|
|
47397
|
-
|
|
47398
|
-
|
|
47399
|
-
|
|
47400
|
-
|
|
47401
|
-
|
|
47402
|
-
|
|
47403
|
-
|
|
47404
|
-
|
|
47405
|
-
|
|
47406
|
-
|
|
47407
|
-
|
|
47408
|
-
|
|
47409
|
-
|
|
47410
|
-
|
|
47411
|
-
|
|
47412
|
-
|
|
47413
|
-
|
|
47414
|
-
|
|
47415
|
-
|
|
47416
|
-
|
|
47417
|
-
|
|
47418
|
-
|
|
47419
|
-
|
|
47420
|
-
|
|
47421
|
-
|
|
47422
|
-
|
|
47423
|
-
|
|
47424
|
-
|
|
47425
|
-
|
|
47426
|
-
|
|
47427
|
-
|
|
47428
|
-
|
|
47429
|
-
|
|
47430
|
-
|
|
47431
|
-
|
|
47432
|
-
|
|
47433
|
-
|
|
47434
|
-
|
|
47435
|
-
|
|
47387
|
+
function buildLoginSpecs() {
|
|
47388
|
+
return {
|
|
47389
|
+
banner: buildCommandSpec({
|
|
47390
|
+
command: "login banner",
|
|
47391
|
+
description: "Display xcsh banner with logo and connection information.",
|
|
47392
|
+
usage: "xcsh login banner",
|
|
47393
|
+
examples: [
|
|
47394
|
+
{
|
|
47395
|
+
command: "xcsh login banner",
|
|
47396
|
+
description: "Show the xcsh welcome banner"
|
|
47397
|
+
}
|
|
47398
|
+
],
|
|
47399
|
+
category: "login",
|
|
47400
|
+
related: ["login profile show"]
|
|
47401
|
+
}),
|
|
47402
|
+
"profile list": buildCommandSpec({
|
|
47403
|
+
command: "login profile list",
|
|
47404
|
+
description: "List all saved connection profiles.",
|
|
47405
|
+
usage: "xcsh login profile list",
|
|
47406
|
+
examples: [
|
|
47407
|
+
{
|
|
47408
|
+
command: "xcsh login profile list",
|
|
47409
|
+
description: "List saved profiles"
|
|
47410
|
+
},
|
|
47411
|
+
{
|
|
47412
|
+
command: "xcsh login profile list --output json",
|
|
47413
|
+
description: "Get profiles as JSON"
|
|
47414
|
+
}
|
|
47415
|
+
],
|
|
47416
|
+
category: "login",
|
|
47417
|
+
related: [
|
|
47418
|
+
"login profile show",
|
|
47419
|
+
"login profile create",
|
|
47420
|
+
"login profile use"
|
|
47421
|
+
]
|
|
47422
|
+
}),
|
|
47423
|
+
"profile show": buildCommandSpec({
|
|
47424
|
+
command: "login profile show",
|
|
47425
|
+
description: "Show current connection profile and authentication status.",
|
|
47426
|
+
usage: "xcsh login profile show [name]",
|
|
47427
|
+
flags: [
|
|
47428
|
+
{
|
|
47429
|
+
name: "name",
|
|
47430
|
+
description: "Profile name to show (optional, defaults to active)",
|
|
47431
|
+
type: "string"
|
|
47432
|
+
}
|
|
47433
|
+
],
|
|
47434
|
+
examples: [
|
|
47435
|
+
{
|
|
47436
|
+
command: "xcsh login profile show",
|
|
47437
|
+
description: "Show active profile"
|
|
47438
|
+
},
|
|
47439
|
+
{
|
|
47440
|
+
command: "xcsh login profile show production",
|
|
47441
|
+
description: "Show specific profile"
|
|
47442
|
+
}
|
|
47443
|
+
],
|
|
47444
|
+
category: "login",
|
|
47445
|
+
related: ["login profile list", "login profile use"]
|
|
47446
|
+
}),
|
|
47447
|
+
"profile create": buildCommandSpec({
|
|
47448
|
+
command: "login profile create",
|
|
47449
|
+
description: "Create a new connection profile with URL and credentials.",
|
|
47450
|
+
usage: "xcsh login profile create <name>",
|
|
47451
|
+
flags: [
|
|
47452
|
+
{
|
|
47453
|
+
name: "name",
|
|
47454
|
+
description: "Profile name",
|
|
47455
|
+
type: "string",
|
|
47456
|
+
required: true
|
|
47457
|
+
}
|
|
47458
|
+
],
|
|
47459
|
+
examples: [
|
|
47460
|
+
{
|
|
47461
|
+
command: "xcsh login profile create production",
|
|
47462
|
+
description: "Create a new profile named 'production'"
|
|
47463
|
+
}
|
|
47464
|
+
],
|
|
47465
|
+
category: "login",
|
|
47466
|
+
related: ["login profile list", "login profile use"]
|
|
47467
|
+
}),
|
|
47468
|
+
"profile use": buildCommandSpec({
|
|
47469
|
+
command: "login profile use",
|
|
47470
|
+
description: "Switch to a different connection profile.",
|
|
47471
|
+
usage: "xcsh login profile use <name>",
|
|
47472
|
+
flags: [
|
|
47436
47473
|
{
|
|
47437
|
-
|
|
47438
|
-
|
|
47474
|
+
name: "name",
|
|
47475
|
+
description: "Profile name to activate",
|
|
47476
|
+
type: "string",
|
|
47477
|
+
required: true
|
|
47439
47478
|
}
|
|
47440
|
-
|
|
47441
|
-
|
|
47442
|
-
|
|
47443
|
-
|
|
47444
|
-
|
|
47445
|
-
|
|
47446
|
-
|
|
47447
|
-
|
|
47448
|
-
|
|
47449
|
-
|
|
47450
|
-
|
|
47451
|
-
|
|
47452
|
-
|
|
47453
|
-
|
|
47454
|
-
|
|
47455
|
-
|
|
47456
|
-
|
|
47479
|
+
],
|
|
47480
|
+
examples: [
|
|
47481
|
+
{
|
|
47482
|
+
command: "xcsh login profile use staging",
|
|
47483
|
+
description: "Switch to staging profile"
|
|
47484
|
+
}
|
|
47485
|
+
],
|
|
47486
|
+
category: "login",
|
|
47487
|
+
related: ["login profile list", "login profile show"]
|
|
47488
|
+
}),
|
|
47489
|
+
"context show": buildCommandSpec({
|
|
47490
|
+
command: "login context show",
|
|
47491
|
+
description: "Show the current default namespace context.",
|
|
47492
|
+
usage: "xcsh login context show",
|
|
47493
|
+
examples: [
|
|
47494
|
+
{
|
|
47495
|
+
command: "xcsh login context show",
|
|
47496
|
+
description: "Display current namespace"
|
|
47497
|
+
}
|
|
47498
|
+
],
|
|
47499
|
+
category: "login",
|
|
47500
|
+
related: ["login context set", "login context list"]
|
|
47501
|
+
}),
|
|
47502
|
+
"context set": buildCommandSpec({
|
|
47503
|
+
command: "login context set",
|
|
47504
|
+
description: "Set the default namespace for subsequent operations.",
|
|
47505
|
+
usage: "xcsh login context set <namespace>",
|
|
47506
|
+
flags: [
|
|
47507
|
+
{
|
|
47508
|
+
name: "namespace",
|
|
47509
|
+
description: "Namespace to set as default",
|
|
47510
|
+
type: "string",
|
|
47511
|
+
required: true
|
|
47512
|
+
}
|
|
47513
|
+
],
|
|
47514
|
+
examples: [
|
|
47515
|
+
{
|
|
47516
|
+
command: "xcsh login context set production",
|
|
47517
|
+
description: "Set production as default namespace"
|
|
47518
|
+
}
|
|
47519
|
+
],
|
|
47520
|
+
category: "login",
|
|
47521
|
+
related: ["login context show", "login context list"]
|
|
47522
|
+
})
|
|
47523
|
+
};
|
|
47457
47524
|
}
|
|
47458
|
-
|
|
47459
|
-
|
|
47460
|
-
|
|
47461
|
-
|
|
47462
|
-
|
|
47463
|
-
|
|
47464
|
-
|
|
47465
|
-
|
|
47466
|
-
|
|
47467
|
-
|
|
47468
|
-
|
|
47469
|
-
// Green
|
|
47470
|
-
case "flag":
|
|
47471
|
-
return "#ffc107";
|
|
47472
|
-
// Yellow
|
|
47473
|
-
case "value":
|
|
47474
|
-
return "#9c27b0";
|
|
47475
|
-
// Purple
|
|
47476
|
-
default:
|
|
47477
|
-
return "#ffffff";
|
|
47525
|
+
function getCommandSpec(commandPath) {
|
|
47526
|
+
const cloudstatusSpecs = buildCloudstatusSpecs();
|
|
47527
|
+
const loginSpecs = buildLoginSpecs();
|
|
47528
|
+
const normalized = commandPath.toLowerCase().trim();
|
|
47529
|
+
if (normalized.startsWith("cloudstatus ")) {
|
|
47530
|
+
const subcommand = normalized.replace("cloudstatus ", "");
|
|
47531
|
+
return cloudstatusSpecs[subcommand];
|
|
47532
|
+
}
|
|
47533
|
+
if (normalized.startsWith("login ")) {
|
|
47534
|
+
const subcommand = normalized.replace("login ", "");
|
|
47535
|
+
return loginSpecs[subcommand];
|
|
47478
47536
|
}
|
|
47537
|
+
return void 0;
|
|
47479
47538
|
}
|
|
47480
|
-
function
|
|
47481
|
-
|
|
47482
|
-
|
|
47483
|
-
|
|
47484
|
-
|
|
47485
|
-
|
|
47486
|
-
|
|
47487
|
-
|
|
47488
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: categoryColor, bold: isSelected, inverse: isSelected, children: suggestion.label.padEnd(maxLabelWidth) }),
|
|
47489
|
-
suggestion.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", children: [
|
|
47490
|
-
" - ",
|
|
47491
|
-
suggestion.description
|
|
47492
|
-
] })
|
|
47493
|
-
] });
|
|
47539
|
+
function toGlobalFlagSpec(flag) {
|
|
47540
|
+
return {
|
|
47541
|
+
name: flag.name,
|
|
47542
|
+
type: flag.type ?? "string",
|
|
47543
|
+
description: flag.description,
|
|
47544
|
+
shorthand: flag.alias ?? "",
|
|
47545
|
+
default: flag.default ?? ""
|
|
47546
|
+
};
|
|
47494
47547
|
}
|
|
47495
|
-
function
|
|
47496
|
-
|
|
47497
|
-
|
|
47498
|
-
|
|
47499
|
-
|
|
47500
|
-
|
|
47501
|
-
|
|
47502
|
-
|
|
47503
|
-
|
|
47504
|
-
|
|
47505
|
-
|
|
47506
|
-
|
|
47507
|
-
|
|
47508
|
-
|
|
47509
|
-
|
|
47510
|
-
}
|
|
47511
|
-
if (key.downArrow) {
|
|
47512
|
-
onNavigate("down");
|
|
47513
|
-
return;
|
|
47514
|
-
}
|
|
47515
|
-
if (key.return || key.tab) {
|
|
47516
|
-
const selected = suggestions.at(selectedIndex);
|
|
47517
|
-
if (selected) {
|
|
47518
|
-
onSelect(selected);
|
|
47519
|
-
}
|
|
47520
|
-
return;
|
|
47521
|
-
}
|
|
47522
|
-
if (key.escape) {
|
|
47523
|
-
onCancel();
|
|
47524
|
-
return;
|
|
47525
|
-
}
|
|
47526
|
-
},
|
|
47527
|
-
{ isActive }
|
|
47528
|
-
);
|
|
47529
|
-
if (suggestions.length === 0) {
|
|
47530
|
-
return null;
|
|
47548
|
+
function buildCustomDomainSpec(domainName) {
|
|
47549
|
+
const domain = customDomains.get(domainName);
|
|
47550
|
+
if (!domain) return null;
|
|
47551
|
+
const subcommands = [];
|
|
47552
|
+
for (const [cmdName, cmd] of domain.commands) {
|
|
47553
|
+
subcommands.push({
|
|
47554
|
+
path: [domainName, cmdName],
|
|
47555
|
+
use: cmd.usage ?? cmdName,
|
|
47556
|
+
short: cmd.descriptionShort,
|
|
47557
|
+
long: cmd.description,
|
|
47558
|
+
example: "",
|
|
47559
|
+
aliases: cmd.aliases ?? [],
|
|
47560
|
+
flags: [],
|
|
47561
|
+
subcommands: []
|
|
47562
|
+
});
|
|
47531
47563
|
}
|
|
47532
|
-
const
|
|
47533
|
-
|
|
47534
|
-
|
|
47535
|
-
|
|
47536
|
-
|
|
47537
|
-
|
|
47538
|
-
|
|
47539
|
-
|
|
47540
|
-
|
|
47541
|
-
|
|
47542
|
-
|
|
47543
|
-
|
|
47544
|
-
|
|
47545
|
-
const showScrollDown = startIndex + maxVisible < totalCount;
|
|
47546
|
-
const maxLabelWidth = Math.max(
|
|
47547
|
-
...visibleSuggestions.map((s) => s.label.length)
|
|
47548
|
-
);
|
|
47549
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
47550
|
-
Box_default,
|
|
47551
|
-
{
|
|
47552
|
-
flexDirection: "column",
|
|
47553
|
-
borderStyle: "round",
|
|
47554
|
-
borderColor: "#CA260A",
|
|
47555
|
-
paddingX: 1,
|
|
47556
|
-
children: [
|
|
47557
|
-
showScrollUp && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", dimColor: true, children: [
|
|
47558
|
-
"\u25B2",
|
|
47559
|
-
" (",
|
|
47560
|
-
startIndex,
|
|
47561
|
-
" more above)"
|
|
47562
|
-
] }),
|
|
47563
|
-
visibleSuggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
47564
|
-
SuggestionItem,
|
|
47565
|
-
{
|
|
47566
|
-
suggestion,
|
|
47567
|
-
isSelected: startIndex + index === selectedIndex,
|
|
47568
|
-
index: startIndex + index,
|
|
47569
|
-
maxLabelWidth
|
|
47570
|
-
},
|
|
47571
|
-
suggestion.value
|
|
47572
|
-
)),
|
|
47573
|
-
showScrollDown && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", dimColor: true, children: [
|
|
47574
|
-
"\u25BC",
|
|
47575
|
-
" (",
|
|
47576
|
-
totalCount - startIndex - maxVisible,
|
|
47577
|
-
" more below)"
|
|
47578
|
-
] }),
|
|
47579
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "#666666", dimColor: true, children: "Tab: select | Up/Down: navigate | Esc: cancel" }) })
|
|
47580
|
-
]
|
|
47564
|
+
for (const [groupName, group] of domain.subcommands) {
|
|
47565
|
+
const groupSubcommands = [];
|
|
47566
|
+
for (const [cmdName, cmd] of group.commands) {
|
|
47567
|
+
groupSubcommands.push({
|
|
47568
|
+
path: [domainName, groupName, cmdName],
|
|
47569
|
+
use: cmd.usage ?? cmdName,
|
|
47570
|
+
short: cmd.descriptionShort,
|
|
47571
|
+
long: cmd.description,
|
|
47572
|
+
example: "",
|
|
47573
|
+
aliases: cmd.aliases ?? [],
|
|
47574
|
+
flags: [],
|
|
47575
|
+
subcommands: []
|
|
47576
|
+
});
|
|
47581
47577
|
}
|
|
47582
|
-
|
|
47578
|
+
subcommands.push({
|
|
47579
|
+
path: [domainName, groupName],
|
|
47580
|
+
use: groupName,
|
|
47581
|
+
short: group.descriptionShort,
|
|
47582
|
+
long: group.description,
|
|
47583
|
+
example: "",
|
|
47584
|
+
aliases: [],
|
|
47585
|
+
flags: [],
|
|
47586
|
+
subcommands: groupSubcommands
|
|
47587
|
+
});
|
|
47588
|
+
}
|
|
47589
|
+
return {
|
|
47590
|
+
path: [domainName],
|
|
47591
|
+
use: domainName,
|
|
47592
|
+
short: domain.descriptionShort,
|
|
47593
|
+
long: domain.description,
|
|
47594
|
+
example: "",
|
|
47595
|
+
aliases: [],
|
|
47596
|
+
flags: [],
|
|
47597
|
+
subcommands
|
|
47598
|
+
};
|
|
47583
47599
|
}
|
|
47584
|
-
|
|
47585
|
-
|
|
47586
|
-
|
|
47587
|
-
|
|
47588
|
-
const
|
|
47589
|
-
|
|
47590
|
-
|
|
47591
|
-
|
|
47592
|
-
|
|
47593
|
-
|
|
47594
|
-
|
|
47595
|
-
|
|
47596
|
-
|
|
47597
|
-
|
|
47598
|
-
}
|
|
47599
|
-
}, []);
|
|
47600
|
-
const handleCtrlC = (0, import_react25.useCallback)(() => {
|
|
47601
|
-
const now = Date.now();
|
|
47602
|
-
const elapsed = now - lastPressRef.current;
|
|
47603
|
-
if (elapsed < windowMs && lastPressRef.current !== 0) {
|
|
47604
|
-
reset();
|
|
47605
|
-
onDoublePress?.();
|
|
47606
|
-
return true;
|
|
47607
|
-
}
|
|
47608
|
-
lastPressRef.current = now;
|
|
47609
|
-
setIsWaiting(true);
|
|
47610
|
-
onFirstPress?.();
|
|
47611
|
-
if (timeoutRef.current) {
|
|
47612
|
-
clearTimeout(timeoutRef.current);
|
|
47613
|
-
}
|
|
47614
|
-
timeoutRef.current = setTimeout(() => {
|
|
47615
|
-
setIsWaiting(false);
|
|
47616
|
-
}, windowMs);
|
|
47617
|
-
return false;
|
|
47618
|
-
}, [windowMs, onFirstPress, onDoublePress, reset]);
|
|
47600
|
+
function buildApiDomainSpec(domainName) {
|
|
47601
|
+
const info = domainRegistry.get(domainName);
|
|
47602
|
+
if (!info) return null;
|
|
47603
|
+
const actions = Array.from(validActions);
|
|
47604
|
+
const subcommands = actions.map((action) => ({
|
|
47605
|
+
path: [domainName, action],
|
|
47606
|
+
use: action,
|
|
47607
|
+
short: `${action.charAt(0).toUpperCase() + action.slice(1)} ${info.displayName} resources`,
|
|
47608
|
+
long: `${action.charAt(0).toUpperCase() + action.slice(1)} ${info.displayName} resources in F5 Distributed Cloud`,
|
|
47609
|
+
example: `xcsh ${domainName} ${action}`,
|
|
47610
|
+
aliases: [],
|
|
47611
|
+
flags: [],
|
|
47612
|
+
subcommands: []
|
|
47613
|
+
}));
|
|
47619
47614
|
return {
|
|
47620
|
-
|
|
47621
|
-
|
|
47622
|
-
|
|
47615
|
+
path: [domainName],
|
|
47616
|
+
use: domainName,
|
|
47617
|
+
short: info.descriptionShort,
|
|
47618
|
+
long: info.description,
|
|
47619
|
+
example: "",
|
|
47620
|
+
aliases: info.aliases,
|
|
47621
|
+
flags: [],
|
|
47622
|
+
subcommands
|
|
47623
47623
|
};
|
|
47624
47624
|
}
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
|
|
47628
|
-
|
|
47629
|
-
|
|
47630
|
-
|
|
47631
|
-
const reset = (0, import_react26.useCallback)(() => {
|
|
47632
|
-
setHistoryIndex(-1);
|
|
47633
|
-
}, []);
|
|
47634
|
-
const navigateUp = (0, import_react26.useCallback)(() => {
|
|
47635
|
-
if (history.length === 0) {
|
|
47636
|
-
return null;
|
|
47637
|
-
}
|
|
47638
|
-
const newIndex = Math.min(historyIndex + 1, history.length - 1);
|
|
47639
|
-
setHistoryIndex(newIndex);
|
|
47640
|
-
const command = history[history.length - 1 - newIndex];
|
|
47641
|
-
if (command !== void 0) {
|
|
47642
|
-
onSelect?.(command);
|
|
47643
|
-
return command;
|
|
47625
|
+
function buildFullCLISpec() {
|
|
47626
|
+
const commands = [];
|
|
47627
|
+
for (const domain of customDomains.all()) {
|
|
47628
|
+
const spec = buildCustomDomainSpec(domain.name);
|
|
47629
|
+
if (spec) {
|
|
47630
|
+
commands.push(spec);
|
|
47644
47631
|
}
|
|
47645
|
-
|
|
47646
|
-
|
|
47647
|
-
const
|
|
47648
|
-
if (
|
|
47649
|
-
|
|
47650
|
-
|
|
47651
|
-
|
|
47652
|
-
return "";
|
|
47632
|
+
}
|
|
47633
|
+
const customDomainNames = new Set(customDomains.list());
|
|
47634
|
+
for (const [domainName] of domainRegistry) {
|
|
47635
|
+
if (!customDomainNames.has(domainName)) {
|
|
47636
|
+
const spec = buildApiDomainSpec(domainName);
|
|
47637
|
+
if (spec) {
|
|
47638
|
+
commands.push(spec);
|
|
47653
47639
|
}
|
|
47654
|
-
return null;
|
|
47655
|
-
}
|
|
47656
|
-
const newIndex = historyIndex - 1;
|
|
47657
|
-
setHistoryIndex(newIndex);
|
|
47658
|
-
const command = history[history.length - 1 - newIndex];
|
|
47659
|
-
if (command !== void 0) {
|
|
47660
|
-
onSelect?.(command);
|
|
47661
|
-
return command;
|
|
47662
47640
|
}
|
|
47663
|
-
|
|
47664
|
-
|
|
47641
|
+
}
|
|
47642
|
+
commands.sort((a, b) => (a.path[0] ?? "").localeCompare(b.path[0] ?? ""));
|
|
47665
47643
|
return {
|
|
47666
|
-
|
|
47667
|
-
|
|
47668
|
-
|
|
47669
|
-
isNavigating: historyIndex >= 0,
|
|
47670
|
-
currentIndex: historyIndex
|
|
47644
|
+
version: CLI_VERSION,
|
|
47645
|
+
global_flags: GLOBAL_FLAGS.map(toGlobalFlagSpec),
|
|
47646
|
+
commands
|
|
47671
47647
|
};
|
|
47672
47648
|
}
|
|
47649
|
+
function formatFullCLISpec() {
|
|
47650
|
+
return JSON.stringify(buildFullCLISpec(), null, 2);
|
|
47651
|
+
}
|
|
47673
47652
|
|
|
47674
|
-
// src/
|
|
47675
|
-
|
|
47653
|
+
// src/debug/protocol.ts
|
|
47654
|
+
function getDebugFormat() {
|
|
47655
|
+
const envValue = process.env[`${ENV_PREFIX}_DEBUG_EVENTS`];
|
|
47656
|
+
if (envValue === "jsonl" || envValue === "human") {
|
|
47657
|
+
return envValue;
|
|
47658
|
+
}
|
|
47659
|
+
if (process.env[`${ENV_PREFIX}_DEBUG`] === "true") {
|
|
47660
|
+
return "human";
|
|
47661
|
+
}
|
|
47662
|
+
return "none";
|
|
47663
|
+
}
|
|
47664
|
+
var DebugProtocolImpl = class {
|
|
47665
|
+
format;
|
|
47666
|
+
events = [];
|
|
47667
|
+
startTime;
|
|
47668
|
+
constructor() {
|
|
47669
|
+
this.format = getDebugFormat();
|
|
47670
|
+
this.startTime = Date.now();
|
|
47671
|
+
}
|
|
47672
|
+
/**
|
|
47673
|
+
* Check if debug events are enabled
|
|
47674
|
+
*/
|
|
47675
|
+
isEnabled() {
|
|
47676
|
+
return this.format !== "none";
|
|
47677
|
+
}
|
|
47678
|
+
/**
|
|
47679
|
+
* Check if JSONL format is enabled
|
|
47680
|
+
*/
|
|
47681
|
+
isJsonl() {
|
|
47682
|
+
return this.format === "jsonl";
|
|
47683
|
+
}
|
|
47684
|
+
/**
|
|
47685
|
+
* Emit a debug event
|
|
47686
|
+
*/
|
|
47687
|
+
emit(type, event, data = {}) {
|
|
47688
|
+
if (!this.isEnabled()) return;
|
|
47689
|
+
const entry = {
|
|
47690
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47691
|
+
type,
|
|
47692
|
+
event,
|
|
47693
|
+
data: {
|
|
47694
|
+
...data,
|
|
47695
|
+
elapsedMs: Date.now() - this.startTime
|
|
47696
|
+
}
|
|
47697
|
+
};
|
|
47698
|
+
this.events.push(entry);
|
|
47699
|
+
if (this.format === "jsonl") {
|
|
47700
|
+
console.error(JSON.stringify(entry));
|
|
47701
|
+
} else if (this.format === "human") {
|
|
47702
|
+
const prefix = `DEBUG [${type}:${event}]`;
|
|
47703
|
+
const dataStr = Object.keys(data).length > 0 ? ` ${JSON.stringify(data)}` : "";
|
|
47704
|
+
console.error(`${prefix}${dataStr}`);
|
|
47705
|
+
}
|
|
47706
|
+
}
|
|
47707
|
+
/**
|
|
47708
|
+
* Emit session-related event
|
|
47709
|
+
*/
|
|
47710
|
+
session(event, data = {}) {
|
|
47711
|
+
this.emit("session", event, data);
|
|
47712
|
+
}
|
|
47713
|
+
/**
|
|
47714
|
+
* Emit API-related event
|
|
47715
|
+
*/
|
|
47716
|
+
api(event, data = {}) {
|
|
47717
|
+
this.emit("api", event, data);
|
|
47718
|
+
}
|
|
47719
|
+
/**
|
|
47720
|
+
* Emit authentication-related event
|
|
47721
|
+
*/
|
|
47722
|
+
auth(event, data = {}) {
|
|
47723
|
+
this.emit("auth", event, data);
|
|
47724
|
+
}
|
|
47725
|
+
/**
|
|
47726
|
+
* Emit profile-related event
|
|
47727
|
+
*/
|
|
47728
|
+
profile(event, data = {}) {
|
|
47729
|
+
this.emit("profile", event, data);
|
|
47730
|
+
}
|
|
47731
|
+
/**
|
|
47732
|
+
* Emit UI-related event
|
|
47733
|
+
*/
|
|
47734
|
+
ui(event, data = {}) {
|
|
47735
|
+
this.emit("ui", event, data);
|
|
47736
|
+
}
|
|
47737
|
+
/**
|
|
47738
|
+
* Emit error event
|
|
47739
|
+
*/
|
|
47740
|
+
error(event, error, data = {}) {
|
|
47741
|
+
this.emit("error", event, {
|
|
47742
|
+
...data,
|
|
47743
|
+
error: error instanceof Error ? error.message : String(error),
|
|
47744
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
47745
|
+
});
|
|
47746
|
+
}
|
|
47747
|
+
/**
|
|
47748
|
+
* Get all captured events
|
|
47749
|
+
*/
|
|
47750
|
+
getEvents() {
|
|
47751
|
+
return [...this.events];
|
|
47752
|
+
}
|
|
47753
|
+
/**
|
|
47754
|
+
* Dump session state on exit (for --dump-state-on-exit)
|
|
47755
|
+
*/
|
|
47756
|
+
dumpState(state) {
|
|
47757
|
+
if (!this.isEnabled()) return;
|
|
47758
|
+
console.error("\n=== Session State Dump ===");
|
|
47759
|
+
console.error(JSON.stringify(state, null, 2));
|
|
47760
|
+
console.error("=== End Session State ===\n");
|
|
47761
|
+
}
|
|
47762
|
+
};
|
|
47763
|
+
var debugProtocol = new DebugProtocolImpl();
|
|
47764
|
+
function emitSessionState(session) {
|
|
47765
|
+
debugProtocol.auth("session_state", {
|
|
47766
|
+
isAuthenticated: session.isAuthenticated(),
|
|
47767
|
+
isTokenValidated: session.isTokenValidated(),
|
|
47768
|
+
validationError: session.getValidationError(),
|
|
47769
|
+
serverUrl: session.getServerUrl(),
|
|
47770
|
+
activeProfile: session.getActiveProfileName(),
|
|
47771
|
+
hasApiClient: session.getAPIClient() !== null
|
|
47772
|
+
});
|
|
47773
|
+
const showsWarning = session.isAuthenticated() && !session.isTokenValidated() && session.getValidationError();
|
|
47774
|
+
debugProtocol.auth("warning_check", {
|
|
47775
|
+
shouldShowWarning: !!showsWarning,
|
|
47776
|
+
isAuthenticated: session.isAuthenticated(),
|
|
47777
|
+
isTokenValidated: session.isTokenValidated(),
|
|
47778
|
+
hasValidationError: !!session.getValidationError()
|
|
47779
|
+
});
|
|
47780
|
+
}
|
|
47676
47781
|
|
|
47677
|
-
// src/
|
|
47678
|
-
var
|
|
47679
|
-
|
|
47680
|
-
|
|
47681
|
-
|
|
47682
|
-
|
|
47683
|
-
|
|
47684
|
-
|
|
47685
|
-
|
|
47686
|
-
|
|
47687
|
-
|
|
47688
|
-
|
|
47689
|
-
|
|
47690
|
-
|
|
47691
|
-
|
|
47692
|
-
|
|
47693
|
-
|
|
47694
|
-
|
|
47695
|
-
|
|
47696
|
-
|
|
47697
|
-
|
|
47698
|
-
|
|
47699
|
-
|
|
47700
|
-
}
|
|
47701
|
-
|
|
47702
|
-
|
|
47703
|
-
|
|
47704
|
-
|
|
47705
|
-
|
|
47706
|
-
|
|
47707
|
-
|
|
47708
|
-
|
|
47709
|
-
|
|
47782
|
+
// src/repl/session.ts
|
|
47783
|
+
var NAMESPACE_CACHE_TTL = 5 * 60 * 1e3;
|
|
47784
|
+
var REPLSession = class {
|
|
47785
|
+
_history = null;
|
|
47786
|
+
_namespace;
|
|
47787
|
+
_lastExitCode = 0;
|
|
47788
|
+
_contextPath;
|
|
47789
|
+
_tenant = "";
|
|
47790
|
+
_username = "";
|
|
47791
|
+
_validator;
|
|
47792
|
+
_serverUrl = "";
|
|
47793
|
+
_apiToken = "";
|
|
47794
|
+
_apiClient = null;
|
|
47795
|
+
_outputFormat = "table";
|
|
47796
|
+
_debug = false;
|
|
47797
|
+
_profileManager;
|
|
47798
|
+
_activeProfile = null;
|
|
47799
|
+
_activeProfileName = null;
|
|
47800
|
+
_namespaceCache = [];
|
|
47801
|
+
_namespaceCacheTime = 0;
|
|
47802
|
+
// Token validation state
|
|
47803
|
+
_tokenValidated = false;
|
|
47804
|
+
_validationError = null;
|
|
47805
|
+
constructor(config = {}) {
|
|
47806
|
+
this._namespace = config.namespace ?? this.getDefaultNamespace();
|
|
47807
|
+
this._contextPath = new ContextPath();
|
|
47808
|
+
this._validator = new ContextValidator();
|
|
47809
|
+
this._profileManager = getProfileManager();
|
|
47810
|
+
this._serverUrl = config.serverUrl ?? process.env[`${ENV_PREFIX}_API_URL`] ?? "";
|
|
47811
|
+
this._apiToken = config.apiToken ?? process.env[`${ENV_PREFIX}_API_TOKEN`] ?? "";
|
|
47812
|
+
this._outputFormat = config.outputFormat ?? getOutputFormatFromEnv() ?? "table";
|
|
47813
|
+
this._debug = config.debug ?? process.env[`${ENV_PREFIX}_DEBUG`] === "true";
|
|
47814
|
+
if (this._serverUrl) {
|
|
47815
|
+
this._tenant = this.extractTenant(this._serverUrl);
|
|
47816
|
+
}
|
|
47817
|
+
if (this._serverUrl) {
|
|
47818
|
+
this._apiClient = new APIClient({
|
|
47819
|
+
serverUrl: this._serverUrl,
|
|
47820
|
+
apiToken: this._apiToken,
|
|
47821
|
+
debug: this._debug
|
|
47822
|
+
});
|
|
47823
|
+
}
|
|
47824
|
+
}
|
|
47825
|
+
/**
|
|
47826
|
+
* Initialize the session (async operations)
|
|
47827
|
+
*/
|
|
47828
|
+
async initialize() {
|
|
47829
|
+
try {
|
|
47830
|
+
this._history = await HistoryManager.create(
|
|
47831
|
+
getHistoryFilePath(),
|
|
47832
|
+
1e3
|
|
47833
|
+
);
|
|
47834
|
+
} catch (error) {
|
|
47835
|
+
console.error("Warning: could not initialize history:", error);
|
|
47836
|
+
this._history = new HistoryManager(getHistoryFilePath(), 1e3);
|
|
47837
|
+
}
|
|
47838
|
+
await this.loadActiveProfile();
|
|
47839
|
+
if (this._apiClient?.isAuthenticated()) {
|
|
47840
|
+
debugProtocol.auth("token_validation_start", {
|
|
47841
|
+
serverUrl: this._serverUrl,
|
|
47842
|
+
hasApiClient: true
|
|
47843
|
+
});
|
|
47844
|
+
const result = await this._apiClient.validateToken();
|
|
47845
|
+
this._tokenValidated = result.valid;
|
|
47846
|
+
this._validationError = result.error ?? null;
|
|
47847
|
+
debugProtocol.auth("token_validation_complete", {
|
|
47848
|
+
valid: result.valid,
|
|
47849
|
+
error: result.error,
|
|
47850
|
+
tokenValidated: this._tokenValidated,
|
|
47851
|
+
validationError: this._validationError
|
|
47852
|
+
});
|
|
47853
|
+
if (result.valid) {
|
|
47854
|
+
await this.fetchUserInfo();
|
|
47855
|
+
}
|
|
47856
|
+
}
|
|
47857
|
+
}
|
|
47858
|
+
/**
|
|
47859
|
+
* Fetch user info from the API
|
|
47860
|
+
*/
|
|
47861
|
+
async fetchUserInfo() {
|
|
47862
|
+
if (!this._apiClient) return;
|
|
47863
|
+
try {
|
|
47864
|
+
const response = await this._apiClient.get("/api/web/custom/user/info");
|
|
47865
|
+
if (response.ok && response.data) {
|
|
47866
|
+
this._username = response.data.email || response.data.name || response.data.username || "";
|
|
47867
|
+
}
|
|
47868
|
+
} catch {
|
|
47869
|
+
}
|
|
47870
|
+
}
|
|
47871
|
+
/**
|
|
47872
|
+
* Load the active profile from profile manager
|
|
47873
|
+
* Note: Environment variables take priority over profile settings
|
|
47874
|
+
* If no active profile is set but exactly one profile exists, auto-activate it
|
|
47875
|
+
*/
|
|
47876
|
+
async loadActiveProfile() {
|
|
47877
|
+
try {
|
|
47878
|
+
let activeName = await this._profileManager.getActive();
|
|
47879
|
+
if (!activeName) {
|
|
47880
|
+
const profiles = await this._profileManager.list();
|
|
47881
|
+
if (profiles.length === 1 && profiles[0]) {
|
|
47882
|
+
const singleProfile = profiles[0];
|
|
47883
|
+
await this._profileManager.setActive(singleProfile.name);
|
|
47884
|
+
activeName = singleProfile.name;
|
|
47885
|
+
}
|
|
47886
|
+
}
|
|
47887
|
+
if (activeName) {
|
|
47888
|
+
const profile = await this._profileManager.get(activeName);
|
|
47889
|
+
if (profile) {
|
|
47890
|
+
this._activeProfileName = activeName;
|
|
47891
|
+
this._activeProfile = profile;
|
|
47892
|
+
const envUrl = process.env[`${ENV_PREFIX}_API_URL`];
|
|
47893
|
+
const envToken = process.env[`${ENV_PREFIX}_API_TOKEN`];
|
|
47894
|
+
const envNamespace = process.env[`${ENV_PREFIX}_NAMESPACE`];
|
|
47895
|
+
if (!envUrl && profile.apiUrl) {
|
|
47896
|
+
this._serverUrl = profile.apiUrl;
|
|
47897
|
+
this._tenant = this.extractTenant(profile.apiUrl);
|
|
47898
|
+
}
|
|
47899
|
+
if (!envToken && profile.apiToken) {
|
|
47900
|
+
this._apiToken = profile.apiToken;
|
|
47901
|
+
}
|
|
47902
|
+
if (!envNamespace && profile.defaultNamespace) {
|
|
47903
|
+
this._namespace = profile.defaultNamespace;
|
|
47904
|
+
}
|
|
47905
|
+
if (this._serverUrl) {
|
|
47906
|
+
this._apiClient = new APIClient({
|
|
47907
|
+
serverUrl: this._serverUrl,
|
|
47908
|
+
apiToken: this._apiToken,
|
|
47909
|
+
debug: this._debug
|
|
47910
|
+
});
|
|
47911
|
+
}
|
|
47912
|
+
}
|
|
47913
|
+
}
|
|
47914
|
+
} catch (error) {
|
|
47915
|
+
debugProtocol.error("profile_load_failed", error, {
|
|
47916
|
+
activeProfile: this._activeProfileName
|
|
47917
|
+
});
|
|
47918
|
+
}
|
|
47710
47919
|
}
|
|
47711
|
-
|
|
47712
|
-
|
|
47713
|
-
|
|
47714
|
-
|
|
47715
|
-
|
|
47716
|
-
const padding = " ".repeat(maxLen - env3.name.length + 3);
|
|
47717
|
-
const flagNote = env3.relatedFlag ? ` [${env3.relatedFlag}]` : "";
|
|
47718
|
-
lines.push(` ${env3.name}${padding}${env3.description}${flagNote}`);
|
|
47920
|
+
/**
|
|
47921
|
+
* Get the default namespace from environment or config
|
|
47922
|
+
*/
|
|
47923
|
+
getDefaultNamespace() {
|
|
47924
|
+
return process.env[`${ENV_PREFIX}_NAMESPACE`] ?? "default";
|
|
47719
47925
|
}
|
|
47720
|
-
|
|
47721
|
-
|
|
47722
|
-
|
|
47723
|
-
|
|
47724
|
-
|
|
47725
|
-
|
|
47726
|
-
|
|
47727
|
-
|
|
47728
|
-
|
|
47729
|
-
|
|
47730
|
-
|
|
47731
|
-
|
|
47732
|
-
|
|
47733
|
-
|
|
47734
|
-
function wrapText3(text, width, indent) {
|
|
47735
|
-
const prefix = " ".repeat(indent);
|
|
47736
|
-
const words = text.split(/\s+/);
|
|
47737
|
-
const lines = [];
|
|
47738
|
-
let currentLine = prefix;
|
|
47739
|
-
for (const word of words) {
|
|
47740
|
-
if (currentLine.length + word.length + 1 > width && currentLine !== prefix) {
|
|
47741
|
-
lines.push(currentLine);
|
|
47742
|
-
currentLine = prefix + word;
|
|
47743
|
-
} else {
|
|
47744
|
-
currentLine += (currentLine === prefix ? "" : " ") + word;
|
|
47926
|
+
/**
|
|
47927
|
+
* Extract tenant name from server URL
|
|
47928
|
+
*/
|
|
47929
|
+
extractTenant(url) {
|
|
47930
|
+
try {
|
|
47931
|
+
const parsed = new URL(url);
|
|
47932
|
+
const hostname = parsed.hostname;
|
|
47933
|
+
const parts = hostname.split(".");
|
|
47934
|
+
if (parts.length > 0 && parts[0]) {
|
|
47935
|
+
return parts[0];
|
|
47936
|
+
}
|
|
47937
|
+
return hostname;
|
|
47938
|
+
} catch {
|
|
47939
|
+
return "";
|
|
47745
47940
|
}
|
|
47746
47941
|
}
|
|
47747
|
-
|
|
47748
|
-
|
|
47942
|
+
/**
|
|
47943
|
+
* Set the default namespace for the session
|
|
47944
|
+
*/
|
|
47945
|
+
setNamespace(ns) {
|
|
47946
|
+
this._namespace = ns;
|
|
47749
47947
|
}
|
|
47750
|
-
|
|
47751
|
-
|
|
47752
|
-
|
|
47753
|
-
|
|
47754
|
-
|
|
47755
|
-
colorBoldWhite(`${CLI_NAME} - ${CLI_FULL_NAME} v${CLI_VERSION}`),
|
|
47756
|
-
"",
|
|
47757
|
-
"DESCRIPTION",
|
|
47758
|
-
...wrapText3(CLI_DESCRIPTION_LONG, 80, 2),
|
|
47759
|
-
"",
|
|
47760
|
-
"USAGE",
|
|
47761
|
-
` ${CLI_NAME} Enter interactive REPL mode`,
|
|
47762
|
-
` ${CLI_NAME} <domain> <action> Execute command non-interactively`,
|
|
47763
|
-
` ${CLI_NAME} help [topic] Show help for a topic`,
|
|
47764
|
-
"",
|
|
47765
|
-
"EXAMPLES",
|
|
47766
|
-
` ${CLI_NAME} tenant_and_identity list namespace List all namespaces`,
|
|
47767
|
-
` ${CLI_NAME} virtual get http_loadbalancer Get a specific load balancer`,
|
|
47768
|
-
` ${CLI_NAME} dns list List DNS zones`,
|
|
47769
|
-
` ${CLI_NAME} waf list -ns prod List WAF policies in prod`,
|
|
47770
|
-
"",
|
|
47771
|
-
...formatDomainsSection(),
|
|
47772
|
-
"",
|
|
47773
|
-
...formatGlobalFlags(),
|
|
47774
|
-
"",
|
|
47775
|
-
...formatEnvVarsSection(),
|
|
47776
|
-
"",
|
|
47777
|
-
...formatConfigSection(),
|
|
47778
|
-
"",
|
|
47779
|
-
"NAVIGATION (Interactive Mode)",
|
|
47780
|
-
" <domain> Navigate into a domain (e.g., 'dns', 'lb')",
|
|
47781
|
-
" /domain Navigate directly to domain from anywhere",
|
|
47782
|
-
" .. Go up one level",
|
|
47783
|
-
" / Return to root",
|
|
47784
|
-
" context Show current navigation context",
|
|
47785
|
-
"",
|
|
47786
|
-
"BUILTINS",
|
|
47787
|
-
" help Show this help",
|
|
47788
|
-
" domains List all available domains",
|
|
47789
|
-
" clear Clear the screen",
|
|
47790
|
-
" history Show command history",
|
|
47791
|
-
" quit, exit Exit the shell",
|
|
47792
|
-
""
|
|
47793
|
-
];
|
|
47794
|
-
}
|
|
47795
|
-
function formatGlobalFlags() {
|
|
47796
|
-
return [
|
|
47797
|
-
"GLOBAL FLAGS",
|
|
47798
|
-
" -v, --version Show version number",
|
|
47799
|
-
" -h, --help Show this help",
|
|
47800
|
-
" --no-color Disable color output",
|
|
47801
|
-
" -o, --output <fmt> Output format (json, yaml, table)",
|
|
47802
|
-
" -ns, --namespace <ns> Target namespace",
|
|
47803
|
-
" --spec Output command specification as JSON (for AI)"
|
|
47804
|
-
];
|
|
47805
|
-
}
|
|
47806
|
-
function formatEnvironmentVariables() {
|
|
47807
|
-
return formatEnvVarsSection();
|
|
47808
|
-
}
|
|
47809
|
-
function formatDomainHelp(domain) {
|
|
47810
|
-
const output = ["", colorBoldWhite(`${domain.displayName}`), ""];
|
|
47811
|
-
output.push(` ${domain.description}`);
|
|
47812
|
-
output.push("");
|
|
47813
|
-
if (domain.category || domain.complexity) {
|
|
47814
|
-
const meta = [];
|
|
47815
|
-
if (domain.category) meta.push(`Category: ${domain.category}`);
|
|
47816
|
-
if (domain.complexity) meta.push(`Complexity: ${domain.complexity}`);
|
|
47817
|
-
output.push(colorDim(` ${meta.join(" | ")}`));
|
|
47818
|
-
output.push("");
|
|
47948
|
+
/**
|
|
47949
|
+
* Get the current default namespace
|
|
47950
|
+
*/
|
|
47951
|
+
getNamespace() {
|
|
47952
|
+
return this._namespace;
|
|
47819
47953
|
}
|
|
47820
|
-
|
|
47821
|
-
|
|
47822
|
-
|
|
47823
|
-
|
|
47824
|
-
|
|
47825
|
-
list: "List resources",
|
|
47826
|
-
get: "Get a specific resource by name",
|
|
47827
|
-
create: "Create a new resource",
|
|
47828
|
-
delete: "Delete a resource",
|
|
47829
|
-
replace: "Replace a resource configuration",
|
|
47830
|
-
apply: "Apply configuration from file",
|
|
47831
|
-
status: "Get resource status",
|
|
47832
|
-
patch: "Patch a resource",
|
|
47833
|
-
"add-labels": "Add labels to a resource",
|
|
47834
|
-
"remove-labels": "Remove labels from a resource"
|
|
47835
|
-
};
|
|
47836
|
-
for (const action of validActions) {
|
|
47837
|
-
const desc = actionDescriptions2[action] ?? action;
|
|
47838
|
-
output.push(` ${action.padEnd(16)} ${desc}`);
|
|
47954
|
+
/**
|
|
47955
|
+
* Get the exit code of the last command
|
|
47956
|
+
*/
|
|
47957
|
+
getLastExitCode() {
|
|
47958
|
+
return this._lastExitCode;
|
|
47839
47959
|
}
|
|
47840
|
-
|
|
47841
|
-
|
|
47842
|
-
|
|
47843
|
-
|
|
47844
|
-
|
|
47845
|
-
` ${CLI_NAME} ${domain.name} create my-resource -f config.yaml`
|
|
47846
|
-
);
|
|
47847
|
-
output.push(` ${CLI_NAME} ${domain.name} delete my-resource`);
|
|
47848
|
-
output.push("");
|
|
47849
|
-
if (domain.useCases && domain.useCases.length > 0) {
|
|
47850
|
-
output.push("USE CASES");
|
|
47851
|
-
for (const useCase of domain.useCases.slice(0, 5)) {
|
|
47852
|
-
output.push(` - ${useCase}`);
|
|
47853
|
-
}
|
|
47854
|
-
output.push("");
|
|
47960
|
+
/**
|
|
47961
|
+
* Set the exit code of the last command
|
|
47962
|
+
*/
|
|
47963
|
+
setLastExitCode(code) {
|
|
47964
|
+
this._lastExitCode = code;
|
|
47855
47965
|
}
|
|
47856
|
-
|
|
47857
|
-
|
|
47858
|
-
|
|
47859
|
-
|
|
47966
|
+
/**
|
|
47967
|
+
* Get the current navigation context
|
|
47968
|
+
*/
|
|
47969
|
+
getContextPath() {
|
|
47970
|
+
return this._contextPath;
|
|
47971
|
+
}
|
|
47972
|
+
/**
|
|
47973
|
+
* Get the current tenant name
|
|
47974
|
+
*/
|
|
47975
|
+
getTenant() {
|
|
47976
|
+
return this._tenant;
|
|
47977
|
+
}
|
|
47978
|
+
/**
|
|
47979
|
+
* Get the logged-in user's name/email
|
|
47980
|
+
*/
|
|
47981
|
+
getUsername() {
|
|
47982
|
+
return this._username;
|
|
47983
|
+
}
|
|
47984
|
+
/**
|
|
47985
|
+
* Set the username (used when fetched from API)
|
|
47986
|
+
*/
|
|
47987
|
+
setUsername(username) {
|
|
47988
|
+
this._username = username;
|
|
47989
|
+
}
|
|
47990
|
+
/**
|
|
47991
|
+
* Get the context validator
|
|
47992
|
+
*/
|
|
47993
|
+
getValidator() {
|
|
47994
|
+
return this._validator;
|
|
47995
|
+
}
|
|
47996
|
+
/**
|
|
47997
|
+
* Get the history manager
|
|
47998
|
+
*/
|
|
47999
|
+
getHistory() {
|
|
48000
|
+
return this._history;
|
|
48001
|
+
}
|
|
48002
|
+
/**
|
|
48003
|
+
* Get the server URL
|
|
48004
|
+
*/
|
|
48005
|
+
getServerUrl() {
|
|
48006
|
+
return this._serverUrl;
|
|
48007
|
+
}
|
|
48008
|
+
/**
|
|
48009
|
+
* Check if connected to an API server
|
|
48010
|
+
*/
|
|
48011
|
+
isConnected() {
|
|
48012
|
+
return this._serverUrl !== "" && this._apiClient !== null;
|
|
48013
|
+
}
|
|
48014
|
+
/**
|
|
48015
|
+
* Check if authenticated with API
|
|
48016
|
+
*/
|
|
48017
|
+
isAuthenticated() {
|
|
48018
|
+
return this._apiClient?.isAuthenticated() ?? false;
|
|
48019
|
+
}
|
|
48020
|
+
/**
|
|
48021
|
+
* Check if the token has been validated (verified working)
|
|
48022
|
+
*/
|
|
48023
|
+
isTokenValidated() {
|
|
48024
|
+
return this._tokenValidated;
|
|
48025
|
+
}
|
|
48026
|
+
/**
|
|
48027
|
+
* Get the token validation error, if any
|
|
48028
|
+
*/
|
|
48029
|
+
getValidationError() {
|
|
48030
|
+
return this._validationError;
|
|
48031
|
+
}
|
|
48032
|
+
/**
|
|
48033
|
+
* Get the API client
|
|
48034
|
+
*/
|
|
48035
|
+
getAPIClient() {
|
|
48036
|
+
return this._apiClient;
|
|
47860
48037
|
}
|
|
47861
|
-
|
|
47862
|
-
|
|
47863
|
-
|
|
47864
|
-
|
|
48038
|
+
/**
|
|
48039
|
+
* Get the current output format
|
|
48040
|
+
*/
|
|
48041
|
+
getOutputFormat() {
|
|
48042
|
+
return this._outputFormat;
|
|
47865
48043
|
}
|
|
47866
|
-
|
|
47867
|
-
|
|
47868
|
-
|
|
47869
|
-
|
|
47870
|
-
|
|
47871
|
-
const domain = getDomainInfo(domainName);
|
|
47872
|
-
const displayDomain = domain?.displayName ?? domainName;
|
|
47873
|
-
const actionDescriptions2 = {
|
|
47874
|
-
list: {
|
|
47875
|
-
desc: "List all resources in the namespace",
|
|
47876
|
-
usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
|
|
47877
|
-
},
|
|
47878
|
-
get: {
|
|
47879
|
-
desc: "Get a specific resource by name",
|
|
47880
|
-
usage: `${CLI_NAME} ${domainName} get <name> [-o json|yaml|table]`
|
|
47881
|
-
},
|
|
47882
|
-
create: {
|
|
47883
|
-
desc: "Create a new resource",
|
|
47884
|
-
usage: `${CLI_NAME} ${domainName} create <name> -f <file.yaml>`
|
|
47885
|
-
},
|
|
47886
|
-
delete: {
|
|
47887
|
-
desc: "Delete a resource by name",
|
|
47888
|
-
usage: `${CLI_NAME} ${domainName} delete <name>`
|
|
47889
|
-
},
|
|
47890
|
-
replace: {
|
|
47891
|
-
desc: "Replace an existing resource configuration",
|
|
47892
|
-
usage: `${CLI_NAME} ${domainName} replace <name> -f <file.yaml>`
|
|
47893
|
-
},
|
|
47894
|
-
apply: {
|
|
47895
|
-
desc: "Apply configuration from a file (create or update)",
|
|
47896
|
-
usage: `${CLI_NAME} ${domainName} apply -f <file.yaml>`
|
|
47897
|
-
},
|
|
47898
|
-
status: {
|
|
47899
|
-
desc: "Get the current status of a resource",
|
|
47900
|
-
usage: `${CLI_NAME} ${domainName} status <name>`
|
|
47901
|
-
},
|
|
47902
|
-
patch: {
|
|
47903
|
-
desc: "Patch specific fields of a resource",
|
|
47904
|
-
usage: `${CLI_NAME} ${domainName} patch <name> -f <patch.yaml>`
|
|
47905
|
-
},
|
|
47906
|
-
"add-labels": {
|
|
47907
|
-
desc: "Add labels to a resource",
|
|
47908
|
-
usage: `${CLI_NAME} ${domainName} add-labels <name> key=value`
|
|
47909
|
-
},
|
|
47910
|
-
"remove-labels": {
|
|
47911
|
-
desc: "Remove labels from a resource",
|
|
47912
|
-
usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
|
|
47913
|
-
}
|
|
47914
|
-
};
|
|
47915
|
-
const actionInfo = actionDescriptions2[action] ?? {
|
|
47916
|
-
desc: `Execute ${action} operation`,
|
|
47917
|
-
usage: `${CLI_NAME} ${domainName} ${action} [options]`
|
|
47918
|
-
};
|
|
47919
|
-
return [
|
|
47920
|
-
"",
|
|
47921
|
-
colorBoldWhite(`${displayDomain} - ${action}`),
|
|
47922
|
-
"",
|
|
47923
|
-
` ${actionInfo.desc}`,
|
|
47924
|
-
"",
|
|
47925
|
-
"USAGE",
|
|
47926
|
-
` ${actionInfo.usage}`,
|
|
47927
|
-
"",
|
|
47928
|
-
"OPTIONS",
|
|
47929
|
-
" -n, --name <name> Resource name",
|
|
47930
|
-
" -ns, --namespace <ns> Target namespace",
|
|
47931
|
-
" -o, --output <fmt> Output format (json, yaml, table)",
|
|
47932
|
-
" -f, --file <path> Configuration file",
|
|
47933
|
-
"",
|
|
47934
|
-
colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`),
|
|
47935
|
-
""
|
|
47936
|
-
];
|
|
47937
|
-
}
|
|
47938
|
-
function formatTopicHelp(topic) {
|
|
47939
|
-
const lowerTopic = topic.toLowerCase();
|
|
47940
|
-
const domainInfo = getDomainInfo(lowerTopic);
|
|
47941
|
-
if (domainInfo) {
|
|
47942
|
-
return formatDomainHelp(domainInfo);
|
|
48044
|
+
/**
|
|
48045
|
+
* Set the output format
|
|
48046
|
+
*/
|
|
48047
|
+
setOutputFormat(format) {
|
|
48048
|
+
this._outputFormat = format;
|
|
47943
48049
|
}
|
|
47944
|
-
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
case "navigation":
|
|
47950
|
-
case "nav":
|
|
47951
|
-
return formatNavigationHelp();
|
|
47952
|
-
case "env":
|
|
47953
|
-
case "environment":
|
|
47954
|
-
return ["", ...formatEnvironmentVariables(), ""];
|
|
47955
|
-
case "flags":
|
|
47956
|
-
return ["", ...formatGlobalFlags(), ""];
|
|
47957
|
-
default:
|
|
47958
|
-
return [
|
|
47959
|
-
"",
|
|
47960
|
-
`Unknown help topic: ${topic}`,
|
|
47961
|
-
"",
|
|
47962
|
-
"Available topics:",
|
|
47963
|
-
" domains List all available domains",
|
|
47964
|
-
" actions List available actions",
|
|
47965
|
-
" navigation Navigation commands",
|
|
47966
|
-
" env Environment variables",
|
|
47967
|
-
" flags Global flags",
|
|
47968
|
-
" <domain> Help for a specific domain (e.g., 'help dns')",
|
|
47969
|
-
""
|
|
47970
|
-
];
|
|
48050
|
+
/**
|
|
48051
|
+
* Get debug mode status
|
|
48052
|
+
*/
|
|
48053
|
+
isDebug() {
|
|
48054
|
+
return this._debug;
|
|
47971
48055
|
}
|
|
47972
|
-
|
|
47973
|
-
|
|
47974
|
-
|
|
47975
|
-
|
|
47976
|
-
|
|
47977
|
-
const category = domain.category ?? "Other";
|
|
47978
|
-
if (!categories.has(category)) {
|
|
47979
|
-
categories.set(category, []);
|
|
47980
|
-
}
|
|
47981
|
-
categories.get(category)?.push(domain);
|
|
48056
|
+
/**
|
|
48057
|
+
* Get the profile manager
|
|
48058
|
+
*/
|
|
48059
|
+
getProfileManager() {
|
|
48060
|
+
return this._profileManager;
|
|
47982
48061
|
}
|
|
47983
|
-
|
|
47984
|
-
|
|
47985
|
-
|
|
47986
|
-
|
|
47987
|
-
|
|
47988
|
-
(a, b) => a.name.localeCompare(b.name)
|
|
47989
|
-
)) {
|
|
47990
|
-
const aliases = domain.aliases.length > 0 ? colorDim(` (${domain.aliases.join(", ")})`) : "";
|
|
47991
|
-
output.push(
|
|
47992
|
-
` ${domain.name.padEnd(24)} ${domain.descriptionShort}`
|
|
47993
|
-
);
|
|
47994
|
-
if (aliases) {
|
|
47995
|
-
output.push(` ${"".padEnd(24)} Aliases:${aliases}`);
|
|
47996
|
-
}
|
|
47997
|
-
}
|
|
47998
|
-
output.push("");
|
|
48062
|
+
/**
|
|
48063
|
+
* Get the active profile
|
|
48064
|
+
*/
|
|
48065
|
+
getActiveProfile() {
|
|
48066
|
+
return this._activeProfile;
|
|
47999
48067
|
}
|
|
48000
|
-
|
|
48001
|
-
|
|
48002
|
-
|
|
48003
|
-
|
|
48004
|
-
|
|
48005
|
-
colorBoldWhite("Available Actions"),
|
|
48006
|
-
"",
|
|
48007
|
-
" list List all resources in the namespace",
|
|
48008
|
-
" get Get a specific resource by name",
|
|
48009
|
-
" create Create a new resource from a file",
|
|
48010
|
-
" delete Delete a resource by name",
|
|
48011
|
-
" replace Replace a resource configuration",
|
|
48012
|
-
" apply Apply configuration (create or update)",
|
|
48013
|
-
" status Get resource status",
|
|
48014
|
-
" patch Patch specific fields of a resource",
|
|
48015
|
-
" add-labels Add labels to a resource",
|
|
48016
|
-
" remove-labels Remove labels from a resource",
|
|
48017
|
-
"",
|
|
48018
|
-
"USAGE",
|
|
48019
|
-
` ${CLI_NAME} <domain> <action> [options]`,
|
|
48020
|
-
"",
|
|
48021
|
-
"EXAMPLES",
|
|
48022
|
-
` ${CLI_NAME} dns list`,
|
|
48023
|
-
` ${CLI_NAME} lb get my-loadbalancer`,
|
|
48024
|
-
` ${CLI_NAME} waf create my-policy -f policy.yaml`,
|
|
48025
|
-
""
|
|
48026
|
-
];
|
|
48027
|
-
}
|
|
48028
|
-
function formatNavigationHelp() {
|
|
48029
|
-
return [
|
|
48030
|
-
"",
|
|
48031
|
-
colorBoldWhite("Navigation Commands"),
|
|
48032
|
-
"",
|
|
48033
|
-
" <domain> Navigate into a domain context",
|
|
48034
|
-
" /<domain> Navigate directly to domain from anywhere",
|
|
48035
|
-
" .. Go up one level in context",
|
|
48036
|
-
" / Return to root context",
|
|
48037
|
-
" back Go up one level (same as ..)",
|
|
48038
|
-
" root Return to root (same as /)",
|
|
48039
|
-
"",
|
|
48040
|
-
"CONTEXT DISPLAY",
|
|
48041
|
-
" context Show current navigation context",
|
|
48042
|
-
" ctx Alias for context",
|
|
48043
|
-
"",
|
|
48044
|
-
"EXAMPLES",
|
|
48045
|
-
" xcsh> dns # Enter dns domain",
|
|
48046
|
-
" dns> list # Execute list in dns context",
|
|
48047
|
-
" dns> .. # Return to root",
|
|
48048
|
-
" xcsh> /waf # Jump directly to waf",
|
|
48049
|
-
" waf> /dns # Jump from waf to dns",
|
|
48050
|
-
""
|
|
48051
|
-
];
|
|
48052
|
-
}
|
|
48053
|
-
function formatDomainsSection() {
|
|
48054
|
-
const output = ["DOMAINS"];
|
|
48055
|
-
const domains = Array.from(domainRegistry.values()).sort(
|
|
48056
|
-
(a, b) => a.name.localeCompare(b.name)
|
|
48057
|
-
);
|
|
48058
|
-
const maxNameLen = Math.max(...domains.map((d) => d.name.length));
|
|
48059
|
-
for (const domain of domains) {
|
|
48060
|
-
const padding = " ".repeat(maxNameLen - domain.name.length + 2);
|
|
48061
|
-
output.push(` ${domain.name}${padding}${domain.descriptionShort}`);
|
|
48068
|
+
/**
|
|
48069
|
+
* Get the active profile name
|
|
48070
|
+
*/
|
|
48071
|
+
getActiveProfileName() {
|
|
48072
|
+
return this._activeProfileName;
|
|
48062
48073
|
}
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48069
|
-
|
|
48070
|
-
|
|
48071
|
-
|
|
48072
|
-
|
|
48073
|
-
|
|
48074
|
-
|
|
48075
|
-
|
|
48076
|
-
|
|
48074
|
+
/**
|
|
48075
|
+
* Switch to a different profile
|
|
48076
|
+
*/
|
|
48077
|
+
async switchProfile(profileName) {
|
|
48078
|
+
const profile = await this._profileManager.get(profileName);
|
|
48079
|
+
if (!profile) {
|
|
48080
|
+
return false;
|
|
48081
|
+
}
|
|
48082
|
+
const result = await this._profileManager.setActive(profileName);
|
|
48083
|
+
if (!result.success) {
|
|
48084
|
+
return false;
|
|
48085
|
+
}
|
|
48086
|
+
this.clearNamespaceCache();
|
|
48087
|
+
this._tokenValidated = false;
|
|
48088
|
+
this._validationError = null;
|
|
48089
|
+
this._activeProfileName = profileName;
|
|
48090
|
+
this._activeProfile = profile;
|
|
48091
|
+
if (profile.apiUrl) {
|
|
48092
|
+
this._serverUrl = profile.apiUrl;
|
|
48093
|
+
this._tenant = this.extractTenant(profile.apiUrl);
|
|
48077
48094
|
}
|
|
48078
|
-
|
|
48095
|
+
if (profile.apiToken) {
|
|
48096
|
+
this._apiToken = profile.apiToken;
|
|
48097
|
+
}
|
|
48098
|
+
if (profile.defaultNamespace) {
|
|
48099
|
+
this._namespace = profile.defaultNamespace;
|
|
48100
|
+
}
|
|
48101
|
+
if (this._serverUrl) {
|
|
48102
|
+
this._apiClient = new APIClient({
|
|
48103
|
+
serverUrl: this._serverUrl,
|
|
48104
|
+
apiToken: this._apiToken,
|
|
48105
|
+
debug: this._debug
|
|
48106
|
+
});
|
|
48107
|
+
if (this._apiClient.isAuthenticated()) {
|
|
48108
|
+
const validationResult = await this._apiClient.validateToken();
|
|
48109
|
+
this._tokenValidated = validationResult.valid;
|
|
48110
|
+
this._validationError = validationResult.error ?? null;
|
|
48111
|
+
}
|
|
48112
|
+
} else {
|
|
48113
|
+
this._apiClient = null;
|
|
48114
|
+
}
|
|
48115
|
+
return true;
|
|
48079
48116
|
}
|
|
48080
|
-
|
|
48081
|
-
|
|
48082
|
-
|
|
48083
|
-
|
|
48117
|
+
/**
|
|
48118
|
+
* Clear the active profile
|
|
48119
|
+
*/
|
|
48120
|
+
clearActiveProfile() {
|
|
48121
|
+
this._activeProfileName = null;
|
|
48122
|
+
this._activeProfile = null;
|
|
48123
|
+
}
|
|
48124
|
+
/**
|
|
48125
|
+
* Get cached namespaces (returns empty array if cache is stale/empty)
|
|
48126
|
+
*/
|
|
48127
|
+
getNamespaceCache() {
|
|
48128
|
+
const now = Date.now();
|
|
48129
|
+
if (this._namespaceCache.length > 0 && now - this._namespaceCacheTime < NAMESPACE_CACHE_TTL) {
|
|
48130
|
+
return this._namespaceCache;
|
|
48084
48131
|
}
|
|
48085
|
-
|
|
48132
|
+
return [];
|
|
48086
48133
|
}
|
|
48087
|
-
|
|
48088
|
-
|
|
48089
|
-
|
|
48134
|
+
/**
|
|
48135
|
+
* Set namespace cache
|
|
48136
|
+
*/
|
|
48137
|
+
setNamespaceCache(namespaces) {
|
|
48138
|
+
this._namespaceCache = namespaces;
|
|
48139
|
+
this._namespaceCacheTime = Date.now();
|
|
48140
|
+
}
|
|
48141
|
+
/**
|
|
48142
|
+
* Clear namespace cache (called when switching profiles)
|
|
48143
|
+
*/
|
|
48144
|
+
clearNamespaceCache() {
|
|
48145
|
+
this._namespaceCache = [];
|
|
48146
|
+
this._namespaceCacheTime = 0;
|
|
48147
|
+
}
|
|
48148
|
+
/**
|
|
48149
|
+
* Add a command to history
|
|
48150
|
+
*/
|
|
48151
|
+
addToHistory(cmd) {
|
|
48152
|
+
this._history?.add(cmd);
|
|
48153
|
+
}
|
|
48154
|
+
/**
|
|
48155
|
+
* Save history to disk
|
|
48156
|
+
*/
|
|
48157
|
+
async saveHistory() {
|
|
48158
|
+
await this._history?.save();
|
|
48159
|
+
}
|
|
48160
|
+
/**
|
|
48161
|
+
* Clean up session resources
|
|
48162
|
+
*/
|
|
48163
|
+
async cleanup() {
|
|
48164
|
+
await this.saveHistory();
|
|
48165
|
+
}
|
|
48166
|
+
};
|
|
48167
|
+
|
|
48168
|
+
// src/repl/prompt.ts
|
|
48169
|
+
function buildPlainPrompt(_session) {
|
|
48170
|
+
return "> ";
|
|
48090
48171
|
}
|
|
48091
|
-
|
|
48092
|
-
|
|
48093
|
-
|
|
48094
|
-
|
|
48095
|
-
|
|
48096
|
-
|
|
48097
|
-
|
|
48098
|
-
|
|
48099
|
-
|
|
48100
|
-
|
|
48101
|
-
|
|
48102
|
-
|
|
48103
|
-
|
|
48104
|
-
|
|
48105
|
-
|
|
48106
|
-
|
|
48107
|
-
|
|
48108
|
-
|
|
48109
|
-
|
|
48110
|
-
|
|
48111
|
-
|
|
48172
|
+
|
|
48173
|
+
// src/repl/App.tsx
|
|
48174
|
+
var import_react28 = __toESM(require_react(), 1);
|
|
48175
|
+
|
|
48176
|
+
// src/repl/components/InputBox.tsx
|
|
48177
|
+
var import_react23 = __toESM(require_react(), 1);
|
|
48178
|
+
|
|
48179
|
+
// node_modules/ink-text-input/build/index.js
|
|
48180
|
+
var import_react22 = __toESM(require_react(), 1);
|
|
48181
|
+
function TextInput({ value: originalValue, placeholder = "", focus = true, mask, highlightPastedText = false, showCursor = true, onChange, onSubmit }) {
|
|
48182
|
+
const [state, setState] = (0, import_react22.useState)({
|
|
48183
|
+
cursorOffset: (originalValue || "").length,
|
|
48184
|
+
cursorWidth: 0
|
|
48185
|
+
});
|
|
48186
|
+
const { cursorOffset, cursorWidth } = state;
|
|
48187
|
+
(0, import_react22.useEffect)(() => {
|
|
48188
|
+
setState((previousState) => {
|
|
48189
|
+
if (!focus || !showCursor) {
|
|
48190
|
+
return previousState;
|
|
48191
|
+
}
|
|
48192
|
+
const newValue = originalValue || "";
|
|
48193
|
+
if (previousState.cursorOffset > newValue.length - 1) {
|
|
48194
|
+
return {
|
|
48195
|
+
cursorOffset: newValue.length,
|
|
48196
|
+
cursorWidth: 0
|
|
48197
|
+
};
|
|
48198
|
+
}
|
|
48199
|
+
return previousState;
|
|
48200
|
+
});
|
|
48201
|
+
}, [originalValue, focus, showCursor]);
|
|
48202
|
+
const cursorActualWidth = highlightPastedText ? cursorWidth : 0;
|
|
48203
|
+
const value = mask ? mask.repeat(originalValue.length) : originalValue;
|
|
48204
|
+
let renderedValue = value;
|
|
48205
|
+
let renderedPlaceholder = placeholder ? source_default.grey(placeholder) : void 0;
|
|
48206
|
+
if (showCursor && focus) {
|
|
48207
|
+
renderedPlaceholder = placeholder.length > 0 ? source_default.inverse(placeholder[0]) + source_default.grey(placeholder.slice(1)) : source_default.inverse(" ");
|
|
48208
|
+
renderedValue = value.length > 0 ? "" : source_default.inverse(" ");
|
|
48209
|
+
let i = 0;
|
|
48210
|
+
for (const char of value) {
|
|
48211
|
+
renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? source_default.inverse(char) : char;
|
|
48212
|
+
i++;
|
|
48213
|
+
}
|
|
48214
|
+
if (value.length > 0 && cursorOffset === value.length) {
|
|
48215
|
+
renderedValue += source_default.inverse(" ");
|
|
48112
48216
|
}
|
|
48113
|
-
output.push("");
|
|
48114
48217
|
}
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
|
|
48118
|
-
|
|
48119
|
-
|
|
48218
|
+
use_input_default((input, key) => {
|
|
48219
|
+
if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
48220
|
+
return;
|
|
48221
|
+
}
|
|
48222
|
+
if (key.return) {
|
|
48223
|
+
if (onSubmit) {
|
|
48224
|
+
onSubmit(originalValue);
|
|
48225
|
+
}
|
|
48226
|
+
return;
|
|
48227
|
+
}
|
|
48228
|
+
let nextCursorOffset = cursorOffset;
|
|
48229
|
+
let nextValue = originalValue;
|
|
48230
|
+
let nextCursorWidth = 0;
|
|
48231
|
+
if (key.leftArrow) {
|
|
48232
|
+
if (showCursor) {
|
|
48233
|
+
nextCursorOffset--;
|
|
48234
|
+
}
|
|
48235
|
+
} else if (key.rightArrow) {
|
|
48236
|
+
if (showCursor) {
|
|
48237
|
+
nextCursorOffset++;
|
|
48238
|
+
}
|
|
48239
|
+
} else if (key.backspace || key.delete) {
|
|
48240
|
+
if (cursorOffset > 0) {
|
|
48241
|
+
nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset, originalValue.length);
|
|
48242
|
+
nextCursorOffset--;
|
|
48243
|
+
}
|
|
48244
|
+
} else {
|
|
48245
|
+
nextValue = originalValue.slice(0, cursorOffset) + input + originalValue.slice(cursorOffset, originalValue.length);
|
|
48246
|
+
nextCursorOffset += input.length;
|
|
48247
|
+
if (input.length > 1) {
|
|
48248
|
+
nextCursorWidth = input.length;
|
|
48249
|
+
}
|
|
48250
|
+
}
|
|
48251
|
+
if (cursorOffset < 0) {
|
|
48252
|
+
nextCursorOffset = 0;
|
|
48253
|
+
}
|
|
48254
|
+
if (cursorOffset > originalValue.length) {
|
|
48255
|
+
nextCursorOffset = originalValue.length;
|
|
48256
|
+
}
|
|
48257
|
+
setState({
|
|
48258
|
+
cursorOffset: nextCursorOffset,
|
|
48259
|
+
cursorWidth: nextCursorWidth
|
|
48260
|
+
});
|
|
48261
|
+
if (nextValue !== originalValue) {
|
|
48262
|
+
onChange(nextValue);
|
|
48263
|
+
}
|
|
48264
|
+
}, { isActive: focus });
|
|
48265
|
+
return import_react22.default.createElement(Text, null, placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue);
|
|
48120
48266
|
}
|
|
48267
|
+
var build_default = TextInput;
|
|
48121
48268
|
|
|
48122
|
-
// src/
|
|
48123
|
-
var
|
|
48124
|
-
|
|
48125
|
-
|
|
48126
|
-
|
|
48127
|
-
|
|
48128
|
-
|
|
48129
|
-
|
|
48269
|
+
// src/repl/components/InputBox.tsx
|
|
48270
|
+
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
48271
|
+
function HorizontalRule({ width }) {
|
|
48272
|
+
const rule = "\u2500".repeat(Math.max(width, 1));
|
|
48273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "#CA260A", children: rule });
|
|
48274
|
+
}
|
|
48275
|
+
function InputBox({
|
|
48276
|
+
prompt,
|
|
48277
|
+
value,
|
|
48278
|
+
onChange,
|
|
48279
|
+
onSubmit,
|
|
48280
|
+
width = 80,
|
|
48281
|
+
isActive = true,
|
|
48282
|
+
inputKey = 0
|
|
48283
|
+
}) {
|
|
48284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", width, children: [
|
|
48285
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorizontalRule, { width }),
|
|
48286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
|
|
48287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "#ffffff", children: prompt }),
|
|
48288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
48289
|
+
build_default,
|
|
48290
|
+
{
|
|
48291
|
+
value,
|
|
48292
|
+
onChange,
|
|
48293
|
+
onSubmit,
|
|
48294
|
+
focus: isActive
|
|
48295
|
+
},
|
|
48296
|
+
inputKey
|
|
48297
|
+
)
|
|
48298
|
+
] }),
|
|
48299
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorizontalRule, { width })
|
|
48300
|
+
] });
|
|
48301
|
+
}
|
|
48302
|
+
|
|
48303
|
+
// src/repl/components/StatusBar.tsx
|
|
48304
|
+
import { execSync } from "child_process";
|
|
48305
|
+
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
48306
|
+
function getStatusIcon(gitInfo) {
|
|
48307
|
+
if (gitInfo.ahead > 0 && gitInfo.behind > 0) {
|
|
48308
|
+
return "\u21C5";
|
|
48309
|
+
}
|
|
48310
|
+
if (gitInfo.ahead > 0) {
|
|
48311
|
+
return "\u2191";
|
|
48130
48312
|
}
|
|
48131
|
-
|
|
48132
|
-
|
|
48133
|
-
*/
|
|
48134
|
-
has(name) {
|
|
48135
|
-
return this.domains.has(name);
|
|
48313
|
+
if (gitInfo.behind > 0) {
|
|
48314
|
+
return "\u2193";
|
|
48136
48315
|
}
|
|
48137
|
-
|
|
48138
|
-
|
|
48139
|
-
*/
|
|
48140
|
-
get(name) {
|
|
48141
|
-
return this.domains.get(name);
|
|
48316
|
+
if (gitInfo.isDirty) {
|
|
48317
|
+
return "*";
|
|
48142
48318
|
}
|
|
48143
|
-
|
|
48144
|
-
|
|
48145
|
-
|
|
48146
|
-
|
|
48147
|
-
return
|
|
48319
|
+
return "\u2713";
|
|
48320
|
+
}
|
|
48321
|
+
function getStatusColor(gitInfo) {
|
|
48322
|
+
if (gitInfo.ahead > 0 || gitInfo.behind > 0) {
|
|
48323
|
+
return "#2196f3";
|
|
48148
48324
|
}
|
|
48149
|
-
|
|
48150
|
-
|
|
48151
|
-
*/
|
|
48152
|
-
all() {
|
|
48153
|
-
return Array.from(this.domains.values());
|
|
48325
|
+
if (gitInfo.isDirty) {
|
|
48326
|
+
return "#ffc107";
|
|
48154
48327
|
}
|
|
48155
|
-
|
|
48156
|
-
|
|
48157
|
-
|
|
48158
|
-
|
|
48159
|
-
|
|
48160
|
-
|
|
48161
|
-
|
|
48162
|
-
|
|
48163
|
-
|
|
48164
|
-
|
|
48165
|
-
|
|
48166
|
-
return {
|
|
48167
|
-
|
|
48168
|
-
|
|
48169
|
-
|
|
48170
|
-
|
|
48171
|
-
|
|
48172
|
-
};
|
|
48328
|
+
return "#00c853";
|
|
48329
|
+
}
|
|
48330
|
+
function StatusBar({
|
|
48331
|
+
gitInfo,
|
|
48332
|
+
width = 80,
|
|
48333
|
+
hint = "Ctrl+C: quit"
|
|
48334
|
+
}) {
|
|
48335
|
+
const renderLeft = () => {
|
|
48336
|
+
if (gitInfo?.inRepo) {
|
|
48337
|
+
const icon = getStatusIcon(gitInfo);
|
|
48338
|
+
const color = getStatusColor(gitInfo);
|
|
48339
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
48340
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#ffffff", children: gitInfo.repoName }),
|
|
48341
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: "/" }),
|
|
48342
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color, children: gitInfo.branch }),
|
|
48343
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
48344
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color, children: icon })
|
|
48345
|
+
] });
|
|
48173
48346
|
}
|
|
48174
|
-
|
|
48175
|
-
|
|
48176
|
-
|
|
48177
|
-
|
|
48178
|
-
|
|
48347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#ffffff", children: CLI_NAME });
|
|
48348
|
+
};
|
|
48349
|
+
const renderRight = () => {
|
|
48350
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: hint });
|
|
48351
|
+
};
|
|
48352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { width, paddingX: 1, justifyContent: "space-between", children: [
|
|
48353
|
+
renderLeft(),
|
|
48354
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Spacer, {}),
|
|
48355
|
+
renderRight()
|
|
48356
|
+
] });
|
|
48357
|
+
}
|
|
48358
|
+
function getGitInfo() {
|
|
48359
|
+
const defaultInfo = {
|
|
48360
|
+
inRepo: false,
|
|
48361
|
+
repoName: "",
|
|
48362
|
+
branch: "",
|
|
48363
|
+
isDirty: false,
|
|
48364
|
+
ahead: 0,
|
|
48365
|
+
behind: 0
|
|
48366
|
+
};
|
|
48367
|
+
try {
|
|
48368
|
+
try {
|
|
48369
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
48370
|
+
encoding: "utf-8",
|
|
48371
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
48372
|
+
});
|
|
48373
|
+
} catch {
|
|
48374
|
+
return defaultInfo;
|
|
48179
48375
|
}
|
|
48180
|
-
|
|
48181
|
-
|
|
48182
|
-
|
|
48183
|
-
|
|
48376
|
+
let repoName = "";
|
|
48377
|
+
try {
|
|
48378
|
+
const topLevel = execSync("git rev-parse --show-toplevel", {
|
|
48379
|
+
encoding: "utf-8",
|
|
48380
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
48381
|
+
}).trim();
|
|
48382
|
+
repoName = topLevel.split("/").pop() ?? "";
|
|
48383
|
+
} catch {
|
|
48384
|
+
repoName = "repo";
|
|
48184
48385
|
}
|
|
48185
|
-
|
|
48186
|
-
|
|
48187
|
-
|
|
48188
|
-
|
|
48189
|
-
|
|
48190
|
-
|
|
48191
|
-
|
|
48192
|
-
|
|
48193
|
-
const cmdName = restArgs[0]?.toLowerCase() ?? "";
|
|
48194
|
-
if (cmdName === "--help" || cmdName === "-h" || cmdName === "help") {
|
|
48195
|
-
return this.showSubcommandHelp(domain, subgroup);
|
|
48196
|
-
}
|
|
48197
|
-
const cmdArgs = restArgs.slice(1);
|
|
48198
|
-
const cmd2 = subgroup.commands.get(cmdName);
|
|
48199
|
-
if (cmd2) {
|
|
48200
|
-
return cmd2.execute(cmdArgs, session);
|
|
48201
|
-
}
|
|
48202
|
-
for (const [, command] of subgroup.commands) {
|
|
48203
|
-
if (command.aliases?.includes(cmdName)) {
|
|
48204
|
-
return command.execute(cmdArgs, session);
|
|
48205
|
-
}
|
|
48206
|
-
}
|
|
48207
|
-
return {
|
|
48208
|
-
output: [
|
|
48209
|
-
`Unknown command: ${domainName} ${firstArg} ${cmdName}`,
|
|
48210
|
-
``,
|
|
48211
|
-
`Run '${domainName} ${firstArg}' for available commands.`
|
|
48212
|
-
],
|
|
48213
|
-
shouldExit: false,
|
|
48214
|
-
shouldClear: false,
|
|
48215
|
-
contextChanged: false,
|
|
48216
|
-
error: "Unknown command"
|
|
48217
|
-
};
|
|
48386
|
+
let branch = "";
|
|
48387
|
+
try {
|
|
48388
|
+
branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
48389
|
+
encoding: "utf-8",
|
|
48390
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
48391
|
+
}).trim();
|
|
48392
|
+
} catch {
|
|
48393
|
+
branch = "unknown";
|
|
48218
48394
|
}
|
|
48219
|
-
|
|
48220
|
-
|
|
48221
|
-
|
|
48395
|
+
let isDirty = false;
|
|
48396
|
+
try {
|
|
48397
|
+
const status = execSync("git status --porcelain", {
|
|
48398
|
+
encoding: "utf-8",
|
|
48399
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
48400
|
+
});
|
|
48401
|
+
isDirty = status.trim().length > 0;
|
|
48402
|
+
} catch {
|
|
48222
48403
|
}
|
|
48223
|
-
|
|
48224
|
-
|
|
48225
|
-
|
|
48226
|
-
|
|
48404
|
+
let ahead = 0;
|
|
48405
|
+
let behind = 0;
|
|
48406
|
+
try {
|
|
48407
|
+
const counts = execSync(
|
|
48408
|
+
"git rev-list --left-right --count HEAD...@{upstream}",
|
|
48409
|
+
{
|
|
48410
|
+
encoding: "utf-8",
|
|
48411
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
48412
|
+
}
|
|
48413
|
+
).trim();
|
|
48414
|
+
const [aheadStr, behindStr] = counts.split(/\s+/);
|
|
48415
|
+
ahead = parseInt(aheadStr ?? "0", 10) || 0;
|
|
48416
|
+
behind = parseInt(behindStr ?? "0", 10) || 0;
|
|
48417
|
+
} catch {
|
|
48227
48418
|
}
|
|
48228
48419
|
return {
|
|
48229
|
-
|
|
48230
|
-
|
|
48231
|
-
|
|
48232
|
-
|
|
48233
|
-
|
|
48234
|
-
|
|
48235
|
-
shouldClear: false,
|
|
48236
|
-
contextChanged: false,
|
|
48237
|
-
error: "Unknown command"
|
|
48420
|
+
inRepo: true,
|
|
48421
|
+
repoName,
|
|
48422
|
+
branch,
|
|
48423
|
+
isDirty,
|
|
48424
|
+
ahead,
|
|
48425
|
+
behind
|
|
48238
48426
|
};
|
|
48427
|
+
} catch {
|
|
48428
|
+
return defaultInfo;
|
|
48239
48429
|
}
|
|
48240
|
-
|
|
48241
|
-
|
|
48242
|
-
|
|
48243
|
-
|
|
48244
|
-
|
|
48245
|
-
|
|
48246
|
-
|
|
48247
|
-
|
|
48248
|
-
|
|
48249
|
-
|
|
48250
|
-
|
|
48251
|
-
|
|
48252
|
-
|
|
48253
|
-
|
|
48254
|
-
|
|
48255
|
-
|
|
48256
|
-
|
|
48257
|
-
|
|
48430
|
+
}
|
|
48431
|
+
|
|
48432
|
+
// src/repl/components/Suggestions.tsx
|
|
48433
|
+
var import_react24 = __toESM(require_react(), 1);
|
|
48434
|
+
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
48435
|
+
function getCategoryColor(category) {
|
|
48436
|
+
switch (category) {
|
|
48437
|
+
case "domain":
|
|
48438
|
+
return "#2196f3";
|
|
48439
|
+
// Blue
|
|
48440
|
+
case "action":
|
|
48441
|
+
return "#4caf50";
|
|
48442
|
+
// Green
|
|
48443
|
+
case "flag":
|
|
48444
|
+
return "#ffc107";
|
|
48445
|
+
// Yellow
|
|
48446
|
+
case "value":
|
|
48447
|
+
return "#9c27b0";
|
|
48448
|
+
// Purple
|
|
48449
|
+
default:
|
|
48450
|
+
return "#ffffff";
|
|
48451
|
+
}
|
|
48452
|
+
}
|
|
48453
|
+
function SuggestionItem({
|
|
48454
|
+
suggestion,
|
|
48455
|
+
isSelected,
|
|
48456
|
+
maxLabelWidth
|
|
48457
|
+
}) {
|
|
48458
|
+
const categoryColor = getCategoryColor(suggestion.category);
|
|
48459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
48460
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: isSelected ? "#CA260A" : "#333333", children: isSelected ? "\u25B6 " : " " }),
|
|
48461
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: categoryColor, bold: isSelected, inverse: isSelected, children: suggestion.label.padEnd(maxLabelWidth) }),
|
|
48462
|
+
suggestion.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", children: [
|
|
48463
|
+
" - ",
|
|
48464
|
+
suggestion.description
|
|
48465
|
+
] })
|
|
48466
|
+
] });
|
|
48467
|
+
}
|
|
48468
|
+
function Suggestions({
|
|
48469
|
+
suggestions,
|
|
48470
|
+
selectedIndex,
|
|
48471
|
+
onSelect,
|
|
48472
|
+
onNavigate,
|
|
48473
|
+
onCancel,
|
|
48474
|
+
maxVisible = 20,
|
|
48475
|
+
isActive = true
|
|
48476
|
+
}) {
|
|
48477
|
+
use_input_default(
|
|
48478
|
+
(_input, key) => {
|
|
48479
|
+
if (!isActive) return;
|
|
48480
|
+
if (key.upArrow) {
|
|
48481
|
+
onNavigate("up");
|
|
48482
|
+
return;
|
|
48258
48483
|
}
|
|
48259
|
-
|
|
48260
|
-
|
|
48261
|
-
|
|
48262
|
-
text: name,
|
|
48263
|
-
description: cmd.descriptionShort,
|
|
48264
|
-
category: "command"
|
|
48265
|
-
});
|
|
48266
|
-
}
|
|
48484
|
+
if (key.downArrow) {
|
|
48485
|
+
onNavigate("down");
|
|
48486
|
+
return;
|
|
48267
48487
|
}
|
|
48268
|
-
return
|
|
48269
|
-
|
|
48270
|
-
|
|
48271
|
-
|
|
48272
|
-
if (subgroup && args.length === 1) {
|
|
48273
|
-
for (const [name, cmd] of subgroup.commands) {
|
|
48274
|
-
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
48275
|
-
suggestions.push({
|
|
48276
|
-
text: name,
|
|
48277
|
-
description: cmd.descriptionShort,
|
|
48278
|
-
category: "command"
|
|
48279
|
-
});
|
|
48488
|
+
if (key.return || key.tab) {
|
|
48489
|
+
const selected = suggestions.at(selectedIndex);
|
|
48490
|
+
if (selected) {
|
|
48491
|
+
onSelect(selected);
|
|
48280
48492
|
}
|
|
48493
|
+
return;
|
|
48281
48494
|
}
|
|
48282
|
-
|
|
48283
|
-
|
|
48284
|
-
|
|
48285
|
-
const cmdName = args[1]?.toLowerCase() ?? "";
|
|
48286
|
-
const cmd = subgroup.commands.get(cmdName);
|
|
48287
|
-
if (cmd?.completion) {
|
|
48288
|
-
const completions = await cmd.completion(
|
|
48289
|
-
partial,
|
|
48290
|
-
args.slice(2),
|
|
48291
|
-
session
|
|
48292
|
-
);
|
|
48293
|
-
return completions.map((text) => ({
|
|
48294
|
-
text,
|
|
48295
|
-
description: "",
|
|
48296
|
-
category: "argument"
|
|
48297
|
-
}));
|
|
48495
|
+
if (key.escape) {
|
|
48496
|
+
onCancel();
|
|
48497
|
+
return;
|
|
48298
48498
|
}
|
|
48299
|
-
}
|
|
48300
|
-
|
|
48301
|
-
|
|
48302
|
-
|
|
48303
|
-
|
|
48304
|
-
* This ensures consistent professional formatting across all domains.
|
|
48305
|
-
*/
|
|
48306
|
-
showDomainHelp(domain) {
|
|
48307
|
-
return {
|
|
48308
|
-
output: formatCustomDomainHelp(domain),
|
|
48309
|
-
shouldExit: false,
|
|
48310
|
-
shouldClear: false,
|
|
48311
|
-
contextChanged: false
|
|
48312
|
-
};
|
|
48313
|
-
}
|
|
48314
|
-
/**
|
|
48315
|
-
* Show help for a subcommand group using the unified help formatter.
|
|
48316
|
-
* This ensures consistent professional formatting across all subcommands.
|
|
48317
|
-
*/
|
|
48318
|
-
showSubcommandHelp(domain, subgroup) {
|
|
48319
|
-
return {
|
|
48320
|
-
output: formatSubcommandHelp(domain.name, subgroup),
|
|
48321
|
-
shouldExit: false,
|
|
48322
|
-
shouldClear: false,
|
|
48323
|
-
contextChanged: false
|
|
48324
|
-
};
|
|
48499
|
+
},
|
|
48500
|
+
{ isActive }
|
|
48501
|
+
);
|
|
48502
|
+
if (suggestions.length === 0) {
|
|
48503
|
+
return null;
|
|
48325
48504
|
}
|
|
48326
|
-
|
|
48327
|
-
|
|
48328
|
-
|
|
48329
|
-
|
|
48330
|
-
|
|
48331
|
-
|
|
48332
|
-
|
|
48333
|
-
|
|
48334
|
-
|
|
48505
|
+
const totalCount = suggestions.length;
|
|
48506
|
+
const startIndex = Math.max(
|
|
48507
|
+
0,
|
|
48508
|
+
Math.min(
|
|
48509
|
+
selectedIndex - Math.floor(maxVisible / 2),
|
|
48510
|
+
totalCount - maxVisible
|
|
48511
|
+
)
|
|
48512
|
+
);
|
|
48513
|
+
const visibleSuggestions = suggestions.slice(
|
|
48514
|
+
startIndex,
|
|
48515
|
+
startIndex + maxVisible
|
|
48516
|
+
);
|
|
48517
|
+
const showScrollUp = startIndex > 0;
|
|
48518
|
+
const showScrollDown = startIndex + maxVisible < totalCount;
|
|
48519
|
+
const maxLabelWidth = Math.max(
|
|
48520
|
+
...visibleSuggestions.map((s) => s.label.length)
|
|
48521
|
+
);
|
|
48522
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
48523
|
+
Box_default,
|
|
48524
|
+
{
|
|
48525
|
+
flexDirection: "column",
|
|
48526
|
+
borderStyle: "round",
|
|
48527
|
+
borderColor: "#CA260A",
|
|
48528
|
+
paddingX: 1,
|
|
48529
|
+
children: [
|
|
48530
|
+
showScrollUp && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", dimColor: true, children: [
|
|
48531
|
+
"\u25B2",
|
|
48532
|
+
" (",
|
|
48533
|
+
startIndex,
|
|
48534
|
+
" more above)"
|
|
48535
|
+
] }),
|
|
48536
|
+
visibleSuggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
48537
|
+
SuggestionItem,
|
|
48538
|
+
{
|
|
48539
|
+
suggestion,
|
|
48540
|
+
isSelected: startIndex + index === selectedIndex,
|
|
48541
|
+
index: startIndex + index,
|
|
48542
|
+
maxLabelWidth
|
|
48543
|
+
},
|
|
48544
|
+
suggestion.value
|
|
48545
|
+
)),
|
|
48546
|
+
showScrollDown && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "#666666", dimColor: true, children: [
|
|
48547
|
+
"\u25BC",
|
|
48548
|
+
" (",
|
|
48549
|
+
totalCount - startIndex - maxVisible,
|
|
48550
|
+
" more below)"
|
|
48551
|
+
] }),
|
|
48552
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "#666666", dimColor: true, children: "Tab: select | Up/Down: navigate | Esc: cancel" }) })
|
|
48553
|
+
]
|
|
48554
|
+
}
|
|
48555
|
+
);
|
|
48335
48556
|
}
|
|
48336
|
-
|
|
48557
|
+
|
|
48558
|
+
// src/repl/hooks/useDoubleCtrlC.ts
|
|
48559
|
+
var import_react25 = __toESM(require_react(), 1);
|
|
48560
|
+
function useDoubleCtrlC(options = {}) {
|
|
48561
|
+
const { windowMs = 500, onFirstPress, onDoublePress } = options;
|
|
48562
|
+
const lastPressRef = (0, import_react25.useRef)(0);
|
|
48563
|
+
const [isWaiting, setIsWaiting] = (0, import_react25.useState)(false);
|
|
48564
|
+
const timeoutRef = (0, import_react25.useRef)(null);
|
|
48565
|
+
const reset = (0, import_react25.useCallback)(() => {
|
|
48566
|
+
lastPressRef.current = 0;
|
|
48567
|
+
setIsWaiting(false);
|
|
48568
|
+
if (timeoutRef.current) {
|
|
48569
|
+
clearTimeout(timeoutRef.current);
|
|
48570
|
+
timeoutRef.current = null;
|
|
48571
|
+
}
|
|
48572
|
+
}, []);
|
|
48573
|
+
const handleCtrlC = (0, import_react25.useCallback)(() => {
|
|
48574
|
+
const now = Date.now();
|
|
48575
|
+
const elapsed = now - lastPressRef.current;
|
|
48576
|
+
if (elapsed < windowMs && lastPressRef.current !== 0) {
|
|
48577
|
+
reset();
|
|
48578
|
+
onDoublePress?.();
|
|
48579
|
+
return true;
|
|
48580
|
+
}
|
|
48581
|
+
lastPressRef.current = now;
|
|
48582
|
+
setIsWaiting(true);
|
|
48583
|
+
onFirstPress?.();
|
|
48584
|
+
if (timeoutRef.current) {
|
|
48585
|
+
clearTimeout(timeoutRef.current);
|
|
48586
|
+
}
|
|
48587
|
+
timeoutRef.current = setTimeout(() => {
|
|
48588
|
+
setIsWaiting(false);
|
|
48589
|
+
}, windowMs);
|
|
48590
|
+
return false;
|
|
48591
|
+
}, [windowMs, onFirstPress, onDoublePress, reset]);
|
|
48337
48592
|
return {
|
|
48338
|
-
|
|
48339
|
-
|
|
48340
|
-
|
|
48341
|
-
contextChanged: false,
|
|
48342
|
-
error: message
|
|
48593
|
+
handleCtrlC,
|
|
48594
|
+
reset,
|
|
48595
|
+
isWaiting
|
|
48343
48596
|
};
|
|
48344
48597
|
}
|
|
48345
|
-
|
|
48598
|
+
|
|
48599
|
+
// src/repl/hooks/useHistory.ts
|
|
48600
|
+
var import_react26 = __toESM(require_react(), 1);
|
|
48601
|
+
function useHistory(options) {
|
|
48602
|
+
const { history, onSelect } = options;
|
|
48603
|
+
const [historyIndex, setHistoryIndex] = (0, import_react26.useState)(-1);
|
|
48604
|
+
const reset = (0, import_react26.useCallback)(() => {
|
|
48605
|
+
setHistoryIndex(-1);
|
|
48606
|
+
}, []);
|
|
48607
|
+
const navigateUp = (0, import_react26.useCallback)(() => {
|
|
48608
|
+
if (history.length === 0) {
|
|
48609
|
+
return null;
|
|
48610
|
+
}
|
|
48611
|
+
const newIndex = Math.min(historyIndex + 1, history.length - 1);
|
|
48612
|
+
setHistoryIndex(newIndex);
|
|
48613
|
+
const command = history[history.length - 1 - newIndex];
|
|
48614
|
+
if (command !== void 0) {
|
|
48615
|
+
onSelect?.(command);
|
|
48616
|
+
return command;
|
|
48617
|
+
}
|
|
48618
|
+
return null;
|
|
48619
|
+
}, [history, historyIndex, onSelect]);
|
|
48620
|
+
const navigateDown = (0, import_react26.useCallback)(() => {
|
|
48621
|
+
if (historyIndex <= 0) {
|
|
48622
|
+
if (historyIndex === 0) {
|
|
48623
|
+
setHistoryIndex(-1);
|
|
48624
|
+
onSelect?.("");
|
|
48625
|
+
return "";
|
|
48626
|
+
}
|
|
48627
|
+
return null;
|
|
48628
|
+
}
|
|
48629
|
+
const newIndex = historyIndex - 1;
|
|
48630
|
+
setHistoryIndex(newIndex);
|
|
48631
|
+
const command = history[history.length - 1 - newIndex];
|
|
48632
|
+
if (command !== void 0) {
|
|
48633
|
+
onSelect?.(command);
|
|
48634
|
+
return command;
|
|
48635
|
+
}
|
|
48636
|
+
return null;
|
|
48637
|
+
}, [history, historyIndex, onSelect]);
|
|
48346
48638
|
return {
|
|
48347
|
-
|
|
48348
|
-
|
|
48349
|
-
|
|
48350
|
-
|
|
48351
|
-
|
|
48352
|
-
rawStdout: content
|
|
48639
|
+
navigateUp,
|
|
48640
|
+
navigateDown,
|
|
48641
|
+
reset,
|
|
48642
|
+
isNavigating: historyIndex >= 0,
|
|
48643
|
+
currentIndex: historyIndex
|
|
48353
48644
|
};
|
|
48354
48645
|
}
|
|
48355
48646
|
|
|
48647
|
+
// src/repl/hooks/useCompletion.ts
|
|
48648
|
+
var import_react27 = __toESM(require_react(), 1);
|
|
48649
|
+
|
|
48356
48650
|
// src/domains/login/profile/list.ts
|
|
48357
48651
|
var listCommand = {
|
|
48358
48652
|
name: "list",
|
|
@@ -53175,6 +53469,10 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
|
|
|
53175
53469
|
formatRootHelp().forEach((line) => console.log(line));
|
|
53176
53470
|
process.exit(0);
|
|
53177
53471
|
}
|
|
53472
|
+
if (options.spec && commandArgs.length === 0) {
|
|
53473
|
+
console.log(formatFullCLISpec());
|
|
53474
|
+
process.exit(0);
|
|
53475
|
+
}
|
|
53178
53476
|
if (options.help && commandArgs.length > 0) {
|
|
53179
53477
|
commandArgs.push("--help");
|
|
53180
53478
|
}
|
|
@@ -53201,6 +53499,8 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
|
|
|
53201
53499
|
process.stdout.write("Initializing...");
|
|
53202
53500
|
const session = new REPLSession();
|
|
53203
53501
|
await session.initialize();
|
|
53502
|
+
debugProtocol.session("init", { mode: "repl" });
|
|
53503
|
+
emitSessionState(session);
|
|
53204
53504
|
process.stdout.write("\r\x1B[K");
|
|
53205
53505
|
renderBanner(cliLogoMode, "startup");
|
|
53206
53506
|
if (session.isAuthenticated() && !session.isTokenValidated() && session.getValidationError()) {
|
|
@@ -53246,6 +53546,11 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
|
|
|
53246
53546
|
async function executeNonInteractive(args) {
|
|
53247
53547
|
const session = new REPLSession();
|
|
53248
53548
|
await session.initialize();
|
|
53549
|
+
debugProtocol.session("init", {
|
|
53550
|
+
mode: "non-interactive",
|
|
53551
|
+
command: args.join(" ")
|
|
53552
|
+
});
|
|
53553
|
+
emitSessionState(session);
|
|
53249
53554
|
if (session.isAuthenticated() && !session.isTokenValidated() && session.getValidationError()) {
|
|
53250
53555
|
console.error(
|
|
53251
53556
|
`${colors.yellow}Warning: ${session.getValidationError()}${colors.reset}`
|