@vendian/cli 0.0.29 → 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 +127 -67
  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.29" : 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, 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,48 +3441,57 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId }) {
3411
3441
  }
3412
3442
  });
3413
3443
  }
3414
- async function showAgentLog({ agent, state, env, platform }) {
3415
- const logs = agentLogEntries(state.agentLogs, agent.path);
3416
- const agentObj = state.agents.find((a) => (a.relativePath || ".") === agent.path);
3417
- const runtime = agentObj ? agentRuntimeStatus(agentObj, state.agentRunState) : { status: "unknown", label: "offline" };
3418
- const status = friendlyStatus(runtime.status);
3444
+ async function showAgentLog({ agent, getState, onAttachLiveUpdates, env, platform }) {
3445
+ const liveState = typeof getState === "function" ? getState : () => getState;
3446
+ const H = term.height || 24;
3447
+ const visibleCount = Math.max(4, H - 14);
3419
3448
  let scrollOffset = 0;
3420
- const visibleCount = Math.max(8, (term.height || 24) - 14);
3449
+ let autoScroll = true;
3450
+ let done = false;
3451
+ function currentLogs() {
3452
+ return agentLogEntries(liveState().agentLogs, agent.path);
3453
+ }
3421
3454
  function draw() {
3455
+ if (done) return;
3456
+ const curState = liveState();
3457
+ const logs = currentLogs();
3458
+ const agentObj = curState.agents.find((a) => (a.relativePath || ".") === agent.path);
3459
+ const runtime = agentObj ? agentRuntimeStatus(agentObj, curState.agentRunState) : { status: "unknown", label: "offline" };
3460
+ const status = friendlyStatus(runtime.status);
3461
+ const W = term.width || 80;
3462
+ if (autoScroll || scrollOffset > Math.max(0, logs.length - visibleCount)) {
3463
+ scrollOffset = Math.max(0, logs.length - visibleCount);
3464
+ autoScroll = logs.length > 0;
3465
+ }
3422
3466
  term.clear();
3423
- drawHeader({ env, platform, serveState: state });
3467
+ drawHeader({ env, platform, serveState: curState });
3424
3468
  term("\n");
3425
3469
  term.bold(` ${agent.name}
3426
3470
  `);
3427
- term.gray(` ${agent.path} \u2014 `);
3471
+ term.gray(" ");
3428
3472
  switch (status.color) {
3429
3473
  case "green":
3430
- term.green(`${status.icon} ${status.text}
3431
- `);
3474
+ term.green(`${status.icon} ${status.text}`);
3432
3475
  break;
3433
3476
  case "brightBlue":
3434
- term.brightBlue(`${status.icon} ${status.text}
3435
- `);
3477
+ term.brightBlue(`${status.icon} ${status.text}`);
3436
3478
  break;
3437
3479
  case "yellow":
3438
- term.yellow(`${status.icon} ${status.text}
3439
- `);
3480
+ term.yellow(`${status.icon} ${status.text}`);
3440
3481
  break;
3441
3482
  case "red":
3442
- term.red(`${status.icon} ${status.text}
3443
- `);
3483
+ term.red(`${status.icon} ${status.text}`);
3444
3484
  break;
3445
3485
  default:
3446
- term.gray(`${status.icon} ${status.text}
3447
- `);
3486
+ term.gray(`${status.icon} ${status.text}`);
3448
3487
  }
3449
- term("\n");
3488
+ term("\n\n");
3450
3489
  if (logs.length === 0) {
3451
- 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");
3452
3491
  } else {
3453
- 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));
3454
3493
  const visible = logs.slice(eff, eff + visibleCount);
3455
- const msgW = Math.max(40, (term.width || 80) - 16);
3494
+ const msgW = Math.max(30, W - 16);
3456
3495
  for (const entry of visible) {
3457
3496
  const t = formatLogTime(entry.timestamp);
3458
3497
  const text = clip(formatAgentLogEntry(entry), msgW);
@@ -3465,6 +3504,9 @@ async function showAgentLog({ agent, state, env, platform }) {
3465
3504
  `);
3466
3505
  } else if (entry.eventType === "job_started") {
3467
3506
  term.brightBlue(`\u25B6 ${text}
3507
+ `);
3508
+ } else if (entry.eventType === "progress") {
3509
+ term.cyan(`\u2192 ${text}
3468
3510
  `);
3469
3511
  } else {
3470
3512
  term.gray(`\u2014 ${text}
@@ -3472,38 +3514,56 @@ async function showAgentLog({ agent, state, env, platform }) {
3472
3514
  }
3473
3515
  }
3474
3516
  if (logs.length > visibleCount) {
3517
+ const eff2 = Math.min(eff, Math.max(0, logs.length - visibleCount));
3475
3518
  term.gray(`
3476
- Showing ${eff + 1}\u2013${eff + visible.length} of ${logs.length} entries
3477
- `);
3519
+ ${eff2 + 1}\u2013${eff2 + visible.length} of ${logs.length}`);
3520
+ if (autoScroll) term.cyan(" \u2193 live");
3521
+ term("\n");
3478
3522
  }
3479
3523
  }
3480
- term("\n");
3524
+ term.moveTo(1, Math.max(H - 1, 6));
3481
3525
  term.gray(" ");
3482
3526
  term.brightBlue.bold("\u2191\u2193");
3483
- term.gray(" scroll ");
3484
- term.brightBlue.bold("PgUp/PgDn");
3485
- term.gray(" page ");
3527
+ term.gray("/PgUp/PgDn scroll ");
3528
+ if (!autoScroll) {
3529
+ term.brightBlue.bold("End");
3530
+ term.gray(" resume live ");
3531
+ }
3486
3532
  term.brightBlue.bold("Esc");
3487
3533
  term.gray(" back\n");
3488
3534
  }
3535
+ if (typeof onAttachLiveUpdates === "function") {
3536
+ onAttachLiveUpdates(() => draw());
3537
+ }
3489
3538
  draw();
3490
3539
  await new Promise((resolve) => {
3491
3540
  function handler(name) {
3541
+ if (done) return;
3542
+ const logs = currentLogs();
3492
3543
  const maxOff = Math.max(0, logs.length - visibleCount);
3493
3544
  if (name === "ESCAPE") {
3545
+ done = true;
3494
3546
  term.off("key", handler);
3495
3547
  resolve();
3496
3548
  } else if (name === "UP") {
3549
+ autoScroll = false;
3497
3550
  scrollOffset = Math.max(0, scrollOffset - 1);
3498
3551
  draw();
3499
3552
  } else if (name === "DOWN") {
3500
3553
  scrollOffset = Math.min(maxOff, scrollOffset + 1);
3554
+ if (scrollOffset >= maxOff) autoScroll = true;
3501
3555
  draw();
3502
3556
  } else if (name === "PAGE_UP") {
3503
- scrollOffset = Math.max(0, scrollOffset - visibleCount);
3557
+ autoScroll = false;
3558
+ scrollOffset = Math.max(0, scrollOffset - Math.max(1, visibleCount - 1));
3504
3559
  draw();
3505
3560
  } else if (name === "PAGE_DOWN") {
3506
- scrollOffset = Math.min(maxOff, scrollOffset + visibleCount);
3561
+ scrollOffset = Math.min(maxOff, scrollOffset + Math.max(1, visibleCount - 1));
3562
+ if (scrollOffset >= maxOff) autoScroll = true;
3563
+ draw();
3564
+ } else if (name === "END" || name === "g") {
3565
+ autoScroll = true;
3566
+ scrollOffset = maxOff;
3507
3567
  draw();
3508
3568
  }
3509
3569
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendian/cli",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Public Vendian CLI bootstrapper and launcher",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,