@robinmordasiewicz/f5xc-xcsh 1.0.91-2601040044 → 1.0.91-2601040346

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.
Files changed (2) hide show
  1. package/dist/index.js +584 -117
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -47052,8 +47052,8 @@ function getLogoModeFromEnv(envPrefix) {
47052
47052
  var CLI_NAME = "xcsh";
47053
47053
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
47054
47054
  function getVersion() {
47055
- if ("v1.0.91-2601040044") {
47056
- return "v1.0.91-2601040044";
47055
+ if ("v1.0.91-2601040346") {
47056
+ return "v1.0.91-2601040346";
47057
47057
  }
47058
47058
  if (process.env.XCSH_VERSION) {
47059
47059
  return process.env.XCSH_VERSION;
@@ -145585,6 +145585,387 @@ function buildConnectionInfo(profileName, apiUrl, hasToken, namespace, isConnect
145585
145585
  return info;
145586
145586
  }
145587
145587
 
145588
+ // src/validation/namespace.ts
145589
+ function validateNamespaceScope(domain, action, currentNamespace, resourceType) {
145590
+ const opInfo = getOperationDescription(domain, action, resourceType);
145591
+ if (!opInfo?.namespaceScope) {
145592
+ return { valid: true };
145593
+ }
145594
+ const scope = opInfo.namespaceScope;
145595
+ const normalizedNamespace = currentNamespace.toLowerCase();
145596
+ switch (scope) {
145597
+ case "system":
145598
+ if (normalizedNamespace !== "system") {
145599
+ return {
145600
+ valid: false,
145601
+ scope,
145602
+ message: `${action} on ${resourceType || domain} requires the 'system' namespace`,
145603
+ suggestion: "system"
145604
+ };
145605
+ }
145606
+ return { valid: true, scope };
145607
+ case "shared":
145608
+ if (normalizedNamespace !== "shared") {
145609
+ return {
145610
+ valid: false,
145611
+ scope,
145612
+ message: `${action} on ${resourceType || domain} requires the 'shared' namespace`,
145613
+ suggestion: "shared"
145614
+ };
145615
+ }
145616
+ return { valid: true, scope };
145617
+ case "any":
145618
+ default:
145619
+ return { valid: true, scope };
145620
+ }
145621
+ }
145622
+
145623
+ // src/validation/safety.ts
145624
+ function checkOperationSafety(domain, action, resourceType) {
145625
+ const opInfo = getOperationDescription(domain, action, resourceType);
145626
+ const dangerLevel = opInfo?.dangerLevel || "low";
145627
+ const requiresConfirmation2 = opInfo?.confirmationRequired ?? dangerLevel === "high";
145628
+ const sideEffects = opInfo?.sideEffects;
145629
+ const result = {
145630
+ proceed: dangerLevel !== "high",
145631
+ // High danger requires explicit confirmation
145632
+ dangerLevel,
145633
+ requiresConfirmation: requiresConfirmation2
145634
+ };
145635
+ if (dangerLevel === "high") {
145636
+ result.warning = formatHighDangerWarning(domain, action, sideEffects);
145637
+ } else if (dangerLevel === "medium") {
145638
+ result.warning = formatMediumDangerWarning(domain, action, sideEffects);
145639
+ }
145640
+ if (sideEffects) {
145641
+ result.sideEffects = sideEffects;
145642
+ }
145643
+ return result;
145644
+ }
145645
+ function formatHighDangerWarning(_domain, _action, sideEffects) {
145646
+ const lines = [
145647
+ colorRed("\u26A0\uFE0F WARNING: This is a HIGH DANGER operation"),
145648
+ ""
145649
+ ];
145650
+ if (sideEffects) {
145651
+ if (sideEffects.deletes && sideEffects.deletes.length > 0) {
145652
+ lines.push(
145653
+ colorRed(` Will DELETE: ${sideEffects.deletes.join(", ")}`)
145654
+ );
145655
+ }
145656
+ if (sideEffects.updates && sideEffects.updates.length > 0) {
145657
+ lines.push(` Will UPDATE: ${sideEffects.updates.join(", ")}`);
145658
+ }
145659
+ if (sideEffects.creates && sideEffects.creates.length > 0) {
145660
+ lines.push(
145661
+ colorDim(` Will CREATE: ${sideEffects.creates.join(", ")}`)
145662
+ );
145663
+ }
145664
+ lines.push("");
145665
+ }
145666
+ lines.push(
145667
+ colorRed("This action may be destructive and cannot be undone.")
145668
+ );
145669
+ return lines.join("\n");
145670
+ }
145671
+ function formatMediumDangerWarning(_domain, _action, sideEffects) {
145672
+ const lines = [
145673
+ colorYellow("\u26A0\uFE0F CAUTION: This operation may have significant effects"),
145674
+ ""
145675
+ ];
145676
+ if (sideEffects) {
145677
+ const effects = [];
145678
+ if (sideEffects.creates && sideEffects.creates.length > 0) {
145679
+ effects.push(`creates: ${sideEffects.creates.join(", ")}`);
145680
+ }
145681
+ if (sideEffects.updates && sideEffects.updates.length > 0) {
145682
+ effects.push(`updates: ${sideEffects.updates.join(", ")}`);
145683
+ }
145684
+ if (sideEffects.deletes && sideEffects.deletes.length > 0) {
145685
+ effects.push(`deletes: ${sideEffects.deletes.join(", ")}`);
145686
+ }
145687
+ if (effects.length > 0) {
145688
+ lines.push(` Side effects: ${effects.join("; ")}`);
145689
+ lines.push("");
145690
+ }
145691
+ }
145692
+ return lines.join("\n");
145693
+ }
145694
+
145695
+ // src/validation/reserved-words.ts
145696
+ var RESERVED_ACTIONS = /* @__PURE__ */ new Set([
145697
+ // CRUD operations
145698
+ "create",
145699
+ "delete",
145700
+ "list",
145701
+ "get",
145702
+ "update",
145703
+ "apply",
145704
+ "patch",
145705
+ // Info operations
145706
+ "describe",
145707
+ "show",
145708
+ "status",
145709
+ "logs",
145710
+ "events",
145711
+ // Modification
145712
+ "edit",
145713
+ "replace",
145714
+ "set",
145715
+ "unset",
145716
+ // Lifecycle
145717
+ "start",
145718
+ "stop",
145719
+ "restart",
145720
+ "rollout",
145721
+ "scale",
145722
+ // Connection
145723
+ "attach",
145724
+ "detach",
145725
+ "connect",
145726
+ "disconnect",
145727
+ // Registration
145728
+ "register",
145729
+ "deregister",
145730
+ "associate",
145731
+ "disassociate",
145732
+ // AWS-specific
145733
+ "put",
145734
+ "modify",
145735
+ // Built-in xcsh commands
145736
+ "help",
145737
+ "quit",
145738
+ "exit",
145739
+ "clear",
145740
+ "history",
145741
+ "refresh",
145742
+ "context",
145743
+ "banner",
145744
+ "profile",
145745
+ "completion",
145746
+ // Common CLI patterns
145747
+ "run",
145748
+ "exec",
145749
+ "wait",
145750
+ "open",
145751
+ "close",
145752
+ "inspect",
145753
+ "validate",
145754
+ "diff",
145755
+ "plan",
145756
+ "import",
145757
+ "export"
145758
+ ]);
145759
+ var SHELL_METACHARACTERS = /[;&|`$()<>\\#!{}[\]*?~\n\r]/;
145760
+ var DANGEROUS_PATTERNS = [
145761
+ {
145762
+ pattern: /^-/,
145763
+ message: "Name cannot start with a hyphen (would be interpreted as a flag)"
145764
+ },
145765
+ {
145766
+ pattern: /^--/,
145767
+ message: "Name cannot start with double-hyphen (would be interpreted as a long flag)"
145768
+ },
145769
+ {
145770
+ pattern: /\.\./,
145771
+ message: "Name cannot contain '..' (path traversal risk)"
145772
+ },
145773
+ {
145774
+ pattern: /^\./,
145775
+ message: "Name cannot start with '.' (hidden file pattern)"
145776
+ },
145777
+ {
145778
+ pattern: /\//,
145779
+ message: "Name cannot contain '/' (path separator)"
145780
+ },
145781
+ {
145782
+ pattern: /\\$/,
145783
+ message: "Name cannot end with backslash (escape sequence risk)"
145784
+ },
145785
+ {
145786
+ pattern: /\s/,
145787
+ message: "Name cannot contain whitespace"
145788
+ },
145789
+ {
145790
+ pattern: /^_/,
145791
+ message: "Name cannot start with underscore (reserved for internal use)"
145792
+ }
145793
+ ];
145794
+ var DANGEROUS_COMMANDS = /* @__PURE__ */ new Set([
145795
+ // File destruction
145796
+ "rm",
145797
+ "rm-rf",
145798
+ "rmdir",
145799
+ "del",
145800
+ "unlink",
145801
+ "shred",
145802
+ // Privilege escalation
145803
+ "sudo",
145804
+ "su",
145805
+ "chmod",
145806
+ "chown",
145807
+ "chattr",
145808
+ "setfacl",
145809
+ // Network tools (payload download)
145810
+ "wget",
145811
+ "curl",
145812
+ "nc",
145813
+ "netcat",
145814
+ "socat",
145815
+ "telnet",
145816
+ "ssh",
145817
+ "scp",
145818
+ "rsync",
145819
+ // Shell execution
145820
+ "bash",
145821
+ "sh",
145822
+ "zsh",
145823
+ "csh",
145824
+ "ksh",
145825
+ "fish",
145826
+ "pwsh",
145827
+ "powershell",
145828
+ // Process execution
145829
+ "exec",
145830
+ "eval",
145831
+ "source",
145832
+ "nohup",
145833
+ "xargs",
145834
+ // Process termination
145835
+ "kill",
145836
+ "pkill",
145837
+ "killall",
145838
+ "killall5",
145839
+ // Disk operations
145840
+ "dd",
145841
+ "mkfs",
145842
+ "fdisk",
145843
+ "parted",
145844
+ "format",
145845
+ // System control
145846
+ "reboot",
145847
+ "shutdown",
145848
+ "halt",
145849
+ "poweroff",
145850
+ "init",
145851
+ "systemctl",
145852
+ // Container escape
145853
+ "docker",
145854
+ "kubectl",
145855
+ "nsenter",
145856
+ "chroot"
145857
+ ]);
145858
+ var CONTROL_CHARS = /[\x00-\x1f\x7f]/;
145859
+ var RFC1035_PATTERN = /^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/;
145860
+
145861
+ // src/validation/resource-name.ts
145862
+ var DEFAULT_OPTIONS = {
145863
+ maxLength: 63,
145864
+ allowUppercase: false,
145865
+ skipFormatValidation: false
145866
+ };
145867
+ function validateResourceName(name, options = {}) {
145868
+ const opts = { ...DEFAULT_OPTIONS, ...options };
145869
+ const nameLower = name.toLowerCase();
145870
+ if (!name || name.trim().length === 0) {
145871
+ return {
145872
+ valid: false,
145873
+ category: "invalid",
145874
+ message: "Resource name cannot be empty"
145875
+ };
145876
+ }
145877
+ if (name.length > opts.maxLength) {
145878
+ return {
145879
+ valid: false,
145880
+ category: "invalid",
145881
+ message: `Name exceeds maximum length of ${opts.maxLength} characters (got ${name.length})`,
145882
+ suggestion: sanitizeName(name.slice(0, opts.maxLength))
145883
+ };
145884
+ }
145885
+ if (CONTROL_CHARS.test(name)) {
145886
+ return {
145887
+ valid: false,
145888
+ category: "security",
145889
+ message: "Name contains control characters which are not allowed"
145890
+ };
145891
+ }
145892
+ if (SHELL_METACHARACTERS.test(name)) {
145893
+ const match = name.match(SHELL_METACHARACTERS);
145894
+ const char = match?.[0] ?? "unknown";
145895
+ const charDesc = getCharacterDescription(char);
145896
+ return {
145897
+ valid: false,
145898
+ category: "security",
145899
+ message: `Name contains shell metacharacter '${charDesc}' which could enable command injection`
145900
+ };
145901
+ }
145902
+ if (RESERVED_ACTIONS.has(nameLower)) {
145903
+ return {
145904
+ valid: false,
145905
+ category: "reserved",
145906
+ message: `'${name}' is a reserved CLI action word and cannot be used as a resource name`,
145907
+ suggestion: `my-${name}`
145908
+ };
145909
+ }
145910
+ if (DANGEROUS_COMMANDS.has(nameLower)) {
145911
+ return {
145912
+ valid: false,
145913
+ category: "dangerous",
145914
+ message: `'${name}' matches a dangerous system command and cannot be used as a resource name`
145915
+ };
145916
+ }
145917
+ for (const { pattern, message } of DANGEROUS_PATTERNS) {
145918
+ if (pattern.test(name)) {
145919
+ return {
145920
+ valid: false,
145921
+ category: "dangerous",
145922
+ message
145923
+ };
145924
+ }
145925
+ }
145926
+ if (!opts.skipFormatValidation) {
145927
+ const nameToCheck = opts.allowUppercase ? nameLower : name;
145928
+ if (!RFC1035_PATTERN.test(nameToCheck.toLowerCase())) {
145929
+ return {
145930
+ valid: false,
145931
+ category: "invalid",
145932
+ message: "Name must start with a letter, end with alphanumeric, and contain only lowercase letters, numbers, and hyphens",
145933
+ suggestion: sanitizeName(name)
145934
+ };
145935
+ }
145936
+ }
145937
+ return { valid: true };
145938
+ }
145939
+ function getCharacterDescription(char) {
145940
+ const descriptions = {
145941
+ ";": "; (command separator)",
145942
+ "&": "& (background execution)",
145943
+ "|": "| (pipe)",
145944
+ "`": "` (command substitution)",
145945
+ $: "$ (variable/command expansion)",
145946
+ "(": "( (subshell)",
145947
+ ")": ") (subshell)",
145948
+ "<": "< (input redirection)",
145949
+ ">": "> (output redirection)",
145950
+ "\\": "\\ (escape)",
145951
+ "#": "# (comment)",
145952
+ "!": "! (history expansion)",
145953
+ "{": "{ (brace expansion)",
145954
+ "}": "} (brace expansion)",
145955
+ "[": "[ (bracket expansion)",
145956
+ "]": "] (bracket expansion)",
145957
+ "*": "* (glob pattern)",
145958
+ "?": "? (glob pattern)",
145959
+ "~": "~ (home directory)",
145960
+ "\n": "newline",
145961
+ "\r": "carriage return"
145962
+ };
145963
+ return descriptions[char] ?? char;
145964
+ }
145965
+ function sanitizeName(name) {
145966
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").replace(/-{2,}/g, "-").slice(0, 63) || "resource";
145967
+ }
145968
+
145588
145969
  // src/domains/login/profile/create.ts
145589
145970
  var createCommand2 = {
145590
145971
  name: "create",
@@ -145611,6 +145992,18 @@ var createCommand2 = {
145611
145992
  ].join("\n")
145612
145993
  );
145613
145994
  }
145995
+ const nameValidation = validateResourceName(name, {
145996
+ maxLength: 128,
145997
+ // Profiles can have longer names than API resources
145998
+ resourceType: "profile"
145999
+ });
146000
+ if (!nameValidation.valid) {
146001
+ const lines = [`Invalid profile name: ${nameValidation.message}`];
146002
+ if (nameValidation.suggestion) {
146003
+ lines.push("", `Suggested: ${nameValidation.suggestion}`);
146004
+ }
146005
+ return errorResult(lines.join("\n"));
146006
+ }
145614
146007
  const existing = await manager.get(name);
145615
146008
  if (existing) {
145616
146009
  return errorResult(
@@ -148271,7 +148664,7 @@ var queryCommand = {
148271
148664
  // src/domains/ai_services/chat.ts
148272
148665
  import * as readline from "readline";
148273
148666
  function parseChatArgs(args, session) {
148274
- const { remainingArgs } = parseDomainOutputFlags(
148667
+ const { options, remainingArgs } = parseDomainOutputFlags(
148275
148668
  args,
148276
148669
  session.getOutputFormat()
148277
148670
  );
@@ -148290,7 +148683,11 @@ function parseChatArgs(args, session) {
148290
148683
  }
148291
148684
  i++;
148292
148685
  }
148293
- return { spec, namespace };
148686
+ return {
148687
+ spec,
148688
+ namespace,
148689
+ suppressOutput: options.format === "none"
148690
+ };
148294
148691
  }
148295
148692
  function showChatHelp() {
148296
148693
  return [
@@ -148505,13 +148902,19 @@ var chatCommand = {
148505
148902
  usage: "[--namespace <ns>]",
148506
148903
  aliases: ["interactive", "i"],
148507
148904
  async execute(args, session) {
148508
- const { spec, namespace } = parseChatArgs(args, session);
148905
+ const { spec, namespace, suppressOutput } = parseChatArgs(
148906
+ args,
148907
+ session
148908
+ );
148509
148909
  if (spec) {
148510
148910
  const cmdSpec = getCommandSpec("generative_ai chat");
148511
148911
  if (cmdSpec) {
148512
148912
  return successResult([formatSpec(cmdSpec)]);
148513
148913
  }
148514
148914
  }
148915
+ if (suppressOutput) {
148916
+ return successResult([]);
148917
+ }
148515
148918
  if (!process.stdin.isTTY) {
148516
148919
  return errorResult(
148517
148920
  "Chat mode requires an interactive terminal. Use 'ai query' for non-interactive queries."
@@ -148529,7 +148932,7 @@ var chatCommand = {
148529
148932
 
148530
148933
  // src/domains/ai_services/feedback.ts
148531
148934
  function parseFeedbackArgs(args, session) {
148532
- const { remainingArgs } = parseDomainOutputFlags(
148935
+ const { options, remainingArgs } = parseDomainOutputFlags(
148533
148936
  args,
148534
148937
  session.getOutputFormat()
148535
148938
  );
@@ -148577,6 +148980,8 @@ function parseFeedbackArgs(args, session) {
148577
148980
  i++;
148578
148981
  }
148579
148982
  return {
148983
+ format: options.format,
148984
+ noColor: options.noColor,
148580
148985
  spec,
148581
148986
  namespace,
148582
148987
  positive,
@@ -148593,13 +148998,25 @@ var feedbackCommand = {
148593
148998
  usage: "--positive | --negative <type> [--comment <text>] [--query-id <id>]",
148594
148999
  aliases: ["fb", "rate"],
148595
149000
  async execute(args, session) {
148596
- const { spec, namespace, positive, negativeType, comment, queryId } = parseFeedbackArgs(args, session);
149001
+ const {
149002
+ format,
149003
+ noColor,
149004
+ spec,
149005
+ namespace,
149006
+ positive,
149007
+ negativeType,
149008
+ comment,
149009
+ queryId
149010
+ } = parseFeedbackArgs(args, session);
148597
149011
  if (spec) {
148598
149012
  const cmdSpec = getCommandSpec("generative_ai feedback");
148599
149013
  if (cmdSpec) {
148600
149014
  return successResult([formatSpec(cmdSpec)]);
148601
149015
  }
148602
149016
  }
149017
+ if (format === "none") {
149018
+ return successResult([]);
149019
+ }
148603
149020
  if (!positive && !negativeType) {
148604
149021
  const validTypes = getValidFeedbackTypes().join(", ");
148605
149022
  return errorResult(
@@ -148644,6 +149061,17 @@ Negative types: ${validTypes}`
148644
149061
  positive_feedback: {},
148645
149062
  comment: comment ?? void 0
148646
149063
  });
149064
+ const result2 = {
149065
+ status: "success",
149066
+ type: "positive",
149067
+ query_id: targetQueryId,
149068
+ message: "Positive feedback submitted successfully."
149069
+ };
149070
+ if (format === "json" || format === "yaml" || format === "tsv") {
149071
+ return successResult(
149072
+ formatDomainOutput(result2, { format, noColor })
149073
+ );
149074
+ }
148647
149075
  return successResult([
148648
149076
  "Positive feedback submitted successfully.",
148649
149077
  `Query ID: ${targetQueryId}`
@@ -148658,6 +149086,19 @@ Negative types: ${validTypes}`
148658
149086
  },
148659
149087
  comment: comment ?? void 0
148660
149088
  });
149089
+ const result = {
149090
+ status: "success",
149091
+ type: "negative",
149092
+ query_id: targetQueryId,
149093
+ reason: negativeType ?? "OTHER",
149094
+ ...comment ? { comment } : {},
149095
+ message: "Negative feedback submitted successfully."
149096
+ };
149097
+ if (format === "json" || format === "yaml" || format === "tsv") {
149098
+ return successResult(
149099
+ formatDomainOutput(result, { format, noColor })
149100
+ );
149101
+ }
148661
149102
  return successResult([
148662
149103
  "Negative feedback submitted successfully.",
148663
149104
  `Query ID: ${targetQueryId}`,
@@ -148909,13 +149350,94 @@ var evalSubcommands = {
148909
149350
  defaultCommand: evalQueryCommand
148910
149351
  };
148911
149352
 
149353
+ // src/domains/domain-overview.ts
149354
+ function formatDomainOverview(config) {
149355
+ const lines = [];
149356
+ lines.push("");
149357
+ lines.push(`${config.name} - ${config.description}`);
149358
+ lines.push("");
149359
+ if (config.commands.length > 0) {
149360
+ lines.push("Commands:");
149361
+ const maxNameLen = Math.max(
149362
+ ...config.commands.map((cmd) => cmd.name.length)
149363
+ );
149364
+ const padding = Math.min(maxNameLen + 2, 20);
149365
+ for (const cmd of config.commands) {
149366
+ lines.push(` ${cmd.name.padEnd(padding)} ${cmd.description}`);
149367
+ }
149368
+ lines.push("");
149369
+ }
149370
+ if (config.examples.length > 0) {
149371
+ lines.push("Examples:");
149372
+ for (const example of config.examples) {
149373
+ lines.push(` ${example}`);
149374
+ }
149375
+ lines.push("");
149376
+ }
149377
+ if (config.supportsOutputFormats) {
149378
+ lines.push("Output formats: --output json|yaml|table|tsv|none");
149379
+ lines.push("");
149380
+ }
149381
+ if (config.notes && config.notes.length > 0) {
149382
+ for (const note of config.notes) {
149383
+ lines.push(note);
149384
+ }
149385
+ lines.push("");
149386
+ }
149387
+ return lines;
149388
+ }
149389
+
148912
149390
  // src/domains/ai_services/index.ts
149391
+ var entryCommand = {
149392
+ name: "ai_services",
149393
+ description: "AI-powered query and chat services for F5 Distributed Cloud",
149394
+ descriptionShort: "AI assistant queries and feedback",
149395
+ descriptionMedium: "Query the AI assistant for help with F5 XC platform operations, configurations, and troubleshooting.",
149396
+ async execute(args, session) {
149397
+ const hasQuestion = args.length > 0 && !args[0]?.startsWith("--");
149398
+ if (hasQuestion) {
149399
+ return queryCommand.execute(args, session);
149400
+ }
149401
+ const overview = formatDomainOverview({
149402
+ name: "AI Services",
149403
+ description: "Query and chat with the F5 XC AI assistant",
149404
+ commands: [
149405
+ {
149406
+ name: "query <question>",
149407
+ description: "Ask a single question"
149408
+ },
149409
+ {
149410
+ name: "chat",
149411
+ description: "Start interactive chat session"
149412
+ },
149413
+ {
149414
+ name: "feedback",
149415
+ description: "Provide feedback on AI responses"
149416
+ }
149417
+ ],
149418
+ examples: [
149419
+ "query 'How do I create an HTTP load balancer?'",
149420
+ "query 'What is my WAF policy configuration?' --namespace prod",
149421
+ "chat",
149422
+ "feedback positive"
149423
+ ],
149424
+ supportsOutputFormats: true,
149425
+ notes: ["Use 'eval' subcommand for AI evaluation testing."]
149426
+ });
149427
+ return {
149428
+ output: overview,
149429
+ contextChanged: true,
149430
+ shouldExit: false,
149431
+ shouldClear: false
149432
+ };
149433
+ }
149434
+ };
148913
149435
  var aiServicesDomain = {
148914
149436
  name: "ai_services",
148915
149437
  description: "Interact with the F5 Distributed Cloud AI assistant for natural language queries about platform operations. Ask questions about load balancers, WAF configurations, site status, security events, or any platform topic. Supports single queries with follow-up suggestions, interactive multi-turn chat sessions, and feedback submission to improve AI responses.",
148916
149438
  descriptionShort: "AI assistant queries and feedback",
148917
149439
  descriptionMedium: "Query the AI assistant for help with F5 XC platform operations, configurations, security analysis, and troubleshooting.",
148918
- defaultCommand: queryCommand,
149440
+ defaultCommand: entryCommand,
148919
149441
  commands: /* @__PURE__ */ new Map([
148920
149442
  ["query", queryCommand],
148921
149443
  ["chat", chatCommand],
@@ -150510,13 +151032,13 @@ var RESERVED_API_ACTIONS = /* @__PURE__ */ new Set([
150510
151032
  "add-labels",
150511
151033
  "remove-labels"
150512
151034
  ]);
150513
- function isReservedAction(name) {
151035
+ function isReservedAction2(name) {
150514
151036
  return RESERVED_API_ACTIONS.has(name.toLowerCase());
150515
151037
  }
150516
151038
  function validateExtension(extension) {
150517
151039
  const conflicts = [];
150518
151040
  for (const [name] of extension.commands) {
150519
- if (isReservedAction(name)) {
151041
+ if (isReservedAction2(name)) {
150520
151042
  conflicts.push(name);
150521
151043
  }
150522
151044
  }
@@ -151861,114 +152383,15 @@ function useGitStatus(options = {}) {
151861
152383
  return { gitInfo, refresh, lastRefresh };
151862
152384
  }
151863
152385
 
151864
- // src/validation/namespace.ts
151865
- function validateNamespaceScope(domain, action, currentNamespace, resourceType) {
151866
- const opInfo = getOperationDescription(domain, action, resourceType);
151867
- if (!opInfo?.namespaceScope) {
151868
- return { valid: true };
151869
- }
151870
- const scope = opInfo.namespaceScope;
151871
- const normalizedNamespace = currentNamespace.toLowerCase();
151872
- switch (scope) {
151873
- case "system":
151874
- if (normalizedNamespace !== "system") {
151875
- return {
151876
- valid: false,
151877
- scope,
151878
- message: `${action} on ${resourceType || domain} requires the 'system' namespace`,
151879
- suggestion: "system"
151880
- };
151881
- }
151882
- return { valid: true, scope };
151883
- case "shared":
151884
- if (normalizedNamespace !== "shared") {
151885
- return {
151886
- valid: false,
151887
- scope,
151888
- message: `${action} on ${resourceType || domain} requires the 'shared' namespace`,
151889
- suggestion: "shared"
151890
- };
151891
- }
151892
- return { valid: true, scope };
151893
- case "any":
151894
- default:
151895
- return { valid: true, scope };
151896
- }
151897
- }
151898
-
151899
- // src/validation/safety.ts
151900
- function checkOperationSafety(domain, action, resourceType) {
151901
- const opInfo = getOperationDescription(domain, action, resourceType);
151902
- const dangerLevel = opInfo?.dangerLevel || "low";
151903
- const requiresConfirmation2 = opInfo?.confirmationRequired ?? dangerLevel === "high";
151904
- const sideEffects = opInfo?.sideEffects;
151905
- const result = {
151906
- proceed: dangerLevel !== "high",
151907
- // High danger requires explicit confirmation
151908
- dangerLevel,
151909
- requiresConfirmation: requiresConfirmation2
151910
- };
151911
- if (dangerLevel === "high") {
151912
- result.warning = formatHighDangerWarning(domain, action, sideEffects);
151913
- } else if (dangerLevel === "medium") {
151914
- result.warning = formatMediumDangerWarning(domain, action, sideEffects);
151915
- }
151916
- if (sideEffects) {
151917
- result.sideEffects = sideEffects;
151918
- }
151919
- return result;
151920
- }
151921
- function formatHighDangerWarning(_domain, _action, sideEffects) {
151922
- const lines = [
151923
- colorRed("\u26A0\uFE0F WARNING: This is a HIGH DANGER operation"),
151924
- ""
151925
- ];
151926
- if (sideEffects) {
151927
- if (sideEffects.deletes && sideEffects.deletes.length > 0) {
151928
- lines.push(
151929
- colorRed(` Will DELETE: ${sideEffects.deletes.join(", ")}`)
151930
- );
151931
- }
151932
- if (sideEffects.updates && sideEffects.updates.length > 0) {
151933
- lines.push(` Will UPDATE: ${sideEffects.updates.join(", ")}`);
151934
- }
151935
- if (sideEffects.creates && sideEffects.creates.length > 0) {
151936
- lines.push(
151937
- colorDim(` Will CREATE: ${sideEffects.creates.join(", ")}`)
151938
- );
151939
- }
151940
- lines.push("");
151941
- }
151942
- lines.push(
151943
- colorRed("This action may be destructive and cannot be undone.")
151944
- );
151945
- return lines.join("\n");
151946
- }
151947
- function formatMediumDangerWarning(_domain, _action, sideEffects) {
151948
- const lines = [
151949
- colorYellow("\u26A0\uFE0F CAUTION: This operation may have significant effects"),
151950
- ""
151951
- ];
151952
- if (sideEffects) {
151953
- const effects = [];
151954
- if (sideEffects.creates && sideEffects.creates.length > 0) {
151955
- effects.push(`creates: ${sideEffects.creates.join(", ")}`);
151956
- }
151957
- if (sideEffects.updates && sideEffects.updates.length > 0) {
151958
- effects.push(`updates: ${sideEffects.updates.join(", ")}`);
151959
- }
151960
- if (sideEffects.deletes && sideEffects.deletes.length > 0) {
151961
- effects.push(`deletes: ${sideEffects.deletes.join(", ")}`);
151962
- }
151963
- if (effects.length > 0) {
151964
- lines.push(` Side effects: ${effects.join("; ")}`);
151965
- lines.push("");
151966
- }
151967
- }
151968
- return lines.join("\n");
151969
- }
151970
-
151971
152386
  // src/repl/executor.ts
152387
+ var WRITE_OPERATIONS = /* @__PURE__ */ new Set([
152388
+ "create",
152389
+ "replace",
152390
+ "apply",
152391
+ "patch",
152392
+ "add-labels",
152393
+ "remove-labels"
152394
+ ]);
151972
152395
  var BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
151973
152396
  "help",
151974
152397
  "--help",
@@ -152653,6 +153076,50 @@ async function executeAPICommand(session, ctx, cmd) {
152653
153076
  );
152654
153077
  const { resourceType, name, namespace, outputFormat, spec, noColor } = parseCommandArgs(args, domainResourceTypes);
152655
153078
  const effectiveNamespace = namespace ?? session.getNamespace();
153079
+ if (effectiveNamespace) {
153080
+ const nsNameValidation = validateResourceName(effectiveNamespace, {
153081
+ resourceType: "namespace"
153082
+ });
153083
+ if (!nsNameValidation.valid) {
153084
+ const lines = [
153085
+ `Error: Invalid namespace name '${effectiveNamespace}'`,
153086
+ "",
153087
+ nsNameValidation.message ?? "Invalid namespace name"
153088
+ ];
153089
+ if (nsNameValidation.suggestion) {
153090
+ lines.push("", `Suggested: ${nsNameValidation.suggestion}`);
153091
+ }
153092
+ return {
153093
+ output: lines,
153094
+ shouldExit: false,
153095
+ shouldClear: false,
153096
+ contextChanged: false,
153097
+ error: nsNameValidation.message ?? "Invalid namespace name"
153098
+ };
153099
+ }
153100
+ }
153101
+ if (WRITE_OPERATIONS.has(action) && name) {
153102
+ const nameValidation = validateResourceName(name, {
153103
+ resourceType: resourceType ?? canonicalDomain
153104
+ });
153105
+ if (!nameValidation.valid) {
153106
+ const lines = [
153107
+ `Error: Invalid resource name '${name}'`,
153108
+ "",
153109
+ nameValidation.message ?? "Invalid resource name"
153110
+ ];
153111
+ if (nameValidation.suggestion) {
153112
+ lines.push("", `Suggested: ${nameValidation.suggestion}`);
153113
+ }
153114
+ return {
153115
+ output: lines,
153116
+ shouldExit: false,
153117
+ shouldClear: false,
153118
+ contextChanged: false,
153119
+ error: nameValidation.message ?? "Invalid resource name"
153120
+ };
153121
+ }
153122
+ }
152656
153123
  const effectiveResource = resourceType ?? canonicalDomain;
152657
153124
  const nsValidation = validateNamespaceScope(
152658
153125
  canonicalDomain,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "1.0.91-2601040044",
3
+ "version": "1.0.91-2601040346",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {