nolo-cli 0.1.37 → 0.1.38

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.
@@ -73,6 +73,47 @@ export type RunAgentTurnResult = {
73
73
  streamInterrupted?: boolean;
74
74
  };
75
75
 
76
+ class Spinner {
77
+ private timer: any = null;
78
+ private frameIndex = 0;
79
+ private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
80
+ private isTTY: boolean;
81
+
82
+ constructor(
83
+ private output: OutputLike,
84
+ private text: string
85
+ ) {
86
+ this.isTTY = Boolean((output as any).isTTY ?? process.stdout.isTTY);
87
+ }
88
+
89
+ start() {
90
+ if (!this.isTTY) {
91
+ this.output.write(`\n${this.text}...\n`);
92
+ return;
93
+ }
94
+
95
+ this.frameIndex = 0;
96
+ this.output.write(`\x1b[36m${this.frames[0]}\x1b[39m ${this.text}...`);
97
+
98
+ this.timer = setInterval(() => {
99
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
100
+ this.output.write(`\r\x1b[36m${this.frames[this.frameIndex]}\x1b[39m ${this.text}...`);
101
+ }, 80);
102
+ }
103
+
104
+ stop() {
105
+ if (this.timer) {
106
+ clearInterval(this.timer);
107
+ this.timer = null;
108
+ }
109
+
110
+ if (this.isTTY) {
111
+ this.output.write("\r\x1b[K");
112
+ }
113
+ }
114
+ }
115
+
116
+
76
117
  const SERVER_PLATFORM_TOOL_NAMES = new Set([
77
118
  "addTableRow",
78
119
  "addTableRows",
@@ -502,7 +543,8 @@ function buildTransportErrorHint(serverUrl: string, error: unknown) {
502
543
  }
503
544
 
504
545
  async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
505
- options.output.write(`\n${options.agentName} -> working...\n`);
546
+ const spinner = new Spinner(options.output, `${options.agentName} -> working`);
547
+ spinner.start();
506
548
 
507
549
  const fetchImpl = options.fetchImpl ?? fetch;
508
550
  let res: Response;
@@ -556,14 +598,16 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
556
598
  ...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
557
599
  stream: !options.noStream && !options.background,
558
600
  }),
559
- });
560
- } catch (error) {
561
- options.output.write(buildTransportErrorHint(options.serverUrl, error));
562
- return { exitCode: 1 };
563
- }
564
-
601
+ });
602
+ } catch (error) {
603
+ spinner.stop();
604
+ options.output.write(buildTransportErrorHint(options.serverUrl, error));
605
+ return { exitCode: 1 };
606
+ }
607
+
565
608
  const contentType = res.headers.get("content-type") || "";
566
609
  if (contentType.includes("text/event-stream") && res.body) {
610
+ spinner.stop();
567
611
  const result = await readStreamingAgentRun(options, res);
568
612
  await recordTaskRunOutcome({
569
613
  options,
@@ -577,14 +621,15 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
577
621
  });
578
622
  return result;
579
623
  }
580
-
581
- let data: any = {};
582
- try {
583
- data = await res.json();
584
- } catch {
585
- data = {};
586
- }
587
-
624
+
625
+ spinner.stop();
626
+ let data: any = {};
627
+ try {
628
+ data = await res.json();
629
+ } catch {
630
+ data = {};
631
+ }
632
+
588
633
  if (!res.ok) {
589
634
  options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
590
635
  if (data?.error || data?.message) {
@@ -691,7 +736,8 @@ async function runLocalAgentTurnForCli(
691
736
  return { exitCode: 1 };
692
737
  }
693
738
 
694
- options.output.write(`\n${options.agentName} -> working locally...\n`);
739
+ const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
740
+ spinner.start();
695
741
  const toolEvents: LocalAgentToolEvent[] = [];
696
742
  try {
697
743
  const traceLocalTools = shouldTraceLocalTools(options);
@@ -716,11 +762,15 @@ async function runLocalAgentTurnForCli(
716
762
  ? {
717
763
  onToolEvent: (event) => {
718
764
  if (collectToolEvidence) toolEvents.push(event);
719
- if (traceLocalTools) options.output.write(formatToolTraceEvent(event));
765
+ if (traceLocalTools) {
766
+ spinner.stop();
767
+ options.output.write(formatToolTraceEvent(event));
768
+ }
720
769
  },
721
770
  }
722
771
  : {}),
723
772
  });
773
+ spinner.stop();
724
774
  const content = result.content.trim();
725
775
  if (content) {
726
776
  options.output.write(`\n${options.agentName} > ${content}\n`);
@@ -736,6 +786,7 @@ async function runLocalAgentTurnForCli(
736
786
  });
737
787
  return { exitCode: 0, dialogId: result.dialogId };
738
788
  } catch (error) {
789
+ spinner.stop();
739
790
  if (settings.reportFailure) {
740
791
  options.output.write(
741
792
  `[nolo] Local agent run failed: ${
@@ -73,6 +73,47 @@ export type RunAgentTurnResult = {
73
73
  streamInterrupted?: boolean;
74
74
  };
75
75
 
76
+ class Spinner {
77
+ private timer: any = null;
78
+ private frameIndex = 0;
79
+ private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
80
+ private isTTY: boolean;
81
+
82
+ constructor(
83
+ private output: OutputLike,
84
+ private text: string
85
+ ) {
86
+ this.isTTY = Boolean((output as any).isTTY ?? process.stdout.isTTY);
87
+ }
88
+
89
+ start() {
90
+ if (!this.isTTY) {
91
+ this.output.write(`\n${this.text}...\n`);
92
+ return;
93
+ }
94
+
95
+ this.frameIndex = 0;
96
+ this.output.write(`\x1b[36m${this.frames[0]}\x1b[39m ${this.text}...`);
97
+
98
+ this.timer = setInterval(() => {
99
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
100
+ this.output.write(`\r\x1b[36m${this.frames[this.frameIndex]}\x1b[39m ${this.text}...`);
101
+ }, 80);
102
+ }
103
+
104
+ stop() {
105
+ if (this.timer) {
106
+ clearInterval(this.timer);
107
+ this.timer = null;
108
+ }
109
+
110
+ if (this.isTTY) {
111
+ this.output.write("\r\x1b[K");
112
+ }
113
+ }
114
+ }
115
+
116
+
76
117
  const SERVER_PLATFORM_TOOL_NAMES = new Set([
77
118
  "addTableRow",
78
119
  "addTableRows",
@@ -502,7 +543,8 @@ function buildTransportErrorHint(serverUrl: string, error: unknown) {
502
543
  }
503
544
 
504
545
  async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
505
- options.output.write(`\n${options.agentName} -> working...\n`);
546
+ const spinner = new Spinner(options.output, `${options.agentName} -> working`);
547
+ spinner.start();
506
548
 
507
549
  const fetchImpl = options.fetchImpl ?? fetch;
508
550
  let res: Response;
@@ -556,14 +598,16 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
556
598
  ...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
557
599
  stream: !options.noStream && !options.background,
558
600
  }),
559
- });
560
- } catch (error) {
561
- options.output.write(buildTransportErrorHint(options.serverUrl, error));
562
- return { exitCode: 1 };
563
- }
564
-
601
+ });
602
+ } catch (error) {
603
+ spinner.stop();
604
+ options.output.write(buildTransportErrorHint(options.serverUrl, error));
605
+ return { exitCode: 1 };
606
+ }
607
+
565
608
  const contentType = res.headers.get("content-type") || "";
566
609
  if (contentType.includes("text/event-stream") && res.body) {
610
+ spinner.stop();
567
611
  const result = await readStreamingAgentRun(options, res);
568
612
  await recordTaskRunOutcome({
569
613
  options,
@@ -577,14 +621,15 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
577
621
  });
578
622
  return result;
579
623
  }
580
-
581
- let data: any = {};
582
- try {
583
- data = await res.json();
584
- } catch {
585
- data = {};
586
- }
587
-
624
+
625
+ spinner.stop();
626
+ let data: any = {};
627
+ try {
628
+ data = await res.json();
629
+ } catch {
630
+ data = {};
631
+ }
632
+
588
633
  if (!res.ok) {
589
634
  options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
590
635
  if (data?.error || data?.message) {
@@ -691,7 +736,8 @@ async function runLocalAgentTurnForCli(
691
736
  return { exitCode: 1 };
692
737
  }
693
738
 
694
- options.output.write(`\n${options.agentName} -> working locally...\n`);
739
+ const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
740
+ spinner.start();
695
741
  const toolEvents: LocalAgentToolEvent[] = [];
696
742
  try {
697
743
  const traceLocalTools = shouldTraceLocalTools(options);
@@ -716,11 +762,15 @@ async function runLocalAgentTurnForCli(
716
762
  ? {
717
763
  onToolEvent: (event) => {
718
764
  if (collectToolEvidence) toolEvents.push(event);
719
- if (traceLocalTools) options.output.write(formatToolTraceEvent(event));
765
+ if (traceLocalTools) {
766
+ spinner.stop();
767
+ options.output.write(formatToolTraceEvent(event));
768
+ }
720
769
  },
721
770
  }
722
771
  : {}),
723
772
  });
773
+ spinner.stop();
724
774
  const content = result.content.trim();
725
775
  if (content) {
726
776
  options.output.write(`\n${options.agentName} > ${content}\n`);
@@ -736,6 +786,7 @@ async function runLocalAgentTurnForCli(
736
786
  });
737
787
  return { exitCode: 0, dialogId: result.dialogId };
738
788
  } catch (error) {
789
+ spinner.stop();
739
790
  if (settings.reportFailure) {
740
791
  options.output.write(
741
792
  `[nolo] Local agent run failed: ${
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {