@askalf/dario 5.4.4 → 5.4.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.
@@ -112,17 +112,18 @@ export const HitsTab = {
112
112
  const detailRows = 9;
113
113
  const listRows = Math.max(3, totalRows - detailRows - 2);
114
114
  if (state.buffer.length === 0) {
115
- lines.push(' ' + brand('Hits') + dim(' — live request stream'));
115
+ lines.push(truncate(' ' + brand('Hits') + dim(' — live request stream'), w));
116
116
  lines.push('');
117
117
  if (state.connectionError) {
118
- lines.push(' ' + fg('red', `SSE error: ${state.connectionError}`));
119
- lines.push(' ' + dim('Is `dario proxy` running? The stream reconnects automatically on the next mount.'));
118
+ // The error text is upstream-supplied and unbounded.
119
+ lines.push(truncate(' ' + fg('red', `SSE error: ${state.connectionError}`), w));
120
+ lines.push(truncate(' ' + dim('Is `dario proxy` running? The stream reconnects automatically on the next mount.'), w));
120
121
  }
121
122
  else if (!state.subscribed) {
122
- lines.push(' ' + dim('Connecting to /analytics/stream …'));
123
+ lines.push(truncate(' ' + dim('Connecting to /analytics/stream …'), w));
123
124
  }
124
125
  else {
125
- lines.push(' ' + dim('Waiting for requests. Send one through dario to see it land here.'));
126
+ lines.push(truncate(' ' + dim('Waiting for requests. Send one through dario to see it land here.'), w));
126
127
  }
127
128
  return lines.join('\n');
128
129
  }
@@ -136,26 +137,34 @@ export const HitsTab = {
136
137
  const colTime = 9;
137
138
  const colModel = 18;
138
139
  const colIn = 8, colOut = 7, colLat = 7, colStatus = 5;
139
- lines.push(' ' + brand('Hits') +
140
- dim(` ${state.buffer.length} buffered · ${state.subscribed ? fg('green', 'live') : fg('yellow', 'disconnected')}`));
140
+ lines.push(truncate(' ' + brand('Hits') +
141
+ dim(` ${state.buffer.length} buffered · ${state.subscribed ? fg('green', 'live') : fg('yellow', 'disconnected')}`), w));
141
142
  // ── Overage-halt banner (v4.1, dario#288) ──────────────────
142
143
  // Pinned at the top so it's always visible while scrolling the buffer.
144
+ //
145
+ // Both lines are ordered most-actionable-first and kept short enough
146
+ // to fit 80 columns, because a banner that wraps costs a second
147
+ // physical row and pushes the panel head off the top of the screen —
148
+ // and it wraps exactly when the user needs to read it. The account is
149
+ // last on line 1 so a narrow terminal clips the least-critical field;
150
+ // the resume instructions on line 2 fit outright.
143
151
  if (state.halt) {
144
152
  const since = formatTimestamp(state.halt.since);
145
153
  const cooldown = formatRemaining(state.halt.cooldownUntil - Date.now());
146
- const line1 = ` ${fg('red', '⚠ HALTED')} ${state.halt.request.claim} detected at ${since} on ${state.halt.request.model} (account=${state.halt.request.account})`;
147
- const line2 = ` ${dim('→ New /v1/messages requests return 503 until')} ${fg('cyan', 'R')} ${dim('here, or')} ${fg('cyan', 'dario resume')}${dim(' from any shell. Auto-resume in')} ${cooldown}${dim('.')}`;
148
- lines.push(line1);
149
- lines.push(line2);
154
+ const line1 = ` ${fg('red', '⚠ HALTED')} ${state.halt.request.claim} · ${since} · ${shortenModel(state.halt.request.model)} · ${dim(state.halt.request.account)}`;
155
+ const line2 = ` ${dim('→ 503 until')} ${fg('cyan', 'R')} ${dim('or')} ${fg('cyan', 'dario resume')} ${dim( auto-resume in ${cooldown}`)}`;
156
+ lines.push(truncate(line1, w));
157
+ lines.push(truncate(line2, w));
150
158
  }
151
159
  lines.push('');
152
- // Header row (aligned with data rows)
153
- lines.push(' ' + dim(pad('time', colTime) +
160
+ // Header row truncated to the same budget as the data rows below
161
+ // (`w - 2`) so the columns stay aligned when the terminal is narrow.
162
+ lines.push(truncate(' ' + dim(pad('time', colTime) +
154
163
  pad('model', colModel) +
155
164
  pad('in', colIn) +
156
165
  pad('out', colOut) +
157
166
  pad('lat', colLat) +
158
- pad('st', colStatus)));
167
+ pad('st', colStatus)), w - 2));
159
168
  for (let i = startIdx; i < endIdx; i++) {
160
169
  const r = newestFirst[i];
161
170
  // Flag any non-subscription billing red — the same condition the
@@ -185,16 +194,16 @@ export const HitsTab = {
185
194
  }
186
195
  // Scroll hint
187
196
  if (newestFirst.length > listRows) {
188
- lines.push(' ' + dim(`${state.selectedIdx + 1} / ${newestFirst.length} ` +
197
+ lines.push(truncate(' ' + dim(`${state.selectedIdx + 1} / ${newestFirst.length} ` +
189
198
  (startIdx > 0 ? '↑ more ' : '') +
190
- (endIdx < newestFirst.length ? '↓ more' : '')));
199
+ (endIdx < newestFirst.length ? '↓ more' : '')), w));
191
200
  }
192
201
  // Separator
193
202
  lines.push(' ' + dim(BOX.horizontal.repeat(w - 2)));
194
203
  // Detail pane
195
204
  if (state.selectedIdx >= 0 && state.selectedIdx < newestFirst.length) {
196
205
  const r = newestFirst[state.selectedIdx];
197
- lines.push(' ' + brand('Selected') + dim(` ${formatTime(r.timestamp)}`));
206
+ lines.push(truncate(' ' + brand('Selected') + dim(` ${formatTime(r.timestamp)}`), w));
198
207
  lines.push(' ' + renderKvRow('Account', r.account, w - 4));
199
208
  lines.push(' ' + renderKvRow('Model', r.model, w - 4));
200
209
  lines.push(' ' + renderKvRow('Billing bucket', billingBucketFromClaim(r.claim), w - 4));
@@ -205,7 +214,7 @@ export const HitsTab = {
205
214
  }
206
215
  else {
207
216
  lines.push('');
208
- lines.push(' ' + dim('Use ↑↓ to select a request for details.'));
217
+ lines.push(truncate(' ' + dim('Use ↑↓ to select a request for details.'), w));
209
218
  }
210
219
  return lines.join('\n');
211
220
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "5.4.4",
3
+ "version": "5.4.5",
4
4
  "description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
5
5
  "type": "module",
6
6
  "bin": {