@qodalis/cli 2.0.0-beta.8 → 2.0.0-beta.9

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
@@ -411,7 +411,7 @@ var CliBackgroundServiceRegistry_TOKEN = "cli-background-service-registry";
411
411
  var CliPackageManagerService_TOKEN = "cli-package-manager-service";
412
412
 
413
413
  // src/lib/version.ts
414
- var LIBRARY_VERSION = "2.0.0-beta.8";
414
+ var LIBRARY_VERSION = "2.0.0-beta.9";
415
415
  var API_VERSION = 2;
416
416
  var CliAliasCommandProcessor = class {
417
417
  constructor() {
@@ -1337,6 +1337,13 @@ var CliEvalCommandProcessor = class {
1337
1337
  };
1338
1338
  }
1339
1339
  async processCommand(command, context) {
1340
+ if (!context.options?.allowEval) {
1341
+ context.writer.writeError(
1342
+ "eval is disabled by default for security. Set allowEval: true in CliOptions to enable it."
1343
+ );
1344
+ context.process.exit(1, { silent: true });
1345
+ return;
1346
+ }
1340
1347
  try {
1341
1348
  const output = eval(command.value ?? "");
1342
1349
  if (Array.isArray(output)) {
@@ -2698,6 +2705,78 @@ var SYSTEM_OPTIONS = [
2698
2705
  options: [
2699
2706
  { label: "English", value: "en" }
2700
2707
  ]
2708
+ },
2709
+ {
2710
+ key: "cursorBlink",
2711
+ label: "Cursor Blink",
2712
+ description: "Whether the terminal cursor blinks",
2713
+ type: "boolean",
2714
+ defaultValue: true
2715
+ },
2716
+ {
2717
+ key: "cursorStyle",
2718
+ label: "Cursor Style",
2719
+ description: "Terminal cursor shape",
2720
+ type: "select",
2721
+ defaultValue: "block",
2722
+ options: [
2723
+ { label: "Block", value: "block" },
2724
+ { label: "Underline", value: "underline" },
2725
+ { label: "Bar", value: "bar" }
2726
+ ]
2727
+ },
2728
+ {
2729
+ key: "scrollback",
2730
+ label: "Scrollback",
2731
+ description: "Number of scrollback lines to retain",
2732
+ type: "number",
2733
+ defaultValue: 1e3,
2734
+ validator: (value) => {
2735
+ if (value < 0 || value > 1e5) {
2736
+ return { valid: false, message: "Scrollback must be between 0 and 100,000" };
2737
+ }
2738
+ return { valid: true };
2739
+ }
2740
+ },
2741
+ {
2742
+ key: "fontSize",
2743
+ label: "Font Size",
2744
+ description: "Terminal font size in pixels",
2745
+ type: "number",
2746
+ defaultValue: 20,
2747
+ validator: (value) => {
2748
+ if (value < 8 || value > 40) {
2749
+ return { valid: false, message: "Font size must be between 8 and 40" };
2750
+ }
2751
+ return { valid: true };
2752
+ }
2753
+ },
2754
+ {
2755
+ key: "fontFamily",
2756
+ label: "Font Family",
2757
+ description: "Terminal font family",
2758
+ type: "string",
2759
+ defaultValue: "monospace"
2760
+ },
2761
+ {
2762
+ key: "smoothScrollDuration",
2763
+ label: "Smooth Scroll",
2764
+ description: "Smooth scroll duration in milliseconds (0 to disable)",
2765
+ type: "number",
2766
+ defaultValue: 0,
2767
+ validator: (value) => {
2768
+ if (value < 0 || value > 1e3) {
2769
+ return { valid: false, message: "Smooth scroll duration must be between 0 and 1,000 ms" };
2770
+ }
2771
+ return { valid: true };
2772
+ }
2773
+ },
2774
+ {
2775
+ key: "greeting",
2776
+ label: "Greeting",
2777
+ description: "Show animated time-based greeting on startup",
2778
+ type: "boolean",
2779
+ defaultValue: true
2701
2780
  }
2702
2781
  ];
2703
2782
  var LOG_LEVEL_MAP = {
@@ -2996,6 +3075,26 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
2996
3075
  } catch {
2997
3076
  }
2998
3077
  }
3078
+ if (context2.terminal?.options) {
3079
+ if (settings["cursorBlink"] !== void 0) {
3080
+ context2.terminal.options.cursorBlink = settings["cursorBlink"];
3081
+ }
3082
+ if (settings["cursorStyle"]) {
3083
+ context2.terminal.options.cursorStyle = settings["cursorStyle"];
3084
+ }
3085
+ if (settings["scrollback"] !== void 0) {
3086
+ context2.terminal.options.scrollback = settings["scrollback"];
3087
+ }
3088
+ if (settings["fontSize"] !== void 0) {
3089
+ context2.terminal.options.fontSize = settings["fontSize"];
3090
+ }
3091
+ if (settings["fontFamily"]) {
3092
+ context2.terminal.options.fontFamily = settings["fontFamily"];
3093
+ }
3094
+ if (settings["smoothScrollDuration"] !== void 0) {
3095
+ context2.terminal.options.smoothScrollDuration = settings["smoothScrollDuration"];
3096
+ }
3097
+ }
2999
3098
  }
3000
3099
  // ── Child processor builders ────────────────────────────────────────
3001
3100
  buildListProcessor() {
@@ -3097,6 +3196,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3097
3196
  command: "set",
3098
3197
  description: "Set a configuration value (e.g. configure set system.logLevel Debug)",
3099
3198
  valueRequired: true,
3199
+ acceptsRawInput: true,
3100
3200
  processCommand: async (command2, context2) => {
3101
3201
  const raw = command2.value;
3102
3202
  const spaceIndex = raw.indexOf(" ");
@@ -3240,12 +3340,15 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3240
3340
  }
3241
3341
  return { value: num };
3242
3342
  }
3243
- case "boolean":
3343
+ case "boolean": {
3344
+ const lower = rawValue.toLowerCase();
3345
+ if (["true", "1", "yes"].includes(lower)) return { value: true };
3346
+ if (["false", "0", "no"].includes(lower)) return { value: false };
3244
3347
  return {
3245
- value: ["true", "1", "yes"].includes(
3246
- rawValue.toLowerCase()
3247
- )
3348
+ value: null,
3349
+ error: translator.t("cli.configure.invalid_boolean", 'Invalid boolean value: "{value}". Use true or false.', { value: rawValue })
3248
3350
  };
3351
+ }
3249
3352
  case "select":
3250
3353
  if (option.options) {
3251
3354
  const match = option.options.find(
@@ -4430,6 +4533,13 @@ var ScriptLoaderService = class {
4430
4533
  xhr.send();
4431
4534
  });
4432
4535
  }
4536
+ /**
4537
+ * Inject an inline script into the page.
4538
+ *
4539
+ * **Security:** This runs `code` in the host page context with full
4540
+ * DOM access. Only call with trusted content (e.g. verified UMD bundles
4541
+ * loaded via the package manager). Never pass user-supplied strings.
4542
+ */
4433
4543
  injectBodyScript(code) {
4434
4544
  const script = document.createElement("script");
4435
4545
  script.text = code;
@@ -4497,6 +4607,10 @@ var CliPackageManagerService = class {
4497
4607
  constructor() {
4498
4608
  this.storageKey = "cli-packages";
4499
4609
  this.QODALIS_COMMAND_PREFIX = "@qodalis/cli-";
4610
+ /** In-memory cache; `null` means "not yet loaded from storage". */
4611
+ this.cache = null;
4612
+ /** Whether the one-time localStorage migration has been checked. */
4613
+ this.migrationDone = false;
4500
4614
  }
4501
4615
  /**
4502
4616
  * Set the key-value store instance for persistence.
@@ -4505,17 +4619,27 @@ var CliPackageManagerService = class {
4505
4619
  this.store = store;
4506
4620
  }
4507
4621
  /**
4508
- * Retrieves the list of packages
4509
- * @returns {Package[]} Array of packages
4622
+ * Retrieves the list of packages.
4623
+ * Uses an in-memory cache after the first load.
4510
4624
  */
4511
4625
  async getPackages() {
4512
- const oldPackages = localStorage.getItem("cliPackages");
4513
- if (oldPackages) {
4514
- localStorage.removeItem("cliPackages");
4515
- await this.store.set(this.storageKey, JSON.parse(oldPackages));
4626
+ if (this.cache !== null) {
4627
+ return this.cache;
4628
+ }
4629
+ if (!this.migrationDone) {
4630
+ this.migrationDone = true;
4631
+ try {
4632
+ const oldPackages = localStorage.getItem("cliPackages");
4633
+ if (oldPackages) {
4634
+ localStorage.removeItem("cliPackages");
4635
+ await this.store.set(this.storageKey, JSON.parse(oldPackages));
4636
+ }
4637
+ } catch {
4638
+ }
4516
4639
  }
4517
4640
  const packages = await this.store.get(this.storageKey);
4518
- return packages ? packages : [];
4641
+ this.cache = packages ?? [];
4642
+ return this.cache;
4519
4643
  }
4520
4644
  /**
4521
4645
  * Retrieves a package by name from the list.
@@ -4559,7 +4683,7 @@ var CliPackageManagerService = class {
4559
4683
  if (packages.length === updatedPackages.length) {
4560
4684
  throw new Error(`Package with name "${packageName}" not found.`);
4561
4685
  }
4562
- this.savePackages(updatedPackages);
4686
+ await this.savePackages(updatedPackages);
4563
4687
  return packageToRemove;
4564
4688
  }
4565
4689
  /**
@@ -4575,6 +4699,7 @@ var CliPackageManagerService = class {
4575
4699
  await this.savePackages(packages);
4576
4700
  }
4577
4701
  async savePackages(packages) {
4702
+ this.cache = packages;
4578
4703
  await this.store.set(this.storageKey, packages);
4579
4704
  }
4580
4705
  };
@@ -5651,9 +5776,10 @@ var CliPackagesCommandProcessor = class {
5651
5776
 
5652
5777
  // src/lib/server/cli-server-connection.ts
5653
5778
  var CliServerConnection = class {
5654
- constructor(_config, _backgroundServices) {
5779
+ constructor(_config, _backgroundServices, _logger) {
5655
5780
  this._config = _config;
5656
5781
  this._backgroundServices = _backgroundServices;
5782
+ this._logger = _logger;
5657
5783
  this._connected = false;
5658
5784
  this._commands = [];
5659
5785
  this._capabilities = null;
@@ -5677,7 +5803,8 @@ var CliServerConnection = class {
5677
5803
  this._connected = true;
5678
5804
  this._capabilities = await this.fetchCapabilities();
5679
5805
  this.connectEventSocket();
5680
- } catch {
5806
+ } catch (e) {
5807
+ this._logger?.warn(`Failed to connect to server "${this._config.name}": ${e}`);
5681
5808
  this._connected = false;
5682
5809
  this._commands = [];
5683
5810
  this._capabilities = null;
@@ -5705,7 +5832,8 @@ var CliServerConnection = class {
5705
5832
  const response = await this.httpFetch(url);
5706
5833
  if (!response.ok) return null;
5707
5834
  return response.json();
5708
- } catch {
5835
+ } catch (e) {
5836
+ this._logger?.debug(`Failed to fetch capabilities for "${this._config.name}": ${e}`);
5709
5837
  return null;
5710
5838
  }
5711
5839
  }
@@ -5735,7 +5863,8 @@ var CliServerConnection = class {
5735
5863
  const url = `${this.normalizeUrl(this._config.url)}/api/v1/qcli/version`;
5736
5864
  const response = await this.httpFetch(url);
5737
5865
  return response.ok;
5738
- } catch {
5866
+ } catch (e) {
5867
+ this._logger?.debug(`Ping failed for "${this._config.name}": ${e}`);
5739
5868
  return false;
5740
5869
  }
5741
5870
  }
@@ -5783,10 +5912,12 @@ var CliServerConnection = class {
5783
5912
  this.closeEventSocketDirect();
5784
5913
  }
5785
5914
  });
5786
- this._backgroundServices.start(serviceName).catch(() => {
5915
+ this._backgroundServices.start(serviceName).catch((e) => {
5916
+ this._logger?.debug(`Background event service failed for "${this._config.name}", falling back to direct: ${e}`);
5787
5917
  this.connectEventSocketDirect();
5788
5918
  });
5789
- } catch {
5919
+ } catch (e) {
5920
+ this._logger?.debug(`Failed to register event service for "${this._config.name}", falling back to direct: ${e}`);
5790
5921
  this.connectEventSocketDirect();
5791
5922
  }
5792
5923
  }
@@ -5811,7 +5942,8 @@ var CliServerConnection = class {
5811
5942
  };
5812
5943
  this._eventSocket.onerror = () => {
5813
5944
  };
5814
- } catch {
5945
+ } catch (e) {
5946
+ this._logger?.debug(`WebSocket not available for "${this._config.name}": ${e}`);
5815
5947
  }
5816
5948
  }
5817
5949
  handleServerDisconnect() {
@@ -6098,12 +6230,13 @@ var CliServerManager = class {
6098
6230
  this.unregisterBareAliases();
6099
6231
  this.registerBareAliases();
6100
6232
  }
6101
- async connectAll(servers, logger, backgroundServices) {
6233
+ async connectAll(servers, logger, backgroundServices, cliLogger) {
6102
6234
  this._logger = logger;
6235
+ this._cliLogger = cliLogger;
6103
6236
  this._backgroundServices = backgroundServices;
6104
6237
  for (const config of servers) {
6105
6238
  if (config.enabled === false) continue;
6106
- const connection = new CliServerConnection(config, backgroundServices);
6239
+ const connection = new CliServerConnection(config, backgroundServices, cliLogger);
6107
6240
  this.connections.set(config.name, connection);
6108
6241
  connection.onDisconnect = () => {
6109
6242
  this.handleDisconnect(config.name);
@@ -10547,22 +10680,31 @@ var CliCommandExecutionContext = class {
10547
10680
  var ServiceLogBuffer = class {
10548
10681
  constructor(maxSize = 1e3) {
10549
10682
  this.maxSize = maxSize;
10550
- this.entries = [];
10683
+ this.head = 0;
10684
+ this.size = 0;
10685
+ this.buffer = new Array(maxSize);
10551
10686
  }
10552
10687
  add(message, level = "info") {
10553
- this.entries.push({ timestamp: /* @__PURE__ */ new Date(), level, message });
10554
- if (this.entries.length > this.maxSize) {
10555
- this.entries.shift();
10688
+ const entry = { timestamp: /* @__PURE__ */ new Date(), level, message };
10689
+ this.buffer[this.head] = entry;
10690
+ this.head = (this.head + 1) % this.maxSize;
10691
+ if (this.size < this.maxSize) {
10692
+ this.size++;
10556
10693
  }
10557
10694
  }
10558
10695
  get(limit) {
10559
- if (limit === void 0) {
10560
- return [...this.entries];
10696
+ const count = limit !== void 0 ? Math.min(limit, this.size) : this.size;
10697
+ const result = new Array(count);
10698
+ const start = (this.head - count + this.maxSize) % this.maxSize;
10699
+ for (let i = 0; i < count; i++) {
10700
+ result[i] = this.buffer[(start + i) % this.maxSize];
10561
10701
  }
10562
- return this.entries.slice(-limit);
10702
+ return result;
10563
10703
  }
10564
10704
  clear() {
10565
- this.entries = [];
10705
+ this.buffer = new Array(this.maxSize);
10706
+ this.head = 0;
10707
+ this.size = 0;
10566
10708
  }
10567
10709
  };
10568
10710
 
@@ -10708,18 +10850,19 @@ var CliWorkerServiceRunner = class {
10708
10850
  }
10709
10851
  async stop() {
10710
10852
  if (!this.worker) return;
10853
+ const worker = this.worker;
10711
10854
  const stopMsg = { type: "stop" };
10712
- this.worker.postMessage(stopMsg);
10855
+ worker.postMessage(stopMsg);
10713
10856
  await Promise.race([
10714
10857
  new Promise((resolve) => {
10715
- const originalOnMessage = this.worker.onmessage;
10716
- this.worker.onmessage = (ev) => {
10717
- if (ev.data.type === "status" && ev.data.status === "stopped") {
10718
- resolve();
10719
- }
10858
+ const originalOnMessage = worker.onmessage;
10859
+ worker.onmessage = (ev) => {
10720
10860
  if (originalOnMessage) {
10721
10861
  originalOnMessage(ev);
10722
10862
  }
10863
+ if (ev.data.type === "status" && ev.data.status === "stopped") {
10864
+ resolve();
10865
+ }
10723
10866
  };
10724
10867
  }),
10725
10868
  new Promise((resolve) => setTimeout(resolve, 5e3))
@@ -10727,8 +10870,12 @@ var CliWorkerServiceRunner = class {
10727
10870
  this.terminate();
10728
10871
  }
10729
10872
  terminate() {
10730
- this.worker?.terminate();
10731
- this.worker = null;
10873
+ if (this.worker) {
10874
+ this.worker.onmessage = null;
10875
+ this.worker.onerror = null;
10876
+ this.worker.terminate();
10877
+ this.worker = null;
10878
+ }
10732
10879
  }
10733
10880
  };
10734
10881
 
@@ -10856,7 +11003,7 @@ var CliBackgroundServiceRegistry = class {
10856
11003
  }
10857
11004
  await this.start(name);
10858
11005
  }
10859
- startJob(name, task, _options) {
11006
+ async startJob(name, task, _options) {
10860
11007
  const jobService = {
10861
11008
  name,
10862
11009
  type: "job",
@@ -11060,10 +11207,6 @@ var CliBackgroundServiceRegistry = class {
11060
11207
  }
11061
11208
  }
11062
11209
  };
11063
- var ANSI_RE = /\x1b\[[0-9;]*m/g;
11064
- function visibleLength(text) {
11065
- return text.replace(ANSI_RE, "").length;
11066
- }
11067
11210
  var CliTerminalWriter = class {
11068
11211
  constructor(terminal) {
11069
11212
  this.terminal = terminal;
@@ -11162,15 +11305,48 @@ var CliTerminalWriter = class {
11162
11305
  this.writeln(row.map((item) => item.padEnd(colWidth)).join(""));
11163
11306
  }
11164
11307
  }
11308
+ writeLink(text, url) {
11309
+ this.writeln(`\x1B]8;;${url}\x07${text}\x1B]8;;\x07`);
11310
+ }
11311
+ writeBox(content, options) {
11312
+ const lines = Array.isArray(content) ? content : [content];
11313
+ const pad2 = options?.padding ?? 1;
11314
+ const color = options?.borderColor;
11315
+ const wrap = (s) => color ? this.wrapInColor(s, color) : s;
11316
+ const maxContentWidth = Math.max(
11317
+ ...lines.map((l) => cliCore.visibleLength(l)),
11318
+ options?.title ? cliCore.visibleLength(options.title) + 2 : 0
11319
+ );
11320
+ const innerWidth = maxContentWidth + pad2 * 2;
11321
+ if (options?.title) {
11322
+ const title = ` ${options.title} `;
11323
+ const remaining = innerWidth - cliCore.visibleLength(title);
11324
+ const left = Math.floor(remaining / 2);
11325
+ const right = remaining - left;
11326
+ this.writeln(wrap("\u250C" + "\u2500".repeat(left) + title + "\u2500".repeat(right) + "\u2510"));
11327
+ } else {
11328
+ this.writeln(wrap("\u250C" + "\u2500".repeat(innerWidth) + "\u2510"));
11329
+ }
11330
+ const padStr = " ".repeat(pad2);
11331
+ for (const line of lines) {
11332
+ const rightPad = innerWidth - pad2 * 2 - cliCore.visibleLength(line);
11333
+ this.writeln(wrap("\u2502") + padStr + line + " ".repeat(Math.max(0, rightPad)) + padStr + wrap("\u2502"));
11334
+ }
11335
+ this.writeln(wrap("\u2514" + "\u2500".repeat(innerWidth) + "\u2518"));
11336
+ }
11337
+ writeIndented(text, level = 1) {
11338
+ const indent = " ".repeat(level);
11339
+ this.writeln(`${indent}${text}`);
11340
+ }
11165
11341
  writeTable(headers, rows) {
11166
11342
  const colWidths = headers.map(
11167
11343
  (header, colIndex) => Math.max(
11168
- visibleLength(header),
11169
- ...rows.map((row) => visibleLength(row[colIndex] ?? ""))
11344
+ cliCore.visibleLength(header),
11345
+ ...rows.map((row) => cliCore.visibleLength(row[colIndex] ?? ""))
11170
11346
  )
11171
11347
  );
11172
11348
  const padText = (text, width) => {
11173
- const visible = visibleLength(text ?? "");
11349
+ const visible = cliCore.visibleLength(text ?? "");
11174
11350
  const padding = Math.max(0, width - visible);
11175
11351
  return (text ?? "") + " ".repeat(padding);
11176
11352
  };
@@ -13936,8 +14112,8 @@ var CliExecutionContext = class {
13936
14112
  };
13937
14113
  this.services = deps.services;
13938
14114
  this.translator = deps.translator;
13939
- const stateStoreManager = deps.stateStoreManager;
13940
- this.state = stateStoreManager.getStateStore("shared");
14115
+ this.stateStoreManager = deps.stateStoreManager;
14116
+ this.state = this.stateStoreManager.getStateStore("shared");
13941
14117
  this.options = cliOptions;
13942
14118
  this.writer = new CliTerminalWriter(terminal);
13943
14119
  const spinner = new CliTerminalSpinner(terminal);
@@ -14007,7 +14183,7 @@ var CliExecutionContext = class {
14007
14183
  });
14008
14184
  const termEl = this.terminal.element;
14009
14185
  if (termEl) {
14010
- window.addEventListener("keydown", (event) => {
14186
+ this.windowKeydownListener = (event) => {
14011
14187
  if (!termEl.contains(document.activeElement)) return;
14012
14188
  if (event.ctrlKey && !event.altKey && !event.metaKey) {
14013
14189
  const key = event.key.toLowerCase();
@@ -14015,7 +14191,8 @@ var CliExecutionContext = class {
14015
14191
  event.preventDefault();
14016
14192
  }
14017
14193
  }
14018
- }, true);
14194
+ };
14195
+ window.addEventListener("keydown", this.windowKeydownListener, true);
14019
14196
  }
14020
14197
  }
14021
14198
  setCurrentLine(line) {
@@ -14216,6 +14393,11 @@ var CliExecutionContext = class {
14216
14393
  this.clearAllManagedTimers();
14217
14394
  this.resizeDisposable?.dispose();
14218
14395
  this.resizeDisposable = null;
14396
+ if (this.windowKeydownListener) {
14397
+ window.removeEventListener("keydown", this.windowKeydownListener, true);
14398
+ this.windowKeydownListener = void 0;
14399
+ }
14400
+ this.stateStoreManager.dispose();
14219
14401
  }
14220
14402
  // -- Mode stack management --
14221
14403
  get currentMode() {
@@ -14242,12 +14424,6 @@ var CliExecutionContext = class {
14242
14424
  }
14243
14425
  }
14244
14426
  };
14245
-
14246
- // src/lib/services/capturing-terminal-writer.ts
14247
- var ANSI_RE2 = /\x1b\[[0-9;]*m/g;
14248
- function stripAnsi(text) {
14249
- return text.replace(ANSI_RE2, "");
14250
- }
14251
14427
  var CapturingTerminalWriter = class {
14252
14428
  constructor(inner) {
14253
14429
  this.inner = inner;
@@ -14262,12 +14438,12 @@ var CapturingTerminalWriter = class {
14262
14438
  }
14263
14439
  // -- stdout-equivalent methods (captured) --------------------------------
14264
14440
  write(text) {
14265
- this._lines.push(stripAnsi(text));
14441
+ this._lines.push(cliCore.stripAnsi(text));
14266
14442
  this.inner.write(text);
14267
14443
  }
14268
14444
  writeln(text) {
14269
14445
  if (text !== void 0 && text !== "") {
14270
- this._lines.push(stripAnsi(text));
14446
+ this._lines.push(cliCore.stripAnsi(text));
14271
14447
  }
14272
14448
  this.inner.writeln(text);
14273
14449
  }
@@ -14290,11 +14466,11 @@ var CapturingTerminalWriter = class {
14290
14466
  this.inner.writeInfo(message);
14291
14467
  }
14292
14468
  writeWarning(message) {
14293
- this._stderrLines.push(stripAnsi(message));
14469
+ this._stderrLines.push(cliCore.stripAnsi(message));
14294
14470
  this.inner.writeWarning(message);
14295
14471
  }
14296
14472
  writeError(message) {
14297
- this._stderrLines.push(stripAnsi(message));
14473
+ this._stderrLines.push(cliCore.stripAnsi(message));
14298
14474
  this.inner.writeError(message);
14299
14475
  }
14300
14476
  // -- pass-through methods ------------------------------------------------
@@ -14319,6 +14495,19 @@ var CapturingTerminalWriter = class {
14319
14495
  writeColumns(items, options) {
14320
14496
  this.inner.writeColumns(items, options);
14321
14497
  }
14498
+ writeLink(text, url) {
14499
+ this._lines.push(cliCore.stripAnsi(text));
14500
+ this.inner.writeLink(text, url);
14501
+ }
14502
+ writeBox(content, options) {
14503
+ const lines = Array.isArray(content) ? content : [content];
14504
+ lines.forEach((l) => this._lines.push(cliCore.stripAnsi(l)));
14505
+ this.inner.writeBox(content, options);
14506
+ }
14507
+ writeIndented(text, level) {
14508
+ this._lines.push(cliCore.stripAnsi(text));
14509
+ this.inner.writeIndented(text, level);
14510
+ }
14322
14511
  // -- captured data accessors ---------------------------------------------
14323
14512
  /**
14324
14513
  * Returns true if any stdout-equivalent output was captured.
@@ -14450,6 +14639,7 @@ var CliCommandExecutor = class {
14450
14639
  let lastExitSuccess = true;
14451
14640
  let shouldRunNext = true;
14452
14641
  let pipelineData = void 0;
14642
+ let lastCapturingWriter;
14453
14643
  for (let i = 0; i < parts.length; i++) {
14454
14644
  const part = parts[i];
14455
14645
  if (part.type === "&&") {
@@ -14500,7 +14690,7 @@ var CliCommandExecutor = class {
14500
14690
  continue;
14501
14691
  }
14502
14692
  if (shouldRunNext) {
14503
- await this.appendStderrToFile(nextPart.value, context2);
14693
+ await this.appendStderrToFile(nextPart.value, context2, lastCapturingWriter);
14504
14694
  }
14505
14695
  continue;
14506
14696
  } else if (part.type === "2>") {
@@ -14512,7 +14702,7 @@ var CliCommandExecutor = class {
14512
14702
  continue;
14513
14703
  }
14514
14704
  if (shouldRunNext) {
14515
- await this.writeStderrToFile(nextPart.value, context2);
14705
+ await this.writeStderrToFile(nextPart.value, context2, lastCapturingWriter);
14516
14706
  }
14517
14707
  continue;
14518
14708
  }
@@ -14520,7 +14710,7 @@ var CliCommandExecutor = class {
14520
14710
  continue;
14521
14711
  }
14522
14712
  try {
14523
- await this.executeSingleCommand(
14713
+ lastCapturingWriter = await this.executeSingleCommand(
14524
14714
  part.value,
14525
14715
  pipelineData,
14526
14716
  rootContext
@@ -14732,7 +14922,6 @@ var CliCommandExecutor = class {
14732
14922
  commandContext.writer
14733
14923
  );
14734
14924
  commandContext.writer = capturingWriter;
14735
- this.lastCapturingWriter = capturingWriter;
14736
14925
  try {
14737
14926
  const hooks = processor.hooks ?? [];
14738
14927
  for (const hook of hooks.filter((h) => h.when === "before")) {
@@ -14758,6 +14947,7 @@ var CliCommandExecutor = class {
14758
14947
  if (!process.exited) {
14759
14948
  process.end();
14760
14949
  }
14950
+ return capturingWriter;
14761
14951
  } catch (e) {
14762
14952
  context2.spinner?.hide();
14763
14953
  if (e instanceof ProcessExitedError) {
@@ -14785,6 +14975,7 @@ var CliCommandExecutor = class {
14785
14975
  } finally {
14786
14976
  abortSub.unsubscribe();
14787
14977
  }
14978
+ return capturingWriter;
14788
14979
  }
14789
14980
  completeProcess(processEntry, context2, exitCode) {
14790
14981
  if (!processEntry) return;
@@ -14802,8 +14993,8 @@ var CliCommandExecutor = class {
14802
14993
  } catch {
14803
14994
  }
14804
14995
  }
14805
- async writeStderrToFile(filePath, context2) {
14806
- const stderr = this.lastCapturingWriter?.getCapturedStderr();
14996
+ async writeStderrToFile(filePath, context2, capturingWriter) {
14997
+ const stderr = capturingWriter?.getCapturedStderr();
14807
14998
  if (!stderr) return;
14808
14999
  const FS_TOKEN2 = "cli-file-system-service";
14809
15000
  let fs;
@@ -14827,8 +15018,8 @@ var CliCommandExecutor = class {
14827
15018
  context2.writer.writeError(`2> failed: ${e.message || e}`);
14828
15019
  }
14829
15020
  }
14830
- async appendStderrToFile(filePath, context2) {
14831
- const stderr = this.lastCapturingWriter?.getCapturedStderr();
15021
+ async appendStderrToFile(filePath, context2, capturingWriter) {
15022
+ const stderr = capturingWriter?.getCapturedStderr();
14832
15023
  if (!stderr) return;
14833
15024
  const FS_TOKEN2 = "cli-file-system-service";
14834
15025
  let fs;
@@ -15338,10 +15529,27 @@ var welcomeModule = {
15338
15529
  }
15339
15530
  recordWelcomeMessageDisplay();
15340
15531
  context2.showPrompt();
15341
- await context2.textAnimator?.showText(getGreetingBasedOnTime(), {
15342
- speed: 60,
15343
- removeAfterTyping: true
15344
- });
15532
+ let showGreeting = true;
15533
+ try {
15534
+ const storeManager = context2.services.get(
15535
+ CliStateStoreManager_TOKEN
15536
+ );
15537
+ const configureStore = storeManager.getStateStore(
15538
+ cliCore.CLI_CONFIGURE_STORE_NAME
15539
+ );
15540
+ await configureStore.initialize();
15541
+ const configState = configureStore.getState();
15542
+ if (configState?.["system"]?.["greeting"] === false) {
15543
+ showGreeting = false;
15544
+ }
15545
+ } catch {
15546
+ }
15547
+ if (showGreeting) {
15548
+ await context2.textAnimator?.showText(getGreetingBasedOnTime(), {
15549
+ speed: 60,
15550
+ removeAfterTyping: true
15551
+ });
15552
+ }
15345
15553
  }
15346
15554
  };
15347
15555
  function shouldDisplayWelcomeMessage(showOption) {
@@ -15377,6 +15585,7 @@ var CliBoot = class {
15377
15585
  this.initialized = false;
15378
15586
  this.initializing = false;
15379
15587
  this.bootedModules = /* @__PURE__ */ new Set();
15588
+ this.initializedProcessors = /* @__PURE__ */ new Set();
15380
15589
  this.moduleRegistry = new cliCore.CliModuleRegistry();
15381
15590
  }
15382
15591
  /**
@@ -15393,6 +15602,7 @@ var CliBoot = class {
15393
15602
  return;
15394
15603
  }
15395
15604
  this.initializing = true;
15605
+ this.moduleRegistry.onWarn = (msg) => context2.logger.warn(msg);
15396
15606
  cliCore.initializeBrowserEnvironment({
15397
15607
  context: context2,
15398
15608
  registry: this.moduleRegistry
@@ -15445,7 +15655,7 @@ var CliBoot = class {
15445
15655
  try {
15446
15656
  await module.onInit(context2);
15447
15657
  } catch (e) {
15448
- console.error(
15658
+ context2.logger.error(
15449
15659
  `Error in onInit for module "${module.name}":`,
15450
15660
  e
15451
15661
  );
@@ -15468,7 +15678,7 @@ var CliBoot = class {
15468
15678
  });
15469
15679
  }
15470
15680
  } catch (e) {
15471
- console.error(
15681
+ context2.logger.error(
15472
15682
  `Setup failed for module "${module.name}":`,
15473
15683
  e
15474
15684
  );
@@ -15558,14 +15768,16 @@ var CliBoot = class {
15558
15768
  });
15559
15769
  }
15560
15770
  async bootShared(context2) {
15561
- await this.initializeProcessorsInternal(
15562
- context2,
15563
- this.registry.processors
15771
+ const uninitialized = this.registry.processors.filter(
15772
+ (p) => !this.initializedProcessors.has(p)
15564
15773
  );
15565
- await cliCore.delay(300);
15774
+ if (uninitialized.length > 0) {
15775
+ await this.initializeProcessorsInternal(context2, uninitialized);
15776
+ }
15566
15777
  }
15567
15778
  async initializeProcessorsInternal(context2, processors, parent) {
15568
15779
  for (const p of processors) {
15780
+ if (this.initializedProcessors.has(p)) continue;
15569
15781
  try {
15570
15782
  p.parent = parent;
15571
15783
  if (p.initialize) {
@@ -15576,6 +15788,7 @@ var CliBoot = class {
15576
15788
  await processorContext.state.initialize();
15577
15789
  await p.initialize(processorContext);
15578
15790
  }
15791
+ this.initializedProcessors.add(p);
15579
15792
  if (p.processors && p.processors.length > 0) {
15580
15793
  await this.initializeProcessorsInternal(
15581
15794
  context2,
@@ -15584,7 +15797,7 @@ var CliBoot = class {
15584
15797
  );
15585
15798
  }
15586
15799
  } catch (e) {
15587
- console.error(
15800
+ context2.logger.error(
15588
15801
  `Error initializing processor "${p.command}":`,
15589
15802
  e
15590
15803
  );
@@ -15702,9 +15915,10 @@ var CliKeyValueStore = class {
15702
15915
  this.db = event.target.result;
15703
15916
  resolve();
15704
15917
  };
15705
- request.onerror = (event) => {
15706
- console.error("Error initializing IndexedDB:", event);
15707
- reject();
15918
+ request.onerror = () => {
15919
+ reject(new Error(
15920
+ `Failed to open IndexedDB "${this.dbName}": ${request.error?.message ?? "unknown error"}`
15921
+ ));
15708
15922
  };
15709
15923
  });
15710
15924
  }
@@ -15725,13 +15939,15 @@ var CliKeyValueStore = class {
15725
15939
  request.onsuccess = () => {
15726
15940
  resolve(request.result);
15727
15941
  };
15728
- request.onerror = (event) => {
15729
- console.error("Error getting value:", event);
15730
- reject(void 0);
15942
+ request.onerror = () => {
15943
+ reject(new Error(
15944
+ `IndexedDB get("${key}") failed: ${request.error?.message ?? "unknown error"}`
15945
+ ));
15731
15946
  };
15732
15947
  } catch (e) {
15733
- console.error("Error getting value:", e);
15734
- reject(void 0);
15948
+ reject(new Error(
15949
+ `IndexedDB get("${key}") failed: ${e instanceof Error ? e.message : e}`
15950
+ ));
15735
15951
  }
15736
15952
  });
15737
15953
  }
@@ -15750,9 +15966,10 @@ var CliKeyValueStore = class {
15750
15966
  const store = transaction.objectStore(this.storeName);
15751
15967
  const request = store.put(value, key);
15752
15968
  request.onsuccess = () => resolve();
15753
- request.onerror = (event) => {
15754
- console.error("Error setting value:", event);
15755
- reject();
15969
+ request.onerror = () => {
15970
+ reject(new Error(
15971
+ `IndexedDB set("${key}") failed: ${request.error?.message ?? "unknown error"}`
15972
+ ));
15756
15973
  };
15757
15974
  });
15758
15975
  }
@@ -15770,9 +15987,10 @@ var CliKeyValueStore = class {
15770
15987
  const store = transaction.objectStore(this.storeName);
15771
15988
  const request = store.delete(key);
15772
15989
  request.onsuccess = () => resolve();
15773
- request.onerror = (event) => {
15774
- console.error("Error removing value:", event);
15775
- reject();
15990
+ request.onerror = () => {
15991
+ reject(new Error(
15992
+ `IndexedDB remove("${key}") failed: ${request.error?.message ?? "unknown error"}`
15993
+ ));
15776
15994
  };
15777
15995
  });
15778
15996
  }
@@ -15789,9 +16007,10 @@ var CliKeyValueStore = class {
15789
16007
  const store = transaction.objectStore(this.storeName);
15790
16008
  const request = store.clear();
15791
16009
  request.onsuccess = () => resolve();
15792
- request.onerror = (event) => {
15793
- console.error("Error clearing store:", event);
15794
- reject();
16010
+ request.onerror = () => {
16011
+ reject(new Error(
16012
+ `IndexedDB clear() failed: ${request.error?.message ?? "unknown error"}`
16013
+ ));
15795
16014
  };
15796
16015
  });
15797
16016
  }
@@ -15845,6 +16064,13 @@ var CliStateStore = class {
15845
16064
  );
15846
16065
  }
15847
16066
  }
16067
+ /**
16068
+ * Complete the BehaviorSubject so all subscribers are notified
16069
+ * and no further emissions occur. Called during engine teardown.
16070
+ */
16071
+ dispose() {
16072
+ this.state$.complete();
16073
+ }
15848
16074
  };
15849
16075
 
15850
16076
  // src/lib/state/cli-state-store-manager.ts
@@ -15880,6 +16106,18 @@ var CliStateStoreManager = class {
15880
16106
  });
15881
16107
  return entries;
15882
16108
  }
16109
+ /**
16110
+ * Dispose all stores (completes their BehaviorSubjects).
16111
+ * Called during engine teardown.
16112
+ */
16113
+ dispose() {
16114
+ this.stores.forEach((store) => {
16115
+ if (store.dispose) {
16116
+ store.dispose();
16117
+ }
16118
+ });
16119
+ this.stores.clear();
16120
+ }
15883
16121
  };
15884
16122
 
15885
16123
  // src/lib/editor/syntax/registry.ts
@@ -16923,7 +17161,7 @@ function createServerModule() {
16923
17161
  }
16924
17162
 
16925
17163
  // src/lib/engine/cli-engine.ts
16926
- var CliEngine = class {
17164
+ var CliEngine = class _CliEngine {
16927
17165
  constructor(container, options) {
16928
17166
  this.container = container;
16929
17167
  this.options = options;
@@ -17070,9 +17308,9 @@ var CliEngine = class {
17070
17308
  ]);
17071
17309
  if (this.options?.servers && this.options.servers.length > 0) {
17072
17310
  await serverManager.connectAll(this.options.servers, {
17073
- warn: (msg) => console.warn(msg),
17074
- info: (msg) => console.log(msg)
17075
- }, this.executionContext.backgroundServices);
17311
+ warn: (msg) => this.executionContext.logger.warn(msg),
17312
+ info: (msg) => this.executionContext.logger.info(msg)
17313
+ }, this.executionContext.backgroundServices, this.executionContext.logger);
17076
17314
  }
17077
17315
  const serverModule = createServerModule();
17078
17316
  const allModules = this.options?.snapshot ? [serverModule, ...this.userModules] : [welcomeModule, serverModule, ...this.userModules];
@@ -17106,7 +17344,7 @@ var CliEngine = class {
17106
17344
  try {
17107
17345
  await module.onAfterBoot(this.executionContext);
17108
17346
  } catch (e) {
17109
- console.error(
17347
+ this.executionContext.logger.error(
17110
17348
  `Error in onAfterBoot for module "${module.name}":`,
17111
17349
  e
17112
17350
  );
@@ -17129,7 +17367,7 @@ var CliEngine = class {
17129
17367
  try {
17130
17368
  module.onDestroy(this.executionContext);
17131
17369
  } catch (e) {
17132
- console.error(`Error in onDestroy for module "${module.name}":`, e);
17370
+ this.executionContext.logger.error(`Error in onDestroy for module "${module.name}":`, e);
17133
17371
  }
17134
17372
  }
17135
17373
  }
@@ -17301,17 +17539,29 @@ var CliEngine = class {
17301
17539
  this.executionContext?.handleTerminalResize();
17302
17540
  });
17303
17541
  }
17542
+ static {
17543
+ /** Maximum time (ms) to wait for container layout before rejecting. */
17544
+ this.LAYOUT_TIMEOUT_MS = 1e4;
17545
+ }
17304
17546
  /**
17305
17547
  * Wait until the container element has non-zero dimensions.
17306
17548
  * xterm.js requires the host element to be laid out before open() is called.
17549
+ * Rejects after {@link LAYOUT_TIMEOUT_MS} if the container stays hidden.
17307
17550
  */
17308
17551
  waitForLayout() {
17309
- return new Promise((resolve) => {
17552
+ return new Promise((resolve, reject) => {
17553
+ const deadline = Date.now() + _CliEngine.LAYOUT_TIMEOUT_MS;
17310
17554
  const check = () => {
17311
17555
  if (this.container.offsetWidth > 0 && this.container.offsetHeight > 0) {
17312
17556
  resolve();
17313
17557
  return;
17314
17558
  }
17559
+ if (Date.now() >= deadline) {
17560
+ reject(new Error(
17561
+ `CliEngine.start(): container has zero dimensions after ${_CliEngine.LAYOUT_TIMEOUT_MS}ms. Ensure the container element is visible and laid out before calling start().`
17562
+ ));
17563
+ return;
17564
+ }
17315
17565
  requestAnimationFrame(check);
17316
17566
  };
17317
17567
  check();
@@ -17543,6 +17793,18 @@ function createTestWriter() {
17543
17793
  for (const item of items) {
17544
17794
  stdout.push(item);
17545
17795
  }
17796
+ },
17797
+ writeLink(text, _url) {
17798
+ stdout.push(text);
17799
+ },
17800
+ writeBox(content, _options) {
17801
+ const lines = Array.isArray(content) ? content : [content];
17802
+ for (const line of lines) {
17803
+ stdout.push(line);
17804
+ }
17805
+ },
17806
+ writeIndented(text, level = 1) {
17807
+ stdout.push(" ".repeat(level) + text);
17546
17808
  }
17547
17809
  };
17548
17810
  }