@qodalis/cli 2.0.0-beta.16 → 2.0.0-beta.17

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/public-api.js CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  var cliCore = require('@qodalis/cli-core');
4
- var rxjs = require('rxjs');
5
- var operators = require('rxjs/operators');
6
4
  var xterm = require('@xterm/xterm');
7
5
  var addonFit = require('@xterm/addon-fit');
8
6
  var addonWebLinks = require('@xterm/addon-web-links');
9
7
  var addonUnicode11 = require('@xterm/addon-unicode11');
10
8
  var addonSerialize = require('@xterm/addon-serialize');
9
+ var rxjs = require('rxjs');
10
+ var operators = require('rxjs/operators');
11
11
 
12
12
  // src/lib/parsers/command-parser.ts
13
13
  var CommandParser = class {
@@ -413,7 +413,7 @@ var CliKeyValueStore_TOKEN = "cli-key-value-store";
413
413
  var SyntaxHighlighterRegistry_TOKEN = "syntax-highlighter-registry";
414
414
 
415
415
  // src/lib/version.ts
416
- var LIBRARY_VERSION = "2.0.0-beta.16";
416
+ var LIBRARY_VERSION = "2.0.0-beta.17";
417
417
  var API_VERSION = 2;
418
418
  var ANONYMOUS = "\0anonymous";
419
419
  function aliasOwner(context2) {
@@ -2699,6 +2699,150 @@ var CliYesCommandProcessor = class {
2699
2699
  );
2700
2700
  }
2701
2701
  };
2702
+
2703
+ // src/lib/addons/overlay.ts
2704
+ var OverlayAddon = class {
2705
+ constructor() {
2706
+ this.overlayNode = document.createElement("div");
2707
+ this.overlayNode.style.cssText = `border-radius: 15px;
2708
+ font-size: xx-large;
2709
+ opacity: 0.75;
2710
+ padding: 0.2em 0.5em 0.2em 0.5em;
2711
+ position: absolute;
2712
+ -webkit-user-select: none;
2713
+ -webkit-transition: opacity 180ms ease-in;
2714
+ -moz-user-select: none;
2715
+ -moz-transition: opacity 180ms ease-in;`;
2716
+ this.overlayNode.addEventListener(
2717
+ "mousedown",
2718
+ (e) => {
2719
+ e.preventDefault();
2720
+ e.stopPropagation();
2721
+ },
2722
+ true
2723
+ );
2724
+ }
2725
+ activate(terminal) {
2726
+ this.terminal = terminal;
2727
+ }
2728
+ dispose() {
2729
+ }
2730
+ showOverlay(msg, timeout) {
2731
+ const { terminal, overlayNode } = this;
2732
+ if (!terminal.element) return;
2733
+ overlayNode.style.color = "#101010";
2734
+ overlayNode.style.backgroundColor = "#f0f0f0";
2735
+ overlayNode.textContent = msg;
2736
+ overlayNode.style.opacity = "0.75";
2737
+ if (!overlayNode.parentNode) {
2738
+ terminal.element.appendChild(overlayNode);
2739
+ }
2740
+ const divSize = terminal.element.getBoundingClientRect();
2741
+ const overlaySize = overlayNode.getBoundingClientRect();
2742
+ overlayNode.style.top = (divSize.height - overlaySize.height) / 2 + "px";
2743
+ overlayNode.style.left = (divSize.width - overlaySize.width) / 2 + "px";
2744
+ if (this.overlayTimeout) clearTimeout(this.overlayTimeout);
2745
+ if (!timeout) return;
2746
+ this.overlayTimeout = window.setTimeout(() => {
2747
+ overlayNode.style.opacity = "0";
2748
+ this.overlayTimeout = window.setTimeout(() => {
2749
+ if (overlayNode.parentNode) {
2750
+ overlayNode.parentNode.removeChild(overlayNode);
2751
+ }
2752
+ this.overlayTimeout = void 0;
2753
+ overlayNode.style.opacity = "0.75";
2754
+ }, 200);
2755
+ }, timeout || 1500);
2756
+ }
2757
+ };
2758
+ var CliDragDropService = class {
2759
+ constructor(_container) {
2760
+ this._container = _container;
2761
+ this._subject = new rxjs.Subject();
2762
+ this._dragOver = (e) => e.preventDefault();
2763
+ this._drop = (e) => {
2764
+ e.preventDefault();
2765
+ const files = Array.from(e.dataTransfer?.files ?? []);
2766
+ if (files.length > 0) {
2767
+ this._subject.next(files);
2768
+ }
2769
+ };
2770
+ this._container.addEventListener("dragover", this._dragOver);
2771
+ this._container.addEventListener("drop", this._drop);
2772
+ }
2773
+ get onFileDrop() {
2774
+ return this._subject.asObservable();
2775
+ }
2776
+ destroy() {
2777
+ this._container.removeEventListener("dragover", this._dragOver);
2778
+ this._container.removeEventListener("drop", this._drop);
2779
+ this._subject.complete();
2780
+ }
2781
+ };
2782
+
2783
+ // src/lib/engine/cli-terminal-setup.ts
2784
+ var NARROW_VIEWPORT = 640;
2785
+ function defaultFontSize() {
2786
+ const width = typeof window !== "undefined" ? window.innerWidth : 0;
2787
+ return width > 0 && width <= NARROW_VIEWPORT ? 14 : 20;
2788
+ }
2789
+ function getTerminalOptions(userOptions) {
2790
+ return {
2791
+ cursorBlink: true,
2792
+ allowProposedApi: true,
2793
+ fontSize: defaultFontSize(),
2794
+ theme: cliCore.DefaultThemes.default,
2795
+ convertEol: true,
2796
+ ...userOptions ?? {}
2797
+ };
2798
+ }
2799
+ function initializeTerminal(container, opts) {
2800
+ const terminal = new xterm.Terminal(opts);
2801
+ const fitAddon = new addonFit.FitAddon();
2802
+ terminal.loadAddon(fitAddon);
2803
+ terminal.loadAddon(new addonWebLinks.WebLinksAddon());
2804
+ terminal.loadAddon(new OverlayAddon());
2805
+ terminal.loadAddon(new addonUnicode11.Unicode11Addon());
2806
+ const serializeAddon = new addonSerialize.SerializeAddon();
2807
+ terminal.loadAddon(serializeAddon);
2808
+ terminal.open(container);
2809
+ fitAddon.fit();
2810
+ requestAnimationFrame(() => fitAddon.fit());
2811
+ if (document.fonts?.ready) {
2812
+ document.fonts.ready.then(() => {
2813
+ requestAnimationFrame(() => fitAddon.fit());
2814
+ });
2815
+ }
2816
+ container.classList.add("terminal-container");
2817
+ container.style.background = opts.theme?.background ?? "#000";
2818
+ const wheelListener = (e) => e.preventDefault();
2819
+ container.addEventListener("wheel", wheelListener, { passive: false });
2820
+ terminal.focus();
2821
+ const dragDropService = new CliDragDropService(container);
2822
+ return { terminal, fitAddon, serializeAddon, dragDropService, wheelListener };
2823
+ }
2824
+ var LAYOUT_TIMEOUT_MS = 1e4;
2825
+ function waitForLayout(container) {
2826
+ return new Promise((resolve, reject) => {
2827
+ const deadline = Date.now() + LAYOUT_TIMEOUT_MS;
2828
+ const check = () => {
2829
+ if (container.offsetWidth > 0 && container.offsetHeight > 0) {
2830
+ resolve();
2831
+ return;
2832
+ }
2833
+ if (Date.now() >= deadline) {
2834
+ reject(new Error(
2835
+ `CliEngine.start(): container has zero dimensions after ${LAYOUT_TIMEOUT_MS}ms. Ensure the container element is visible and laid out before calling start().`
2836
+ ));
2837
+ return;
2838
+ }
2839
+ requestAnimationFrame(check);
2840
+ };
2841
+ check();
2842
+ });
2843
+ }
2844
+
2845
+ // src/lib/processors/configure/cli-configure-command-processor.ts
2702
2846
  var SYSTEM_OPTIONS = [
2703
2847
  {
2704
2848
  key: "logLevel",
@@ -2775,7 +2919,7 @@ var SYSTEM_OPTIONS = [
2775
2919
  label: "Font Size",
2776
2920
  description: "Terminal font size in pixels",
2777
2921
  type: "number",
2778
- defaultValue: 20,
2922
+ defaultValue: defaultFontSize(),
2779
2923
  validator: (value) => {
2780
2924
  if (value < 8 || value > 40) {
2781
2925
  return { valid: false, message: "Font size must be between 8 and 40" };
@@ -2855,7 +2999,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
2855
2999
  async initialize(context2) {
2856
3000
  const state = context2.state.getState();
2857
3001
  if (state?.system) {
2858
- this.applySystemSettings(state.system, context2);
3002
+ this.applySystemSettings(state.system, context2, true);
2859
3003
  }
2860
3004
  }
2861
3005
  writeDescription(context2) {
@@ -3090,7 +3234,11 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3090
3234
  }
3091
3235
  return result;
3092
3236
  }
3093
- applySystemSettings(settings, context2) {
3237
+ /**
3238
+ * @param onBoot at startup, terminal options still at their default are skipped so the
3239
+ * host's `terminalOptions` (e.g. its own fontSize) are not overridden.
3240
+ */
3241
+ applySystemSettings(settings, context2, onBoot = false) {
3094
3242
  if (settings["logLevel"]) {
3095
3243
  const level = LOG_LEVEL_MAP[settings["logLevel"]];
3096
3244
  if (level !== void 0) {
@@ -3104,23 +3252,29 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3104
3252
  translator?.setLocale(settings["language"]);
3105
3253
  }
3106
3254
  if (context2.terminal?.options) {
3107
- if (settings["cursorBlink"] !== void 0) {
3108
- context2.terminal.options.cursorBlink = settings["cursorBlink"];
3255
+ const terminal = onBoot ? Object.fromEntries(
3256
+ Object.entries(settings).filter(([key, value]) => {
3257
+ const option = SYSTEM_OPTIONS.find((o) => o.key === key);
3258
+ return !option || value !== option.defaultValue;
3259
+ })
3260
+ ) : settings;
3261
+ if (terminal["cursorBlink"] !== void 0) {
3262
+ context2.terminal.options.cursorBlink = terminal["cursorBlink"];
3109
3263
  }
3110
- if (settings["cursorStyle"]) {
3111
- context2.terminal.options.cursorStyle = settings["cursorStyle"];
3264
+ if (terminal["cursorStyle"]) {
3265
+ context2.terminal.options.cursorStyle = terminal["cursorStyle"];
3112
3266
  }
3113
- if (settings["scrollback"] !== void 0) {
3114
- context2.terminal.options.scrollback = settings["scrollback"];
3267
+ if (terminal["scrollback"] !== void 0) {
3268
+ context2.terminal.options.scrollback = terminal["scrollback"];
3115
3269
  }
3116
- if (settings["fontSize"] !== void 0) {
3117
- context2.terminal.options.fontSize = settings["fontSize"];
3270
+ if (terminal["fontSize"] !== void 0) {
3271
+ context2.terminal.options.fontSize = terminal["fontSize"];
3118
3272
  }
3119
- if (settings["fontFamily"]) {
3120
- context2.terminal.options.fontFamily = settings["fontFamily"];
3273
+ if (terminal["fontFamily"]) {
3274
+ context2.terminal.options.fontFamily = terminal["fontFamily"];
3121
3275
  }
3122
- if (settings["smoothScrollDuration"] !== void 0) {
3123
- context2.terminal.options.smoothScrollDuration = settings["smoothScrollDuration"];
3276
+ if (terminal["smoothScrollDuration"] !== void 0) {
3277
+ context2.terminal.options.smoothScrollDuration = terminal["smoothScrollDuration"];
3124
3278
  }
3125
3279
  }
3126
3280
  }
@@ -3718,6 +3872,7 @@ var CliHelpCommandProcessor = class {
3718
3872
  return 0;
3719
3873
  }
3720
3874
  );
3875
+ const narrow = isNarrow(context2);
3721
3876
  sortedModules.forEach(([module, processors]) => {
3722
3877
  const displayName = this.formatModuleName(module);
3723
3878
  writer.writeln(
@@ -3726,7 +3881,7 @@ var CliHelpCommandProcessor = class {
3726
3881
  processors.forEach((processor) => {
3727
3882
  const icon = processor?.metadata?.icon || cliCore.CliIcon.Extension;
3728
3883
  const name = writer.wrapInColor(
3729
- processor.command.padEnd(20),
3884
+ narrow ? processor.command : processor.command.padEnd(20),
3730
3885
  cliCore.CliForegroundColor.Cyan
3731
3886
  );
3732
3887
  const aliasText = processor.aliases?.length ? `${DIM} ${processor.aliases.join(", ")}${RESET}` : "";
@@ -3735,6 +3890,11 @@ var CliHelpCommandProcessor = class {
3735
3890
  processor.command,
3736
3891
  context2
3737
3892
  );
3893
+ if (narrow) {
3894
+ writer.writeln(` ${icon} ${name}${aliasText}`);
3895
+ writer.writeln(` ${desc}`);
3896
+ return;
3897
+ }
3738
3898
  writer.writeln(` ${icon} ${name} ${desc}${aliasText}`);
3739
3899
  });
3740
3900
  writer.writeln();
@@ -3844,9 +4004,10 @@ var CliHelpCommandProcessor = class {
3844
4004
  writer.writeln(
3845
4005
  ` ${BOLD}${writer.wrapInColor(t.t("cli.help.subcommands", "Subcommands"), cliCore.CliForegroundColor.Yellow)}${RESET}`
3846
4006
  );
4007
+ const narrow = isNarrow(context2);
3847
4008
  processor.processors.forEach((sub) => {
3848
4009
  const subName = writer.wrapInColor(
3849
- sub.command.padEnd(16),
4010
+ narrow ? sub.command : sub.command.padEnd(16),
3850
4011
  cliCore.CliForegroundColor.Cyan
3851
4012
  );
3852
4013
  const subAliases = sub.aliases?.length ? `${DIM} (${sub.aliases.join(", ")})${RESET}` : "";
@@ -3855,6 +4016,11 @@ var CliHelpCommandProcessor = class {
3855
4016
  `${processor.command}.${sub.command}`,
3856
4017
  context2
3857
4018
  );
4019
+ if (narrow) {
4020
+ writer.writeln(` ${subName}${subAliases}`);
4021
+ writer.writeln(` ${subDesc}`);
4022
+ return;
4023
+ }
3858
4024
  writer.writeln(
3859
4025
  ` ${subName} ${subDesc}${subAliases}`
3860
4026
  );
@@ -3868,7 +4034,7 @@ var CliHelpCommandProcessor = class {
3868
4034
  ` ${BOLD}${writer.wrapInColor(t.t("cli.help.options", "Options"), cliCore.CliForegroundColor.Yellow)}${RESET}`
3869
4035
  );
3870
4036
  commandParams.forEach((param) => {
3871
- this.writeParameter(param, writer, t);
4037
+ this.writeParameter(param, writer, t, isNarrow(context2));
3872
4038
  });
3873
4039
  }
3874
4040
  if (globalParams.length > 0) {
@@ -3877,7 +4043,7 @@ var CliHelpCommandProcessor = class {
3877
4043
  ` ${BOLD}${writer.wrapInColor(t.t("cli.help.global_options", "Global options"), cliCore.CliForegroundColor.Yellow)}${RESET}`
3878
4044
  );
3879
4045
  globalParams.forEach((param) => {
3880
- this.writeParameter(param, writer, t);
4046
+ this.writeParameter(param, writer, t, isNarrow(context2));
3881
4047
  });
3882
4048
  }
3883
4049
  if (processor.metadata?.requireServer) {
@@ -3899,7 +4065,7 @@ var CliHelpCommandProcessor = class {
3899
4065
  }
3900
4066
  return module.charAt(0).toUpperCase() + module.slice(1);
3901
4067
  }
3902
- writeParameter(param, writer, translator) {
4068
+ writeParameter(param, writer, translator, narrow = false) {
3903
4069
  const aliases = param.aliases?.length ? `, ${param.aliases.map((a) => `-${a}`).join(", ")}` : "";
3904
4070
  const nameStr = writer.wrapInColor(
3905
4071
  `--${param.name}${aliases}`,
@@ -3908,11 +4074,19 @@ var CliHelpCommandProcessor = class {
3908
4074
  const typeStr = `${DIM}<${param.type}>${RESET}`;
3909
4075
  const required = param.required ? writer.wrapInColor(` ${translator.t("cli.help.required", "(required)")}`, cliCore.CliForegroundColor.Red) : "";
3910
4076
  const defaultVal = param.defaultValue !== void 0 && !param.required ? `${DIM} [${param.defaultValue}]${RESET}` : "";
4077
+ if (narrow) {
4078
+ writer.writeln(` ${nameStr} ${typeStr}${required}${defaultVal}`);
4079
+ writer.writeln(` ${param.description}`);
4080
+ return;
4081
+ }
3911
4082
  writer.writeln(
3912
4083
  ` ${nameStr} ${typeStr} ${param.description}${required}${defaultVal}`
3913
4084
  );
3914
4085
  }
3915
4086
  };
4087
+ function isNarrow(context2) {
4088
+ return (context2.terminal?.cols ?? 80) < 60;
4089
+ }
3916
4090
 
3917
4091
  // src/lib/constants/index.ts
3918
4092
  var CLi_Name_Art_Full = `
@@ -9832,11 +10006,12 @@ function hexToRgb(hex) {
9832
10006
  const n = parseInt(m[1], 16);
9833
10007
  return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
9834
10008
  }
9835
- function colorSwatch(hex) {
9836
- if (!hex) return " ";
10009
+ function colorSwatch(hex, width = 2) {
10010
+ const cell = " ".repeat(width);
10011
+ if (!hex) return cell;
9837
10012
  const rgb = hexToRgb(hex);
9838
- if (!rgb) return " ";
9839
- return `\x1B[48;2;${rgb.r};${rgb.g};${rgb.b}m \x1B[0m`;
10013
+ if (!rgb) return cell;
10014
+ return `\x1B[48;2;${rgb.r};${rgb.g};${rgb.b}m${cell}\x1B[0m`;
9840
10015
  }
9841
10016
  function colorText(text, hex) {
9842
10017
  if (!hex) return text;
@@ -9903,11 +10078,20 @@ var CliThemeCommandProcessor = class {
9903
10078
  )
9904
10079
  );
9905
10080
  context2.writer.writeln();
10081
+ const narrow = (context2.terminal?.cols ?? 80) < 60;
9906
10082
  for (const [name, info] of entries) {
9907
10083
  const active = name === currentName ? " (active)" : "";
9908
10084
  const swatches = PALETTE_KEYS.map(
9909
10085
  (k) => colorSwatch(info.theme[k])
9910
10086
  ).join("");
10087
+ if (narrow) {
10088
+ const compact = PALETTE_KEYS.slice(0, 8).map((k) => colorSwatch(info.theme[k], 1)).join("");
10089
+ context2.writer.writeln(
10090
+ ` ${compact} ${context2.writer.wrapInColor(name, cliCore.CliForegroundColor.Cyan)}${active}`
10091
+ );
10092
+ context2.writer.writeln(` ${info.description}`);
10093
+ continue;
10094
+ }
9911
10095
  const nameLabel = context2.writer.wrapInColor(
9912
10096
  name.padEnd(22),
9913
10097
  cliCore.CliForegroundColor.Cyan
@@ -10609,37 +10793,106 @@ var CliPingCommandProcessor = class {
10609
10793
  var ICliEnvironment_TOKEN = "cli-environment";
10610
10794
  var CliEnvironment = class {
10611
10795
  constructor() {
10612
- this.vars = /* @__PURE__ */ new Map();
10613
- this.vars.set("SHELL", "/bin/sh");
10614
- this.vars.set("TERM", "xterm-256color");
10615
- this.vars.set("LANG", "en_US.UTF-8");
10616
- this.vars.set("PWD", "/");
10617
- this.vars.set("HOME", "/home/user");
10618
- this.vars.set("USER", "user");
10619
- this.vars.set("PATH", "/usr/local/bin:/usr/bin:/bin");
10796
+ this.defaults = /* @__PURE__ */ new Map();
10797
+ /** Variables computed on read; a user's own value wins over them. */
10798
+ this.bound = /* @__PURE__ */ new Map();
10799
+ this.profiles = /* @__PURE__ */ new Map();
10800
+ this.profileKey = () => "";
10801
+ this.defaults.set("SHELL", "/bin/sh");
10802
+ this.defaults.set("TERM", "xterm-256color");
10803
+ this.defaults.set("LANG", "en_US.UTF-8");
10804
+ this.defaults.set("PWD", "/");
10805
+ this.defaults.set("HOME", "/home/user");
10806
+ this.defaults.set("USER", "user");
10807
+ this.defaults.set("PATH", "/usr/local/bin:/usr/bin:/bin");
10808
+ }
10809
+ /** Compute a variable on every read; the seeded value is the fallback while it returns nothing. */
10810
+ bind(name, resolver) {
10811
+ this.bound.set(name, resolver);
10812
+ }
10813
+ /** Pick whose changes apply (the signed-in user's name, '' when signed out). */
10814
+ setProfileResolver(resolver) {
10815
+ this.profileKey = () => resolver() ?? "";
10816
+ }
10817
+ /** Restore saved profiles (replaces the ones in memory). */
10818
+ load(snapshot) {
10819
+ this.profiles.clear();
10820
+ for (const [key, profile] of Object.entries(snapshot ?? {})) {
10821
+ this.profiles.set(key, {
10822
+ vars: new Map(Object.entries(profile?.vars ?? {})),
10823
+ unset: new Set(profile?.unset ?? [])
10824
+ });
10825
+ }
10826
+ }
10827
+ snapshot() {
10828
+ const result = {};
10829
+ for (const [key, profile] of this.profiles) {
10830
+ if (!profile.vars.size && !profile.unset.size) continue;
10831
+ result[key] = { vars: Object.fromEntries(profile.vars), unset: [...profile.unset] };
10832
+ }
10833
+ return result;
10620
10834
  }
10621
10835
  get(name) {
10622
- return this.vars.get(name);
10836
+ const profile = this.profiles.get(this.profileKey());
10837
+ if (profile?.vars.has(name)) return profile.vars.get(name);
10838
+ if (profile?.unset.has(name)) return void 0;
10839
+ return this.bound.get(name)?.() || this.defaults.get(name);
10623
10840
  }
10624
10841
  set(name, value) {
10625
- this.vars.set(name, value);
10842
+ const profile = this.currentProfile();
10843
+ profile.unset.delete(name);
10844
+ profile.vars.set(name, value);
10845
+ this.onChange?.(this.snapshot());
10626
10846
  }
10627
10847
  unset(name) {
10628
- this.vars.delete(name);
10848
+ const profile = this.currentProfile();
10849
+ profile.vars.delete(name);
10850
+ if (this.defaults.has(name) || this.bound.has(name)) profile.unset.add(name);
10851
+ this.onChange?.(this.snapshot());
10629
10852
  }
10630
10853
  getAll() {
10854
+ const profile = this.profiles.get(this.profileKey());
10855
+ const names = /* @__PURE__ */ new Set([...this.defaults.keys(), ...this.bound.keys(), ...profile?.vars.keys() ?? []]);
10631
10856
  const result = {};
10632
- for (const [k, v] of this.vars) {
10633
- result[k] = v;
10857
+ for (const name of names) {
10858
+ const value = this.get(name);
10859
+ if (value !== void 0) result[name] = value;
10634
10860
  }
10635
10861
  return result;
10636
10862
  }
10637
10863
  has(name) {
10638
- return this.vars.has(name);
10864
+ return this.get(name) !== void 0;
10865
+ }
10866
+ currentProfile() {
10867
+ const key = this.profileKey();
10868
+ let profile = this.profiles.get(key);
10869
+ if (!profile) {
10870
+ profile = { vars: /* @__PURE__ */ new Map(), unset: /* @__PURE__ */ new Set() };
10871
+ this.profiles.set(key, profile);
10872
+ }
10873
+ return profile;
10639
10874
  }
10640
10875
  };
10641
10876
 
10642
10877
  // src/lib/processors/cli-env-command-processor.ts
10878
+ var ASSIGNMENT = /([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|\S*)/y;
10879
+ var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
10880
+ function parseAssignments(raw) {
10881
+ const result = [];
10882
+ for (const token of raw.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []) {
10883
+ ASSIGNMENT.lastIndex = 0;
10884
+ const match = ASSIGNMENT.exec(token);
10885
+ if (match && ASSIGNMENT.lastIndex === token.length) {
10886
+ const quoted = /^(["']).*\1$/.test(match[2]);
10887
+ result.push({ name: match[1], value: quoted ? match[2].slice(1, -1) : match[2] });
10888
+ } else if (NAME.test(token)) {
10889
+ result.push({ name: token });
10890
+ } else {
10891
+ return null;
10892
+ }
10893
+ }
10894
+ return result;
10895
+ }
10643
10896
  var CliExportCommandProcessor = class {
10644
10897
  constructor() {
10645
10898
  this.command = "export";
@@ -10647,6 +10900,21 @@ var CliExportCommandProcessor = class {
10647
10900
  this.author = cliCore.DefaultLibraryAuthor;
10648
10901
  this.acceptsRawInput = true;
10649
10902
  this.metadata = { icon: "\u{1F4E6}", module: "system" };
10903
+ this.stateConfiguration = {
10904
+ storeName: "environment",
10905
+ initialState: { profiles: {} }
10906
+ };
10907
+ }
10908
+ /** Restore saved variables and save every later change (export, unset, VAR=value). */
10909
+ async initialize(context2) {
10910
+ const env = context2.services.get(ICliEnvironment_TOKEN);
10911
+ if (!(env instanceof CliEnvironment)) return;
10912
+ const store = context2.state;
10913
+ env.load(store.getState()?.profiles);
10914
+ env.onChange = (profiles) => {
10915
+ store.updateState({ profiles });
10916
+ void store.persist();
10917
+ };
10650
10918
  }
10651
10919
  async processCommand(command2, context2) {
10652
10920
  const env = context2.services.getRequired(
@@ -10663,20 +10931,21 @@ var CliExportCommandProcessor = class {
10663
10931
  }
10664
10932
  return;
10665
10933
  }
10666
- const match = raw.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
10667
- if (!match) {
10934
+ const assignments = parseAssignments(raw);
10935
+ if (!assignments?.length) {
10668
10936
  context2.writer.writeError(
10669
10937
  `export: invalid syntax: ${raw}`
10670
10938
  );
10671
10939
  context2.process.exit(1, { silent: true });
10672
10940
  return;
10673
10941
  }
10674
- const varName = match[1];
10675
- let varValue = match[2];
10676
- if (varValue.startsWith('"') && varValue.endsWith('"') || varValue.startsWith("'") && varValue.endsWith("'")) {
10677
- varValue = varValue.slice(1, -1);
10942
+ for (const { name, value } of assignments) {
10943
+ if (value !== void 0) {
10944
+ env.set(name, value);
10945
+ } else if (!env.has(name)) {
10946
+ env.set(name, "");
10947
+ }
10678
10948
  }
10679
- env.set(varName, varValue);
10680
10949
  }
10681
10950
  writeDescription(context2) {
10682
10951
  const { writer, translator: t } = context2;
@@ -10692,6 +10961,13 @@ var CliExportCommandProcessor = class {
10692
10961
  writer.writeln(
10693
10962
  ` ${writer.wrapInColor('export VAR="hello world"', cliCore.CliForegroundColor.Cyan)} ${t.t("cli.export.set_spaces", "Set with spaces")}`
10694
10963
  );
10964
+ writer.writeln(
10965
+ ` ${writer.wrapInColor("export A=1 B=2", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.export.set_many", "Set several at once")}`
10966
+ );
10967
+ writer.writeln();
10968
+ writer.writeln(
10969
+ t.t("cli.export.saved", "Variables are saved for the signed-in user and used as $VAR or ${VAR} in commands.")
10970
+ );
10695
10971
  }
10696
10972
  };
10697
10973
  var CliUnsetCommandProcessor = class {
@@ -10706,16 +10982,15 @@ var CliUnsetCommandProcessor = class {
10706
10982
  const env = context2.services.getRequired(
10707
10983
  ICliEnvironment_TOKEN
10708
10984
  );
10709
- const varName = (command2.value || "").trim();
10710
- if (!varName) {
10985
+ const names = (command2.value || "").trim().split(/\s+/).filter(Boolean);
10986
+ if (!names.length) {
10711
10987
  context2.writer.writeError("unset: missing variable name");
10712
10988
  context2.process.exit(1, { silent: true });
10713
10989
  return;
10714
10990
  }
10715
- if (!env.has(varName)) {
10716
- return;
10991
+ for (const name of names) {
10992
+ if (env.has(name)) env.unset(name);
10717
10993
  }
10718
- env.unset(varName);
10719
10994
  }
10720
10995
  writeDescription(context2) {
10721
10996
  const { writer, translator: t } = context2;
@@ -10723,7 +10998,7 @@ var CliUnsetCommandProcessor = class {
10723
10998
  writer.writeln();
10724
10999
  writer.writeln(t.t("cli.common.usage", "Usage:"));
10725
11000
  writer.writeln(
10726
- ` ${writer.wrapInColor("unset VAR", cliCore.CliForegroundColor.Cyan)}`
11001
+ ` ${writer.wrapInColor("unset VAR [VAR...]", cliCore.CliForegroundColor.Cyan)}`
10727
11002
  );
10728
11003
  }
10729
11004
  };
@@ -10773,7 +11048,6 @@ var CliEnvCommandProcessor = class {
10773
11048
  );
10774
11049
  }
10775
11050
  };
10776
- var DEFAULT_FONT_SIZE = 20;
10777
11051
  var MIN_FONT_SIZE = 8;
10778
11052
  var MAX_FONT_SIZE = 40;
10779
11053
  var STEP = 2;
@@ -10786,7 +11060,7 @@ var CliFontSizeCommandProcessor = class {
10786
11060
  this.metadata = { icon: cliCore.CliIcon.Settings, module: "system" };
10787
11061
  this.stateConfiguration = {
10788
11062
  storeName: "font-size",
10789
- initialState: { fontSize: DEFAULT_FONT_SIZE }
11063
+ initialState: {}
10790
11064
  };
10791
11065
  this.processors = [
10792
11066
  {
@@ -10805,9 +11079,9 @@ var CliFontSizeCommandProcessor = class {
10805
11079
  },
10806
11080
  {
10807
11081
  command: "reset",
10808
- description: `Reset font size to default (${DEFAULT_FONT_SIZE}px)`,
11082
+ description: "Reset font size to the default",
10809
11083
  processCommand: async (_, context2) => {
10810
- await this.setFontSize(context2, DEFAULT_FONT_SIZE);
11084
+ await this.resetFontSize(context2);
10811
11085
  }
10812
11086
  },
10813
11087
  {
@@ -10831,6 +11105,7 @@ var CliFontSizeCommandProcessor = class {
10831
11105
  ];
10832
11106
  }
10833
11107
  async initialize(context2) {
11108
+ this.hostFontSize = context2.terminal?.options?.fontSize;
10834
11109
  const state = context2.state.getState();
10835
11110
  if (state?.fontSize != null && context2.terminal?.options) {
10836
11111
  context2.terminal.options.fontSize = state.fontSize;
@@ -10851,7 +11126,7 @@ var CliFontSizeCommandProcessor = class {
10851
11126
  ` ${context2.writer.wrapInColor("font-size set <n>", cliCore.CliForegroundColor.Cyan)} Set to exact size`
10852
11127
  );
10853
11128
  context2.writer.writeln(
10854
- ` ${context2.writer.wrapInColor("font-size reset", cliCore.CliForegroundColor.Cyan)} Reset to default (${DEFAULT_FONT_SIZE}px)`
11129
+ ` ${context2.writer.wrapInColor("font-size reset", cliCore.CliForegroundColor.Cyan)} Reset to default (${this.getDefault()}px)`
10855
11130
  );
10856
11131
  }
10857
11132
  getCurrent(context2) {
@@ -10860,7 +11135,19 @@ var CliFontSizeCommandProcessor = class {
10860
11135
  return fromTerminal;
10861
11136
  }
10862
11137
  const state = context2.state.getState();
10863
- return state?.fontSize ?? DEFAULT_FONT_SIZE;
11138
+ return state?.fontSize ?? this.getDefault();
11139
+ }
11140
+ getDefault() {
11141
+ return this.hostFontSize ?? defaultFontSize();
11142
+ }
11143
+ async resetFontSize(context2) {
11144
+ const size = this.getDefault();
11145
+ if (context2.terminal?.options) {
11146
+ context2.terminal.options.fontSize = size;
11147
+ }
11148
+ context2.state.updateState({ fontSize: void 0 });
11149
+ await context2.state.persist();
11150
+ context2.writer.writeSuccess(`Font size set to ${size}px`);
10864
11151
  }
10865
11152
  async setFontSize(context2, size) {
10866
11153
  const clamped = Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, size));
@@ -11811,6 +12098,7 @@ var CliTimerManager = class {
11811
12098
  this.managedTimers.clear();
11812
12099
  }
11813
12100
  };
12101
+ var MIN_READABLE_COLUMN = 8;
11814
12102
  var CliTerminalWriter = class {
11815
12103
  constructor(terminal) {
11816
12104
  this.terminal = terminal;
@@ -11942,6 +12230,24 @@ var CliTerminalWriter = class {
11942
12230
  const indent = " ".repeat(level);
11943
12231
  this.writeln(`${indent}${text}`);
11944
12232
  }
12233
+ /** One block per row, header names as keys; used when a table cannot fit the terminal. */
12234
+ writeRecords(headers, rows) {
12235
+ const keyWidth = Math.max(...headers.map((h) => cliCore.visibleLength(h)));
12236
+ rows.forEach((row, index) => {
12237
+ if (index > 0) this.write("\r\n");
12238
+ headers.forEach((header, i) => {
12239
+ const value = row[i] ?? "";
12240
+ if (!header.trim()) {
12241
+ if (value.trim()) this.write(`${value}\r
12242
+ `);
12243
+ return;
12244
+ }
12245
+ const key = header + " ".repeat(keyWidth - cliCore.visibleLength(header));
12246
+ this.write(`${this.wrapInColor(key, cliCore.CliForegroundColor.Yellow)} : ${value}\r
12247
+ `);
12248
+ });
12249
+ });
12250
+ }
11945
12251
  writeTable(headers, rows, options) {
11946
12252
  const cols = this.terminal.cols;
11947
12253
  const naturalWidths = headers.map(
@@ -11979,6 +12285,11 @@ var CliTerminalWriter = class {
11979
12285
  used++;
11980
12286
  }
11981
12287
  }
12288
+ const tooNarrow = colWidths.reduce((s, w) => s + w, 0) > Math.max(available, 0) || colWidths.some((w, i) => w < Math.min(naturalWidths[i], MIN_READABLE_COLUMN));
12289
+ if (tooNarrow) {
12290
+ this.writeRecords(headers, rows);
12291
+ return;
12292
+ }
11982
12293
  const truncateText = (text, maxWidth) => {
11983
12294
  const t = text ?? "";
11984
12295
  const vis = cliCore.visibleLength(t);
@@ -12416,6 +12727,10 @@ var InputModeBase = class {
12416
12727
  * Cut a line to the terminal width. Lines redrawn in place must stay on one row:
12417
12728
  * a wrapped line takes two rows and breaks the cursor-up arithmetic of the redraw.
12418
12729
  */
12730
+ /** A prompt label without a trailing colon, for modes that append their own ": ". */
12731
+ label(promptText) {
12732
+ return promptText.replace(/:\s*$/, "");
12733
+ }
12419
12734
  fitLine(text) {
12420
12735
  return cliCore.truncateToWidth(text, Math.max(1, this.host.getTerminalCols() - 1));
12421
12736
  }
@@ -12590,6 +12905,7 @@ var ConfirmInputMode = class extends InputModeBase {
12590
12905
  async handleInput(data) {
12591
12906
  if (data === "\r") {
12592
12907
  if (this.buffer === "") {
12908
+ this.host.terminal.write(this.defaultValue ? "y" : "n");
12593
12909
  this.resolveAndPop(this.defaultValue);
12594
12910
  } else {
12595
12911
  const lower = this.buffer.toLowerCase();
@@ -12771,7 +13087,7 @@ var SelectInputMode = class extends InputModeBase {
12771
13087
  const selectedOpt = this.options.find((o) => o.value === value);
12772
13088
  const label = selectedOpt ? selectedOpt.label : value;
12773
13089
  this.host.terminal.write("\x1B[2K\r");
12774
- this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText} \x1B[36m${label}\x1B[0m`);
13090
+ this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.label(this.promptText)}: \x1B[36m${label}\x1B[0m`);
12775
13091
  this.host.terminal.write("\x1B[?25h");
12776
13092
  super.resolveAndPop(value);
12777
13093
  }
@@ -12781,7 +13097,7 @@ var SelectInputMode = class extends InputModeBase {
12781
13097
  abort() {
12782
13098
  this.clearRenderedLines();
12783
13099
  this.host.terminal.write("\x1B[2K\r");
12784
- this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText} \x1B[2mcancelled\x1B[0m`);
13100
+ this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.label(this.promptText)}: \x1B[2mcancelled\x1B[0m`);
12785
13101
  this.host.terminal.write("\x1B[?25h");
12786
13102
  super.abort();
12787
13103
  }
@@ -12942,15 +13258,15 @@ var SelectInputMode = class extends InputModeBase {
12942
13258
  if (this.selectOptions?.searchable) {
12943
13259
  if (this.filter.length > 0) {
12944
13260
  this.host.terminal.write(
12945
- this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}: ${this.filter}`)
13261
+ this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: ${this.filter}`)
12946
13262
  );
12947
13263
  } else {
12948
13264
  this.host.terminal.write(
12949
- this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2mType to filter...\x1B[0m`)
13265
+ this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: \x1B[2mType to filter...\x1B[0m`)
12950
13266
  );
12951
13267
  }
12952
13268
  } else {
12953
- this.host.terminal.write(this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}:`));
13269
+ this.host.terminal.write(this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}:`));
12954
13270
  }
12955
13271
  }
12956
13272
  /**
@@ -13122,7 +13438,7 @@ var InlineSelectInputMode = class extends InputModeBase {
13122
13438
  const selectedOpt = this.selectableOptions.find((o) => o.value === value);
13123
13439
  const label = selectedOpt ? selectedOpt.label : value;
13124
13440
  this.host.terminal.write("\x1B[2K\r");
13125
- this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText}: \x1B[36m${label}\x1B[0m`);
13441
+ this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.label(this.promptText)}: \x1B[36m${label}\x1B[0m`);
13126
13442
  this.host.terminal.write("\x1B[?25h");
13127
13443
  super.resolveAndPop(value);
13128
13444
  }
@@ -13132,7 +13448,7 @@ var InlineSelectInputMode = class extends InputModeBase {
13132
13448
  abort() {
13133
13449
  this.clearExtraLines();
13134
13450
  this.host.terminal.write("\x1B[2K\r");
13135
- this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText}: \x1B[2mcancelled\x1B[0m`);
13451
+ this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.label(this.promptText)}: \x1B[2mcancelled\x1B[0m`);
13136
13452
  this.host.terminal.write("\x1B[?25h");
13137
13453
  super.abort();
13138
13454
  }
@@ -13172,13 +13488,13 @@ var InlineSelectInputMode = class extends InputModeBase {
13172
13488
  * Get the prompt prefix string (without ANSI escapes for width calculation).
13173
13489
  */
13174
13490
  getPromptPrefix() {
13175
- return `? ${this.promptText}: `;
13491
+ return `? ${this.label(this.promptText)}: `;
13176
13492
  }
13177
13493
  /**
13178
13494
  * Get the rendered prompt prefix (with ANSI color codes).
13179
13495
  */
13180
13496
  getRenderedPromptPrefix() {
13181
- return `\x1B[36m?\x1B[0m ${this.promptText}: `;
13497
+ return `\x1B[36m?\x1B[0m ${this.label(this.promptText)}: `;
13182
13498
  }
13183
13499
  /**
13184
13500
  * Calculate the width needed for a single option.
@@ -13375,7 +13691,7 @@ var MultiSelectInputMode = class extends InputModeBase {
13375
13691
  });
13376
13692
  const summary = labels.length > 0 ? labels.join(", ") : "none";
13377
13693
  this.host.terminal.write("\x1B[2K\r");
13378
- this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText} \x1B[36m${summary}\x1B[0m`);
13694
+ this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.label(this.promptText)}: \x1B[36m${summary}\x1B[0m`);
13379
13695
  this.host.terminal.write("\x1B[?25h");
13380
13696
  super.resolveAndPop(value);
13381
13697
  }
@@ -13385,7 +13701,7 @@ var MultiSelectInputMode = class extends InputModeBase {
13385
13701
  abort() {
13386
13702
  this.clearRenderedLines();
13387
13703
  this.host.terminal.write("\x1B[2K\r");
13388
- this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText} \x1B[2mcancelled\x1B[0m`);
13704
+ this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.label(this.promptText)}: \x1B[2mcancelled\x1B[0m`);
13389
13705
  this.host.terminal.write("\x1B[?25h");
13390
13706
  super.abort();
13391
13707
  }
@@ -13559,15 +13875,15 @@ var MultiSelectInputMode = class extends InputModeBase {
13559
13875
  if (this.selectOptions?.searchable) {
13560
13876
  if (this.filter.length > 0) {
13561
13877
  this.host.terminal.write(
13562
- this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}: ${this.filter}`)
13878
+ this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: ${this.filter}`)
13563
13879
  );
13564
13880
  } else {
13565
13881
  this.host.terminal.write(
13566
- this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2mType to filter...\x1B[0m`)
13882
+ this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: \x1B[2mType to filter...\x1B[0m`)
13567
13883
  );
13568
13884
  }
13569
13885
  } else {
13570
- this.host.terminal.write(this.fitLine(`\x1B[36m?\x1B[0m ${this.promptText}:`));
13886
+ this.host.terminal.write(this.fitLine(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}:`));
13571
13887
  }
13572
13888
  }
13573
13889
  /**
@@ -13902,13 +14218,13 @@ var FileInputMode = class extends InputModeBase {
13902
14218
  this.waiting = false;
13903
14219
  if (!results || this.isResolved) {
13904
14220
  this.host.terminal.write("\x1B[2K\r");
13905
- this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2m(cancelled)\x1B[0m\r
14221
+ this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: \x1B[2m(cancelled)\x1B[0m\r
13906
14222
  `);
13907
14223
  if (!this.isResolved) this.abort();
13908
14224
  return;
13909
14225
  }
13910
14226
  this.host.terminal.write("\x1B[2K\r");
13911
- this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}:\r
14227
+ this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}:\r
13912
14228
  `);
13913
14229
  let totalSize = 0;
13914
14230
  for (const file of results) {
@@ -13926,7 +14242,7 @@ var FileInputMode = class extends InputModeBase {
13926
14242
  this.waiting = false;
13927
14243
  if (this.isResolved) return;
13928
14244
  this.host.terminal.write("\x1B[2K\r");
13929
- this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[31m(error)\x1B[0m\r
14245
+ this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.label(this.promptText)}: \x1B[31m(error)\x1B[0m\r
13930
14246
  `);
13931
14247
  this.abort();
13932
14248
  }
@@ -15396,7 +15712,7 @@ function substituteVars(input, variables) {
15396
15712
  );
15397
15713
  return result;
15398
15714
  }
15399
- function expandEnvironmentVars(command2, context2) {
15715
+ function expandEnvironmentVars(command2, context2, lastExitCode) {
15400
15716
  const env = context2.services.get(ICliEnvironment_TOKEN);
15401
15717
  if (!env) {
15402
15718
  return command2;
@@ -15404,6 +15720,9 @@ function expandEnvironmentVars(command2, context2) {
15404
15720
  const parts = command2.split("'");
15405
15721
  for (let i = 0; i < parts.length; i++) {
15406
15722
  if (i % 2 === 0) {
15723
+ if (lastExitCode !== void 0) {
15724
+ parts[i] = parts[i].replace(/\$\?/g, String(lastExitCode));
15725
+ }
15407
15726
  parts[i] = parts[i].replace(
15408
15727
  /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
15409
15728
  (_, name) => env.get(name) ?? ""
@@ -15412,17 +15731,30 @@ function expandEnvironmentVars(command2, context2) {
15412
15731
  /\$([A-Za-z_][A-Za-z0-9_]*)/g,
15413
15732
  (_, name) => env.get(name) ?? ""
15414
15733
  );
15734
+ const home = env.get("HOME");
15735
+ if (home) {
15736
+ parts[i] = parts[i].replace(
15737
+ /(^|\s)~(?=\/|\s|$)/g,
15738
+ (match, before, offset) => i > 0 && offset === 0 && !before ? match : `${before}${home}`
15739
+ );
15740
+ }
15415
15741
  }
15416
15742
  }
15417
15743
  return parts.join("'");
15418
15744
  }
15419
15745
 
15420
15746
  // src/lib/executor/cli-command-executor.ts
15747
+ function toExitStatus(code) {
15748
+ if (code === void 0) return 0;
15749
+ return code < 0 ? 1 : code;
15750
+ }
15421
15751
  var CliCommandExecutor = class {
15422
15752
  constructor(registry) {
15423
15753
  this.registry = registry;
15424
15754
  this.commandParser = new CommandParser();
15425
15755
  this.globalParameters = [];
15756
+ /** Exit status of the last command, expanded as `$?` (0 = success). */
15757
+ this.lastExitCode = 0;
15426
15758
  this.registerGlobalParameter(versionGlobalParameter);
15427
15759
  this.registerGlobalParameter(helpGlobalParameter);
15428
15760
  this.registerGlobalParameter(contextGlobalParameter);
@@ -15526,6 +15858,7 @@ var CliCommandExecutor = class {
15526
15858
  rootContext
15527
15859
  );
15528
15860
  lastExitSuccess = context2.process.exitCode === void 0 || context2.process.exitCode === 0;
15861
+ this.lastExitCode = toExitStatus(context2.process.exitCode);
15529
15862
  rootContext.lastCommandResult = {
15530
15863
  command: part.value,
15531
15864
  success: lastExitSuccess
@@ -15533,6 +15866,7 @@ var CliCommandExecutor = class {
15533
15866
  pipelineData = context2.process.data;
15534
15867
  } catch (e) {
15535
15868
  lastExitSuccess = false;
15869
+ this.lastExitCode = 1;
15536
15870
  rootContext.lastCommandResult = {
15537
15871
  command: part.value,
15538
15872
  success: false
@@ -15548,7 +15882,7 @@ var CliCommandExecutor = class {
15548
15882
  }
15549
15883
  async executeSingleCommand(command2, data, context2) {
15550
15884
  const process = context2.process;
15551
- command2 = expandEnvironmentVars(command2, context2);
15885
+ command2 = expandEnvironmentVars(command2, context2, this.lastExitCode);
15552
15886
  const assignMatch = command2.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
15553
15887
  if (assignMatch) {
15554
15888
  const env = context2.services.get(ICliEnvironment_TOKEN);
@@ -16604,30 +16938,6 @@ var CliDefaultPingServerService = class {
16604
16938
  return new Promise((resolve) => setTimeout(resolve, 2e3));
16605
16939
  }
16606
16940
  };
16607
- var CliDragDropService = class {
16608
- constructor(_container) {
16609
- this._container = _container;
16610
- this._subject = new rxjs.Subject();
16611
- this._dragOver = (e) => e.preventDefault();
16612
- this._drop = (e) => {
16613
- e.preventDefault();
16614
- const files = Array.from(e.dataTransfer?.files ?? []);
16615
- if (files.length > 0) {
16616
- this._subject.next(files);
16617
- }
16618
- };
16619
- this._container.addEventListener("dragover", this._dragOver);
16620
- this._container.addEventListener("drop", this._drop);
16621
- }
16622
- get onFileDrop() {
16623
- return this._subject.asObservable();
16624
- }
16625
- destroy() {
16626
- this._container.removeEventListener("dragover", this._dragOver);
16627
- this._container.removeEventListener("drop", this._drop);
16628
- this._subject.complete();
16629
- }
16630
- };
16631
16941
 
16632
16942
  // src/lib/services/cli-permission-registry.ts
16633
16943
  var CliPermissionRegistry = class {
@@ -16652,62 +16962,6 @@ var CliPermissionRegistry = class {
16652
16962
  }
16653
16963
  };
16654
16964
 
16655
- // src/lib/addons/overlay.ts
16656
- var OverlayAddon = class {
16657
- constructor() {
16658
- this.overlayNode = document.createElement("div");
16659
- this.overlayNode.style.cssText = `border-radius: 15px;
16660
- font-size: xx-large;
16661
- opacity: 0.75;
16662
- padding: 0.2em 0.5em 0.2em 0.5em;
16663
- position: absolute;
16664
- -webkit-user-select: none;
16665
- -webkit-transition: opacity 180ms ease-in;
16666
- -moz-user-select: none;
16667
- -moz-transition: opacity 180ms ease-in;`;
16668
- this.overlayNode.addEventListener(
16669
- "mousedown",
16670
- (e) => {
16671
- e.preventDefault();
16672
- e.stopPropagation();
16673
- },
16674
- true
16675
- );
16676
- }
16677
- activate(terminal) {
16678
- this.terminal = terminal;
16679
- }
16680
- dispose() {
16681
- }
16682
- showOverlay(msg, timeout) {
16683
- const { terminal, overlayNode } = this;
16684
- if (!terminal.element) return;
16685
- overlayNode.style.color = "#101010";
16686
- overlayNode.style.backgroundColor = "#f0f0f0";
16687
- overlayNode.textContent = msg;
16688
- overlayNode.style.opacity = "0.75";
16689
- if (!overlayNode.parentNode) {
16690
- terminal.element.appendChild(overlayNode);
16691
- }
16692
- const divSize = terminal.element.getBoundingClientRect();
16693
- const overlaySize = overlayNode.getBoundingClientRect();
16694
- overlayNode.style.top = (divSize.height - overlaySize.height) / 2 + "px";
16695
- overlayNode.style.left = (divSize.width - overlaySize.width) / 2 + "px";
16696
- if (this.overlayTimeout) clearTimeout(this.overlayTimeout);
16697
- if (!timeout) return;
16698
- this.overlayTimeout = window.setTimeout(() => {
16699
- overlayNode.style.opacity = "0";
16700
- this.overlayTimeout = window.setTimeout(() => {
16701
- if (overlayNode.parentNode) {
16702
- overlayNode.parentNode.removeChild(overlayNode);
16703
- }
16704
- this.overlayTimeout = void 0;
16705
- overlayNode.style.opacity = "0.75";
16706
- }, 200);
16707
- }, timeout || 1500);
16708
- }
16709
- };
16710
-
16711
16965
  // src/lib/storage/cli-key-value-store.ts
16712
16966
  var CliKeyValueStore = class {
16713
16967
  constructor() {
@@ -17882,61 +18136,6 @@ function createServerModule() {
17882
18136
  services: []
17883
18137
  };
17884
18138
  }
17885
- function getTerminalOptions(userOptions) {
17886
- return {
17887
- cursorBlink: true,
17888
- allowProposedApi: true,
17889
- fontSize: 20,
17890
- theme: cliCore.DefaultThemes.default,
17891
- convertEol: true,
17892
- ...userOptions ?? {}
17893
- };
17894
- }
17895
- function initializeTerminal(container, opts) {
17896
- const terminal = new xterm.Terminal(opts);
17897
- const fitAddon = new addonFit.FitAddon();
17898
- terminal.loadAddon(fitAddon);
17899
- terminal.loadAddon(new addonWebLinks.WebLinksAddon());
17900
- terminal.loadAddon(new OverlayAddon());
17901
- terminal.loadAddon(new addonUnicode11.Unicode11Addon());
17902
- const serializeAddon = new addonSerialize.SerializeAddon();
17903
- terminal.loadAddon(serializeAddon);
17904
- terminal.open(container);
17905
- fitAddon.fit();
17906
- requestAnimationFrame(() => fitAddon.fit());
17907
- if (document.fonts?.ready) {
17908
- document.fonts.ready.then(() => {
17909
- requestAnimationFrame(() => fitAddon.fit());
17910
- });
17911
- }
17912
- container.classList.add("terminal-container");
17913
- container.style.background = opts.theme?.background ?? "#000";
17914
- const wheelListener = (e) => e.preventDefault();
17915
- container.addEventListener("wheel", wheelListener, { passive: false });
17916
- terminal.focus();
17917
- const dragDropService = new CliDragDropService(container);
17918
- return { terminal, fitAddon, serializeAddon, dragDropService, wheelListener };
17919
- }
17920
- var LAYOUT_TIMEOUT_MS = 1e4;
17921
- function waitForLayout(container) {
17922
- return new Promise((resolve, reject) => {
17923
- const deadline = Date.now() + LAYOUT_TIMEOUT_MS;
17924
- const check = () => {
17925
- if (container.offsetWidth > 0 && container.offsetHeight > 0) {
17926
- resolve();
17927
- return;
17928
- }
17929
- if (Date.now() >= deadline) {
17930
- reject(new Error(
17931
- `CliEngine.start(): container has zero dimensions after ${LAYOUT_TIMEOUT_MS}ms. Ensure the container element is visible and laid out before calling start().`
17932
- ));
17933
- return;
17934
- }
17935
- requestAnimationFrame(check);
17936
- };
17937
- check();
17938
- });
17939
- }
17940
18139
 
17941
18140
  // src/lib/editor/syntax/registry.ts
17942
18141
  var SyntaxHighlighterRegistry = class {
@@ -18328,6 +18527,14 @@ var CliEngine = class {
18328
18527
  { ...this.options ?? {}, terminalOptions: terminalOpts }
18329
18528
  );
18330
18529
  this.executionContext.initializeTerminalListeners();
18530
+ const environment = services.get(ICliEnvironment_TOKEN);
18531
+ if (environment instanceof CliEnvironment) {
18532
+ const ctx = this.executionContext;
18533
+ environment.bind("USER", () => ctx.userSession?.user.name);
18534
+ environment.bind("HOME", () => ctx.userSession?.user.homeDir);
18535
+ environment.bind("PWD", () => ctx.promptPathProvider?.());
18536
+ environment.setProfileResolver(() => ctx.userSession?.user.name);
18537
+ }
18331
18538
  this.executionContext.backgroundServices.setProcessRegistry(processRegistry);
18332
18539
  services.set([{
18333
18540
  provide: CliBackgroundServiceRegistry_TOKEN,
@@ -19033,6 +19240,7 @@ exports.builtinProcessors = builtinProcessors;
19033
19240
  exports.computeDiff = computeDiff;
19034
19241
  exports.contextGlobalParameter = contextGlobalParameter;
19035
19242
  exports.createServerModule = createServerModule;
19243
+ exports.defaultFontSize = defaultFontSize;
19036
19244
  exports.describeShellClose = describeShellClose;
19037
19245
  exports.executeOnServer = executeOnServer;
19038
19246
  exports.expandEnvironmentVars = expandEnvironmentVars;
@@ -19050,6 +19258,7 @@ exports.initializeTerminal = initializeTerminal;
19050
19258
  exports.isWasmAccelerated = isWasmAccelerated;
19051
19259
  exports.miscProcessors = miscProcessors;
19052
19260
  exports.openLink = openLink;
19261
+ exports.parseAssignments = parseAssignments;
19053
19262
  exports.reconcileArgs = reconcileArgs;
19054
19263
  exports.renderServerResponse = renderServerResponse;
19055
19264
  exports.renderSingleOutput = renderSingleOutput;