@qodalis/cli 2.3.0 → 2.4.0

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
@@ -518,7 +518,7 @@ var CliKeyValueStore_TOKEN = createCliToken("cli-key-value-store");
518
518
  var SyntaxHighlighterRegistry_TOKEN = createCliToken("syntax-highlighter-registry");
519
519
 
520
520
  // src/lib/version.ts
521
- var LIBRARY_VERSION = "2.3.0";
521
+ var LIBRARY_VERSION = "2.4.0";
522
522
  var API_VERSION = 2;
523
523
  var ANONYMOUS = "\0anonymous";
524
524
  function aliasOwner(context2) {
@@ -11187,7 +11187,78 @@ var CliServicesCommandProcessor = class {
11187
11187
  );
11188
11188
  }
11189
11189
  };
11190
+ var CLI_EVENT_HISTORY_SIZE = 200;
11191
+ function matchesEventPattern(type, pattern) {
11192
+ if (pattern === "*") return true;
11193
+ if (pattern.endsWith(":*")) {
11194
+ return type.startsWith(pattern.slice(0, -1));
11195
+ }
11196
+ return type === pattern;
11197
+ }
11198
+ var CliEventBus = class {
11199
+ constructor(onHandlerError = (error) => console.error("[CLI] Event handler failed:", error), historySize = CLI_EVENT_HISTORY_SIZE) {
11200
+ this.onHandlerError = onHandlerError;
11201
+ this.historySize = historySize;
11202
+ this.subject = new Subject();
11203
+ this.recent = [];
11204
+ this.disposed = false;
11205
+ }
11206
+ emit(type, data, options) {
11207
+ if (this.disposed) return;
11208
+ const event = {
11209
+ type,
11210
+ data,
11211
+ source: options?.source,
11212
+ timestamp: Date.now()
11213
+ };
11214
+ this.recent.push(event);
11215
+ if (this.recent.length > this.historySize) {
11216
+ this.recent.shift();
11217
+ }
11218
+ this.subject.next(event);
11219
+ }
11220
+ on(pattern, handler) {
11221
+ const subscription = this.events$(pattern).subscribe((event) => {
11222
+ try {
11223
+ handler(event);
11224
+ } catch (error) {
11225
+ this.onHandlerError(error, event);
11226
+ }
11227
+ });
11228
+ return () => subscription.unsubscribe();
11229
+ }
11230
+ once(pattern, handler) {
11231
+ let done = false;
11232
+ const unsubscribe = this.on(pattern, (event) => {
11233
+ if (done) return;
11234
+ done = true;
11235
+ unsubscribe();
11236
+ handler(event);
11237
+ });
11238
+ return unsubscribe;
11239
+ }
11240
+ events$(pattern = "*") {
11241
+ return this.subject.asObservable().pipe(filter((event) => matchesEventPattern(event.type, pattern)));
11242
+ }
11243
+ history(pattern = "*", limit) {
11244
+ const matching = this.recent.filter(
11245
+ (event) => matchesEventPattern(event.type, pattern)
11246
+ );
11247
+ return limit !== void 0 ? matching.slice(-limit) : matching;
11248
+ }
11249
+ /** Stop delivering events and complete all observables. */
11250
+ dispose() {
11251
+ if (this.disposed) return;
11252
+ this.disposed = true;
11253
+ this.subject.complete();
11254
+ this.recent.length = 0;
11255
+ }
11256
+ };
11257
+
11258
+ // src/lib/processors/system/cli-events-command-processor.ts
11190
11259
  var DATA_PREVIEW_LENGTH = 120;
11260
+ var SERVER_EVENT = "server:event";
11261
+ var SERVER_ENVELOPE_FIELDS = /* @__PURE__ */ new Set(["type", "timestamp"]);
11191
11262
  var RESERVED_NAMESPACES = [
11192
11263
  "boot",
11193
11264
  "command",
@@ -11199,26 +11270,83 @@ var RESERVED_NAMESPACES = [
11199
11270
  "session",
11200
11271
  "user"
11201
11272
  ];
11273
+ function serverEventOf(event) {
11274
+ return event.type === SERVER_EVENT ? event.data : void 0;
11275
+ }
11276
+ function matchesFilter(event, filter2) {
11277
+ const remote = serverEventOf(event);
11278
+ if (!remote) {
11279
+ return !filter2.server && matchesEventPattern(event.type, filter2.pattern);
11280
+ }
11281
+ if (filter2.local) return false;
11282
+ if (filter2.server && remote.server.toLowerCase() !== filter2.server.toLowerCase()) return false;
11283
+ return matchesEventPattern(remote.type, filter2.pattern) || matchesEventPattern(SERVER_EVENT, filter2.pattern);
11284
+ }
11202
11285
  function canSee(event, context2) {
11203
11286
  return !event.type.startsWith("user:") || cliAuthorize(context2, CliUsersPermissions.AuditRead);
11204
11287
  }
11288
+ function formatServerEventFields(data) {
11289
+ if (!data || typeof data !== "object") return "";
11290
+ const parts = [];
11291
+ for (const [key, value] of Object.entries(data)) {
11292
+ if (SERVER_ENVELOPE_FIELDS.has(key) || value === void 0 || value === null) continue;
11293
+ const text = typeof value === "string" ? value : JSON.stringify(value);
11294
+ parts.push(`${key}=${/\s/.test(text) ? JSON.stringify(text) : text}`);
11295
+ }
11296
+ return shorten(parts.join(" "));
11297
+ }
11298
+ function shorten(text) {
11299
+ return text.length > DATA_PREVIEW_LENGTH ? `${text.slice(0, DATA_PREVIEW_LENGTH - 1)}\u2026` : text;
11300
+ }
11205
11301
  function formatEvent(event, context2) {
11206
11302
  const { writer } = context2;
11207
- const time = new Date(event.timestamp).toISOString().slice(11, 23);
11208
- const source = event.source ? ` ${writer.wrapInColor(`[${event.source}]`, CliForegroundColor.Blue)}` : "";
11303
+ const remote = serverEventOf(event);
11304
+ let time = event.timestamp;
11305
+ let type = event.type;
11306
+ let source = event.source;
11209
11307
  let data = "";
11210
- if (event.data !== void 0) {
11308
+ if (remote) {
11309
+ const sent = remote.data?.timestamp;
11310
+ if (typeof sent === "string" && !isNaN(Date.parse(sent))) time = sent;
11311
+ type = remote.type;
11312
+ source = remote.server;
11313
+ data = formatServerEventFields(remote.data);
11314
+ } else if (event.data !== void 0) {
11211
11315
  try {
11212
11316
  data = JSON.stringify(event.data) ?? "";
11213
11317
  } catch {
11214
11318
  data = String(event.data);
11215
11319
  }
11216
- if (data.length > DATA_PREVIEW_LENGTH) {
11217
- data = `${data.slice(0, DATA_PREVIEW_LENGTH - 1)}\u2026`;
11218
- }
11320
+ data = shorten(data);
11219
11321
  }
11220
- return `${writer.wrapInColor(time, CliForegroundColor.Yellow)} ${writer.wrapInColor(event.type, CliForegroundColor.Cyan)}${source}${data ? ` ${data}` : ""}`;
11322
+ const when = new Date(time).toISOString().slice(11, 23);
11323
+ const from = source ? ` ${writer.wrapInColor(`[${source}]`, remote ? CliForegroundColor.Magenta : CliForegroundColor.Blue)}` : "";
11324
+ return `${writer.wrapInColor(when, CliForegroundColor.Yellow)} ${writer.wrapInColor(type, CliForegroundColor.Cyan)}${from}${data ? ` ${data}` : ""}`;
11325
+ }
11326
+ function filterOf(command2) {
11327
+ const server = command2.args["server"] ?? command2.args["s"];
11328
+ return {
11329
+ pattern: joinShellWords(command2.value ?? "") || "*",
11330
+ server: server === void 0 || server === true ? void 0 : String(server),
11331
+ local: !!(command2.args["local"] || command2.args["l"])
11332
+ };
11221
11333
  }
11334
+ var FILTER_PARAMETERS = [
11335
+ {
11336
+ name: "server",
11337
+ aliases: ["s"],
11338
+ type: "string",
11339
+ description: "Only the events of this server",
11340
+ required: false
11341
+ },
11342
+ {
11343
+ name: "local",
11344
+ aliases: ["l"],
11345
+ type: "boolean",
11346
+ description: "Only the terminal's own events",
11347
+ required: false
11348
+ }
11349
+ ];
11222
11350
  function busOf(context2) {
11223
11351
  if (!context2.events) {
11224
11352
  context2.writer.writeError(
@@ -11230,7 +11358,7 @@ function busOf(context2) {
11230
11358
  var CliEventsCommandProcessor = class {
11231
11359
  constructor() {
11232
11360
  this.command = "events";
11233
- this.description = "Watch, list and emit events on the event bus";
11361
+ this.description = "Watch, list and emit events of the terminal and the connected servers";
11234
11362
  this.author = DefaultLibraryAuthor;
11235
11363
  this.version = "1.0.0";
11236
11364
  this.metadata = {
@@ -11243,16 +11371,17 @@ var CliEventsCommandProcessor = class {
11243
11371
  command: "watch",
11244
11372
  description: "Print events as they happen (Ctrl+C to stop)",
11245
11373
  acceptsRawInput: true,
11374
+ parameters: FILTER_PARAMETERS,
11246
11375
  processCommand: async (command2, context2) => {
11247
11376
  const bus = busOf(context2);
11248
11377
  if (!bus) return;
11249
- const pattern = joinShellWords(command2.value ?? "") || "*";
11378
+ const filter2 = filterOf(command2);
11250
11379
  context2.writer.writeInfo(
11251
- `Watching ${context2.writer.wrapInColor(pattern, CliForegroundColor.Cyan)} \u2014 press Ctrl+C to stop`
11380
+ `Watching ${context2.writer.wrapInColor(filter2.pattern, CliForegroundColor.Cyan)} \u2014 press Ctrl+C to stop`
11252
11381
  );
11253
11382
  await new Promise((resolve) => {
11254
- const off = bus.on(pattern, (event) => {
11255
- if (canSee(event, context2)) {
11383
+ const off = bus.on("*", (event) => {
11384
+ if (matchesFilter(event, filter2) && canSee(event, context2)) {
11256
11385
  context2.writer.writeln(formatEvent(event, context2));
11257
11386
  }
11258
11387
  });
@@ -11278,6 +11407,12 @@ var CliEventsCommandProcessor = class {
11278
11407
  context2.writer.writeln(
11279
11408
  ` events watch boot:completed ${t.t("cli.events.watch.help.one_type", "One type")}`
11280
11409
  );
11410
+ context2.writer.writeln(
11411
+ ` events watch job:* -s node ${t.t("cli.events.watch.help.server_jobs", "Job events of the server node")}`
11412
+ );
11413
+ context2.writer.writeln(
11414
+ ` events watch --local ${t.t("cli.events.watch.help.local_only", "Only the terminal's own events")}`
11415
+ );
11281
11416
  }
11282
11417
  },
11283
11418
  {
@@ -11286,6 +11421,7 @@ var CliEventsCommandProcessor = class {
11286
11421
  description: "Show recent events (kept in memory, not persisted)",
11287
11422
  acceptsRawInput: true,
11288
11423
  parameters: [
11424
+ ...FILTER_PARAMETERS,
11289
11425
  {
11290
11426
  name: "limit",
11291
11427
  aliases: ["n"],
@@ -11297,13 +11433,13 @@ var CliEventsCommandProcessor = class {
11297
11433
  processCommand: async (command2, context2) => {
11298
11434
  const bus = busOf(context2);
11299
11435
  if (!bus) return;
11300
- const pattern = joinShellWords(command2.value ?? "") || "*";
11436
+ const filter2 = filterOf(command2);
11301
11437
  const limit = Number(
11302
11438
  command2.args["limit"] ?? command2.args["n"] ?? 20
11303
11439
  );
11304
- const events = bus.history(pattern).filter((event) => canSee(event, context2)).slice(-(limit > 0 ? limit : 20));
11440
+ const events = bus.history("*").filter((event) => matchesFilter(event, filter2) && canSee(event, context2)).slice(-(limit > 0 ? limit : 20));
11305
11441
  if (events.length === 0) {
11306
- context2.writer.writeInfo(`No events matching ${pattern}`);
11442
+ context2.writer.writeInfo(`No events matching ${filter2.pattern}`);
11307
11443
  return;
11308
11444
  }
11309
11445
  for (const event of events) {
@@ -11322,6 +11458,9 @@ var CliEventsCommandProcessor = class {
11322
11458
  context2.writer.writeln(
11323
11459
  ` events log server:* -n 50 ${t.t("cli.events.log.help.last_50_server", "Last 50 server events")}`
11324
11460
  );
11461
+ context2.writer.writeln(
11462
+ ` events log job:failed ${t.t("cli.events.log.help.failed_jobs", "Recent failed job runs, on every server")}`
11463
+ );
11325
11464
  }
11326
11465
  },
11327
11466
  {
@@ -11393,6 +11532,9 @@ var CliEventsCommandProcessor = class {
11393
11532
  writer.writeln(
11394
11533
  t.t("cli.events.help.patterns", "Patterns: an exact type (package:installed), a namespace (package:*) or * for all.")
11395
11534
  );
11535
+ writer.writeln(
11536
+ t.t("cli.events.help.servers", "Events of the connected servers (job runs, user, token and file changes, ...) are shown with the server name; each server sends only the events you may see. Filter with --server <name>, or --local for the terminal's own events.")
11537
+ );
11396
11538
  writer.writeln(
11397
11539
  t.t("cli.events.help.in_memory", "Events are kept in memory for this terminal only; nothing is persisted.")
11398
11540
  );
@@ -21998,73 +22140,6 @@ function createServerModule() {
21998
22140
  services: []
21999
22141
  };
22000
22142
  }
22001
- var CLI_EVENT_HISTORY_SIZE = 200;
22002
- function matchesEventPattern(type, pattern) {
22003
- if (pattern === "*") return true;
22004
- if (pattern.endsWith(":*")) {
22005
- return type.startsWith(pattern.slice(0, -1));
22006
- }
22007
- return type === pattern;
22008
- }
22009
- var CliEventBus = class {
22010
- constructor(onHandlerError = (error) => console.error("[CLI] Event handler failed:", error), historySize = CLI_EVENT_HISTORY_SIZE) {
22011
- this.onHandlerError = onHandlerError;
22012
- this.historySize = historySize;
22013
- this.subject = new Subject();
22014
- this.recent = [];
22015
- this.disposed = false;
22016
- }
22017
- emit(type, data, options) {
22018
- if (this.disposed) return;
22019
- const event = {
22020
- type,
22021
- data,
22022
- source: options?.source,
22023
- timestamp: Date.now()
22024
- };
22025
- this.recent.push(event);
22026
- if (this.recent.length > this.historySize) {
22027
- this.recent.shift();
22028
- }
22029
- this.subject.next(event);
22030
- }
22031
- on(pattern, handler) {
22032
- const subscription = this.events$(pattern).subscribe((event) => {
22033
- try {
22034
- handler(event);
22035
- } catch (error) {
22036
- this.onHandlerError(error, event);
22037
- }
22038
- });
22039
- return () => subscription.unsubscribe();
22040
- }
22041
- once(pattern, handler) {
22042
- let done = false;
22043
- const unsubscribe = this.on(pattern, (event) => {
22044
- if (done) return;
22045
- done = true;
22046
- unsubscribe();
22047
- handler(event);
22048
- });
22049
- return unsubscribe;
22050
- }
22051
- events$(pattern = "*") {
22052
- return this.subject.asObservable().pipe(filter((event) => matchesEventPattern(event.type, pattern)));
22053
- }
22054
- history(pattern = "*", limit) {
22055
- const matching = this.recent.filter(
22056
- (event) => matchesEventPattern(event.type, pattern)
22057
- );
22058
- return limit !== void 0 ? matching.slice(-limit) : matching;
22059
- }
22060
- /** Stop delivering events and complete all observables. */
22061
- dispose() {
22062
- if (this.disposed) return;
22063
- this.disposed = true;
22064
- this.subject.complete();
22065
- this.recent.length = 0;
22066
- }
22067
- };
22068
22143
 
22069
22144
  // src/lib/editor/syntax/registry.ts
22070
22145
  var SyntaxHighlighterRegistry = class {