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