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