@qodalis/cli 2.0.0-beta.12 → 2.0.0-beta.14

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, ServerVersionNegotiator, formatJson, visibleLength, stripAnsi, getRightOfWord, CancellablePromise, getParameterValue, CLI_CONFIGURE_STORE_NAME, CliModuleRegistry, initializeBrowserEnvironment, satisfiesVersionRange, ICliDragDropService_TOKEN, ICliCompletionProvider_TOKEN, delay, colorFirstWord, clearTerminalLine } from '@qodalis/cli-core';
1
+ import { CliLogLevel, DefaultThemes, DefaultThemeInfos, DefaultLibraryAuthor, CliIcon, CliForegroundColor, LIBRARY_VERSION as LIBRARY_VERSION$1, resolveConfigurationCategories, ICliTranslationService_TOKEN, ICliAuthorizationService_TOKEN, resolveHeaders, appendQueryParams, buildAuthenticatedWebSocketUrl, ServerVersionNegotiator, CliHttpError, formatJson, visibleLength, stripAnsi, getRightOfWord, CancellablePromise, isCliAccessDeniedError, getParameterValue, ICliCommandGuard_TOKEN, CLI_CONFIGURE_STORE_NAME, CliModuleRegistry, initializeBrowserEnvironment, ICliPermissionRegistry_TOKEN, satisfiesVersionRange, CliServerPermissions, ICliDragDropService_TOKEN, cliAccessMetadataGuards, ICliServerAuthService_TOKEN, ICliCompletionProvider_TOKEN, delay, colorFirstWord, clearTerminalLine, ICliServerAuthTokenProvider_TOKEN } 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';
@@ -408,10 +408,31 @@ var ICliPingServerService_TOKEN = "cli-ping-server-service";
408
408
  var CliModuleRegistry_TOKEN = "cli-module-registry";
409
409
  var CliBackgroundServiceRegistry_TOKEN = "cli-background-service-registry";
410
410
  var CliPackageManagerService_TOKEN = "cli-package-manager-service";
411
+ var CliKeyValueStore_TOKEN = "cli-key-value-store";
412
+ var SyntaxHighlighterRegistry_TOKEN = "syntax-highlighter-registry";
411
413
 
412
414
  // src/lib/version.ts
413
- var LIBRARY_VERSION = "2.0.0-beta.12";
415
+ var LIBRARY_VERSION = "2.0.0-beta.14";
414
416
  var API_VERSION = 2;
417
+ var ANONYMOUS = "\0anonymous";
418
+ function aliasOwner(context2) {
419
+ let auth;
420
+ try {
421
+ auth = context2.services.get(ICliAuthorizationService_TOKEN);
422
+ } catch {
423
+ auth = void 0;
424
+ }
425
+ if (!auth) return void 0;
426
+ return auth.getPrincipal()?.name ?? ANONYMOUS;
427
+ }
428
+ function aliasesFor(state, owner) {
429
+ if (owner === void 0) return state?.aliases ?? {};
430
+ return state?.aliasesByUser?.[owner] ?? {};
431
+ }
432
+ function withAliases(state, owner, aliases) {
433
+ if (owner === void 0) return { aliases };
434
+ return { aliasesByUser: { ...state?.aliasesByUser ?? {}, [owner]: aliases } };
435
+ }
415
436
  var CliAliasCommandProcessor = class {
416
437
  constructor() {
417
438
  this.command = "alias";
@@ -428,26 +449,33 @@ var CliAliasCommandProcessor = class {
428
449
  },
429
450
  storeName: "aliases"
430
451
  };
452
+ /** Shared aliases (used when no auth module is loaded) */
431
453
  this.userAliases = {};
454
+ this.aliasState = {};
432
455
  this.processors = [
433
456
  {
434
457
  command: "ls",
435
458
  description: "List all aliases",
436
459
  processCommand: async (_, context2) => {
437
460
  const { writer } = context2;
461
+ const visible = this.aliasesFor(context2);
438
462
  writer.writeln("Aliases:");
439
- Object.entries(this.userAliases).forEach(
463
+ Object.entries(visible).forEach(
440
464
  ([alias, command2]) => {
441
465
  writer.writeInfo(` ${alias} -> ${command2}`);
442
466
  }
443
467
  );
444
- if (Object.keys(this.userAliases).length === 0) {
468
+ if (Object.keys(visible).length === 0) {
445
469
  writer.writeInfo(" No aliases defined");
446
470
  }
447
471
  }
448
472
  }
449
473
  ];
450
474
  }
475
+ /** Aliases visible to the current principal */
476
+ aliasesFor(context2) {
477
+ return aliasesFor(this.aliasState, aliasOwner(context2));
478
+ }
451
479
  writeDescription(context2) {
452
480
  const { writer, translator: t } = context2;
453
481
  writer.writeln(t.t("cli.alias.long_description", "Create shortcut aliases for frequently used commands"));
@@ -473,7 +501,7 @@ var CliAliasCommandProcessor = class {
473
501
  );
474
502
  }
475
503
  async processCommand(command2, context2) {
476
- const registry = context2.services.get(
504
+ const registry = context2.services.getRequired(
477
505
  CliProcessorsRegistry_TOKEN
478
506
  );
479
507
  const { writer } = context2;
@@ -493,18 +521,20 @@ var CliAliasCommandProcessor = class {
493
521
  }
494
522
  context2.writer.writeInfo(`${alias} -> ${command2.args[alias]}`);
495
523
  }
496
- context2.state.updateState({
497
- aliases: {
498
- ...this.userAliases,
524
+ const owner = aliasOwner(context2);
525
+ context2.state.updateState(
526
+ withAliases(this.aliasState, owner, {
527
+ ...aliasesFor(this.aliasState, owner),
499
528
  ...command2.args
500
- }
501
- });
529
+ })
530
+ );
502
531
  await context2.state.persist();
503
532
  }
504
533
  async initialize(context2) {
505
534
  this.stateSubscription?.unsubscribe();
506
- this.stateSubscription = context2.state.select((x) => x["aliases"]).subscribe((aliases) => {
507
- this.userAliases = aliases ?? {};
535
+ this.stateSubscription = context2.state.select((x) => x).subscribe((state) => {
536
+ this.aliasState = state ?? {};
537
+ this.userAliases = this.aliasState.aliases ?? {};
508
538
  });
509
539
  }
510
540
  };
@@ -1332,7 +1362,9 @@ var CliEvalCommandProcessor = class {
1332
1362
  this.acceptsRawInput = true;
1333
1363
  this.metadata = {
1334
1364
  icon: "\u{1F9EE}",
1335
- module: "misc"
1365
+ module: "misc",
1366
+ // Enforced only when an auth module is loaded (eval also needs allowEval)
1367
+ requiredPermissions: ["system:eval"]
1336
1368
  };
1337
1369
  }
1338
1370
  async processCommand(command, context) {
@@ -2447,7 +2479,9 @@ var CliUnAliasCommandProcessor = class {
2447
2479
  async processCommand(command2, context2) {
2448
2480
  const { writer } = context2;
2449
2481
  const aliasToRemove = command2.value;
2450
- const { aliases } = context2.state.getState();
2482
+ const state = context2.state.getState();
2483
+ const owner = aliasOwner(context2);
2484
+ const aliases = aliasesFor(state, owner);
2451
2485
  if (!aliases[aliasToRemove]) {
2452
2486
  writer.writeError(`Alias ${aliasToRemove} not found`);
2453
2487
  context2.process.exit(-1);
@@ -2457,9 +2491,7 @@ var CliUnAliasCommandProcessor = class {
2457
2491
  Object.keys(aliases).filter((alias) => alias !== aliasToRemove).forEach((alias) => {
2458
2492
  updated[alias] = aliases[alias];
2459
2493
  });
2460
- context2.state.updateState({
2461
- aliases: updated
2462
- });
2494
+ context2.state.updateState(withAliases(state, owner, updated));
2463
2495
  await context2.state.persist();
2464
2496
  }
2465
2497
  };
@@ -2813,7 +2845,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
2813
2845
  ];
2814
2846
  }
2815
2847
  async processCommand(command2, context2) {
2816
- const registry = context2.services.get(
2848
+ const registry = context2.services.getRequired(
2817
2849
  CliProcessorsRegistry_TOKEN
2818
2850
  );
2819
2851
  const pluginCategories = resolveConfigurationCategories(registry);
@@ -2910,10 +2942,10 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
2910
2942
  if (stateKey === "system") {
2911
2943
  const langOption = options.find((o) => o.key === "language");
2912
2944
  if (langOption) {
2913
- try {
2914
- const translator = context2.services.get(
2915
- ICliTranslationService_TOKEN
2916
- );
2945
+ const translator = context2.services.get(
2946
+ ICliTranslationService_TOKEN
2947
+ );
2948
+ if (translator) {
2917
2949
  const locales = translator.getAvailableLocales();
2918
2950
  if (locales.length > 0) {
2919
2951
  langOption.options = locales.map((l) => ({
@@ -2921,7 +2953,6 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
2921
2953
  value: l
2922
2954
  }));
2923
2955
  }
2924
- } catch {
2925
2956
  }
2926
2957
  }
2927
2958
  }
@@ -3066,13 +3097,10 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3066
3097
  }
3067
3098
  }
3068
3099
  if (settings["language"]) {
3069
- try {
3070
- const translator = context2.services.get(
3071
- ICliTranslationService_TOKEN
3072
- );
3073
- translator.setLocale(settings["language"]);
3074
- } catch {
3075
- }
3100
+ const translator = context2.services.get(
3101
+ ICliTranslationService_TOKEN
3102
+ );
3103
+ translator?.setLocale(settings["language"]);
3076
3104
  }
3077
3105
  if (context2.terminal?.options) {
3078
3106
  if (settings["cursorBlink"] !== void 0) {
@@ -3102,7 +3130,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3102
3130
  aliases: ["ls"],
3103
3131
  description: "List all configuration options and their current values",
3104
3132
  processCommand: async (_, context2) => {
3105
- const registry = context2.services.get(
3133
+ const registry = context2.services.getRequired(
3106
3134
  CliProcessorsRegistry_TOKEN
3107
3135
  );
3108
3136
  const allOptions = this.getAllOptions(registry);
@@ -3164,7 +3192,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3164
3192
  }
3165
3193
  const category = path.substring(0, dotIndex);
3166
3194
  const key = path.substring(dotIndex + 1);
3167
- const registry = context2.services.get(
3195
+ const registry = context2.services.getRequired(
3168
3196
  CliProcessorsRegistry_TOKEN
3169
3197
  );
3170
3198
  const allOptions = this.getAllOptions(registry);
@@ -3216,7 +3244,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3216
3244
  }
3217
3245
  const category = path.substring(0, dotIndex);
3218
3246
  const key = path.substring(dotIndex + 1);
3219
- const registry = context2.services.get(
3247
+ const registry = context2.services.getRequired(
3220
3248
  CliProcessorsRegistry_TOKEN
3221
3249
  );
3222
3250
  const allOptions = this.getAllOptions(registry);
@@ -3276,7 +3304,7 @@ var CliConfigureCommandProcessor = class _CliConfigureCommandProcessor {
3276
3304
  t.t("cli.configure.system_reset", "System configuration reset to defaults")
3277
3305
  );
3278
3306
  } else {
3279
- const registry = context2.services.get(
3307
+ const registry = context2.services.getRequired(
3280
3308
  CliProcessorsRegistry_TOKEN
3281
3309
  );
3282
3310
  const categories2 = resolveConfigurationCategories(registry);
@@ -3672,7 +3700,7 @@ var CliHelpCommandProcessor = class {
3672
3700
  async processCommand(command2, context2) {
3673
3701
  const { writer } = context2;
3674
3702
  const t = context2.translator;
3675
- const registry = context2.services.get(
3703
+ const registry = context2.services.getRequired(
3676
3704
  CliProcessorsRegistry_TOKEN
3677
3705
  );
3678
3706
  const [_, ...commandsToHelp] = command2.command.split(" ");
@@ -3772,24 +3800,23 @@ var CliHelpCommandProcessor = class {
3772
3800
  writeProcessorDescription(processor, context2) {
3773
3801
  const { writer } = context2;
3774
3802
  const t = context2.translator;
3775
- const icon = processor.metadata?.icon || "";
3776
- const name = `${BOLD}${writer.wrapInColor(processor.command, CliForegroundColor.Cyan)}${RESET}`;
3777
3803
  const version = `${DIM}v${processor.version || "1.0.0"}${RESET}`;
3778
- writer.writeln();
3779
- writer.writeln(` ${icon}${icon ? " " : ""}${name} ${version}`);
3804
+ const headerLines = [];
3780
3805
  if (processor.description) {
3781
- const desc = this.translateDescription(
3782
- processor.description,
3783
- processor.command,
3784
- context2
3806
+ headerLines.push(
3807
+ this.translateDescription(processor.description, processor.command, context2)
3785
3808
  );
3786
- writer.writeln(` ${desc}`);
3787
3809
  }
3788
3810
  if (processor.aliases?.length) {
3789
- writer.writeln(
3790
- ` ${DIM}${t.t("cli.help.aliases", "Aliases:")}${RESET} ${processor.aliases.map((a) => writer.wrapInColor(a, CliForegroundColor.Magenta)).join(", ")}`
3811
+ headerLines.push(
3812
+ `${DIM}${t.t("cli.help.aliases", "Aliases:")}${RESET} ${processor.aliases.map((a) => writer.wrapInColor(a, CliForegroundColor.Magenta)).join(", ")}`
3791
3813
  );
3792
3814
  }
3815
+ writer.writeln();
3816
+ writer.writeBox(headerLines, {
3817
+ title: `${writer.wrapInColor(processor.command, CliForegroundColor.Cyan)} ${version}`,
3818
+ borderColor: CliForegroundColor.White
3819
+ });
3793
3820
  if (processor.originalProcessor) {
3794
3821
  writer.writeln();
3795
3822
  writer.writeln(
@@ -3955,20 +3982,13 @@ var CliVersionCommandProcessor = class {
3955
3982
  writer.writeln(
3956
3983
  writer.wrapInColor(getCliNameArt(context2.terminal.cols), CliForegroundColor.Cyan)
3957
3984
  );
3958
- writer.writeln(
3959
- ` ${DIM2}CLI${RESET2} ${writer.wrapInColor(`v${LIBRARY_VERSION}`, CliForegroundColor.Green)} ${DIM2}Core${RESET2} ${writer.wrapInColor(`v${LIBRARY_VERSION$1}`, CliForegroundColor.Green)}`
3960
- );
3961
- let framework = "vanilla";
3962
- try {
3963
- framework = context2.services.get("cli-framework");
3964
- } catch {
3965
- }
3966
- writer.writeln(
3967
- ` ${DIM2}Framework${RESET2} ${writer.wrapInColor(framework, CliForegroundColor.Cyan)}`
3968
- );
3969
- writer.writeln(
3970
- ` ${DIM2}Docs${RESET2} ${writer.wrapInColor("https://cli.qodalis.com/docs/", CliForegroundColor.Blue)}`
3971
- );
3985
+ const framework = context2.services.get("cli-framework") ?? "vanilla";
3986
+ writer.writeBox([
3987
+ `${DIM2}CLI${RESET2} ${writer.wrapInColor(`v${LIBRARY_VERSION}`, CliForegroundColor.Green)}`,
3988
+ `${DIM2}Core${RESET2} ${writer.wrapInColor(`v${LIBRARY_VERSION$1}`, CliForegroundColor.Green)}`,
3989
+ `${DIM2}Framework${RESET2} ${writer.wrapInColor(framework, CliForegroundColor.Cyan)}`,
3990
+ `${DIM2}Docs${RESET2} ${writer.wrapInColor("https://cli.qodalis.com/docs/", CliForegroundColor.Blue)}`
3991
+ ], { borderColor: CliForegroundColor.White });
3972
3992
  writer.writeln();
3973
3993
  }
3974
3994
  writeDescription(context2) {
@@ -4026,11 +4046,7 @@ function collectSystemInfo(context2) {
4026
4046
  const lines = [];
4027
4047
  lines.push(`- **CLI Version:** ${LIBRARY_VERSION}`);
4028
4048
  lines.push(`- **Core Version:** ${LIBRARY_VERSION$1}`);
4029
- let framework = "vanilla";
4030
- try {
4031
- framework = context2.services.get("cli-framework");
4032
- } catch {
4033
- }
4049
+ const framework = context2.services.get("cli-framework") ?? "vanilla";
4034
4050
  lines.push(`- **Framework:** ${framework}`);
4035
4051
  lines.push(`- **Terminal:** ${context2.terminal.cols}x${context2.terminal.rows}`);
4036
4052
  if (typeof navigator !== "undefined") {
@@ -4102,12 +4118,8 @@ async function runInteractiveFlow(type, initialTitle, context2) {
4102
4118
  `${writer.wrapInColor("\u2500\u2500 System Info (auto-attached) \u2500\u2500", CliForegroundColor.Cyan)}`
4103
4119
  );
4104
4120
  writer.writeln(` CLI: ${LIBRARY_VERSION} Core: ${LIBRARY_VERSION$1}`);
4105
- let framework = "vanilla";
4106
- try {
4107
- framework = context2.services.get("cli-framework");
4108
- } catch {
4109
- }
4110
- writer.writeln(` Framework: ${framework} Terminal: ${context2.terminal.cols}x${context2.terminal.rows}`);
4121
+ const previewFramework = context2.services.get("cli-framework") ?? "vanilla";
4122
+ writer.writeln(` Framework: ${previewFramework} Terminal: ${context2.terminal.cols}x${context2.terminal.rows}`);
4111
4123
  if (typeof navigator !== "undefined") {
4112
4124
  writer.writeln(` Platform: ${navigator.platform} Language: ${navigator.language}`);
4113
4125
  }
@@ -4249,7 +4261,7 @@ var CliHistoryCommandProcessor = class {
4249
4261
  command: "clear",
4250
4262
  description: "Clears the command history",
4251
4263
  processCommand: async (_, context2) => {
4252
- const commandHistory = context2.services.get(
4264
+ const commandHistory = context2.services.getRequired(
4253
4265
  CliCommandHistory_TOKEN
4254
4266
  );
4255
4267
  await commandHistory.clearHistory();
@@ -4272,7 +4284,7 @@ var CliHistoryCommandProcessor = class {
4272
4284
  description: "Search command history for a pattern",
4273
4285
  parameters: searchParameters,
4274
4286
  processCommand: async (cmd, context2) => {
4275
- const commandHistory = context2.services.get(
4287
+ const commandHistory = context2.services.getRequired(
4276
4288
  CliCommandHistory_TOKEN
4277
4289
  );
4278
4290
  const query = (cmd.value ?? "").trim();
@@ -4326,7 +4338,7 @@ var CliHistoryCommandProcessor = class {
4326
4338
  }
4327
4339
  async processCommand(_, context2) {
4328
4340
  const { writer } = context2;
4329
- const commandHistory = context2.services.get(
4341
+ const commandHistory = context2.services.getRequired(
4330
4342
  CliCommandHistory_TOKEN
4331
4343
  );
4332
4344
  const history = commandHistory.getHistory();
@@ -4715,7 +4727,9 @@ var CliPackagesCommandProcessor = class {
4715
4727
  this.metadata = {
4716
4728
  sealed: true,
4717
4729
  icon: "\u{1F4E6}",
4718
- module: "system"
4730
+ module: "system",
4731
+ // Enforced only when an auth module is loaded
4732
+ requiredPermissions: ["system:packages"]
4719
4733
  };
4720
4734
  this.stateConfiguration = {
4721
4735
  initialState: {
@@ -4773,7 +4787,7 @@ var CliPackagesCommandProcessor = class {
4773
4787
  const { progressBar, writer } = context2;
4774
4788
  const { signal, cleanup } = scope.createAbortSignal(context2);
4775
4789
  progressBar.show();
4776
- context2.setStatusText("Browsing packages");
4790
+ context2.notifier.info("Browsing packages");
4777
4791
  const prefix = packagesManager.QODALIS_COMMAND_PREFIX;
4778
4792
  const knownShortNames = [
4779
4793
  "browser-storage",
@@ -4966,7 +4980,7 @@ var CliPackagesCommandProcessor = class {
4966
4980
  const packages = command2.value.split(" ");
4967
4981
  for (const pkg of packages) {
4968
4982
  if (signal.aborted) break;
4969
- context2.setStatusText(`Installing ${pkg}`);
4983
+ context2.notifier.info(`Installing ${pkg}`);
4970
4984
  await scope.addPackage(pkg, context2, signal);
4971
4985
  }
4972
4986
  cleanup();
@@ -5007,7 +5021,7 @@ var CliPackagesCommandProcessor = class {
5007
5021
  const packages = command2.value.split(" ");
5008
5022
  for (const pkg of packages) {
5009
5023
  if (signal.aborted) break;
5010
- context2.setStatusText(`Removing ${pkg}`);
5024
+ context2.notifier.info(`Removing ${pkg}`);
5011
5025
  await scope.removePackage(pkg, context2);
5012
5026
  }
5013
5027
  cleanup();
@@ -5043,7 +5057,7 @@ var CliPackagesCommandProcessor = class {
5043
5057
  const fullName = name.startsWith(prefix) ? name : `${prefix}${name}`;
5044
5058
  progressBar.show();
5045
5059
  progressBar.setText(`Fetching versions for ${name}`);
5046
- context2.setStatusText(`Fetching versions for ${name}`);
5060
+ context2.notifier.info(`Fetching versions for ${name}`);
5047
5061
  try {
5048
5062
  const response = await scriptsLoader.getScript(
5049
5063
  `https://registry.npmjs.org/${encodeURIComponent(fullName)}`,
@@ -5507,11 +5521,11 @@ var CliPackagesCommandProcessor = class {
5507
5521
  );
5508
5522
  }
5509
5523
  async initialize(context2) {
5510
- this.registry = context2.services.get(
5524
+ this.registry = context2.services.getRequired(
5511
5525
  CliProcessorsRegistry_TOKEN
5512
5526
  );
5513
- const store = context2.services.get(
5514
- "cli-key-value-store"
5527
+ const store = context2.services.getRequired(
5528
+ CliKeyValueStore_TOKEN
5515
5529
  );
5516
5530
  this.packagesManager.setStore(store);
5517
5531
  context2.services.set([{
@@ -5777,10 +5791,11 @@ var CliPackagesCommandProcessor = class {
5777
5791
  }
5778
5792
  };
5779
5793
  var CliServerConnection = class _CliServerConnection {
5780
- constructor(_config, _backgroundServices, _logger) {
5794
+ constructor(_config, _backgroundServices, _logger, _authService) {
5781
5795
  this._config = _config;
5782
5796
  this._backgroundServices = _backgroundServices;
5783
5797
  this._logger = _logger;
5798
+ this._authService = _authService;
5784
5799
  this._connected = false;
5785
5800
  this._commands = [];
5786
5801
  this._capabilities = null;
@@ -5789,6 +5804,8 @@ var CliServerConnection = class _CliServerConnection {
5789
5804
  this._basePath = "";
5790
5805
  this._wsReconnectAttempts = 0;
5791
5806
  this._wsReconnectTimer = null;
5807
+ /** True once this connection registered its `server-events:<name>` background service */
5808
+ this._eventServiceRegistered = false;
5792
5809
  this._healthCheckTimer = null;
5793
5810
  this._healthCheckAttempts = 0;
5794
5811
  }
@@ -5819,10 +5836,33 @@ var CliServerConnection = class _CliServerConnection {
5819
5836
  get apiVersion() {
5820
5837
  return this._apiVersion;
5821
5838
  }
5839
+ /**
5840
+ * Resolve auth headers for this server connection.
5841
+ * Uses the auth service (which merges config headers with registered
5842
+ * ICliServerAuthTokenProviders), or falls back to config headers directly.
5843
+ */
5844
+ resolveAuthHeaders() {
5845
+ if (this._authService) {
5846
+ return this._authService.getHeaders(this._config.name, this._config.headers);
5847
+ }
5848
+ return resolveHeaders(this._config.headers);
5849
+ }
5850
+ /**
5851
+ * WebSocket URL for this server with auth: single-use upgrade params from
5852
+ * auth providers when available, otherwise headers copied into the URL.
5853
+ */
5854
+ async buildWebSocketUrl(path) {
5855
+ const url = this.toWebSocketUrl(this.normalizeUrl(this._config.url)) + path;
5856
+ const params = await this._authService?.getWebSocketParams?.(this._config.name);
5857
+ if (params) {
5858
+ return appendQueryParams(url, params);
5859
+ }
5860
+ return buildAuthenticatedWebSocketUrl(url, () => this.resolveAuthHeaders());
5861
+ }
5822
5862
  async connect() {
5823
5863
  try {
5824
5864
  const baseUrl = this.normalizeUrl(this._config.url);
5825
- const negotiated = await ServerVersionNegotiator.discover(baseUrl);
5865
+ const negotiated = await ServerVersionNegotiator.discover(baseUrl, () => this.resolveAuthHeaders());
5826
5866
  if (negotiated) {
5827
5867
  this._apiVersion = negotiated.apiVersion;
5828
5868
  this._basePath = negotiated.basePath;
@@ -6010,15 +6050,25 @@ var CliServerConnection = class _CliServerConnection {
6010
6050
  return;
6011
6051
  }
6012
6052
  const serviceName = `server-events:${this._config.name}`;
6053
+ if (this._eventServiceRegistered && this._backgroundServices.getStatus(serviceName)) {
6054
+ const status = this._backgroundServices.getStatus(serviceName).status;
6055
+ const restart = status === "running" ? this._backgroundServices.restart(serviceName) : this._backgroundServices.start(serviceName);
6056
+ restart.catch((e) => {
6057
+ this._logger?.debug(`Background event service failed for "${this._config.name}", falling back to direct: ${e}`);
6058
+ this.connectEventSocketDirect();
6059
+ });
6060
+ return;
6061
+ }
6013
6062
  try {
6014
6063
  this._backgroundServices.register({
6015
6064
  name: serviceName,
6016
6065
  description: `Event stream for server '${this._config.name}'`,
6017
6066
  type: "daemon",
6018
6067
  onStart: async (ctx) => {
6019
- const baseUrl = this.normalizeUrl(this._config.url);
6020
- const wsUrl = this.toWebSocketUrl(baseUrl) + "/ws/v1/qcli/events";
6021
- ctx.log(`Connecting to ${wsUrl}`);
6068
+ const wsUrl = await this.buildWebSocketUrl("/ws/v1/qcli/events");
6069
+ if (!this._connected) return;
6070
+ ctx.log(`Connecting to ${wsUrl.split("?")[0]}`);
6071
+ this.closeEventSocketDirect();
6022
6072
  this._eventSocket = new WebSocket(wsUrl);
6023
6073
  this._eventSocket.onopen = () => {
6024
6074
  this._wsReconnectAttempts = 0;
@@ -6041,7 +6091,11 @@ var CliServerConnection = class _CliServerConnection {
6041
6091
  } catch {
6042
6092
  }
6043
6093
  };
6044
- this._eventSocket.onclose = () => {
6094
+ this._eventSocket.onclose = (event) => {
6095
+ if (this.isSessionEndedClose(event)) {
6096
+ ctx.log("Event socket closed: session ended", "info");
6097
+ return;
6098
+ }
6045
6099
  if (this._connected) {
6046
6100
  ctx.log("WebSocket closed unexpectedly, scheduling reconnect", "warn");
6047
6101
  this.scheduleWsReconnect();
@@ -6055,6 +6109,7 @@ var CliServerConnection = class _CliServerConnection {
6055
6109
  this.closeEventSocketDirect();
6056
6110
  }
6057
6111
  });
6112
+ this._eventServiceRegistered = true;
6058
6113
  this._backgroundServices.start(serviceName).catch((e) => {
6059
6114
  this._logger?.debug(`Background event service failed for "${this._config.name}", falling back to direct: ${e}`);
6060
6115
  this.connectEventSocketDirect();
@@ -6065,9 +6120,13 @@ var CliServerConnection = class _CliServerConnection {
6065
6120
  }
6066
6121
  }
6067
6122
  connectEventSocketDirect() {
6123
+ void this.connectEventSocketDirectAsync();
6124
+ }
6125
+ async connectEventSocketDirectAsync() {
6068
6126
  try {
6069
- const baseUrl = this.normalizeUrl(this._config.url);
6070
- const wsUrl = this.toWebSocketUrl(baseUrl) + "/ws/v1/qcli/events";
6127
+ const wsUrl = await this.buildWebSocketUrl("/ws/v1/qcli/events");
6128
+ if (!this._connected) return;
6129
+ this.closeEventSocketDirect();
6071
6130
  this._eventSocket = new WebSocket(wsUrl);
6072
6131
  this._eventSocket.onopen = () => {
6073
6132
  this._wsReconnectAttempts = 0;
@@ -6085,7 +6144,8 @@ var CliServerConnection = class _CliServerConnection {
6085
6144
  } catch {
6086
6145
  }
6087
6146
  };
6088
- this._eventSocket.onclose = () => {
6147
+ this._eventSocket.onclose = (event) => {
6148
+ if (this.isSessionEndedClose(event)) return;
6089
6149
  if (this._connected) {
6090
6150
  this.scheduleWsReconnect();
6091
6151
  }
@@ -6096,6 +6156,16 @@ var CliServerConnection = class _CliServerConnection {
6096
6156
  this._logger?.debug(`WebSocket not available for "${this._config.name}": ${e}`);
6097
6157
  }
6098
6158
  }
6159
+ /**
6160
+ * The server closes the event socket with 4401 when the session behind it
6161
+ * ended (logout, revoke, expiry). Retrying with the same credentials cannot
6162
+ * succeed; the auth layer reconnects the server when the session changes.
6163
+ */
6164
+ isSessionEndedClose(event) {
6165
+ if (event?.code !== 4401) return false;
6166
+ this.closeEventSocketDirect();
6167
+ return true;
6168
+ }
6099
6169
  handleServerDisconnect() {
6100
6170
  this._connected = false;
6101
6171
  this._commands = [];
@@ -6170,7 +6240,7 @@ var CliServerConnection = class _CliServerConnection {
6170
6240
  const controller = new AbortController();
6171
6241
  const timer = setTimeout(() => controller.abort(), timeout);
6172
6242
  const headers = {
6173
- ...this._config.headers ?? {},
6243
+ ...this.resolveAuthHeaders(),
6174
6244
  ...init?.headers ?? {}
6175
6245
  };
6176
6246
  return fetch(url, {
@@ -6251,7 +6321,7 @@ async function executeOnServer(connection, serverName, descriptor, command2, con
6251
6321
  chainCommands: commandPath && commandPath.length > 1 ? commandPath.slice(1) : []
6252
6322
  };
6253
6323
  const cmdLabel = commandPath && commandPath.length > 0 ? commandPath.join(" ") : descriptor.command;
6254
- context2.setStatusText(`executing command: ${cmdLabel} on server ${serverName}`);
6324
+ context2.notifier.info(`executing command: ${cmdLabel} on server ${serverName}`);
6255
6325
  try {
6256
6326
  if (connection.capabilities?.streaming) {
6257
6327
  const result = await connection.executeStream(
@@ -6306,6 +6376,7 @@ var CliServerProxyProcessor = class _CliServerProxyProcessor {
6306
6376
  this.processors = descriptor.processors?.map(
6307
6377
  (sub) => new _CliServerProxyProcessor(connection, sub, serverName, true, this.commandPath)
6308
6378
  );
6379
+ this.acceptsRawInput = !this.processors?.length;
6309
6380
  }
6310
6381
  async processCommand(command2, context2) {
6311
6382
  return executeOnServer(
@@ -6354,6 +6425,7 @@ var CliServerMultiProxyProcessor = class _CliServerMultiProxyProcessor {
6354
6425
  }).filter((e) => e !== null);
6355
6426
  return new _CliServerMultiProxyProcessor(subEntries, this.provider, this.commandPath);
6356
6427
  });
6428
+ this.acceptsRawInput = !this.processors?.length;
6357
6429
  }
6358
6430
  async processCommand(command2, context2) {
6359
6431
  const defaultServer = this.provider.defaultServer;
@@ -6439,14 +6511,26 @@ var CliServerMultiProxyProcessor = class _CliServerMultiProxyProcessor {
6439
6511
  // src/lib/server/cli-server-manager.ts
6440
6512
  var CliServerManager_TOKEN = "cli-server-manager";
6441
6513
  var CliServerManager = class {
6442
- constructor(registry) {
6514
+ constructor(registry, authService) {
6443
6515
  this.registry = registry;
6516
+ this.authService = authService;
6444
6517
  this.connections = /* @__PURE__ */ new Map();
6445
6518
  this._defaultServer = null;
6519
+ /** Bare-name aliases this manager registered (only these are ever removed) */
6520
+ this._bareAliases = /* @__PURE__ */ new Set();
6446
6521
  }
6447
6522
  get defaultServer() {
6448
6523
  return this._defaultServer;
6449
6524
  }
6525
+ preferredServer() {
6526
+ if (this._defaultServer && this.connections.get(this._defaultServer)?.connected) {
6527
+ return this._defaultServer;
6528
+ }
6529
+ for (const [name, connection] of this.connections) {
6530
+ if (connection.connected) return name;
6531
+ }
6532
+ return null;
6533
+ }
6450
6534
  setDefaultServer(name) {
6451
6535
  if (name !== null && !this.connections.has(name)) {
6452
6536
  throw new Error(`Unknown server: ${name}`);
@@ -6461,7 +6545,7 @@ var CliServerManager = class {
6461
6545
  this._backgroundServices = backgroundServices;
6462
6546
  for (const config of servers) {
6463
6547
  if (config.enabled === false) continue;
6464
- const connection = new CliServerConnection(config, backgroundServices, cliLogger);
6548
+ const connection = new CliServerConnection(config, backgroundServices, cliLogger, this.authService);
6465
6549
  this.connections.set(config.name, connection);
6466
6550
  connection.onDisconnect = () => {
6467
6551
  this.handleDisconnect(config.name);
@@ -6509,6 +6593,7 @@ var CliServerManager = class {
6509
6593
  await connection.connect();
6510
6594
  if (connection.connected) {
6511
6595
  this.registerProxyProcessors(connection, name);
6596
+ this.unregisterBareAliases();
6512
6597
  this.registerBareAliases();
6513
6598
  return { success: true, commandCount: connection.commands.length };
6514
6599
  }
@@ -6539,11 +6624,12 @@ var CliServerManager = class {
6539
6624
  this.registerBareAliases();
6540
6625
  }
6541
6626
  unregisterBareAliases() {
6542
- for (const p of [...this.registry.processors]) {
6543
- if (p.metadata?.module === "server:multi" || p.metadata?.requireServer && !p.command.includes(":")) {
6627
+ for (const p of this._bareAliases) {
6628
+ if (this.registry.processors.includes(p)) {
6544
6629
  this.registry.unregisterProcessor(p);
6545
6630
  }
6546
6631
  }
6632
+ this._bareAliases.clear();
6547
6633
  }
6548
6634
  registerProxyProcessors(connection, serverName) {
6549
6635
  for (const descriptor of connection.commands) {
@@ -6567,7 +6653,7 @@ var CliServerManager = class {
6567
6653
  }
6568
6654
  for (const [command2, servers] of commandCounts) {
6569
6655
  const existingProcessor = this.registry.findProcessor(command2, []);
6570
- if (existingProcessor && !existingProcessor.metadata?.requireServer)
6656
+ if (existingProcessor && !this._bareAliases.has(existingProcessor))
6571
6657
  continue;
6572
6658
  if (servers.length === 1) {
6573
6659
  const serverName = servers[0];
@@ -6589,6 +6675,7 @@ var CliServerManager = class {
6589
6675
  alias.command = command2;
6590
6676
  alias.aliases = [namespacedCommand];
6591
6677
  this.registry.registerProcessor(alias);
6678
+ this._bareAliases.add(alias);
6592
6679
  } else {
6593
6680
  const entries = servers.map((serverName) => {
6594
6681
  const connection = this.connections.get(serverName);
@@ -6599,6 +6686,7 @@ var CliServerManager = class {
6599
6686
  });
6600
6687
  const multi = new CliServerMultiProxyProcessor(entries, this);
6601
6688
  this.registry.registerProcessor(multi);
6689
+ this._bareAliases.add(multi);
6602
6690
  }
6603
6691
  }
6604
6692
  }
@@ -6744,6 +6832,28 @@ var WASM_SEARCH_PATHS = [
6744
6832
  "./assets/wasm/qodalis_cli_wasm_bg.wasm",
6745
6833
  "/wasm/qodalis_cli_wasm_bg.wasm"
6746
6834
  ];
6835
+ function wasmCandidates(wasmUrl, baseUri) {
6836
+ const baseUrl = (typeof document !== "undefined" ? document.baseURI : void 0);
6837
+ let base;
6838
+ try {
6839
+ base = baseUrl ? new URL(baseUrl) : void 0;
6840
+ } catch {
6841
+ base = void 0;
6842
+ }
6843
+ const resolve = (url) => {
6844
+ if (!base) return url;
6845
+ try {
6846
+ return new URL(url, base).href;
6847
+ } catch {
6848
+ return url;
6849
+ }
6850
+ };
6851
+ const candidates = [];
6852
+ if (wasmUrl) candidates.push(resolve(wasmUrl));
6853
+ if (base) candidates.push(resolve("assets/wasm/qodalis_cli_wasm_bg.wasm"));
6854
+ candidates.push(...WASM_SEARCH_PATHS.map(resolve));
6855
+ return [...new Set(candidates)];
6856
+ }
6747
6857
  var wasm;
6748
6858
  var cachedDataViewMemory0 = null;
6749
6859
  var cachedInt32ArrayMemory0 = null;
@@ -6972,7 +7082,7 @@ async function initWasmAccelerator(wasmUrl) {
6972
7082
  return accelerator;
6973
7083
  }
6974
7084
  try {
6975
- const paths = wasmUrl ? [wasmUrl, ...WASM_SEARCH_PATHS] : WASM_SEARCH_PATHS;
7085
+ const paths = wasmCandidates(wasmUrl);
6976
7086
  const bytes = await fetchWasmBinary(paths);
6977
7087
  await instantiateWasm(bytes);
6978
7088
  accelerator = createWasmAccelerator();
@@ -7012,7 +7122,7 @@ var CliDebugCommandProcessor = class {
7012
7122
  metadata: { icon: CliIcon.Extension, module: "system" },
7013
7123
  processCommand: async (_, context2) => {
7014
7124
  const { writer } = context2;
7015
- const registry = context2.services.get(
7125
+ const registry = context2.services.getRequired(
7016
7126
  CliProcessorsRegistry_TOKEN
7017
7127
  );
7018
7128
  writer.writeln(
@@ -7063,12 +7173,10 @@ var CliDebugCommandProcessor = class {
7063
7173
  metadata: { icon: CliIcon.Module, module: "system" },
7064
7174
  processCommand: async (_, context2) => {
7065
7175
  const { writer } = context2;
7066
- let moduleRegistry;
7067
- try {
7068
- moduleRegistry = context2.services.get(
7069
- CliModuleRegistry_TOKEN
7070
- );
7071
- } catch {
7176
+ const moduleRegistry = context2.services.get(
7177
+ CliModuleRegistry_TOKEN
7178
+ );
7179
+ if (!moduleRegistry) {
7072
7180
  writer.writeError("Module registry not available.");
7073
7181
  return;
7074
7182
  }
@@ -7109,7 +7217,7 @@ var CliDebugCommandProcessor = class {
7109
7217
  metadata: { icon: CliIcon.Database, module: "system" },
7110
7218
  processCommand: async (_, context2) => {
7111
7219
  const { writer } = context2;
7112
- const storeManager = context2.services.get(
7220
+ const storeManager = context2.services.getRequired(
7113
7221
  CliStateStoreManager_TOKEN
7114
7222
  );
7115
7223
  const entries = storeManager.getStoreEntries();
@@ -7188,11 +7296,7 @@ var CliDebugCommandProcessor = class {
7188
7296
  writer.writeln(
7189
7297
  ` ${writer.wrapInColor("CLI Version:", CliForegroundColor.Cyan)} ${LIBRARY_VERSION}`
7190
7298
  );
7191
- let framework = "vanilla";
7192
- try {
7193
- framework = context2.services.get("cli-framework");
7194
- } catch {
7195
- }
7299
+ const framework = context2.services.get("cli-framework") ?? "vanilla";
7196
7300
  writer.writeln(
7197
7301
  ` ${writer.wrapInColor("Framework:", CliForegroundColor.Cyan)} ${framework}`
7198
7302
  );
@@ -7233,7 +7337,7 @@ var CliDebugCommandProcessor = class {
7233
7337
  metadata: { icon: CliIcon.Logs, module: "system" },
7234
7338
  processCommand: async (_, context2) => {
7235
7339
  const { writer } = context2;
7236
- const history = context2.services.get(
7340
+ const history = context2.services.getRequired(
7237
7341
  CliCommandHistory_TOKEN
7238
7342
  );
7239
7343
  const commands = history.getHistory();
@@ -7322,14 +7426,14 @@ var CliDebugCommandProcessor = class {
7322
7426
  writer.writeln(
7323
7427
  ` ${online ? CliIcon.CheckIcon : CliIcon.CrossIcon} Network: ${online ? "online" : "offline"}`
7324
7428
  );
7325
- const registry = context2.services.get(
7429
+ const registry = context2.services.getRequired(
7326
7430
  CliProcessorsRegistry_TOKEN
7327
7431
  );
7328
7432
  writer.writeln(
7329
7433
  ` ${CliIcon.CheckIcon} Processors: ${registry.processors.length} loaded`
7330
7434
  );
7331
7435
  try {
7332
- const moduleRegistry = context2.services.get(
7436
+ const moduleRegistry = context2.services.getRequired(
7333
7437
  CliModuleRegistry_TOKEN
7334
7438
  );
7335
7439
  const modules = moduleRegistry.getAll();
@@ -7345,7 +7449,7 @@ var CliDebugCommandProcessor = class {
7345
7449
  writer.writeln(
7346
7450
  ` ${wasmActive ? CliIcon.CheckIcon : CliIcon.WarningIcon} WASM Accelerator: ${wasmActive ? "loaded" : "using JS fallback"}`
7347
7451
  );
7348
- const historyService = context2.services.get(
7452
+ const historyService = context2.services.getRequired(
7349
7453
  CliCommandHistory_TOKEN
7350
7454
  );
7351
7455
  writer.writeln(
@@ -7359,13 +7463,13 @@ var CliDebugCommandProcessor = class {
7359
7463
  metadata: { icon: CliIcon.Save, module: "system" },
7360
7464
  processCommand: async (_, context2) => {
7361
7465
  const { writer } = context2;
7362
- const registry = context2.services.get(
7466
+ const registry = context2.services.getRequired(
7363
7467
  CliProcessorsRegistry_TOKEN
7364
7468
  );
7365
- const history = context2.services.get(
7469
+ const history = context2.services.getRequired(
7366
7470
  CliCommandHistory_TOKEN
7367
7471
  );
7368
- const storeManager = context2.services.get(
7472
+ const storeManager = context2.services.getRequired(
7369
7473
  CliStateStoreManager_TOKEN
7370
7474
  );
7371
7475
  const report = {
@@ -7398,7 +7502,7 @@ var CliDebugCommandProcessor = class {
7398
7502
  state: storeManager.getStoreEntries()
7399
7503
  };
7400
7504
  try {
7401
- const moduleRegistry = context2.services.get(
7505
+ const moduleRegistry = context2.services.getRequired(
7402
7506
  CliModuleRegistry_TOKEN
7403
7507
  );
7404
7508
  report["modules"] = moduleRegistry.getAll().map((m) => ({
@@ -7416,7 +7520,7 @@ var CliDebugCommandProcessor = class {
7416
7520
  report["services"] = container.getRegisteredTokens();
7417
7521
  }
7418
7522
  try {
7419
- const serverManager = context2.services.get(
7523
+ const serverManager = context2.services.getRequired(
7420
7524
  CliServerManager_TOKEN
7421
7525
  );
7422
7526
  if (serverManager && serverManager.connections.size > 0) {
@@ -7440,13 +7544,9 @@ var CliDebugCommandProcessor = class {
7440
7544
  metadata: { icon: CliIcon.Server, module: "system" },
7441
7545
  processCommand: async (_, context2) => {
7442
7546
  const { writer } = context2;
7443
- let manager;
7444
- try {
7445
- manager = context2.services.get(
7446
- CliServerManager_TOKEN
7447
- );
7448
- } catch {
7449
- }
7547
+ const manager = context2.services.get(
7548
+ CliServerManager_TOKEN
7549
+ );
7450
7550
  if (!manager || manager.connections.size === 0) {
7451
7551
  writer.writeInfo("No servers configured.");
7452
7552
  return;
@@ -7486,70 +7586,48 @@ var CliDebugCommandProcessor = class {
7486
7586
  }
7487
7587
  async processCommand(_, context2) {
7488
7588
  const { writer } = context2;
7489
- const registry = context2.services.get(
7589
+ const registry = context2.services.getRequired(
7490
7590
  CliProcessorsRegistry_TOKEN
7491
7591
  );
7492
- writer.writeln(
7493
- writer.wrapInColor(
7494
- `${CliIcon.Bug} CLI Debug Info`,
7495
- CliForegroundColor.Yellow
7496
- )
7497
- );
7498
- writer.writeDivider({ char: "=" });
7499
- writer.writeln(
7500
- ` ${writer.wrapInColor("Core Version:", CliForegroundColor.Cyan)} ${LIBRARY_VERSION$1}`
7501
- );
7502
- writer.writeln(
7503
- ` ${writer.wrapInColor("CLI Version:", CliForegroundColor.Cyan)} ${LIBRARY_VERSION}`
7592
+ const summaryFramework = context2.services.get("cli-framework") ?? "vanilla";
7593
+ const summaryModuleRegistry = context2.services.get(
7594
+ CliModuleRegistry_TOKEN
7504
7595
  );
7505
- let framework = "vanilla";
7506
- try {
7507
- framework = context2.services.get("cli-framework");
7508
- } catch {
7509
- }
7510
- writer.writeln(
7511
- ` ${writer.wrapInColor("Framework:", CliForegroundColor.Cyan)} ${framework}`
7512
- );
7513
- writer.writeln(
7514
- ` ${writer.wrapInColor("Terminal:", CliForegroundColor.Cyan)} ${context2.terminal.cols}x${context2.terminal.rows}`
7596
+ const summaryServerManager = context2.services.get(
7597
+ CliServerManager_TOKEN
7515
7598
  );
7516
- writer.writeln(
7517
- ` ${writer.wrapInColor("Processors:", CliForegroundColor.Cyan)} ${registry.processors.length}`
7599
+ const history = context2.services.getRequired(
7600
+ CliCommandHistory_TOKEN
7518
7601
  );
7519
- try {
7520
- const moduleRegistry = context2.services.get(
7521
- CliModuleRegistry_TOKEN
7522
- );
7523
- writer.writeln(
7524
- ` ${writer.wrapInColor("Modules:", CliForegroundColor.Cyan)} ${moduleRegistry.getAll().length}`
7602
+ const wasmStatus = isWasmAccelerated() ? "active" : "inactive (JS fallback)";
7603
+ const lines = [
7604
+ `${writer.wrapInColor("Core Version:", CliForegroundColor.Cyan)} ${LIBRARY_VERSION$1}`,
7605
+ `${writer.wrapInColor("CLI Version:", CliForegroundColor.Cyan)} ${LIBRARY_VERSION}`,
7606
+ `${writer.wrapInColor("Framework:", CliForegroundColor.Cyan)} ${summaryFramework}`,
7607
+ `${writer.wrapInColor("Terminal:", CliForegroundColor.Cyan)} ${context2.terminal.cols}x${context2.terminal.rows}`,
7608
+ `${writer.wrapInColor("Processors:", CliForegroundColor.Cyan)} ${registry.processors.length}`
7609
+ ];
7610
+ if (summaryModuleRegistry) {
7611
+ lines.push(
7612
+ `${writer.wrapInColor("Modules:", CliForegroundColor.Cyan)} ${summaryModuleRegistry.getAll().length}`
7525
7613
  );
7526
- } catch {
7527
7614
  }
7528
- try {
7529
- const serverManager = context2.services.get(
7530
- CliServerManager_TOKEN
7615
+ if (summaryServerManager && summaryServerManager.connections.size > 0) {
7616
+ const connected = Array.from(
7617
+ summaryServerManager.connections.values()
7618
+ ).filter((c) => c.connected).length;
7619
+ lines.push(
7620
+ `${writer.wrapInColor("Servers:", CliForegroundColor.Cyan)} ${connected}/${summaryServerManager.connections.size} connected`
7531
7621
  );
7532
- if (serverManager && serverManager.connections.size > 0) {
7533
- const connected = Array.from(
7534
- serverManager.connections.values()
7535
- ).filter((c) => c.connected).length;
7536
- writer.writeln(
7537
- ` ${writer.wrapInColor("Servers:", CliForegroundColor.Cyan)} ${connected}/${serverManager.connections.size} connected`
7538
- );
7539
- }
7540
- } catch {
7541
7622
  }
7542
- const history = context2.services.get(
7543
- CliCommandHistory_TOKEN
7623
+ lines.push(
7624
+ `${writer.wrapInColor("History:", CliForegroundColor.Cyan)} ${history.getHistory().length} commands`,
7625
+ `${writer.wrapInColor("WASM Accelerator:", CliForegroundColor.Cyan)} ${wasmStatus}`
7544
7626
  );
7545
- writer.writeln(
7546
- ` ${writer.wrapInColor("History:", CliForegroundColor.Cyan)} ${history.getHistory().length} commands`
7547
- );
7548
- const wasmStatus = isWasmAccelerated() ? "active" : "inactive (JS fallback)";
7549
- writer.writeln(
7550
- ` ${writer.wrapInColor("WASM Accelerator:", CliForegroundColor.Cyan)} ${wasmStatus}`
7551
- );
7552
- writer.writeDivider({ char: "=" });
7627
+ writer.writeBox(lines, {
7628
+ title: "CLI Debug Info",
7629
+ borderColor: CliForegroundColor.Yellow
7630
+ });
7553
7631
  writer.writeln();
7554
7632
  writer.writeln(`${CliIcon.Light} Subcommands:`);
7555
7633
  for (const sub of this.processors || []) {
@@ -8135,7 +8213,7 @@ var CliNanoCommandProcessor = class {
8135
8213
  this.replaceWith = "";
8136
8214
  this.statusMessage = void 0;
8137
8215
  try {
8138
- this.fs = context2.services.get(FS_TOKEN);
8216
+ this.fs = context2.services.getRequired(FS_TOKEN);
8139
8217
  } catch {
8140
8218
  this.fs = null;
8141
8219
  }
@@ -8474,8 +8552,8 @@ var CliNanoCommandProcessor = class {
8474
8552
  return;
8475
8553
  }
8476
8554
  try {
8477
- const registry = this.context.services.get(
8478
- "syntax-highlighter-registry"
8555
+ const registry = this.context.services.getRequired(
8556
+ SyntaxHighlighterRegistry_TOKEN
8479
8557
  );
8480
8558
  const ext = "." + filePath.split(".").pop()?.toLowerCase();
8481
8559
  const highlighter = registry.getByExtension(ext);
@@ -8740,7 +8818,9 @@ var CliServicesCommandProcessor = class {
8740
8818
  this.metadata = {
8741
8819
  sealed: true,
8742
8820
  icon: "\u2699",
8743
- module: "system"
8821
+ module: "system",
8822
+ // Enforced only when an auth module is loaded
8823
+ requiredPermissions: ["system:services"]
8744
8824
  };
8745
8825
  this.processors = [
8746
8826
  // ── list ──────────────────────────────────────────────
@@ -9112,7 +9192,7 @@ var CliPsCommandProcessor = class {
9112
9192
  async processCommand(_command, context2) {
9113
9193
  let registry;
9114
9194
  try {
9115
- registry = context2.services.get(
9195
+ registry = context2.services.getRequired(
9116
9196
  CliProcessRegistry_TOKEN
9117
9197
  );
9118
9198
  } catch {
@@ -9151,7 +9231,7 @@ var CliKillCommandProcessor = class {
9151
9231
  async processCommand(command2, context2) {
9152
9232
  let registry;
9153
9233
  try {
9154
- registry = context2.services.get(
9234
+ registry = context2.services.getRequired(
9155
9235
  CliProcessRegistry_TOKEN
9156
9236
  );
9157
9237
  } catch {
@@ -9266,11 +9346,10 @@ var CliHtopCommandProcessor = class {
9266
9346
  this.errorTimeout = null;
9267
9347
  }
9268
9348
  async processCommand(_command, context2) {
9269
- try {
9270
- this.registry = context2.services.get(
9271
- CliProcessRegistry_TOKEN
9272
- );
9273
- } catch {
9349
+ this.registry = context2.services.get(
9350
+ CliProcessRegistry_TOKEN
9351
+ ) ?? null;
9352
+ if (!this.registry) {
9274
9353
  context2.writer.writeError("Process registry not available");
9275
9354
  return;
9276
9355
  }
@@ -10410,7 +10489,7 @@ var CliPingCommandProcessor = class {
10410
10489
  context2.writer.writeln(
10411
10490
  `PING ${host} (${url}):`
10412
10491
  );
10413
- context2.setStatusText(`ping ${host}`);
10492
+ context2.notifier.info(`ping ${host}`);
10414
10493
  const times = [];
10415
10494
  let transmitted = 0;
10416
10495
  let received = 0;
@@ -10425,18 +10504,17 @@ var CliPingCommandProcessor = class {
10425
10504
  transmitted++;
10426
10505
  const start = performance.now();
10427
10506
  try {
10428
- const response = await fetch(url, {
10507
+ const response = await context2.http.fetch(url, {
10429
10508
  method: "HEAD",
10430
10509
  mode: "no-cors",
10431
- cache: "no-store",
10432
- signal: context2.signal
10510
+ cache: "no-store"
10433
10511
  });
10434
10512
  const elapsed = performance.now() - start;
10435
10513
  const ms = elapsed.toFixed(1);
10436
10514
  received++;
10437
10515
  times.push(elapsed);
10438
10516
  const status = response.type === "opaque" ? "ok" : `status=${response.status}`;
10439
- context2.setStatusText(`ping ${host}: seq=${seq} time=${ms}ms`);
10517
+ context2.notifier.info(`ping ${host}: seq=${seq} time=${ms}ms`);
10440
10518
  context2.writer.writeln(
10441
10519
  `${url}: seq=${seq} ${status} time=${ms} ms`
10442
10520
  );
@@ -10570,7 +10648,7 @@ var CliExportCommandProcessor = class {
10570
10648
  this.metadata = { icon: "\u{1F4E6}", module: "system" };
10571
10649
  }
10572
10650
  async processCommand(command2, context2) {
10573
- const env = context2.services.get(
10651
+ const env = context2.services.getRequired(
10574
10652
  ICliEnvironment_TOKEN
10575
10653
  );
10576
10654
  const raw = (command2.value || "").trim();
@@ -10624,7 +10702,7 @@ var CliUnsetCommandProcessor = class {
10624
10702
  this.metadata = { icon: "\u{1F5D1}\uFE0F", module: "system" };
10625
10703
  }
10626
10704
  async processCommand(command2, context2) {
10627
- const env = context2.services.get(
10705
+ const env = context2.services.getRequired(
10628
10706
  ICliEnvironment_TOKEN
10629
10707
  );
10630
10708
  const varName = (command2.value || "").trim();
@@ -10658,7 +10736,7 @@ var CliEnvCommandProcessor = class {
10658
10736
  this.metadata = { icon: "\u{1F30D}", module: "system" };
10659
10737
  }
10660
10738
  async processCommand(command2, context2) {
10661
- const env = context2.services.get(
10739
+ const env = context2.services.getRequired(
10662
10740
  ICliEnvironment_TOKEN
10663
10741
  );
10664
10742
  const varName = (command2.value || "").trim();
@@ -10906,16 +10984,9 @@ var CliCommandExecutionContext = class {
10906
10984
  this.createInterval = (cb, ms) => context2.createInterval(cb, ms);
10907
10985
  this.createTimeout = (cb, ms) => context2.createTimeout(cb, ms);
10908
10986
  this.backgroundServices = context2.backgroundServices;
10909
- this.state = context2.services.get(CliStateStoreManager_TOKEN).getProcessorStateStore(processor);
10910
- }
10911
- setStatusText(text) {
10912
- this.context.setStatusText(text);
10913
- }
10914
- clearStatusText() {
10915
- this.context.clearStatusText();
10916
- }
10917
- getStatusText() {
10918
- return this.context.getStatusText();
10987
+ this.http = context2.http;
10988
+ this.notifier = context2.notifier;
10989
+ this.state = context2.services.getRequired(CliStateStoreManager_TOKEN).getProcessorStateStore(processor);
10919
10990
  }
10920
10991
  get promptLength() {
10921
10992
  return this.context.promptLength;
@@ -11465,6 +11536,175 @@ var CliBackgroundServiceRegistry = class {
11465
11536
  }
11466
11537
  }
11467
11538
  };
11539
+ var CliHttpClient = class {
11540
+ constructor(signal) {
11541
+ this.signal = signal;
11542
+ }
11543
+ async fetch(url, init) {
11544
+ return fetch(url, {
11545
+ ...init,
11546
+ signal: this.mergeSignal(init?.signal)
11547
+ });
11548
+ }
11549
+ async get(url, options) {
11550
+ return this.jsonRequest("GET", url, void 0, options);
11551
+ }
11552
+ async post(url, body, options) {
11553
+ return this.jsonRequest("POST", url, body, options);
11554
+ }
11555
+ async put(url, body, options) {
11556
+ return this.jsonRequest("PUT", url, body, options);
11557
+ }
11558
+ async patch(url, body, options) {
11559
+ return this.jsonRequest("PATCH", url, body, options);
11560
+ }
11561
+ async delete(url, options) {
11562
+ return this.jsonRequest("DELETE", url, void 0, options);
11563
+ }
11564
+ async head(url, options) {
11565
+ const { timeout, signal: callerSignal, ...fetchOptions } = options ?? {};
11566
+ let timeoutController;
11567
+ let timeoutId;
11568
+ if (timeout) {
11569
+ timeoutController = new AbortController();
11570
+ timeoutId = setTimeout(() => timeoutController.abort(), timeout);
11571
+ }
11572
+ try {
11573
+ return await fetch(url, {
11574
+ ...fetchOptions,
11575
+ method: "HEAD",
11576
+ signal: this.mergeSignal(callerSignal, timeoutController?.signal)
11577
+ });
11578
+ } finally {
11579
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
11580
+ }
11581
+ }
11582
+ async options(url, options) {
11583
+ return this.jsonRequest("OPTIONS", url, void 0, options);
11584
+ }
11585
+ // ── Internals ────────────────────────────────────────────────────
11586
+ async jsonRequest(method, url, body, options) {
11587
+ const { timeout, ...fetchOptions } = options ?? {};
11588
+ const headers = new Headers(fetchOptions.headers);
11589
+ let resolvedBody;
11590
+ if (body !== void 0 && body !== null) {
11591
+ if (this.isRawBody(body)) {
11592
+ resolvedBody = body;
11593
+ } else {
11594
+ resolvedBody = JSON.stringify(body);
11595
+ if (!headers.has("Content-Type")) {
11596
+ headers.set("Content-Type", "application/json");
11597
+ }
11598
+ }
11599
+ }
11600
+ let timeoutId;
11601
+ let timeoutController;
11602
+ if (timeout) {
11603
+ timeoutController = new AbortController();
11604
+ timeoutId = setTimeout(() => timeoutController.abort(), timeout);
11605
+ }
11606
+ const signal = this.mergeSignal(
11607
+ fetchOptions.signal,
11608
+ timeoutController?.signal
11609
+ );
11610
+ try {
11611
+ const response = await fetch(url, {
11612
+ ...fetchOptions,
11613
+ method,
11614
+ headers,
11615
+ body: resolvedBody,
11616
+ signal
11617
+ });
11618
+ if (!response.ok) {
11619
+ let errorMessage = `${response.status} ${response.statusText}`;
11620
+ let errorBody;
11621
+ try {
11622
+ errorBody = await response.json();
11623
+ const parsed = errorBody;
11624
+ if (parsed.error) {
11625
+ errorMessage = parsed.error;
11626
+ } else if (parsed.message) {
11627
+ errorMessage = parsed.message;
11628
+ }
11629
+ } catch {
11630
+ }
11631
+ throw new CliHttpError(
11632
+ errorMessage,
11633
+ response.status,
11634
+ response.statusText,
11635
+ errorBody
11636
+ );
11637
+ }
11638
+ const text = await response.text();
11639
+ if (!text) {
11640
+ return void 0;
11641
+ }
11642
+ return JSON.parse(text);
11643
+ } finally {
11644
+ if (timeoutId !== void 0) {
11645
+ clearTimeout(timeoutId);
11646
+ }
11647
+ }
11648
+ }
11649
+ isRawBody(body) {
11650
+ return typeof body === "string" || body instanceof FormData || body instanceof Blob || body instanceof ArrayBuffer || body instanceof URLSearchParams || body instanceof ReadableStream;
11651
+ }
11652
+ /**
11653
+ * Merge the command signal with any additional signals (caller, timeout).
11654
+ * Uses AbortSignal.any() when available, falls back to a manual composite
11655
+ * controller for older browsers (pre-Chrome 116 / Safari 17.4).
11656
+ */
11657
+ mergeSignal(...additional) {
11658
+ const signals = [];
11659
+ if (this.signal) signals.push(this.signal);
11660
+ for (const s of additional) {
11661
+ if (s) signals.push(s);
11662
+ }
11663
+ if (signals.length === 0) return void 0;
11664
+ if (signals.length === 1) return signals[0];
11665
+ if (typeof AbortSignal.any === "function") {
11666
+ return AbortSignal.any(signals);
11667
+ }
11668
+ const controller = new AbortController();
11669
+ for (const s of signals) {
11670
+ if (s.aborted) {
11671
+ controller.abort(s.reason);
11672
+ return controller.signal;
11673
+ }
11674
+ s.addEventListener("abort", () => controller.abort(s.reason), { once: true });
11675
+ }
11676
+ return controller.signal;
11677
+ }
11678
+ };
11679
+ var CliNotifier = class {
11680
+ constructor() {
11681
+ this._change$ = new Subject();
11682
+ this.change$ = this._change$.asObservable();
11683
+ }
11684
+ get current() {
11685
+ return this._current;
11686
+ }
11687
+ info(message) {
11688
+ this.set("info", message);
11689
+ }
11690
+ success(message) {
11691
+ this.set("success", message);
11692
+ }
11693
+ warn(message) {
11694
+ this.set("warn", message);
11695
+ }
11696
+ error(message) {
11697
+ this.set("error", message);
11698
+ }
11699
+ clear() {
11700
+ this._current = void 0;
11701
+ this._change$.next(void 0);
11702
+ }
11703
+ set(level, message) {
11704
+ this._current = { level, message, timestamp: Date.now() };
11705
+ this._change$.next(this._current);
11706
+ }
11707
+ };
11468
11708
 
11469
11709
  // src/lib/context/cli-fullscreen-manager.ts
11470
11710
  var CliFullScreenManager = class {
@@ -14426,7 +14666,7 @@ var CliTerminalLineRenderer = class {
14426
14666
  */
14427
14667
  getPromptString(options) {
14428
14668
  const name = options.userName ?? "";
14429
- let promptStart = options.hideUserName || !name ? "" : `\x1B[32m${name}\x1B[0m:`;
14669
+ let promptStart = options.hideUserName ? "" : name ? `\x1B[32m${name}\x1B[0m:` : options.anonymousName ? `\x1B[90m${options.anonymousName}\x1B[0m:` : "";
14430
14670
  if (options.contextProcessor) {
14431
14671
  promptStart = `${options.contextProcessor}`;
14432
14672
  }
@@ -14488,7 +14728,6 @@ var CliExecutionContext = class {
14488
14728
  * Set by the command executor at start/end of executeCommand().
14489
14729
  */
14490
14730
  this.isExecuting = false;
14491
- this.statusTextChange$ = new Subject();
14492
14731
  this.lineBuffer = new CliLineBuffer();
14493
14732
  this.modeStack = [];
14494
14733
  this.timerManager = new CliTimerManager();
@@ -14533,6 +14772,8 @@ var CliExecutionContext = class {
14533
14772
  this.logger.setCliLogLevel(cliOptions?.logLevel || CliLogLevel.ERROR);
14534
14773
  this.commandHistory = deps.commandHistory;
14535
14774
  this.lineRenderer = new CliTerminalLineRenderer(terminal, this.writer);
14775
+ this.http = new CliHttpClient();
14776
+ this.notifier = new CliNotifier();
14536
14777
  this.backgroundServices = new CliBackgroundServiceRegistry(
14537
14778
  this.state,
14538
14779
  deps.services,
@@ -14543,17 +14784,6 @@ var CliExecutionContext = class {
14543
14784
  this.backgroundServices
14544
14785
  );
14545
14786
  }
14546
- setStatusText(text) {
14547
- this._statusText = text;
14548
- this.statusTextChange$.next(text);
14549
- }
14550
- clearStatusText() {
14551
- this._statusText = void 0;
14552
- this.statusTextChange$.next(void 0);
14553
- }
14554
- getStatusText() {
14555
- return this._statusText;
14556
- }
14557
14787
  // -- Public API (ICliExecutionContext) --
14558
14788
  writeToTerminal(text) {
14559
14789
  this.terminal.write(text);
@@ -14747,15 +14977,13 @@ var CliExecutionContext = class {
14747
14977
  }
14748
14978
  // -- CommandLineModeHost interface --
14749
14979
  getPromptOptions() {
14750
- let hideUserName = false;
14751
- try {
14752
- const config = this.services.get("cli-users-module-config");
14753
- hideUserName = config?.hideUserName ?? false;
14754
- } catch {
14755
- }
14980
+ const usersConfig = this.services.get("cli-users-module-config");
14981
+ const hideUserName = usersConfig?.hideUserName ?? false;
14982
+ const anonymousName = usersConfig && !this.userSession ? (usersConfig.anonymousName ?? "anonymous") || void 0 : void 0;
14756
14983
  return {
14757
14984
  userName: this.userSession?.displayName,
14758
14985
  hideUserName,
14986
+ anonymousName,
14759
14987
  contextProcessor: this.contextProcessor?.command,
14760
14988
  pathProvider: this.promptPathProvider
14761
14989
  };
@@ -15000,10 +15228,8 @@ var contextGlobalParameter = {
15000
15228
  // src/lib/executor/cli-io-redirect-handler.ts
15001
15229
  var FS_TOKEN2 = "cli-file-system-service";
15002
15230
  async function writeOutputToFile(filePath, context2) {
15003
- let fs;
15004
- try {
15005
- fs = context2.services.get(FS_TOKEN2);
15006
- } catch {
15231
+ const fs = context2.services.get(FS_TOKEN2);
15232
+ if (!fs) {
15007
15233
  context2.writer.writeError(
15008
15234
  "> redirect requires @qodalis/cli-files plugin"
15009
15235
  );
@@ -15027,10 +15253,8 @@ async function writeOutputToFile(filePath, context2) {
15027
15253
  }
15028
15254
  }
15029
15255
  async function appendOutputToFile(filePath, context2) {
15030
- let fs;
15031
- try {
15032
- fs = context2.services.get(FS_TOKEN2);
15033
- } catch {
15256
+ const fs = context2.services.get(FS_TOKEN2);
15257
+ if (!fs) {
15034
15258
  context2.writer.writeError(
15035
15259
  ">> redirect requires @qodalis/cli-files plugin"
15036
15260
  );
@@ -15056,10 +15280,8 @@ async function appendOutputToFile(filePath, context2) {
15056
15280
  async function writeStderrToFile(filePath, context2, capturingWriter) {
15057
15281
  const stderr = capturingWriter?.getCapturedStderr();
15058
15282
  if (!stderr) return;
15059
- let fs;
15060
- try {
15061
- fs = context2.services.get(FS_TOKEN2);
15062
- } catch {
15283
+ const fs = context2.services.get(FS_TOKEN2);
15284
+ if (!fs) {
15063
15285
  context2.writer.writeError(
15064
15286
  "2> redirect requires @qodalis/cli-files plugin"
15065
15287
  );
@@ -15080,10 +15302,8 @@ async function writeStderrToFile(filePath, context2, capturingWriter) {
15080
15302
  async function appendStderrToFile(filePath, context2, capturingWriter) {
15081
15303
  const stderr = capturingWriter?.getCapturedStderr();
15082
15304
  if (!stderr) return;
15083
- let fs;
15084
- try {
15085
- fs = context2.services.get(FS_TOKEN2);
15086
- } catch {
15305
+ const fs = context2.services.get(FS_TOKEN2);
15306
+ if (!fs) {
15087
15307
  context2.writer.writeError(
15088
15308
  "2>> redirect requires @qodalis/cli-files plugin"
15089
15309
  );
@@ -15103,10 +15323,8 @@ async function appendStderrToFile(filePath, context2, capturingWriter) {
15103
15323
  }
15104
15324
  async function tryExecuteScript(filePath, context2, executeCommand) {
15105
15325
  const FS_TOKEN3 = "cli-file-system-service";
15106
- let fs;
15107
- try {
15108
- fs = context2.services.get(FS_TOKEN3);
15109
- } catch {
15326
+ const fs = context2.services.get(FS_TOKEN3);
15327
+ if (!fs) {
15110
15328
  return false;
15111
15329
  }
15112
15330
  const resolved = fs.resolvePath(filePath);
@@ -15183,10 +15401,8 @@ function substituteVars(input, variables) {
15183
15401
  return result;
15184
15402
  }
15185
15403
  function expandEnvironmentVars(command2, context2) {
15186
- let env;
15187
- try {
15188
- env = context2.services.get(ICliEnvironment_TOKEN);
15189
- } catch {
15404
+ const env = context2.services.get(ICliEnvironment_TOKEN);
15405
+ if (!env) {
15190
15406
  return command2;
15191
15407
  }
15192
15408
  const parts = command2.split("'");
@@ -15331,7 +15547,7 @@ var CliCommandExecutor = class {
15331
15547
  }
15332
15548
  } finally {
15333
15549
  rootContext.isExecuting = false;
15334
- rootContext.clearStatusText?.();
15550
+ rootContext.notifier?.clear();
15335
15551
  }
15336
15552
  }
15337
15553
  async executeSingleCommand(command2, data, context2) {
@@ -15339,8 +15555,8 @@ var CliCommandExecutor = class {
15339
15555
  command2 = expandEnvironmentVars(command2, context2);
15340
15556
  const assignMatch = command2.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
15341
15557
  if (assignMatch) {
15342
- try {
15343
- const env = context2.services.get(ICliEnvironment_TOKEN);
15558
+ const env = context2.services.get(ICliEnvironment_TOKEN);
15559
+ if (env) {
15344
15560
  let val = assignMatch[2];
15345
15561
  if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
15346
15562
  val = val.slice(1, -1);
@@ -15349,15 +15565,13 @@ var CliCommandExecutor = class {
15349
15565
  process.start();
15350
15566
  process.end();
15351
15567
  return;
15352
- } catch {
15353
15568
  }
15354
15569
  }
15355
15570
  process.start();
15356
15571
  let processEntry;
15357
- try {
15358
- const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15572
+ const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15573
+ if (procRegistry) {
15359
15574
  processEntry = procRegistry.register(command2);
15360
- } catch {
15361
15575
  }
15362
15576
  const parsed = this.commandParser.parse(command2);
15363
15577
  let { commandName } = parsed;
@@ -15380,7 +15594,7 @@ var CliCommandExecutor = class {
15380
15594
  }
15381
15595
  if (!processor) {
15382
15596
  const aliasProcessor = this.registry.findProcessor("alias", []);
15383
- const aliases = aliasProcessor?.userAliases ?? {};
15597
+ const aliases = aliasProcessor?.aliasesFor?.(context2) ?? aliasProcessor?.userAliases ?? {};
15384
15598
  if (aliases[mainCommand]) {
15385
15599
  this.completeProcess(processEntry, context2, 0);
15386
15600
  return await this.executeSingleCommand(
@@ -15437,13 +15651,21 @@ var CliCommandExecutor = class {
15437
15651
  }
15438
15652
  const value = processor.acceptsRawInput || processor.valueRequired ? getRightOfWord(commandName, processor.command) : void 0;
15439
15653
  commandToProcess.value = value;
15654
+ const blockedBy = await this.runGuards(commandToProcess, processor, context2);
15655
+ if (blockedBy) {
15656
+ context2.writer.writeError(blockedBy);
15657
+ this.failProcess(processEntry, context2);
15658
+ context2.process.exit(-1, { silent: true });
15659
+ return;
15660
+ }
15440
15661
  const hasPositionalInput = !!value || !!data;
15441
15662
  const hasNamedArgs = Object.keys(args).length > 0;
15442
15663
  if (processor.valueRequired && !hasPositionalInput && !hasNamedArgs) {
15443
15664
  context2.writer.writeError(
15444
15665
  `Value required: ${context2.writer.wrapInColor(`${commandName} <value>`, CliForegroundColor.Cyan)}`
15445
15666
  );
15446
- context2.process.exit(-1);
15667
+ this.failProcess(processEntry, context2);
15668
+ context2.process.exit(-1, { silent: true });
15447
15669
  return;
15448
15670
  }
15449
15671
  if (!this.validateBeforeExecution(context2, processor, args, hasPositionalInput)) {
@@ -15459,7 +15681,8 @@ var CliCommandExecutor = class {
15459
15681
  context2.writer.writeError(
15460
15682
  validationResult?.message || "An error occurred while validating the command."
15461
15683
  );
15462
- context2.process.exit(-1);
15684
+ this.failProcess(processEntry, context2);
15685
+ context2.process.exit(-1, { silent: true });
15463
15686
  return;
15464
15687
  }
15465
15688
  }
@@ -15470,6 +15693,7 @@ var CliCommandExecutor = class {
15470
15693
  );
15471
15694
  const commandAbortController = new AbortController();
15472
15695
  commandContext.signal = commandAbortController.signal;
15696
+ commandContext.http = new CliHttpClient(commandAbortController.signal);
15473
15697
  const abortSub = context2.onAbort.subscribe(() => {
15474
15698
  commandAbortController.abort();
15475
15699
  });
@@ -15522,6 +15746,10 @@ var CliCommandExecutor = class {
15522
15746
  "Process exited successfully with code 0"
15523
15747
  );
15524
15748
  }
15749
+ } else if (isCliAccessDeniedError(e)) {
15750
+ this.failProcess(processEntry, context2);
15751
+ context2.writer.writeError(e.message);
15752
+ context2.process.exit(-1);
15525
15753
  } else {
15526
15754
  this.failProcess(processEntry, context2);
15527
15755
  context2.writer.writeError(`Error executing command: ${e}`);
@@ -15534,19 +15762,13 @@ var CliCommandExecutor = class {
15534
15762
  }
15535
15763
  completeProcess(processEntry, context2, exitCode) {
15536
15764
  if (!processEntry) return;
15537
- try {
15538
- const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15539
- procRegistry.complete(processEntry.pid, exitCode);
15540
- } catch {
15541
- }
15765
+ const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15766
+ procRegistry?.complete(processEntry.pid, exitCode);
15542
15767
  }
15543
15768
  failProcess(processEntry, context2) {
15544
15769
  if (!processEntry) return;
15545
- try {
15546
- const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15547
- procRegistry.fail(processEntry.pid);
15548
- } catch {
15549
- }
15770
+ const failRegistry = context2.services.get(CliProcessRegistry_TOKEN);
15771
+ failRegistry?.fail(processEntry.pid);
15550
15772
  }
15551
15773
  async showHelp(command2, context2) {
15552
15774
  try {
@@ -15612,6 +15834,52 @@ var CliCommandExecutor = class {
15612
15834
  * parameter. In that case the global handler must be skipped so the
15613
15835
  * processor can handle the flag itself.
15614
15836
  */
15837
+ /**
15838
+ * Runs all registered {@link ICliCommandGuard}s that apply to this command,
15839
+ * in priority order. Returns the block message of the first guard that
15840
+ * denies the command, or `undefined` when every guard allows it.
15841
+ * A guard that throws is treated as a denial (fail closed).
15842
+ */
15843
+ async runGuards(command2, processor, context2) {
15844
+ let guards;
15845
+ try {
15846
+ guards = context2.services.getAll(ICliCommandGuard_TOKEN);
15847
+ } catch {
15848
+ return void 0;
15849
+ }
15850
+ if (!guards.length) {
15851
+ return void 0;
15852
+ }
15853
+ const ordered = [...guards].sort(
15854
+ (a, b) => (a.priority ?? 0) - (b.priority ?? 0)
15855
+ );
15856
+ for (const guard of ordered) {
15857
+ let result;
15858
+ try {
15859
+ if (guard.appliesTo && !await guard.appliesTo(processor, command2, context2)) {
15860
+ continue;
15861
+ }
15862
+ result = await guard.canActivate(command2, processor, context2);
15863
+ } catch (e) {
15864
+ return `${processor.command}: blocked by guard '${guard.name}' (${e?.message ?? e})`;
15865
+ }
15866
+ const denial = this.guardDenial(result, processor.command);
15867
+ if (denial) {
15868
+ return denial;
15869
+ }
15870
+ }
15871
+ return void 0;
15872
+ }
15873
+ guardDenial(result, commandName) {
15874
+ const denied = `${commandName}: permission denied`;
15875
+ if (result === void 0 || result === true) return void 0;
15876
+ if (result === false) return denied;
15877
+ if (typeof result === "string") return result || denied;
15878
+ if (typeof result === "object" && result !== null) {
15879
+ return result.allowed ? void 0 : result.message || denied;
15880
+ }
15881
+ return void 0;
15882
+ }
15615
15883
  processorOwnsParameter(processor, globalParam) {
15616
15884
  const globalNames = /* @__PURE__ */ new Set([
15617
15885
  globalParam.name,
@@ -15628,29 +15896,94 @@ var CliCommandExecutor = class {
15628
15896
  // src/lib/services/cli-service-container.ts
15629
15897
  var CliServiceContainer = class {
15630
15898
  constructor() {
15631
- /** Stores single-value providers (resolved or pending). */
15899
+ /** Stores single-value providers (resolved singletons). */
15632
15900
  this.services = /* @__PURE__ */ new Map();
15633
15901
  /** Stores multi-provider arrays. */
15634
15902
  this.multiServices = /* @__PURE__ */ new Map();
15903
+ /** Tokens that cannot be overwritten by subsequent `set()` calls. */
15904
+ this.sealedTokens = /* @__PURE__ */ new Set();
15905
+ /** Stores provider recipes for transient tokens (re-resolved on every get). */
15906
+ this.transientRecipes = /* @__PURE__ */ new Map();
15635
15907
  }
15636
15908
  /**
15637
15909
  * Retrieve a service by its token.
15910
+ * Returns `undefined` when no provider is registered for the token.
15911
+ * For multi-provider tokens, returns the last registered value (use `getAll` for the full array).
15638
15912
  *
15639
15913
  * @param token The token used to register the service.
15640
- * @returns The resolved service instance (or array for multi providers).
15641
- * @throws Error if the token has not been registered.
15914
+ * @returns The resolved service instance, or `undefined` if not registered.
15642
15915
  */
15643
15916
  get(token) {
15917
+ if (this.transientRecipes.has(token)) {
15918
+ return this.resolveProvider(this.transientRecipes.get(token));
15919
+ }
15644
15920
  if (this.multiServices.has(token)) {
15645
- return this.multiServices.get(token);
15921
+ const arr = this.multiServices.get(token);
15922
+ return arr[arr.length - 1];
15646
15923
  }
15647
15924
  if (this.services.has(token)) {
15648
15925
  return this.services.get(token);
15649
15926
  }
15650
- const tokenName = typeof token === "string" ? token : typeof token === "function" ? token.name : String(token);
15651
- throw new Error(
15652
- `CliServiceContainer: No provider found for token "${tokenName}".`
15653
- );
15927
+ return void 0;
15928
+ }
15929
+ /**
15930
+ * Retrieve all registered services for a multi-provider token.
15931
+ * Returns an empty array when no provider is registered for the token.
15932
+ *
15933
+ * @param token The token used to register the service.
15934
+ * @returns Array of all resolved service instances.
15935
+ */
15936
+ getAll(token) {
15937
+ if (this.multiServices.has(token)) {
15938
+ return this.multiServices.get(token);
15939
+ }
15940
+ const single = this.get(token);
15941
+ return single !== void 0 ? [single] : [];
15942
+ }
15943
+ /**
15944
+ * Retrieve a service by its token, throwing if not registered.
15945
+ * Use this when the service is required and its absence is a programming error.
15946
+ * For multi-provider tokens, returns the last registered value (use `getAll` for the full array).
15947
+ *
15948
+ * @param token The token used to register the service.
15949
+ * @returns The resolved service instance.
15950
+ * @throws Error if the token has not been registered.
15951
+ */
15952
+ getRequired(token) {
15953
+ const value = this.get(token);
15954
+ if (value === void 0 && !this.has(token)) {
15955
+ throw new Error(
15956
+ `CliServiceContainer: No provider found for token "${this.tokenToString(token)}".`
15957
+ );
15958
+ }
15959
+ return value;
15960
+ }
15961
+ /**
15962
+ * Check whether a service is registered for the given token.
15963
+ */
15964
+ has(token) {
15965
+ return this.services.has(token) || this.multiServices.has(token) || this.transientRecipes.has(token);
15966
+ }
15967
+ /**
15968
+ * Mark a token as sealed so it cannot be overwritten by future `set()` calls.
15969
+ * Only available on the concrete container — not exposed on `ICliServiceProvider`.
15970
+ */
15971
+ seal(token) {
15972
+ this.sealedTokens.add(token);
15973
+ }
15974
+ /**
15975
+ * Mark multiple tokens as sealed at once.
15976
+ */
15977
+ sealAll(tokens) {
15978
+ for (const token of tokens) {
15979
+ this.sealedTokens.add(token);
15980
+ }
15981
+ }
15982
+ /**
15983
+ * Check whether a token is sealed.
15984
+ */
15985
+ isSealed(token) {
15986
+ return this.sealedTokens.has(token);
15654
15987
  }
15655
15988
  /**
15656
15989
  * Register one or more providers.
@@ -15672,13 +16005,13 @@ var CliServiceContainer = class {
15672
16005
  getRegisteredTokens() {
15673
16006
  const tokens = [];
15674
16007
  for (const key of this.services.keys()) {
15675
- tokens.push(
15676
- typeof key === "string" ? key : typeof key === "function" ? key.name : String(key)
15677
- );
16008
+ tokens.push(this.tokenToString(key));
16009
+ }
16010
+ for (const key of this.transientRecipes.keys()) {
16011
+ tokens.push(`${this.tokenToString(key)} (transient)`);
15678
16012
  }
15679
16013
  for (const key of this.multiServices.keys()) {
15680
- const name = typeof key === "string" ? key : typeof key === "function" ? key.name : String(key);
15681
- tokens.push(`${name} (multi)`);
16014
+ tokens.push(`${this.tokenToString(key)} (multi)`);
15682
16015
  }
15683
16016
  return tokens;
15684
16017
  }
@@ -15689,20 +16022,34 @@ var CliServiceContainer = class {
15689
16022
  const details = [];
15690
16023
  for (const [key, value] of this.services.entries()) {
15691
16024
  details.push({
15692
- token: typeof key === "string" ? key : typeof key === "function" ? key.name : String(key),
16025
+ token: this.tokenToString(key),
15693
16026
  type: this.describeValue(value),
15694
16027
  multi: false
15695
16028
  });
15696
16029
  }
16030
+ for (const [key, provider] of this.transientRecipes.entries()) {
16031
+ const className = provider["useClass"]?.name ?? provider["useFactory"]?.name ?? "transient";
16032
+ details.push({
16033
+ token: this.tokenToString(key),
16034
+ type: `transient(${className})`,
16035
+ multi: false
16036
+ });
16037
+ }
15697
16038
  for (const [key, values] of this.multiServices.entries()) {
15698
16039
  details.push({
15699
- token: typeof key === "string" ? key : typeof key === "function" ? key.name : String(key),
16040
+ token: this.tokenToString(key),
15700
16041
  type: `[${values.map((v) => this.describeValue(v)).join(", ")}]`,
15701
16042
  multi: true
15702
16043
  });
15703
16044
  }
15704
16045
  return details;
15705
16046
  }
16047
+ /**
16048
+ * Convert a token to a human-readable string for diagnostics/errors.
16049
+ */
16050
+ tokenToString(token) {
16051
+ return typeof token === "string" ? token : typeof token === "function" ? token.name : String(token);
16052
+ }
15706
16053
  /**
15707
16054
  * Describe a resolved value for diagnostic display.
15708
16055
  */
@@ -15722,27 +16069,45 @@ var CliServiceContainer = class {
15722
16069
  * Register a single provider.
15723
16070
  */
15724
16071
  registerProvider(provider) {
15725
- const value = this.resolveProvider(provider);
15726
- if (provider.multi) {
15727
- const existing = this.multiServices.get(provider.provide) ?? [];
15728
- existing.push(value);
15729
- this.multiServices.set(provider.provide, existing);
16072
+ if (!provider.multi && this.sealedTokens.has(provider.provide)) {
16073
+ throw new Error(
16074
+ `CliServiceContainer: Cannot override sealed service "${this.tokenToString(provider.provide)}".`
16075
+ );
16076
+ }
16077
+ if (provider["transient"] && !provider.multi) {
16078
+ this.transientRecipes.set(provider.provide, provider);
16079
+ this.services.delete(provider.provide);
15730
16080
  } else {
15731
- this.services.set(provider.provide, value);
16081
+ const value = this.resolveProvider(provider);
16082
+ if (provider.multi) {
16083
+ const existing = this.multiServices.get(provider.provide) ?? [];
16084
+ existing.push(value);
16085
+ this.multiServices.set(provider.provide, existing);
16086
+ } else {
16087
+ this.services.set(provider.provide, value);
16088
+ this.transientRecipes.delete(provider.provide);
16089
+ }
16090
+ }
16091
+ if (provider.sealed) {
16092
+ this.sealedTokens.add(provider.provide);
15732
16093
  }
15733
16094
  }
15734
16095
  /**
15735
16096
  * Resolve a provider definition to a concrete value.
16097
+ *
16098
+ * Note: `deps` are resolved eagerly at registration time, so
16099
+ * dependency tokens must be registered before the provider that needs them.
15736
16100
  */
15737
16101
  resolveProvider(provider) {
15738
16102
  if ("useValue" in provider) {
15739
16103
  return provider.useValue;
15740
16104
  }
16105
+ const deps = (provider["deps"] ?? []).map((dep) => this.getRequired(dep));
15741
16106
  if ("useFactory" in provider) {
15742
- return provider.useFactory();
16107
+ return provider.useFactory(...deps);
15743
16108
  }
15744
16109
  if ("useClass" in provider) {
15745
- return new provider.useClass();
16110
+ return new provider.useClass(...deps);
15746
16111
  }
15747
16112
  throw new Error(
15748
16113
  "CliServiceContainer: Provider must specify useValue, useFactory, or useClass."
@@ -15883,10 +16248,10 @@ var welcomeModule = {
15883
16248
  async onAfterBoot(context2) {
15884
16249
  const config = this.config || {};
15885
16250
  let showOption = config.show;
15886
- try {
15887
- const storeManager = context2.services.get(
15888
- CliStateStoreManager_TOKEN
15889
- );
16251
+ const storeManager = context2.services.get(
16252
+ CliStateStoreManager_TOKEN
16253
+ );
16254
+ if (storeManager) {
15890
16255
  const configureStore = storeManager.getStateStore(
15891
16256
  CLI_CONFIGURE_STORE_NAME
15892
16257
  );
@@ -15895,7 +16260,6 @@ var welcomeModule = {
15895
16260
  if (state?.["system"]?.["welcomeMessage"]) {
15896
16261
  showOption = state["system"]["welcomeMessage"];
15897
16262
  }
15898
- } catch {
15899
16263
  }
15900
16264
  if (showOption) {
15901
16265
  if (!shouldDisplayWelcomeMessage(showOption)) {
@@ -15923,11 +16287,11 @@ var welcomeModule = {
15923
16287
  recordWelcomeMessageDisplay();
15924
16288
  context2.showPrompt();
15925
16289
  let showGreeting = true;
15926
- try {
15927
- const storeManager = context2.services.get(
15928
- CliStateStoreManager_TOKEN
15929
- );
15930
- const configureStore = storeManager.getStateStore(
16290
+ const greetingStoreManager = context2.services.get(
16291
+ CliStateStoreManager_TOKEN
16292
+ );
16293
+ if (greetingStoreManager) {
16294
+ const configureStore = greetingStoreManager.getStateStore(
15931
16295
  CLI_CONFIGURE_STORE_NAME
15932
16296
  );
15933
16297
  await configureStore.initialize();
@@ -15935,7 +16299,6 @@ var welcomeModule = {
15935
16299
  if (configState?.["system"]?.["greeting"] === false) {
15936
16300
  showGreeting = false;
15937
16301
  }
15938
- } catch {
15939
16302
  }
15940
16303
  if (showGreeting) {
15941
16304
  await context2.textAnimator?.showText(getGreetingBasedOnTime(), {
@@ -16039,6 +16402,18 @@ var CliBoot = class {
16039
16402
  if (module.services && module.services.length > 0) {
16040
16403
  this.services.set(module.services);
16041
16404
  }
16405
+ if (module.guards && module.guards.length > 0) {
16406
+ this.services.set(
16407
+ module.guards.map((guard) => ({
16408
+ provide: ICliCommandGuard_TOKEN,
16409
+ useValue: guard,
16410
+ multi: true
16411
+ }))
16412
+ );
16413
+ }
16414
+ if (module.permissions && module.permissions.length > 0) {
16415
+ this.services.get(ICliPermissionRegistry_TOKEN)?.register(module.permissions, module.name);
16416
+ }
16042
16417
  if (module.translations) {
16043
16418
  for (const [locale, translations] of Object.entries(module.translations)) {
16044
16419
  context2.translator.addTranslations(locale, translations);
@@ -16055,8 +16430,8 @@ var CliBoot = class {
16055
16430
  }
16056
16431
  }
16057
16432
  if (module.onSetup) {
16058
- const kvStore = context2.services.get(
16059
- "cli-key-value-store"
16433
+ const kvStore = context2.services.getRequired(
16434
+ CliKeyValueStore_TOKEN
16060
16435
  );
16061
16436
  const setupKey = `cli-module-setup:${module.name}`;
16062
16437
  const setupState = await kvStore.get(setupKey);
@@ -16086,19 +16461,47 @@ var CliBoot = class {
16086
16461
  this.registry.registerProcessor(processor);
16087
16462
  }
16088
16463
  await this.initializeProcessorsInternal(context2, filtered);
16464
+ this.adoptMergedSubProcessors(filtered);
16089
16465
  }
16090
16466
  this.bootedModules.add(module.name);
16091
16467
  if (!this.moduleRegistry.has(module.name)) {
16092
16468
  this.moduleRegistry.track(module);
16093
16469
  }
16094
16470
  }
16471
+ /**
16472
+ * Sub-processors of an `extendsProcessor` processor are merged into the
16473
+ * processor it extends, so they belong to that one: re-parent them so they
16474
+ * inherit its access rules (`requiredPermissions` etc.) and help/root
16475
+ * lookups resolve to the registered processor.
16476
+ */
16477
+ adoptMergedSubProcessors(processors) {
16478
+ for (const processor of processors) {
16479
+ if (!processor.extendsProcessor) continue;
16480
+ const name = processor.command.toLowerCase();
16481
+ const host = this.registry.processors.find(
16482
+ (p) => p.command.toLowerCase() === name
16483
+ );
16484
+ if (!host || host === processor) continue;
16485
+ for (const sub of processor.processors ?? []) {
16486
+ if (host.processors?.includes(sub)) {
16487
+ sub.parent = host;
16488
+ }
16489
+ }
16490
+ }
16491
+ }
16095
16492
  buildCoreModule() {
16096
16493
  return {
16097
16494
  apiVersion: API_VERSION,
16098
16495
  name: "@qodalis/cli-core",
16099
16496
  version: LIBRARY_VERSION$1,
16100
16497
  description: "Core CLI services and utilities",
16101
- processors: [...builtinProcessors]
16498
+ processors: [...builtinProcessors],
16499
+ permissions: [
16500
+ { name: "system:packages", description: "Install, update and remove packages (pkg)" },
16501
+ { name: "system:services", description: "Start and stop background services" },
16502
+ { name: "system:eval", description: "Evaluate JavaScript (eval, also requires allowEval)" },
16503
+ { name: "shell:execute", description: "Open a remote shell on a server (ssh)" }
16504
+ ]
16102
16505
  };
16103
16506
  }
16104
16507
  /**
@@ -16230,6 +16633,29 @@ var CliDragDropService = class {
16230
16633
  }
16231
16634
  };
16232
16635
 
16636
+ // src/lib/services/cli-permission-registry.ts
16637
+ var CliPermissionRegistry = class {
16638
+ constructor() {
16639
+ this.definitions = /* @__PURE__ */ new Map();
16640
+ }
16641
+ register(definitions, module) {
16642
+ for (const def of definitions ?? []) {
16643
+ const name = def?.name?.trim();
16644
+ if (!name) continue;
16645
+ const key = name.toLowerCase();
16646
+ if (!this.definitions.has(key)) {
16647
+ this.definitions.set(key, { ...def, name, module: def.module ?? module });
16648
+ }
16649
+ }
16650
+ }
16651
+ list() {
16652
+ return [...this.definitions.values()].sort((a, b) => a.name.localeCompare(b.name));
16653
+ }
16654
+ get(name) {
16655
+ return this.definitions.get(name.trim().toLowerCase());
16656
+ }
16657
+ };
16658
+
16233
16659
  // src/lib/addons/overlay.ts
16234
16660
  var OverlayAddon = class {
16235
16661
  constructor() {
@@ -16436,16 +16862,19 @@ var CliStateStore = class {
16436
16862
  this.state$.next(this.initialState);
16437
16863
  }
16438
16864
  async persist() {
16439
- const keyValueStore = this.services.get(
16440
- "cli-key-value-store"
16865
+ const keyValueStore = this.services.getRequired(
16866
+ CliKeyValueStore_TOKEN
16441
16867
  );
16442
16868
  await keyValueStore.set(this.storageKey, this.getState());
16443
16869
  }
16444
16870
  async initialize() {
16871
+ const keyValueStore = this.services.get(
16872
+ CliKeyValueStore_TOKEN
16873
+ );
16874
+ if (!keyValueStore) {
16875
+ return;
16876
+ }
16445
16877
  try {
16446
- const keyValueStore = this.services.get(
16447
- "cli-key-value-store"
16448
- );
16449
16878
  const state = await keyValueStore.get(this.storageKey);
16450
16879
  if (state) {
16451
16880
  this.state$.next(state);
@@ -16512,6 +16941,39 @@ var CliStateStoreManager = class {
16512
16941
  this.stores.clear();
16513
16942
  }
16514
16943
  };
16944
+ var CliServerAuthService = class {
16945
+ constructor(services) {
16946
+ this.services = services;
16947
+ }
16948
+ getHeaders(serverName, configHeaders) {
16949
+ const merged = {
16950
+ ...resolveHeaders(configHeaders)
16951
+ };
16952
+ const providers = this.services.getAll(
16953
+ ICliServerAuthTokenProvider_TOKEN
16954
+ );
16955
+ for (const provider of providers) {
16956
+ Object.assign(merged, provider.getHeaders(serverName));
16957
+ }
16958
+ return merged;
16959
+ }
16960
+ async getWebSocketParams(serverName) {
16961
+ const providers = this.services.getAll(
16962
+ ICliServerAuthTokenProvider_TOKEN
16963
+ );
16964
+ let handled = false;
16965
+ const merged = {};
16966
+ for (const provider of providers) {
16967
+ if (!provider.getWebSocketParams) continue;
16968
+ const params = await provider.getWebSocketParams(serverName);
16969
+ if (params) {
16970
+ handled = true;
16971
+ Object.assign(merged, params);
16972
+ }
16973
+ }
16974
+ return handled ? merged : void 0;
16975
+ }
16976
+ };
16515
16977
 
16516
16978
  // src/lib/completion/cli-command-completion-provider.ts
16517
16979
  var CliCommandCompletionProvider = class {
@@ -16659,12 +17121,10 @@ var CliPackageNameCompletionProvider = class {
16659
17121
  if (!INSTALLED_PKG_SUBCOMMANDS.has(subCommand)) {
16660
17122
  return [];
16661
17123
  }
16662
- let manager;
16663
- try {
16664
- manager = this.services.get(
16665
- CliPackageManagerService_TOKEN
16666
- );
16667
- } catch {
17124
+ const manager = this.services.get(
17125
+ CliPackageManagerService_TOKEN
17126
+ );
17127
+ if (!manager) {
16668
17128
  return [];
16669
17129
  }
16670
17130
  const packages = await manager.getPackages();
@@ -16867,7 +17327,7 @@ var ServerListProcessor = class {
16867
17327
  this.description = "Show configured servers and their connection status";
16868
17328
  }
16869
17329
  async processCommand(_command, context2) {
16870
- const manager = context2.services.get(
17330
+ const manager = context2.services.getRequired(
16871
17331
  CliServerManager_TOKEN
16872
17332
  );
16873
17333
  if (!manager || manager.connections.size === 0) {
@@ -16906,7 +17366,7 @@ var ServerStatusProcessor = class {
16906
17366
  context2.process.exit(1);
16907
17367
  return;
16908
17368
  }
16909
- const manager = context2.services.get(
17369
+ const manager = context2.services.getRequired(
16910
17370
  CliServerManager_TOKEN
16911
17371
  );
16912
17372
  const connection = manager?.getConnection(serverName);
@@ -16916,7 +17376,7 @@ var ServerStatusProcessor = class {
16916
17376
  return;
16917
17377
  }
16918
17378
  context2.spinner?.show(`Pinging ${serverName}...`);
16919
- context2.setStatusText(`Pinging ${serverName}`);
17379
+ context2.notifier.info(`Pinging ${serverName}`);
16920
17380
  const reachable = await connection.ping();
16921
17381
  context2.spinner?.hide();
16922
17382
  if (reachable) {
@@ -16947,7 +17407,7 @@ var ServerReconnectProcessor = class {
16947
17407
  context2.process.exit(1);
16948
17408
  return;
16949
17409
  }
16950
- const manager = context2.services.get(
17410
+ const manager = context2.services.getRequired(
16951
17411
  CliServerManager_TOKEN
16952
17412
  );
16953
17413
  if (!manager) {
@@ -16956,7 +17416,7 @@ var ServerReconnectProcessor = class {
16956
17416
  return;
16957
17417
  }
16958
17418
  context2.spinner?.show(`Reconnecting to ${serverName}...`);
16959
- context2.setStatusText(`Reconnecting to ${serverName}`);
17419
+ context2.notifier.info(`Reconnecting to ${serverName}`);
16960
17420
  const result = await manager.reconnect(serverName);
16961
17421
  context2.spinner?.hide();
16962
17422
  if (result.success) {
@@ -16986,7 +17446,7 @@ var ServerDefaultProcessor = class {
16986
17446
  ];
16987
17447
  }
16988
17448
  async processCommand(command2, context2) {
16989
- const manager = context2.services.get(
17449
+ const manager = context2.services.getRequired(
16990
17450
  CliServerManager_TOKEN
16991
17451
  );
16992
17452
  if (!manager) {
@@ -17022,8 +17482,6 @@ var ServerDefaultProcessor = class {
17022
17482
  }
17023
17483
  }
17024
17484
  };
17025
-
17026
- // src/lib/server/cli-ssh-command-processor.ts
17027
17485
  var CliSshCommandProcessor = class {
17028
17486
  constructor() {
17029
17487
  this.command = "ssh";
@@ -17032,7 +17490,9 @@ var CliSshCommandProcessor = class {
17032
17490
  this.valueRequired = false;
17033
17491
  this.metadata = {
17034
17492
  module: "@qodalis/cli-server",
17035
- icon: "\u{1F5A5}"
17493
+ icon: "\u{1F5A5}",
17494
+ // Enforced only when an auth module is loaded; the server checks it too
17495
+ requiredPermissions: [CliServerPermissions.ShellExecute]
17036
17496
  };
17037
17497
  this.parameters = [
17038
17498
  {
@@ -17042,12 +17502,9 @@ var CliSshCommandProcessor = class {
17042
17502
  type: "string"
17043
17503
  }
17044
17504
  ];
17045
- this.socket = null;
17046
- this.context = null;
17047
- this.resolved = false;
17048
17505
  }
17049
17506
  async processCommand(command2, context2) {
17050
- const manager = context2.services.get(
17507
+ const manager = context2.services.getRequired(
17051
17508
  CliServerManager_TOKEN
17052
17509
  );
17053
17510
  if (!manager) {
@@ -17076,26 +17533,34 @@ var CliSshCommandProcessor = class {
17076
17533
  await this.executeInteractive(connection, serverName, context2);
17077
17534
  }
17078
17535
  }
17079
- async onData(data, context2) {
17080
- if (this.socket?.readyState === WebSocket.OPEN) {
17081
- const msg = { type: "stdin", data };
17082
- this.socket.send(JSON.stringify(msg));
17083
- }
17084
- }
17085
- onResize(cols, rows, _context) {
17086
- if (this.socket?.readyState === WebSocket.OPEN) {
17087
- const msg = { type: "resize", cols, rows };
17088
- this.socket.send(JSON.stringify(msg));
17089
- }
17090
- }
17091
- onDispose(_context) {
17092
- if (this.socket) {
17093
- if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
17094
- this.socket.close();
17536
+ /**
17537
+ * Full-screen handler for one session: input, resize and disposal go to
17538
+ * that session's socket only.
17539
+ */
17540
+ fullScreenHandler(session) {
17541
+ const handler = Object.create(this);
17542
+ handler.onData = async (data) => {
17543
+ if (session.socket?.readyState === WebSocket.OPEN) {
17544
+ const msg = { type: "stdin", data };
17545
+ session.socket.send(JSON.stringify(msg));
17095
17546
  }
17096
- this.socket = null;
17097
- }
17098
- this.context = null;
17547
+ };
17548
+ handler.onResize = (cols, rows) => {
17549
+ if (session.socket?.readyState === WebSocket.OPEN) {
17550
+ const msg = { type: "resize", cols, rows };
17551
+ session.socket.send(JSON.stringify(msg));
17552
+ }
17553
+ };
17554
+ handler.onDispose = () => {
17555
+ session.disposed = true;
17556
+ if (session.socket) {
17557
+ if (session.socket.readyState === WebSocket.OPEN || session.socket.readyState === WebSocket.CONNECTING) {
17558
+ session.socket.close();
17559
+ }
17560
+ session.socket = null;
17561
+ }
17562
+ };
17563
+ return handler;
17099
17564
  }
17100
17565
  async resolveServer(command2, shellServers, context2) {
17101
17566
  const empty = { serverName: null, oneShotCmd: null, connection: null };
@@ -17121,7 +17586,7 @@ var CliSshCommandProcessor = class {
17121
17586
  const candidateName = parts[0];
17122
17587
  const entry2 = shellServers.find((s) => s.name === candidateName);
17123
17588
  if (entry2) {
17124
- const oneShotCmd = parts.slice(1).join(" ") || null;
17589
+ const oneShotCmd = this.remoteCommandFromRaw(command2.rawCommand, candidateName) ?? (parts.slice(1).join(" ") || null);
17125
17590
  return {
17126
17591
  serverName: candidateName,
17127
17592
  oneShotCmd,
@@ -17141,7 +17606,7 @@ var CliSshCommandProcessor = class {
17141
17606
  connection: shellServers[0].connection
17142
17607
  };
17143
17608
  }
17144
- const manager = context2.services.get(
17609
+ const manager = context2.services.getRequired(
17145
17610
  CliServerManager_TOKEN
17146
17611
  );
17147
17612
  if (manager?.defaultServer) {
@@ -17175,41 +17640,57 @@ var CliSshCommandProcessor = class {
17175
17640
  };
17176
17641
  }
17177
17642
  async executeInteractive(connection, serverName, context2) {
17178
- this.context = context2;
17643
+ const session = { socket: null, resolved: false, disposed: false };
17179
17644
  const cols = context2.terminal.cols;
17180
17645
  const rows = context2.terminal.rows;
17181
- const wsUrl = this.buildShellUrl(connection, cols, rows);
17646
+ const wsUrl = await this.buildShellUrl(connection, cols, rows);
17182
17647
  context2.spinner?.show(`Connecting to ${serverName}...`);
17183
- this.resolved = false;
17648
+ let fullScreen = false;
17184
17649
  return new Promise((resolve) => {
17650
+ const signal = context2.signal;
17185
17651
  const resolveOnce = () => {
17186
- if (!this.resolved) {
17187
- this.resolved = true;
17652
+ signal?.removeEventListener("abort", onAbort);
17653
+ if (!session.resolved) {
17654
+ session.resolved = true;
17188
17655
  resolve();
17189
17656
  }
17190
17657
  };
17658
+ const onAbort = () => {
17659
+ context2.spinner?.hide();
17660
+ this.cleanup(session);
17661
+ if (fullScreen) context2.exitFullScreenMode();
17662
+ context2.process.exit(130, { silent: true });
17663
+ resolveOnce();
17664
+ };
17665
+ if (signal?.aborted) {
17666
+ onAbort();
17667
+ return;
17668
+ }
17669
+ signal?.addEventListener("abort", onAbort, { once: true });
17191
17670
  try {
17192
- this.socket = new WebSocket(wsUrl);
17671
+ session.socket = new WebSocket(wsUrl);
17193
17672
  } catch (e) {
17194
17673
  context2.spinner?.hide();
17195
17674
  context2.writer.writeError(
17196
17675
  `Failed to connect to '${serverName}': ${e.message}`
17197
17676
  );
17198
- context2.process.exit(1);
17677
+ context2.process.exit(1, { silent: true });
17199
17678
  resolveOnce();
17200
17679
  return;
17201
17680
  }
17202
- this.socket.onopen = () => {
17681
+ session.socket.onopen = () => {
17203
17682
  };
17204
- this.socket.onmessage = (event) => {
17205
- const msg = JSON.parse(event.data);
17683
+ session.socket.onmessage = (event) => {
17684
+ const msg = parseShellMessage(event.data);
17685
+ if (!msg) return;
17206
17686
  switch (msg.type) {
17207
17687
  case "ready":
17208
17688
  context2.spinner?.hide();
17209
17689
  context2.writer.writeInfo(
17210
17690
  `Connected to ${serverName} (${msg.os}, ${msg.shell})`
17211
17691
  );
17212
- context2.enterFullScreenMode(this);
17692
+ context2.enterFullScreenMode(this.fullScreenHandler(session));
17693
+ fullScreen = true;
17213
17694
  context2.terminal.write("\x1B[2J\x1B[H\x1B[?25h");
17214
17695
  break;
17215
17696
  case "stdout":
@@ -17219,70 +17700,82 @@ var CliSshCommandProcessor = class {
17219
17700
  context2.terminal.write(msg.data);
17220
17701
  break;
17221
17702
  case "exit":
17222
- context2.exitFullScreenMode();
17703
+ if (fullScreen) context2.exitFullScreenMode();
17223
17704
  context2.writer.writeInfo(
17224
17705
  `Shell exited with code ${msg.code}.`
17225
17706
  );
17226
- context2.process.exit(msg.code);
17227
- this.cleanup();
17707
+ context2.process.exit(msg.code, { silent: true });
17708
+ this.cleanup(session);
17228
17709
  resolveOnce();
17229
17710
  break;
17230
17711
  case "error":
17231
17712
  context2.spinner?.hide();
17232
- context2.exitFullScreenMode();
17713
+ if (fullScreen) context2.exitFullScreenMode();
17233
17714
  context2.writer.writeError(
17234
17715
  `Server error: ${msg.message}`
17235
17716
  );
17236
- context2.process.exit(1);
17237
- this.cleanup();
17717
+ context2.process.exit(1, { silent: true });
17718
+ this.cleanup(session);
17238
17719
  resolveOnce();
17239
17720
  break;
17240
17721
  }
17241
17722
  };
17242
- this.socket.onclose = () => {
17723
+ let errored = false;
17724
+ session.socket.onclose = (event) => {
17243
17725
  context2.spinner?.hide();
17244
- if (this.context && !this.resolved) {
17245
- context2.exitFullScreenMode();
17246
- context2.writer.writeWarning(
17247
- `Connection to '${serverName}' closed.`
17248
- );
17249
- context2.process.exit(1);
17726
+ if (!session.disposed && !session.resolved) {
17727
+ if (fullScreen) context2.exitFullScreenMode();
17728
+ const reason = describeShellClose(event ?? {}, serverName);
17729
+ if (reason) context2.writer.writeError(reason);
17730
+ else if (errored) context2.writer.writeError(`WebSocket error connecting to '${serverName}'.`);
17731
+ else context2.writer.writeWarning(`Connection to '${serverName}' closed.`);
17732
+ context2.process.exit(1, { silent: true });
17250
17733
  }
17251
- this.cleanup();
17734
+ this.cleanup(session);
17252
17735
  resolveOnce();
17253
17736
  };
17254
- this.socket.onerror = () => {
17255
- context2.spinner?.hide();
17256
- if (!this.resolved) {
17257
- context2.writer.writeError(
17258
- `WebSocket error connecting to '${serverName}'.`
17259
- );
17260
- context2.process.exit(1);
17261
- }
17262
- this.cleanup();
17263
- resolveOnce();
17737
+ session.socket.onerror = () => {
17738
+ errored = true;
17264
17739
  };
17265
17740
  });
17266
17741
  }
17267
17742
  async executeOneShot(connection, serverName, cmd, context2) {
17268
17743
  const cols = context2.terminal.cols;
17269
17744
  const rows = context2.terminal.rows;
17270
- const wsUrl = this.buildShellUrl(connection, cols, rows, cmd);
17745
+ const wsUrl = await this.buildShellUrl(connection, cols, rows, cmd);
17271
17746
  context2.spinner?.show(`Running on ${serverName}...`);
17272
- return new Promise((resolve) => {
17747
+ const session = { socket: null, resolved: false, disposed: false };
17748
+ return new Promise((resolvePromise) => {
17749
+ const signal = context2.signal;
17750
+ const resolve = () => {
17751
+ signal?.removeEventListener("abort", onAbort);
17752
+ resolvePromise();
17753
+ };
17754
+ const onAbort = () => {
17755
+ context2.spinner?.hide();
17756
+ this.cleanup(session);
17757
+ context2.process.exit(130, { silent: true });
17758
+ resolve();
17759
+ };
17760
+ if (signal?.aborted) {
17761
+ onAbort();
17762
+ return;
17763
+ }
17764
+ signal?.addEventListener("abort", onAbort, { once: true });
17273
17765
  try {
17274
- this.socket = new WebSocket(wsUrl);
17766
+ session.socket = new WebSocket(wsUrl);
17275
17767
  } catch (e) {
17276
17768
  context2.spinner?.hide();
17277
17769
  context2.writer.writeError(
17278
17770
  `Failed to connect to '${serverName}': ${e.message}`
17279
17771
  );
17280
- context2.process.exit(1);
17772
+ context2.process.exit(1, { silent: true });
17281
17773
  resolve();
17282
17774
  return;
17283
17775
  }
17284
- this.socket.onmessage = (event) => {
17285
- const msg = JSON.parse(event.data);
17776
+ session.socket.onmessage = (event) => {
17777
+ const msg = parseShellMessage(event.data);
17778
+ if (!msg) return;
17286
17779
  switch (msg.type) {
17287
17780
  case "ready":
17288
17781
  context2.spinner?.hide();
@@ -17295,9 +17788,9 @@ var CliSshCommandProcessor = class {
17295
17788
  break;
17296
17789
  case "exit":
17297
17790
  if (msg.code !== 0) {
17298
- context2.process.exit(msg.code);
17791
+ context2.process.exit(msg.code, { silent: true });
17299
17792
  }
17300
- this.cleanup();
17793
+ this.cleanup(session);
17301
17794
  resolve();
17302
17795
  break;
17303
17796
  case "error":
@@ -17305,50 +17798,82 @@ var CliSshCommandProcessor = class {
17305
17798
  context2.writer.writeError(
17306
17799
  `Server error: ${msg.message}`
17307
17800
  );
17308
- context2.process.exit(1);
17309
- this.cleanup();
17801
+ context2.process.exit(1, { silent: true });
17802
+ this.cleanup(session);
17310
17803
  resolve();
17311
17804
  break;
17312
17805
  }
17313
17806
  };
17314
- this.socket.onclose = () => {
17807
+ let errored = false;
17808
+ session.socket.onclose = (event) => {
17315
17809
  context2.spinner?.hide();
17316
- this.cleanup();
17810
+ const reason = describeShellClose(event ?? {}, serverName);
17811
+ if (reason || errored) {
17812
+ context2.writer.writeError(reason ?? `WebSocket error connecting to '${serverName}'.`);
17813
+ context2.process.exit(1, { silent: true });
17814
+ }
17815
+ this.cleanup(session);
17317
17816
  resolve();
17318
17817
  };
17319
- this.socket.onerror = () => {
17320
- context2.spinner?.hide();
17321
- context2.writer.writeError(
17322
- `WebSocket error connecting to '${serverName}'.`
17323
- );
17324
- context2.process.exit(1);
17325
- this.cleanup();
17326
- resolve();
17818
+ session.socket.onerror = () => {
17819
+ errored = true;
17327
17820
  };
17328
17821
  });
17329
17822
  }
17823
+ /** `ssh <server> <cmd...>` → `<cmd...>` exactly as typed */
17824
+ remoteCommandFromRaw(raw, serverName) {
17825
+ const match = raw?.trim().match(/^\S+\s+(\S+)\s+([\s\S]+)$/);
17826
+ return match && match[1] === serverName ? match[2].trim() : null;
17827
+ }
17330
17828
  buildShellUrl(connection, cols, rows, cmd) {
17331
- const baseUrl = connection.config.url.endsWith("/") ? connection.config.url.slice(0, -1) : connection.config.url;
17332
- const wsBase = baseUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
17333
- let url = `${wsBase}/ws/v1/qcli/shell?cols=${cols}&rows=${rows}`;
17829
+ let path = `/ws/v1/qcli/shell?cols=${cols}&rows=${rows}`;
17334
17830
  if (cmd) {
17335
- url += `&cmd=${encodeURIComponent(cmd)}`;
17831
+ path += `&cmd=${encodeURIComponent(cmd)}`;
17336
17832
  }
17337
- return url;
17833
+ return connection.buildWebSocketUrl(path);
17338
17834
  }
17339
- cleanup() {
17340
- if (this.socket) {
17341
- this.socket.onmessage = null;
17342
- this.socket.onclose = null;
17343
- this.socket.onerror = null;
17344
- if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
17345
- this.socket.close();
17835
+ cleanup(session) {
17836
+ const socket = session.socket;
17837
+ if (socket) {
17838
+ socket.onmessage = null;
17839
+ socket.onclose = null;
17840
+ socket.onerror = null;
17841
+ if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
17842
+ socket.close();
17346
17843
  }
17347
- this.socket = null;
17844
+ session.socket = null;
17348
17845
  }
17349
- this.context = null;
17350
17846
  }
17351
17847
  };
17848
+ function parseShellMessage(data) {
17849
+ try {
17850
+ const msg = typeof data === "string" ? JSON.parse(data) : null;
17851
+ return msg && typeof msg === "object" && typeof msg.type === "string" ? msg : null;
17852
+ } catch {
17853
+ return null;
17854
+ }
17855
+ }
17856
+ function describeShellClose(event, serverName) {
17857
+ const reason = (event.reason ?? "").toLowerCase();
17858
+ switch (event.code) {
17859
+ case 4401:
17860
+ if (reason === "session ended") return `ssh: shell closed by '${serverName}' (session ended or shell access revoked)`;
17861
+ if (reason === "shell access is not configured") {
17862
+ return `ssh: shell access is not configured on '${serverName}'`;
17863
+ }
17864
+ return `ssh: login required on '${serverName}'`;
17865
+ case 4403:
17866
+ return `ssh: permission denied on '${serverName}' (requires shell:execute or a valid shell token)`;
17867
+ case 4404:
17868
+ return `ssh: the shell is disabled on '${serverName}'`;
17869
+ case 4408:
17870
+ return `ssh: shell timed out on '${serverName}'`;
17871
+ case 4429:
17872
+ return `ssh: too many shell sessions on '${serverName}'`;
17873
+ default:
17874
+ return null;
17875
+ }
17876
+ }
17352
17877
 
17353
17878
  // src/lib/server/cli-server-module.ts
17354
17879
  function createServerModule() {
@@ -17383,6 +17908,11 @@ function initializeTerminal(container, opts) {
17383
17908
  terminal.open(container);
17384
17909
  fitAddon.fit();
17385
17910
  requestAnimationFrame(() => fitAddon.fit());
17911
+ if (document.fonts?.ready) {
17912
+ document.fonts.ready.then(() => {
17913
+ requestAnimationFrame(() => fitAddon.fit());
17914
+ });
17915
+ }
17386
17916
  container.classList.add("terminal-container");
17387
17917
  container.style.background = opts.theme?.background ?? "#000";
17388
17918
  const wheelListener = (e) => e.preventDefault();
@@ -17625,19 +18155,21 @@ async function initializeServices(options) {
17625
18155
  const logger = new CliLogger();
17626
18156
  const commandHistory = new CliCommandHistory(store);
17627
18157
  await commandHistory.initialize();
17628
- services.set([{ provide: "cli-key-value-store", useValue: store }]);
18158
+ services.set([{ provide: CliKeyValueStore_TOKEN, useValue: store, sealed: true }]);
17629
18159
  const stateStoreManager = new CliStateStoreManager(services, registry);
17630
18160
  const processRegistry = new CliProcessRegistry();
17631
18161
  services.set([
17632
18162
  {
17633
18163
  provide: CliStateStoreManager_TOKEN,
17634
- useValue: stateStoreManager
18164
+ useValue: stateStoreManager,
18165
+ sealed: true
17635
18166
  },
17636
- { provide: CliProcessorsRegistry_TOKEN, useValue: registry },
17637
- { provide: CliCommandHistory_TOKEN, useValue: commandHistory },
18167
+ { provide: CliProcessorsRegistry_TOKEN, useValue: registry, sealed: true },
18168
+ { provide: CliCommandHistory_TOKEN, useValue: commandHistory, sealed: true },
17638
18169
  {
17639
18170
  provide: CliProcessRegistry_TOKEN,
17640
- useValue: processRegistry
18171
+ useValue: processRegistry,
18172
+ sealed: true
17641
18173
  }
17642
18174
  ]);
17643
18175
  if (pendingServices.length > 0) {
@@ -17648,7 +18180,8 @@ async function initializeServices(options) {
17648
18180
  services.set([
17649
18181
  {
17650
18182
  provide: ICliPingServerService_TOKEN,
17651
- useValue: new CliDefaultPingServerService()
18183
+ useClass: CliDefaultPingServerService,
18184
+ sealed: true
17652
18185
  }
17653
18186
  ]);
17654
18187
  }
@@ -17658,25 +18191,41 @@ async function initializeServices(options) {
17658
18191
  syntaxRegistry.register(new MarkdownHighlighter());
17659
18192
  syntaxRegistry.register(new YamlHighlighter());
17660
18193
  services.set([
17661
- { provide: "syntax-highlighter-registry", useValue: syntaxRegistry }
18194
+ { provide: SyntaxHighlighterRegistry_TOKEN, useValue: syntaxRegistry, sealed: true }
17662
18195
  ]);
17663
18196
  if (!pendingTokens.has(ICliEnvironment_TOKEN)) {
17664
18197
  services.set([
17665
18198
  {
17666
18199
  provide: ICliEnvironment_TOKEN,
17667
- useValue: new CliEnvironment()
18200
+ useClass: CliEnvironment,
18201
+ sealed: true
17668
18202
  }
17669
18203
  ]);
17670
18204
  }
17671
18205
  services.set([
17672
18206
  {
17673
18207
  provide: ICliDragDropService_TOKEN,
17674
- useValue: dragDropService
18208
+ useValue: dragDropService,
18209
+ sealed: true
18210
+ }
18211
+ ]);
18212
+ services.set(
18213
+ cliAccessMetadataGuards.map((guard) => ({
18214
+ provide: ICliCommandGuard_TOKEN,
18215
+ useValue: guard,
18216
+ multi: true
18217
+ }))
18218
+ );
18219
+ services.set([
18220
+ {
18221
+ provide: ICliPermissionRegistry_TOKEN,
18222
+ useValue: new CliPermissionRegistry(),
18223
+ sealed: true
17675
18224
  }
17676
18225
  ]);
17677
18226
  const translator = new CliTranslationService();
17678
18227
  services.set([
17679
- { provide: ICliTranslationService_TOKEN, useValue: translator }
18228
+ { provide: ICliTranslationService_TOKEN, useValue: translator, sealed: true }
17680
18229
  ]);
17681
18230
  return {
17682
18231
  services,
@@ -17745,7 +18294,7 @@ var CliEngine = class {
17745
18294
  * Initialize the terminal, wire up services, boot modules, and show welcome message.
17746
18295
  */
17747
18296
  async start() {
17748
- initWasmAccelerator().catch(() => {
18297
+ initWasmAccelerator(this.options?.wasmUrl).catch(() => {
17749
18298
  });
17750
18299
  await waitForLayout(this.container);
17751
18300
  const terminalOpts = getTerminalOptions(this.options?.terminalOptions);
@@ -17788,10 +18337,18 @@ var CliEngine = class {
17788
18337
  provide: CliBackgroundServiceRegistry_TOKEN,
17789
18338
  useValue: this.executionContext.backgroundServices
17790
18339
  }]);
17791
- const serverManager = new CliServerManager(this.registry);
18340
+ const serverAuthService = new CliServerAuthService(services);
17792
18341
  services.set([
17793
- { provide: CliServerManager_TOKEN, useValue: serverManager }
18342
+ { provide: ICliServerAuthService_TOKEN, useValue: serverAuthService, sealed: true }
18343
+ ]);
18344
+ services.set([
18345
+ {
18346
+ provide: CliServerManager_TOKEN,
18347
+ useClass: CliServerManager,
18348
+ deps: [CliProcessorsRegistry_TOKEN, ICliServerAuthService_TOKEN]
18349
+ }
17794
18350
  ]);
18351
+ const serverManager = services.getRequired(CliServerManager_TOKEN);
17795
18352
  if (this.options?.servers && this.options.servers.length > 0) {
17796
18353
  await serverManager.connectAll(this.options.servers, {
17797
18354
  warn: (msg) => this.executionContext.logger.warn(msg),
@@ -17811,13 +18368,9 @@ var CliEngine = class {
17811
18368
  new CliCommandCompletionProvider(this.registry),
17812
18369
  new CliParameterCompletionProvider(this.registry, executor)
17813
18370
  ];
17814
- let pluginProviders = [];
17815
- try {
17816
- pluginProviders = services.get(
17817
- ICliCompletionProvider_TOKEN
17818
- ) ?? [];
17819
- } catch {
17820
- }
18371
+ const pluginProviders = services.getAll(
18372
+ ICliCompletionProvider_TOKEN
18373
+ );
17821
18374
  this.executionContext.completionEngine.setProviders([
17822
18375
  ...pluginProviders,
17823
18376
  ...defaultProviders
@@ -17925,7 +18478,7 @@ var CliEngine = class {
17925
18478
  if (!this.executionContext) {
17926
18479
  throw new Error("Cannot snapshot before engine has started");
17927
18480
  }
17928
- const stateStoreManager = this.executionContext.services.get(
18481
+ const stateStoreManager = this.executionContext.services.getRequired(
17929
18482
  CliStateStoreManager_TOKEN
17930
18483
  );
17931
18484
  return {
@@ -17952,7 +18505,7 @@ var CliEngine = class {
17952
18505
  });
17953
18506
  }
17954
18507
  await this.executionContext.commandHistory.setHistory(snap.commandHistory);
17955
- const stateStoreManager = this.executionContext.services.get(
18508
+ const stateStoreManager = this.executionContext.services.getRequired(
17956
18509
  CliStateStoreManager_TOKEN
17957
18510
  );
17958
18511
  for (const entry of snap.stateStores) {
@@ -18012,7 +18565,7 @@ var CliTestHarness = class {
18012
18565
  this.services = new CliServiceContainer();
18013
18566
  this.processRegistry = new CliProcessRegistry();
18014
18567
  this.services.set({
18015
- provide: "cli-key-value-store",
18568
+ provide: CliKeyValueStore_TOKEN,
18016
18569
  useValue: createInMemoryKvStore()
18017
18570
  });
18018
18571
  this.services.set({
@@ -18035,6 +18588,24 @@ var CliTestHarness = class {
18035
18588
  provide: ICliEnvironment_TOKEN,
18036
18589
  useValue: new CliEnvironment()
18037
18590
  });
18591
+ this.services.set({
18592
+ provide: ICliPermissionRegistry_TOKEN,
18593
+ useValue: new CliPermissionRegistry()
18594
+ });
18595
+ this.services.set(
18596
+ cliAccessMetadataGuards.map((guard) => ({
18597
+ provide: ICliCommandGuard_TOKEN,
18598
+ useValue: guard,
18599
+ multi: true
18600
+ }))
18601
+ );
18602
+ }
18603
+ /**
18604
+ * Resolve `context.userSession` on every command from this function
18605
+ * (e.g. a session service), instead of the value set via setUserSession().
18606
+ */
18607
+ setUserSessionProvider(provider) {
18608
+ this.userSessionProvider = provider;
18038
18609
  }
18039
18610
  /**
18040
18611
  * Register a service in the test container.
@@ -18119,9 +18690,10 @@ var CliTestHarness = class {
18119
18690
  options: void 0,
18120
18691
  logger: createNoopLogger(),
18121
18692
  process,
18122
- userSession: this.userSession,
18693
+ userSession: this.userSessionProvider ? this.userSessionProvider() : this.userSession,
18123
18694
  state: this.stateStoreManager.getStateStore("shared"),
18124
18695
  backgroundServices: createNoopBackgroundServices(),
18696
+ http: new CliHttpClient(),
18125
18697
  promptLength: 0,
18126
18698
  currentLine: "",
18127
18699
  cursorPosition: 0,
@@ -18144,11 +18716,7 @@ var CliTestHarness = class {
18144
18716
  submitCommand: async (command2) => {
18145
18717
  await this.executor.executeCommand(command2, context2);
18146
18718
  },
18147
- setStatusText: () => {
18148
- },
18149
- clearStatusText: () => {
18150
- },
18151
- getStatusText: () => void 0,
18719
+ notifier: new CliNotifier(),
18152
18720
  createInterval: (cb, ms) => {
18153
18721
  const id = setInterval(cb, ms);
18154
18722
  return {
@@ -18354,6 +18922,6 @@ function createInMemoryKvStore() {
18354
18922
  };
18355
18923
  }
18356
18924
 
18357
- export { API_VERSION, CLi_Name_Art, CapturingTerminalWriter, CliAliasCommandProcessor, CliArgsParser, CliBackgroundServiceRegistry_TOKEN, CliBoot, CliCalCommandProcessor, CliCaptureCommandProcessor, CliClearCommandProcessor, CliClipboard, CliClipboardCommandProcessor, CliColorCommandProcessor, CliCommandExecutionContext, CliCommandExecutor, CliCommandHistory, CliCommandHistory_TOKEN, CliCommandProcessorRegistry, CliConfigureCommandProcessor, CliConvertCommandProcessor, CliDebugCommandProcessor, CliDefaultPingServerService, CliDiffCommandProcessor, CliDragDropService, CliEchoCommandProcessor, CliEngine, CliEnvCommandProcessor, CliEnvironment, CliEvalCommandProcessor, CliExecutionContext, CliExecutionProcess, CliExportCommandProcessor, CliFeedbackCommandProcessor, CliFontSizeCommandProcessor, CliFullScreenManager, CliHelpCommandProcessor, CliHistoryCommandProcessor, CliHotKeysCommandProcessor, CliHtopCommandProcessor, CliInputReader, CliJsonCommandProcessor, CliKeyValueStore, CliKillCommandProcessor, CliLogger, CliLoremCommandProcessor, CliModuleRegistry_TOKEN, CliNanoCommandProcessor, CliOpenCommandProcessor, CliPackageManagerService, CliPackageManagerService_TOKEN, CliPackagesCommandProcessor, CliPingCommandProcessor, CliProcessRegistry, CliProcessRegistry_TOKEN, CliProcessorsRegistry_TOKEN, CliPsCommandProcessor, CliRandomCommandProcessor, CliScreenCommandProcessor, CliSeqCommandProcessor, CliServerCommandProcessor, CliServerConnection, CliServerManager, CliServerManager_TOKEN, CliServerMultiProxyProcessor, CliServerProxyProcessor, CliServiceContainer, CliServicesCommandProcessor, CliSleepCommandProcessor, CliSnippetCommandProcessor, CliSshCommandProcessor, CliStateStore, CliStateStoreManager, CliStateStoreManager_TOKEN, CliTerminalProgressBar, CliTerminalSpinner, CliTerminalTextAnimator, CliTerminalWriter, CliTestHarness, CliThemeCommandProcessor, CliTimeCommandProcessor, CliTimerManager, CliTimestampCommandProcessor, CliUnAliasCommandProcessor, CliUnameCommandProcessor, CliUnsetCommandProcessor, CliUptimeCommandProcessor, CliVersionCommandProcessor, CliYesCommandProcessor, CommandParser, ICliEnvironment_TOKEN, ICliPingServerService_TOKEN, JsFallbackAccelerator, LIBRARY_VERSION, OverlayAddon, ProcessExitedError, ScriptLoaderService, appendOutputToFile, appendStderrToFile, builtinProcessors, computeDiff, contextGlobalParameter, createServerModule, executeOnServer, expandEnvironmentVars, formatDiff, getAccelerator, getCliNameArt, getGreetingBasedOnTime, getTerminalOptions, groupBy, helpGlobalParameter, hotkeysInfo, initWasmAccelerator, initializeServices, initializeTerminal, isWasmAccelerated, miscProcessors, openLink, reconcileArgs, renderServerResponse, renderSingleOutput, substituteVars, systemProcessors, themeInfos, themes, tryExecuteScript, versionGlobalParameter, waitForLayout, welcomeModule, writeOutputToFile, writeStderrToFile };
18925
+ export { API_VERSION, CLi_Name_Art, CapturingTerminalWriter, CliAliasCommandProcessor, CliArgsParser, CliBackgroundServiceRegistry_TOKEN, CliBoot, CliCalCommandProcessor, CliCaptureCommandProcessor, CliClearCommandProcessor, CliClipboard, CliClipboardCommandProcessor, CliColorCommandProcessor, CliCommandExecutionContext, CliCommandExecutor, CliCommandHistory, CliCommandHistory_TOKEN, CliCommandProcessorRegistry, CliConfigureCommandProcessor, CliConvertCommandProcessor, CliDebugCommandProcessor, CliDefaultPingServerService, CliDiffCommandProcessor, CliDragDropService, CliEchoCommandProcessor, CliEngine, CliEnvCommandProcessor, CliEnvironment, CliEvalCommandProcessor, CliExecutionContext, CliExecutionProcess, CliExportCommandProcessor, CliFeedbackCommandProcessor, CliFontSizeCommandProcessor, CliFullScreenManager, CliHelpCommandProcessor, CliHistoryCommandProcessor, CliHotKeysCommandProcessor, CliHtopCommandProcessor, CliHttpClient, CliInputReader, CliJsonCommandProcessor, CliKeyValueStore, CliKeyValueStore_TOKEN, CliKillCommandProcessor, CliLogger, CliLoremCommandProcessor, CliModuleRegistry_TOKEN, CliNanoCommandProcessor, CliNotifier, CliOpenCommandProcessor, CliPackageManagerService, CliPackageManagerService_TOKEN, CliPackagesCommandProcessor, CliPermissionRegistry, CliPingCommandProcessor, CliProcessRegistry, CliProcessRegistry_TOKEN, CliProcessorsRegistry_TOKEN, CliPsCommandProcessor, CliRandomCommandProcessor, CliScreenCommandProcessor, CliSeqCommandProcessor, CliServerCommandProcessor, CliServerConnection, CliServerManager, CliServerManager_TOKEN, CliServerMultiProxyProcessor, CliServerProxyProcessor, CliServiceContainer, CliServicesCommandProcessor, CliSleepCommandProcessor, CliSnippetCommandProcessor, CliSshCommandProcessor, CliStateStore, CliStateStoreManager, CliStateStoreManager_TOKEN, CliTerminalProgressBar, CliTerminalSpinner, CliTerminalTextAnimator, CliTerminalWriter, CliTestHarness, CliThemeCommandProcessor, CliTimeCommandProcessor, CliTimerManager, CliTimestampCommandProcessor, CliUnAliasCommandProcessor, CliUnameCommandProcessor, CliUnsetCommandProcessor, CliUptimeCommandProcessor, CliVersionCommandProcessor, CliYesCommandProcessor, CommandParser, ICliEnvironment_TOKEN, ICliPingServerService_TOKEN, JsFallbackAccelerator, LIBRARY_VERSION, OverlayAddon, ProcessExitedError, ScriptLoaderService, SyntaxHighlighterRegistry_TOKEN, appendOutputToFile, appendStderrToFile, builtinProcessors, computeDiff, contextGlobalParameter, createServerModule, describeShellClose, executeOnServer, expandEnvironmentVars, formatDiff, getAccelerator, getCliNameArt, getGreetingBasedOnTime, getTerminalOptions, groupBy, helpGlobalParameter, hotkeysInfo, initWasmAccelerator, initializeServices, initializeTerminal, isWasmAccelerated, miscProcessors, openLink, reconcileArgs, renderServerResponse, renderSingleOutput, substituteVars, systemProcessors, themeInfos, themes, tryExecuteScript, versionGlobalParameter, waitForLayout, welcomeModule, writeOutputToFile, writeStderrToFile };
18358
18926
  //# sourceMappingURL=public-api.mjs.map
18359
18927
  //# sourceMappingURL=public-api.mjs.map