sidekick-docker 0.2.4 → 0.2.5

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.
@@ -55711,7 +55711,7 @@ var require_DockerClient = __commonJS({
55711
55711
  const validated = schemas_1.ContainerInspectEnvSchema.parse(info);
55712
55712
  return validated.Config.Env ?? [];
55713
55713
  }
55714
- async *streamLogs(id, opts = {}) {
55714
+ async *streamLogs(id, opts = {}, signal) {
55715
55715
  const container = this.docker.getContainer(id);
55716
55716
  const logOpts = {
55717
55717
  follow: true,
@@ -55732,32 +55732,42 @@ var require_DockerClient = __commonJS({
55732
55732
  return;
55733
55733
  }
55734
55734
  const readable = stream;
55735
+ const destroy = () => readable.destroy?.();
55736
+ if (signal) {
55737
+ signal.addEventListener("abort", destroy, { once: true });
55738
+ }
55735
55739
  const buffer = [];
55736
- for await (const chunk of readable) {
55737
- const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
55738
- buffer.push(data);
55739
- const combined = Buffer.concat(buffer);
55740
- buffer.length = 0;
55741
- let offset = 0;
55742
- while (offset + 8 <= combined.length) {
55743
- const streamType = combined[offset];
55744
- const size = combined.readUInt32BE(offset + 4);
55745
- if (offset + 8 + size > combined.length) {
55746
- buffer.push(combined.subarray(offset));
55740
+ try {
55741
+ for await (const chunk of readable) {
55742
+ if (signal?.aborted)
55747
55743
  break;
55748
- }
55749
- const payload = combined.subarray(offset + 8, offset + 8 + size).toString("utf8");
55750
- const streamName = streamType === 2 ? "stderr" : "stdout";
55751
- for (const line of payload.split("\n")) {
55752
- if (line.trim()) {
55753
- yield parseLogLine(line, streamName);
55744
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
55745
+ buffer.push(data);
55746
+ const combined = Buffer.concat(buffer);
55747
+ buffer.length = 0;
55748
+ let offset = 0;
55749
+ while (offset + 8 <= combined.length) {
55750
+ const streamType = combined[offset];
55751
+ const size = combined.readUInt32BE(offset + 4);
55752
+ if (offset + 8 + size > combined.length) {
55753
+ buffer.push(combined.subarray(offset));
55754
+ break;
55755
+ }
55756
+ const payload = combined.subarray(offset + 8, offset + 8 + size).toString("utf8");
55757
+ const streamName = streamType === 2 ? "stderr" : "stdout";
55758
+ for (const line of payload.split("\n")) {
55759
+ if (line.trim()) {
55760
+ yield parseLogLine(line, streamName);
55761
+ }
55754
55762
  }
55763
+ offset += 8 + size;
55764
+ }
55765
+ if (offset < combined.length && buffer.length === 0) {
55766
+ buffer.push(combined.subarray(offset));
55755
55767
  }
55756
- offset += 8 + size;
55757
- }
55758
- if (offset < combined.length && buffer.length === 0) {
55759
- buffer.push(combined.subarray(offset));
55760
55768
  }
55769
+ } finally {
55770
+ destroy();
55761
55771
  }
55762
55772
  }
55763
55773
  parseStats(validated, prevCpu, prevSystem) {
@@ -55813,7 +55823,7 @@ var require_DockerClient = __commonJS({
55813
55823
  systemTotal: systemUsage
55814
55824
  };
55815
55825
  }
55816
- async *streamStats(id) {
55826
+ async *streamStats(id, signal) {
55817
55827
  const container = this.docker.getContainer(id);
55818
55828
  let prevCpu = 0;
55819
55829
  let prevSystem = 0;
@@ -55826,21 +55836,33 @@ var require_DockerClient = __commonJS({
55826
55836
  yield first;
55827
55837
  } catch {
55828
55838
  }
55839
+ if (signal?.aborted)
55840
+ return;
55829
55841
  const stream = await container.stats({ stream: true });
55830
- for await (const chunk of stream) {
55831
- const lines = chunk.toString("utf8").split("\n").filter(Boolean);
55832
- for (const line of lines) {
55833
- try {
55834
- const raw = JSON.parse(line);
55835
- const validated = schemas_1.DockerStatsRawSchema.parse(raw);
55836
- const { stats, cpuTotal, systemTotal } = this.parseStats(validated, prevCpu, prevSystem);
55837
- prevCpu = cpuTotal;
55838
- prevSystem = systemTotal;
55839
- yield stats;
55840
- } catch {
55841
- continue;
55842
+ const destroy = () => stream.destroy?.();
55843
+ if (signal) {
55844
+ signal.addEventListener("abort", destroy, { once: true });
55845
+ }
55846
+ try {
55847
+ for await (const chunk of stream) {
55848
+ if (signal?.aborted)
55849
+ break;
55850
+ const lines = chunk.toString("utf8").split("\n").filter(Boolean);
55851
+ for (const line of lines) {
55852
+ try {
55853
+ const raw = JSON.parse(line);
55854
+ const validated = schemas_1.DockerStatsRawSchema.parse(raw);
55855
+ const { stats, cpuTotal, systemTotal } = this.parseStats(validated, prevCpu, prevSystem);
55856
+ prevCpu = cpuTotal;
55857
+ prevSystem = systemTotal;
55858
+ yield stats;
55859
+ } catch {
55860
+ continue;
55861
+ }
55842
55862
  }
55843
55863
  }
55864
+ } finally {
55865
+ destroy();
55844
55866
  }
55845
55867
  }
55846
55868
  async listImages(all = false) {
@@ -56140,7 +56162,7 @@ var require_ComposeClient = __commonJS({
56140
56162
  async ps(project) {
56141
56163
  return this.exec(["-p", project, "ps", "--format", "json"]);
56142
56164
  }
56143
- async *streamLogs(project, service, tail = 100) {
56165
+ async *streamLogs(project, service, tail = 100, signal) {
56144
56166
  const args = ["-p", project, "logs", "--follow", "--tail", String(tail), "--timestamps"];
56145
56167
  if (service)
56146
56168
  args.push(service);
@@ -56179,6 +56201,14 @@ var require_ComposeClient = __commonJS({
56179
56201
  entries.push(entry);
56180
56202
  resolve?.();
56181
56203
  };
56204
+ const kill = () => {
56205
+ done = true;
56206
+ resolve?.();
56207
+ proc.kill();
56208
+ };
56209
+ if (signal) {
56210
+ signal.addEventListener("abort", kill, { once: true });
56211
+ }
56182
56212
  proc.stdout.on("data", (data) => {
56183
56213
  stdoutBuffer += data.toString();
56184
56214
  const lines = stdoutBuffer.split("\n");
@@ -56220,6 +56250,9 @@ var require_ComposeClient = __commonJS({
56220
56250
  }
56221
56251
  }
56222
56252
  } finally {
56253
+ proc.stdout.removeAllListeners();
56254
+ proc.stderr.removeAllListeners();
56255
+ proc.removeAllListeners();
56223
56256
  proc.kill();
56224
56257
  }
56225
56258
  }
@@ -56399,6 +56432,14 @@ var require_StatsCollector = __commonJS({
56399
56432
  remove(containerId) {
56400
56433
  this.histories.delete(containerId);
56401
56434
  }
56435
+ /** Remove history entries for containers not in the active set. */
56436
+ prune(activeContainerIds) {
56437
+ for (const id of this.histories.keys()) {
56438
+ if (!activeContainerIds.has(id)) {
56439
+ this.histories.delete(id);
56440
+ }
56441
+ }
56442
+ }
56402
56443
  clear() {
56403
56444
  this.histories.clear();
56404
56445
  }
@@ -56801,10 +56842,17 @@ var require_LogTemplateEngine = __commonJS({
56801
56842
  function isVariable(token) {
56802
56843
  return VARIABLE_PATTERNS.some((p) => p.test(token));
56803
56844
  }
56845
+ var DEFAULT_MAX_GROUPS = 500;
56804
56846
  var LogTemplateEngine2 = class {
56805
56847
  // Group templates by token count for efficient lookup
56806
56848
  groups = /* @__PURE__ */ new Map();
56807
56849
  totalLines = 0;
56850
+ groupCount = 0;
56851
+ droppedGroups = 0;
56852
+ maxGroups;
56853
+ constructor(maxGroups = DEFAULT_MAX_GROUPS) {
56854
+ this.maxGroups = maxGroups;
56855
+ }
56808
56856
  /**
56809
56857
  * Process a log line and update templates.
56810
56858
  */
@@ -56815,7 +56863,12 @@ var require_LogTemplateEngine = __commonJS({
56815
56863
  return;
56816
56864
  const groups = this.groups.get(tokens.length);
56817
56865
  if (!groups) {
56866
+ if (this.groupCount >= this.maxGroups) {
56867
+ this.droppedGroups++;
56868
+ return;
56869
+ }
56818
56870
  this.groups.set(tokens.length, [{ tokens: tokens.map((t) => isVariable(t) ? "<*>" : t), count: 1 }]);
56871
+ this.groupCount++;
56819
56872
  return;
56820
56873
  }
56821
56874
  let bestMatch = null;
@@ -56840,8 +56893,11 @@ var require_LogTemplateEngine = __commonJS({
56840
56893
  bestMatch.tokens[i] = "<*>";
56841
56894
  }
56842
56895
  }
56843
- } else {
56896
+ } else if (this.groupCount < this.maxGroups) {
56844
56897
  groups.push({ tokens: tokens.map((t) => isVariable(t) ? "<*>" : t), count: 1 });
56898
+ this.groupCount++;
56899
+ } else {
56900
+ this.droppedGroups++;
56845
56901
  }
56846
56902
  }
56847
56903
  /**
@@ -56864,9 +56920,18 @@ var require_LogTemplateEngine = __commonJS({
56864
56920
  getTotalLines() {
56865
56921
  return this.totalLines;
56866
56922
  }
56923
+ getDiagnostics() {
56924
+ return {
56925
+ groupCount: this.groupCount,
56926
+ droppedGroups: this.droppedGroups,
56927
+ totalLines: this.totalLines
56928
+ };
56929
+ }
56867
56930
  reset() {
56868
56931
  this.groups.clear();
56869
56932
  this.totalLines = 0;
56933
+ this.groupCount = 0;
56934
+ this.droppedGroups = 0;
56870
56935
  }
56871
56936
  };
56872
56937
  exports2.LogTemplateEngine = LogTemplateEngine2;
@@ -94320,6 +94385,19 @@ var DockerState = class {
94320
94385
  this.composeProjects = this.composeDetector.detect(containers, fileConfig);
94321
94386
  this.lastRefresh = /* @__PURE__ */ new Date();
94322
94387
  this.daemonConnected = true;
94388
+ const currentContainerIds = new Set(containers.map((c) => c.id));
94389
+ for (const id of this.inspectedEnv.keys()) {
94390
+ if (!currentContainerIds.has(id)) this.inspectedEnv.delete(id);
94391
+ }
94392
+ for (const id of this.containerChanges.keys()) {
94393
+ if (!currentContainerIds.has(id)) this.containerChanges.delete(id);
94394
+ }
94395
+ const currentImageIds = new Set(images.map((i) => i.id));
94396
+ for (const id of this.imageLayers.keys()) {
94397
+ if (!currentImageIds.has(id)) this.imageLayers.delete(id);
94398
+ }
94399
+ const runningIds = new Set(containers.filter((c) => c.state === "running").map((c) => c.id));
94400
+ this.statsCollector.prune(runningIds);
94323
94401
  } catch {
94324
94402
  this.daemonConnected = false;
94325
94403
  }
@@ -94369,6 +94447,8 @@ var DockerState = class {
94369
94447
  case "destroy":
94370
94448
  this.containers = this.containers.filter((c) => c.id !== resourceId);
94371
94449
  this.statsCollector.remove(resourceId);
94450
+ this.inspectedEnv.delete(resourceId);
94451
+ this.containerChanges.delete(resourceId);
94372
94452
  break;
94373
94453
  case "create":
94374
94454
  this.refresh().catch((e) => console.debug("refresh failed:", e));
@@ -94437,19 +94517,19 @@ var DockerState = class {
94437
94517
  }
94438
94518
  getMetrics() {
94439
94519
  return {
94440
- containers: [...this.containers],
94441
- images: [...this.images],
94442
- volumes: [...this.volumes],
94443
- networks: [...this.networks],
94444
- composeProjects: [...this.composeProjects],
94520
+ containers: this.containers,
94521
+ images: this.images,
94522
+ volumes: this.volumes,
94523
+ networks: this.networks,
94524
+ composeProjects: this.composeProjects,
94445
94525
  statsCollector: this.statsCollector,
94446
94526
  inspectedEnv: this.inspectedEnv,
94447
94527
  containerChanges: this.containerChanges,
94448
94528
  imageLayers: this.imageLayers,
94449
- selectedContainerLogs: [...this.selectedLogs],
94450
- selectedComposeLogs: [...this.selectedComposeLogs],
94451
- secondaryContainerLogs: [...this.secondaryLogs],
94452
- secondaryComposeLogs: [...this.secondaryComposeLogs],
94529
+ selectedContainerLogs: this.selectedLogs,
94530
+ selectedComposeLogs: this.selectedComposeLogs,
94531
+ secondaryContainerLogs: this.secondaryLogs,
94532
+ secondaryComposeLogs: this.secondaryComposeLogs,
94453
94533
  lastRefresh: this.lastRefresh,
94454
94534
  daemonConnected: this.daemonConnected,
94455
94535
  logFilterString: "",
@@ -94593,17 +94673,27 @@ function colorizeTokens(message) {
94593
94673
  return colorFn ? colorFn(t.text) : t.text;
94594
94674
  }).join("");
94595
94675
  }
94676
+ var colorizedCache = /* @__PURE__ */ new WeakMap();
94596
94677
  function colorizeLogEntry(entry, filterMatches) {
94678
+ if (!filterMatches) {
94679
+ const cached = colorizedCache.get(entry);
94680
+ if (cached !== void 0) return cached;
94681
+ }
94597
94682
  const ts = entry.timestamp ? (0, import_sidekick_docker_shared2.formatTimestampTime)(entry.timestamp) : "";
94598
94683
  const tsColored = ts ? ansi.dim(ansi.gray(ts)) + " " : "";
94684
+ let result;
94599
94685
  if (entry.stream === "stderr") {
94600
94686
  const msg = filterMatches ? highlightMatches(entry.message, filterMatches, ansi.red) : ansi.red(entry.message);
94601
- return tsColored + msg;
94687
+ result = tsColored + msg;
94688
+ } else if (filterMatches && filterMatches.length > 0) {
94689
+ result = tsColored + highlightMatches(entry.message, filterMatches);
94690
+ } else {
94691
+ result = tsColored + colorizeTokens(entry.message);
94602
94692
  }
94603
- if (filterMatches && filterMatches.length > 0) {
94604
- return tsColored + highlightMatches(entry.message, filterMatches);
94693
+ if (!filterMatches) {
94694
+ colorizedCache.set(entry, result);
94605
94695
  }
94606
- return tsColored + colorizeTokens(entry.message);
94696
+ return result;
94607
94697
  }
94608
94698
  function highlightMatches(text, matches, baseFn) {
94609
94699
  if (matches.length === 0) return baseFn ? baseFn(text) : colorizeTokens(text);
@@ -94765,8 +94855,8 @@ var ContainersPanel = class {
94765
94855
  label: "Logs",
94766
94856
  render: (_item, metrics) => {
94767
94857
  const logs = metrics.selectedContainerLogs;
94768
- if (logs.length === 0) return "No logs available. Select a container to view logs.";
94769
- return renderLogLines(logs, metrics.logFilterString, metrics.logFilterMode, metrics.logSeverityCounts).join("\n");
94858
+ if (logs.length === 0) return ["No logs available. Select a container to view logs."];
94859
+ return renderLogLines(logs, metrics.logFilterString, metrics.logFilterMode, metrics.logSeverityCounts);
94770
94860
  },
94771
94861
  autoScrollBottom: true
94772
94862
  },
@@ -95065,8 +95155,8 @@ var ServicesPanel = class {
95065
95155
  label: "Logs",
95066
95156
  render: (_item, metrics) => {
95067
95157
  const logs = metrics.selectedComposeLogs;
95068
- if (logs.length === 0) return "No compose logs. Logs will appear when a service produces output.";
95069
- return renderLogLines(logs, metrics.logFilterString, metrics.logFilterMode).join("\n");
95158
+ if (logs.length === 0) return ["No compose logs. Logs will appear when a service produces output."];
95159
+ return renderLogLines(logs, metrics.logFilterString, metrics.logFilterMode);
95070
95160
  },
95071
95161
  autoScrollBottom: true
95072
95162
  }
@@ -95479,6 +95569,8 @@ var BaseStreamManager = class {
95479
95569
  streamPromise = null;
95480
95570
  reconnect = new import_sidekick_docker_shared6.ReconnectScheduler();
95481
95571
  onChange;
95572
+ streamController = null;
95573
+ streamGeneration = 0;
95482
95574
  constructor(onChange) {
95483
95575
  this.onChange = onChange;
95484
95576
  this.currentId = this.emptyId();
@@ -95492,25 +95584,29 @@ var BaseStreamManager = class {
95492
95584
  if (!this.isValidId(id)) return;
95493
95585
  this.aborted = false;
95494
95586
  this.reconnect.reset();
95587
+ this.streamController = new AbortController();
95588
+ this.streamGeneration++;
95495
95589
  this.onBeforeStream();
95496
- this.streamPromise = this.runStream(id);
95590
+ this.streamPromise = this.runStream(id, this.streamController.signal, this.streamGeneration);
95497
95591
  }
95498
- async runStream(id) {
95592
+ async runStream(id, signal, generation) {
95499
95593
  try {
95500
- for await (const item of this.createStream(id)) {
95501
- if (this.aborted || !this.isSameId(id, this.currentId)) break;
95594
+ for await (const item of this.createStream(id, signal)) {
95595
+ if (signal.aborted || this.aborted || !this.isSameId(id, this.currentId)) break;
95502
95596
  this.processItem(id, item);
95503
95597
  this.onChange();
95504
95598
  }
95505
95599
  this.reconnect.reset();
95506
95600
  } catch (err) {
95601
+ if (signal.aborted) return;
95507
95602
  console.debug(`${this.streamLabel} stream error:`, (0, import_sidekick_docker_shared6.errorMessage)(err));
95508
95603
  }
95509
- if (!this.aborted && this.isSameId(id, this.currentId)) {
95604
+ if (!signal.aborted && !this.aborted && this.isSameId(id, this.currentId) && generation === this.streamGeneration) {
95510
95605
  const scheduled = this.reconnect.schedule(() => {
95511
- if (!this.aborted && this.isSameId(id, this.currentId)) {
95606
+ if (!this.aborted && this.isSameId(id, this.currentId) && generation === this.streamGeneration) {
95607
+ this.streamController = new AbortController();
95512
95608
  this.onBeforeStream();
95513
- this.streamPromise = this.runStream(id);
95609
+ this.streamPromise = this.runStream(id, this.streamController.signal, generation);
95514
95610
  }
95515
95611
  });
95516
95612
  if (!scheduled) {
@@ -95521,6 +95617,8 @@ var BaseStreamManager = class {
95521
95617
  stop() {
95522
95618
  this.aborted = true;
95523
95619
  this.currentId = this.emptyId();
95620
+ this.streamController?.abort();
95621
+ this.streamController = null;
95524
95622
  this.streamPromise = null;
95525
95623
  this.reconnect.clear();
95526
95624
  this.onStop();
@@ -95561,8 +95659,8 @@ var LogStreamManager = class extends BaseStreamManager {
95561
95659
  idLabel(id) {
95562
95660
  return id ?? "(none)";
95563
95661
  }
95564
- createStream(id) {
95565
- return this.client.streamLogs(id, { follow: true, tail: 100 });
95662
+ createStream(id, signal) {
95663
+ return this.client.streamLogs(id, { follow: true, tail: 100 }, signal);
95566
95664
  }
95567
95665
  processItem(_id, entry) {
95568
95666
  this.logs.push(entry);
@@ -95594,6 +95692,9 @@ var LogStreamManager = class extends BaseStreamManager {
95594
95692
  getCurrentContainerId() {
95595
95693
  return this.currentId;
95596
95694
  }
95695
+ getTemplateDiagnostics() {
95696
+ return this.templateEngine.getDiagnostics();
95697
+ }
95597
95698
  };
95598
95699
 
95599
95700
  // src/dashboard/StatsStreamManager.ts
@@ -95618,8 +95719,8 @@ var StatsStreamManager = class extends BaseStreamManager {
95618
95719
  idLabel(id) {
95619
95720
  return id ?? "(none)";
95620
95721
  }
95621
- createStream(id) {
95622
- return this.client.streamStats(id);
95722
+ createStream(id, signal) {
95723
+ return this.client.streamStats(id, signal);
95623
95724
  }
95624
95725
  processItem(id, stats) {
95625
95726
  this.collector.push(id, stats);
@@ -95659,8 +95760,8 @@ var ComposeLogStreamManager = class extends BaseStreamManager {
95659
95760
  idLabel(id) {
95660
95761
  return id.project ?? "(none)";
95661
95762
  }
95662
- createStream(id) {
95663
- return this.composeClient.streamLogs(id.project, id.service ?? void 0);
95763
+ createStream(id, signal) {
95764
+ return this.composeClient.streamLogs(id.project, id.service ?? void 0, 100, signal);
95664
95765
  }
95665
95766
  processItem(_id, entry) {
95666
95767
  this.logs.push(entry);
@@ -97222,13 +97323,13 @@ function Dashboard({ panels, metrics, onViewStateChange, execTriggerRef, onExecF
97222
97323
  logFilterString: state.logFilterString,
97223
97324
  logFilterMode: state.logFilterMode
97224
97325
  };
97225
- let detailContent = "";
97326
+ let detailLines = [];
97226
97327
  if (selectedItem && detailTabs.length > 0 && tabIdx >= 0) {
97227
- detailContent = detailTabs[tabIdx].render(selectedItem, enrichedMetrics);
97328
+ const result = detailTabs[tabIdx].render(selectedItem, enrichedMetrics);
97329
+ detailLines = Array.isArray(result) ? result : result.split("\n");
97228
97330
  } else if (!selectedItem) {
97229
- detailContent = "(no item selected)";
97331
+ detailLines = ["(no item selected)"];
97230
97332
  }
97231
- const detailLines = detailContent.split("\n");
97232
97333
  const detailViewportHeight = Math.max(1, rows - RESERVED_UI_ROWS);
97233
97334
  const activeTab = detailTabs[tabIdx];
97234
97335
  const shouldAutoScroll = activeTab?.autoScrollBottom ?? false;
@@ -97380,8 +97481,8 @@ function Dashboard({ panels, metrics, onViewStateChange, execTriggerRef, onExecF
97380
97481
  )
97381
97482
  ] })
97382
97483
  ] }),
97383
- state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.2.4" }),
97384
- state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VersionOverlay, { version: "0.2.4" }),
97484
+ state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.2.5" }),
97485
+ state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VersionOverlay, { version: "0.2.5" }),
97385
97486
  state.overlay === "exec" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
97386
97487
  ExecOverlay,
97387
97488
  {
@@ -97398,7 +97499,7 @@ function Dashboard({ panels, metrics, onViewStateChange, execTriggerRef, onExecF
97398
97499
  filterString: state.filterString,
97399
97500
  containerCount: metrics.containers.length,
97400
97501
  runningCount,
97401
- version: "0.2.4",
97502
+ version: "0.2.5",
97402
97503
  matchCount: state.filterString ? currentItems.length : void 0,
97403
97504
  totalCount: state.filterString ? totalItemCount : void 0,
97404
97505
  lastRefresh: metrics.lastRefresh,
@@ -97477,7 +97578,7 @@ async function dashboardAction(_opts, cmd) {
97477
97578
  onExecFallback
97478
97579
  })
97479
97580
  );
97480
- }, 100);
97581
+ }, 200);
97481
97582
  }
97482
97583
  const logManager = new LogStreamManager(client, () => {
97483
97584
  if (logFlushTimer) return;
@@ -97660,6 +97761,17 @@ async function dashboardAction(_opts, cmd) {
97660
97761
  const refreshInterval = setInterval(() => {
97661
97762
  state.refresh().then(() => scheduleRender()).catch((e) => console.debug("periodic refresh failed:", e));
97662
97763
  }, 3e4);
97764
+ let debugInterval = null;
97765
+ if (process.env.SIDEKICK_DEBUG_STREAMS === "1") {
97766
+ debugInterval = setInterval(() => {
97767
+ const mem = process.memoryUsage();
97768
+ const heapMB = (mem.heapUsed / 1024 / 1024).toFixed(1);
97769
+ const rssMB = (mem.rss / 1024 / 1024).toFixed(1);
97770
+ const templateDiag = logManager.getTemplateDiagnostics();
97771
+ const secondaryDiag = secondaryLogManager.getTemplateDiagnostics();
97772
+ console.debug(`[sidekick-debug] heap=${heapMB}MB rss=${rssMB}MB templates=${JSON.stringify(templateDiag)} secondary=${JSON.stringify(secondaryDiag)}`);
97773
+ }, 6e4);
97774
+ }
97663
97775
  const execTriggerRef = { current: null };
97664
97776
  const onExecFallback = (containerId) => {
97665
97777
  instance.clear();
@@ -97759,6 +97871,10 @@ async function dashboardAction(_opts, cmd) {
97759
97871
  clearInterval(refreshInterval);
97760
97872
  } catch {
97761
97873
  }
97874
+ try {
97875
+ if (debugInterval) clearInterval(debugInterval);
97876
+ } catch {
97877
+ }
97762
97878
  try {
97763
97879
  watcher.stop();
97764
97880
  } catch {
@@ -97839,7 +97955,7 @@ async function logsAction(container, opts) {
97839
97955
 
97840
97956
  // src/cli.ts
97841
97957
  var program2 = new Command();
97842
- program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.2.4").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
97958
+ program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.2.5").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
97843
97959
  await dashboardAction(_opts, cmd);
97844
97960
  });
97845
97961
  program2.command("ps").description("List containers").option("-a, --all", "Show all containers (default: running only)", false).action(async (opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-docker",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Docker management TUI dashboard",
5
5
  "author": "Cesar Andres Lopez <cesarandreslopez@gmail.com>",
6
6
  "repository": {