@vendian/cli 0.0.30 → 0.0.31

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.
Files changed (2) hide show
  1. package/cli-wrapper.mjs +111 -83
  2. package/package.json +1 -1
package/cli-wrapper.mjs CHANGED
@@ -1107,7 +1107,7 @@ import path8 from "node:path";
1107
1107
  import readlinePromises from "node:readline/promises";
1108
1108
 
1109
1109
  // src/version.js
1110
- var CLI_VERSION = true ? "0.0.30" : process.env.npm_package_version || "0.0.0-dev";
1110
+ var CLI_VERSION = true ? "0.0.31" : process.env.npm_package_version || "0.0.0-dev";
1111
1111
 
1112
1112
  // src/npm-update.js
1113
1113
  var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
@@ -2398,6 +2398,7 @@ async function loadTerm() {
2398
2398
  term = tk.default.terminal;
2399
2399
  }
2400
2400
  var HEADER_ROWS_BASE = 7;
2401
+ var HEADER_ROWS_SERVE = 8;
2401
2402
  var SERVE_CONTENT_ROW = 10;
2402
2403
  function clampListIndex(index, count) {
2403
2404
  if (!Number.isFinite(count) || count <= 0) return 0;
@@ -2988,6 +2989,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
2988
2989
  let child = null;
2989
2990
  let startupError = "";
2990
2991
  let overlayActive = false;
2992
+ let overlayRedraw = null;
2991
2993
  let ctrlCArmed = false;
2992
2994
  let ctrlCArmTimer = null;
2993
2995
  let dashboardClosed = false;
@@ -3005,7 +3007,8 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3005
3007
  const status = friendlyStatus(rt.status);
3006
3008
  const path_ = agent.relativePath || ".";
3007
3009
  const runst = state.agentRunState[path_];
3008
- const errMsg2 = rt.status === "error" ? runst?.errorMessage || "Something went wrong \u2014 no details available" : null;
3010
+ const rawErr = runst?.errorMessage || (rt.status === "error" ? "Something went wrong" : null);
3011
+ const errMsg2 = rawErr ? typeof rawErr === "string" ? rawErr : rawErr.message || JSON.stringify(rawErr) : null;
3009
3012
  return { num: i + 1, name, status, errMsg: errMsg2, path: path_ };
3010
3013
  });
3011
3014
  }
@@ -3024,55 +3027,62 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3024
3027
  }
3025
3028
  function redraw() {
3026
3029
  if (dashboardClosed || overlayActive) return;
3030
+ const W = term.width || 80;
3031
+ const H = term.height || 24;
3027
3032
  const agents = agentDisplayList();
3028
3033
  const activity = recentActivityLines();
3029
3034
  const diagnostics = daemonDebugLines();
3030
3035
  const viewport = computeServeDashboardViewport({
3031
- termHeight: term.height || 24,
3036
+ termHeight: H,
3032
3037
  agentCount: agents.length,
3033
3038
  selectedIdx,
3034
3039
  activityCount: activity.length,
3035
3040
  diagnosticsCount: diagnostics.length
3036
3041
  });
3037
3042
  selectedIdx = viewport.selectedIdx;
3038
- drawHeader({ env, platform, serveState: state });
3039
- term.moveTo(1, SERVE_CONTENT_ROW);
3040
- term.eraseDisplayBelow();
3043
+ term.clear();
3041
3044
  agentRowMap.clear();
3045
+ drawHeader({ env, platform, serveState: state });
3046
+ term("\n");
3047
+ let currentRow = HEADER_ROWS_SERVE + 2;
3042
3048
  if (startupError) {
3043
- term.red(` ${fig.cross} ${clip(startupError, (term.width || 80) - 6)}
3049
+ term.red(` ${fig.cross} ${clip(startupError, W - 6)}
3044
3050
  `);
3045
3051
  } else if (stopRequested) {
3046
- term.yellow(` ${fig.warning} ${clip(state.activity || "Stopping your agents\u2026", (term.width || 80) - 6)}
3052
+ term.yellow(` ${fig.warning} ${clip(state.activity || "Stopping your agents\u2026", W - 6)}
3047
3053
  `);
3048
3054
  } else if (state.stopped) {
3049
3055
  term.yellow(` ${fig.warning} Your agents have stopped.
3050
3056
  `);
3051
3057
  } else if (state.connected) {
3052
3058
  term.green(` ${fig.dot} `);
3053
- term.gray(`${clip(state.activity || "Your agents are running", 55)}
3059
+ term.gray(`${clip(state.activity || "Your agents are running", W - 10)}
3054
3060
  `);
3055
3061
  } else {
3056
3062
  term.cyan(` ${fig.dotEmpty} `);
3057
- term.gray(`${clip(state.activity || "Starting up\u2026", 55)}
3063
+ term.gray(`${clip(state.activity || "Starting up\u2026", W - 10)}
3058
3064
  `);
3059
3065
  }
3066
+ currentRow++;
3060
3067
  if (agents.length > 0) {
3061
- if (viewport.showBlankAfterStatus) term("\n");
3068
+ if (viewport.showBlankAfterStatus) {
3069
+ term("\n");
3070
+ currentRow++;
3071
+ }
3062
3072
  term.bold.gray(" Your agents:\n");
3063
- let lineOffset = SERVE_CONTENT_ROW + 2 + (viewport.showBlankAfterStatus ? 1 : 0);
3073
+ currentRow++;
3064
3074
  const visibleAgents = agents.slice(
3065
3075
  viewport.agentWindowStart,
3066
3076
  viewport.agentWindowStart + viewport.visibleAgentCount
3067
3077
  );
3068
3078
  for (const ag of visibleAgents) {
3069
3079
  const isSelected = ag.num - 1 === selectedIdx;
3070
- agentRowMap.set(lineOffset, ag.num - 1);
3080
+ agentRowMap.set(currentRow, ag.num - 1);
3071
3081
  const numStr = String(ag.num).padStart(2);
3072
3082
  const nameStr = clip(ag.name, 28).padEnd(28);
3073
- const statLine = ` ${numStr} ${ag.status.icon} ${nameStr} ${ag.status.text}`;
3074
- if (isSelected) term.bgCyan.black.bold(statLine);
3075
- else {
3083
+ if (isSelected) {
3084
+ term.bgCyan.black.bold(` ${numStr} ${ag.status.icon} ${nameStr} ${ag.status.text}`);
3085
+ } else {
3076
3086
  term(` ${numStr} `);
3077
3087
  switch (ag.status.color) {
3078
3088
  case "green":
@@ -3115,11 +3125,11 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3115
3125
  }
3116
3126
  }
3117
3127
  term("\n");
3118
- lineOffset++;
3128
+ currentRow++;
3119
3129
  if (viewport.showInlineErrors && isSelected && ag.errMsg) {
3120
- term.red(` \u2514\u2500 ${clip(ag.errMsg, (term.width || 80) - 14)}
3130
+ term.red(` \u2514\u2500 ${clip(ag.errMsg, W - 14)}
3121
3131
  `);
3122
- lineOffset++;
3132
+ currentRow++;
3123
3133
  }
3124
3134
  }
3125
3135
  if (viewport.showAgentRange) {
@@ -3127,24 +3137,31 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3127
3137
  const last = viewport.agentWindowStart + visibleAgents.length;
3128
3138
  term.gray(` Showing agents ${first}\u2013${last} of ${agents.length}
3129
3139
  `);
3140
+ currentRow++;
3130
3141
  }
3131
3142
  if (viewport.showAgentHint) {
3132
3143
  term.gray(` Click an agent or use \u2191\u2193 + Enter to see its activity log
3133
3144
  `);
3145
+ currentRow++;
3134
3146
  }
3135
3147
  } else {
3136
- if (viewport.showBlankAfterStatus) term("\n");
3148
+ if (viewport.showBlankAfterStatus) {
3149
+ term("\n");
3150
+ currentRow++;
3151
+ }
3137
3152
  term.gray(" Looking for your agents\u2026\n");
3153
+ currentRow++;
3138
3154
  }
3139
3155
  if (viewport.showActivityHeader) {
3140
3156
  const visibleActivity = activity.slice(-viewport.visibleActivityCount);
3141
- const divW = Math.min((term.width || 80) - 4, 60);
3157
+ const divW = Math.min(W - 4, 60);
3142
3158
  term.gray(` ${"\u2500".repeat(3)} Recent activity ${"\u2500".repeat(Math.max(0, divW - 18))}
3143
3159
  `);
3160
+ currentRow++;
3144
3161
  for (const e of visibleActivity) {
3145
3162
  const t = formatLogTime(e.timestamp);
3146
3163
  const name = clip(e._agentName || "Agent", 20);
3147
- const msg = clip(friendlyActivityLine(e), (term.width || 80) - 30);
3164
+ const msg = clip(friendlyActivityLine(e), W - 32);
3148
3165
  if (e.level === "error" || e.eventType === "completion" && e.success === false) {
3149
3166
  term.gray(` ${t} `);
3150
3167
  term.red(`${name} \u2014 ${msg}
@@ -3161,31 +3178,32 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3161
3178
  term.gray(` ${t} ${name} \u2014 ${msg}
3162
3179
  `);
3163
3180
  }
3181
+ currentRow++;
3164
3182
  }
3165
3183
  }
3166
3184
  if (viewport.showDiagnosticsHeader) {
3167
3185
  const visibleDiagnostics = diagnostics.slice(-viewport.visibleDiagnosticsCount);
3168
- const divW = Math.min((term.width || 80) - 4, 72);
3186
+ const divW = Math.min(W - 4, 72);
3169
3187
  term.gray(` ${"\u2500".repeat(3)} Daemon diagnostics ${"\u2500".repeat(Math.max(0, divW - 23))}
3170
3188
  `);
3189
+ currentRow++;
3171
3190
  for (const entry of visibleDiagnostics) {
3172
3191
  const t = formatLogTime(entry.timestamp);
3173
- const type = clip(entry.eventType || "event", 24).padEnd(24);
3174
- const msg = clip(entry.message || "", (term.width || 80) - 38);
3192
+ const type = clip(entry.eventType || "event", 22).padEnd(22);
3193
+ const msg = clip(entry.message || "", W - 36);
3175
3194
  term.gray(` ${t} `);
3176
- if (entry.level === "error") {
3177
- term.red(`${type} ${msg}
3195
+ if (entry.level === "error") term.red(`${type} ${msg}
3178
3196
  `);
3179
- } else if (entry.level === "warn") {
3180
- term.yellow(`${type} ${msg}
3197
+ else if (entry.level === "warn") term.yellow(`${type} ${msg}
3181
3198
  `);
3182
- } else {
3183
- term.gray(`${type} ${msg}
3199
+ else term.gray(`${type} ${msg}
3184
3200
  `);
3185
- }
3201
+ currentRow++;
3186
3202
  }
3187
3203
  }
3188
- term("\n");
3204
+ const footerRow = Math.max(currentRow + 1, H - 1);
3205
+ if (footerRow < H) term.moveTo(1, footerRow);
3206
+ else term("\n");
3189
3207
  if (ctrlCArmed) {
3190
3208
  term.yellow.bold(" Press Ctrl+C again to exit.\n");
3191
3209
  } else if (stopRequested) {
@@ -3202,7 +3220,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3202
3220
  }
3203
3221
  term.clear();
3204
3222
  drawHeader({ env, platform, serveState: state });
3205
- term.moveTo(1, SERVE_CONTENT_ROW);
3223
+ term("\n");
3206
3224
  term.cyan(` ${fig.dotEmpty} Getting your agents ready\u2026
3207
3225
  `);
3208
3226
  try {
@@ -3233,12 +3251,14 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3233
3251
  (updater) => {
3234
3252
  if (dashboardClosed) return;
3235
3253
  state = updater(state);
3236
- redraw();
3254
+ if (overlayActive && typeof overlayRedraw === "function") overlayRedraw();
3255
+ else redraw();
3237
3256
  },
3238
3257
  ({ message } = {}) => {
3239
3258
  if (dashboardClosed) return;
3240
3259
  if (!state.stopped) state = { ...state, stopped: true, activity: message || "Stopped" };
3241
- redraw();
3260
+ if (overlayActive && typeof overlayRedraw === "function") overlayRedraw();
3261
+ else redraw();
3242
3262
  },
3243
3263
  logStore
3244
3264
  );
@@ -3252,10 +3272,20 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3252
3272
  const clampedIdx = clampListIndex(idx, agents.length);
3253
3273
  if (dashboardClosed || exitPromptActive || agents.length === 0 || idx !== clampedIdx) return;
3254
3274
  overlayActive = true;
3275
+ overlayRedraw = null;
3255
3276
  const ag = agents[clampedIdx];
3256
- showAgentLog({ agent: ag, getState: () => state, env, platform }).then(() => {
3277
+ showAgentLog({
3278
+ agent: ag,
3279
+ getState: () => state,
3280
+ onAttachLiveUpdates: (redrawFn) => {
3281
+ overlayRedraw = redrawFn;
3282
+ },
3283
+ env,
3284
+ platform
3285
+ }).then(() => {
3257
3286
  if (dashboardClosed) return;
3258
3287
  overlayActive = false;
3288
+ overlayRedraw = null;
3259
3289
  term.clear();
3260
3290
  redraw();
3261
3291
  });
@@ -3411,57 +3441,57 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3411
3441
  }
3412
3442
  });
3413
3443
  }
3414
- async function showAgentLog({ agent, getState, env, platform }) {
3444
+ async function showAgentLog({ agent, getState, onAttachLiveUpdates, env, platform }) {
3415
3445
  const liveState = typeof getState === "function" ? getState : () => getState;
3446
+ const H = term.height || 24;
3447
+ const visibleCount = Math.max(4, H - 14);
3416
3448
  let scrollOffset = 0;
3417
3449
  let autoScroll = true;
3418
- const visibleCount = Math.max(8, (term.height || 24) - 14);
3450
+ let done = false;
3419
3451
  function currentLogs() {
3420
3452
  return agentLogEntries(liveState().agentLogs, agent.path);
3421
3453
  }
3422
3454
  function draw() {
3423
- const state = liveState();
3455
+ if (done) return;
3456
+ const curState = liveState();
3424
3457
  const logs = currentLogs();
3425
- const agentObj = state.agents.find((a) => (a.relativePath || ".") === agent.path);
3426
- const runtime = agentObj ? agentRuntimeStatus(agentObj, state.agentRunState) : { status: "unknown", label: "offline" };
3458
+ const agentObj = curState.agents.find((a) => (a.relativePath || ".") === agent.path);
3459
+ const runtime = agentObj ? agentRuntimeStatus(agentObj, curState.agentRunState) : { status: "unknown", label: "offline" };
3427
3460
  const status = friendlyStatus(runtime.status);
3428
- if (autoScroll) {
3461
+ const W = term.width || 80;
3462
+ if (autoScroll || scrollOffset > Math.max(0, logs.length - visibleCount)) {
3429
3463
  scrollOffset = Math.max(0, logs.length - visibleCount);
3464
+ autoScroll = logs.length > 0;
3430
3465
  }
3431
3466
  term.clear();
3432
- drawHeader({ env, platform, serveState: state });
3467
+ drawHeader({ env, platform, serveState: curState });
3433
3468
  term("\n");
3434
3469
  term.bold(` ${agent.name}
3435
3470
  `);
3436
- term.gray(` ${agent.path} \u2014 `);
3471
+ term.gray(" ");
3437
3472
  switch (status.color) {
3438
3473
  case "green":
3439
- term.green(`${status.icon} ${status.text}
3440
- `);
3474
+ term.green(`${status.icon} ${status.text}`);
3441
3475
  break;
3442
3476
  case "brightBlue":
3443
- term.brightBlue(`${status.icon} ${status.text}
3444
- `);
3477
+ term.brightBlue(`${status.icon} ${status.text}`);
3445
3478
  break;
3446
3479
  case "yellow":
3447
- term.yellow(`${status.icon} ${status.text}
3448
- `);
3480
+ term.yellow(`${status.icon} ${status.text}`);
3449
3481
  break;
3450
3482
  case "red":
3451
- term.red(`${status.icon} ${status.text}
3452
- `);
3483
+ term.red(`${status.icon} ${status.text}`);
3453
3484
  break;
3454
3485
  default:
3455
- term.gray(`${status.icon} ${status.text}
3456
- `);
3486
+ term.gray(`${status.icon} ${status.text}`);
3457
3487
  }
3458
- term("\n");
3488
+ term("\n\n");
3459
3489
  if (logs.length === 0) {
3460
- term.gray(" No activity recorded yet. Logs will appear when this agent runs a job.\n");
3490
+ term.gray(" No activity yet \u2014 logs appear here as the agent runs.\n");
3461
3491
  } else {
3462
- const eff = scrollOffset >= logs.length - visibleCount - 2 ? Math.max(0, logs.length - visibleCount) : scrollOffset;
3492
+ const eff = Math.min(scrollOffset, Math.max(0, logs.length - visibleCount));
3463
3493
  const visible = logs.slice(eff, eff + visibleCount);
3464
- const msgW = Math.max(40, (term.width || 80) - 16);
3494
+ const msgW = Math.max(30, W - 16);
3465
3495
  for (const entry of visible) {
3466
3496
  const t = formatLogTime(entry.timestamp);
3467
3497
  const text = clip(formatAgentLogEntry(entry), msgW);
@@ -3474,6 +3504,9 @@ async function showAgentLog({ agent, getState, env, platform }) {
3474
3504
  `);
3475
3505
  } else if (entry.eventType === "job_started") {
3476
3506
  term.brightBlue(`\u25B6 ${text}
3507
+ `);
3508
+ } else if (entry.eventType === "progress") {
3509
+ term.cyan(`\u2192 ${text}
3477
3510
  `);
3478
3511
  } else {
3479
3512
  term.gray(`\u2014 ${text}
@@ -3481,42 +3514,36 @@ async function showAgentLog({ agent, getState, env, platform }) {
3481
3514
  }
3482
3515
  }
3483
3516
  if (logs.length > visibleCount) {
3517
+ const eff2 = Math.min(eff, Math.max(0, logs.length - visibleCount));
3484
3518
  term.gray(`
3485
- Showing ${eff + 1}\u2013${eff + visible.length} of ${logs.length} entries
3486
- `);
3519
+ ${eff2 + 1}\u2013${eff2 + visible.length} of ${logs.length}`);
3520
+ if (autoScroll) term.cyan(" \u2193 live");
3521
+ term("\n");
3487
3522
  }
3488
3523
  }
3489
- term("\n");
3524
+ term.moveTo(1, Math.max(H - 1, 6));
3490
3525
  term.gray(" ");
3491
3526
  term.brightBlue.bold("\u2191\u2193");
3492
- term.gray(" scroll ");
3493
- term.brightBlue.bold("PgUp/PgDn");
3494
- term.gray(" page ");
3495
- if (autoScroll) {
3496
- term.brightBlue.bold("Esc");
3497
- term.gray(" back ");
3498
- term.cyan("\u2193 live\n");
3499
- } else {
3500
- term.brightBlue.bold("Esc");
3501
- term.gray(" back ");
3502
- term.gray("\u2193 ");
3527
+ term.gray("/PgUp/PgDn scroll ");
3528
+ if (!autoScroll) {
3503
3529
  term.brightBlue.bold("End");
3504
- term.gray(" resume live\n");
3530
+ term.gray(" resume live ");
3505
3531
  }
3532
+ term.brightBlue.bold("Esc");
3533
+ term.gray(" back\n");
3534
+ }
3535
+ if (typeof onAttachLiveUpdates === "function") {
3536
+ onAttachLiveUpdates(() => draw());
3506
3537
  }
3507
3538
  draw();
3508
- let overlayDone = false;
3509
- const redrawInterval = setInterval(() => {
3510
- if (!overlayDone) draw();
3511
- }, 500);
3512
3539
  await new Promise((resolve) => {
3513
3540
  function handler(name) {
3541
+ if (done) return;
3514
3542
  const logs = currentLogs();
3515
3543
  const maxOff = Math.max(0, logs.length - visibleCount);
3516
3544
  if (name === "ESCAPE") {
3545
+ done = true;
3517
3546
  term.off("key", handler);
3518
- overlayDone = true;
3519
- clearInterval(redrawInterval);
3520
3547
  resolve();
3521
3548
  } else if (name === "UP") {
3522
3549
  autoScroll = false;
@@ -3528,14 +3555,15 @@ async function showAgentLog({ agent, getState, env, platform }) {
3528
3555
  draw();
3529
3556
  } else if (name === "PAGE_UP") {
3530
3557
  autoScroll = false;
3531
- scrollOffset = Math.max(0, scrollOffset - visibleCount);
3558
+ scrollOffset = Math.max(0, scrollOffset - Math.max(1, visibleCount - 1));
3532
3559
  draw();
3533
3560
  } else if (name === "PAGE_DOWN") {
3534
- scrollOffset = Math.min(maxOff, scrollOffset + visibleCount);
3561
+ scrollOffset = Math.min(maxOff, scrollOffset + Math.max(1, visibleCount - 1));
3535
3562
  if (scrollOffset >= maxOff) autoScroll = true;
3536
3563
  draw();
3537
- } else if (name === "END") {
3564
+ } else if (name === "END" || name === "g") {
3538
3565
  autoScroll = true;
3566
+ scrollOffset = maxOff;
3539
3567
  draw();
3540
3568
  }
3541
3569
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendian/cli",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "Public Vendian CLI bootstrapper and launcher",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,