@mitsein-ai/cli 0.2.2 → 0.3.0

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.
@@ -1,95 +0,0 @@
1
- import { isTTY } from './output.js';
2
- import type { SseEvent } from './sse.js';
3
-
4
- function formatLocalTime(tsSec: number): string {
5
- const d = new Date(tsSec * 1000);
6
- const h = String(d.getHours()).padStart(2, '0');
7
- const m = String(d.getMinutes()).padStart(2, '0');
8
- const s = String(d.getSeconds()).padStart(2, '0');
9
- return `${h}:${m}:${s}`;
10
- }
11
-
12
- function truncateStr(s: string, max: number): string {
13
- return s.length > max ? `${s.slice(0, max)}...` : s;
14
- }
15
-
16
- /** Human-readable line for one SSE event (`waiters._print_event_human`). */
17
- export function printEventHuman(event: SseEvent): void {
18
- const data = event.data;
19
-
20
- if (typeof data !== 'object' || data === null) {
21
- process.stdout.write(`\x1b[2m${String(data)}\x1b[0m\n`);
22
- return;
23
- }
24
-
25
- const etype = event.event_type;
26
- const content =
27
- typeof data.content === 'string' ? data.content : '';
28
- const ts = formatLocalTime(event.timestamp);
29
-
30
- if (etype === 'assistant' || etype === 'text' || etype === 'message') {
31
- process.stdout.write(
32
- `\x1b[2m[${ts}]\x1b[0m \x1b[1massistant:\x1b[0m ${content}\n`
33
- );
34
- } else if (etype === 'tool_call') {
35
- const name =
36
- (typeof data.name === 'string' ? data.name : null) ??
37
- (typeof data.tool_name === 'string' ? data.tool_name : null) ??
38
- '?';
39
- let args = data.arguments ?? data.args ?? '';
40
- if (typeof args === 'string') {
41
- args = truncateStr(args, 80);
42
- } else {
43
- args = JSON.stringify(args);
44
- }
45
- process.stdout.write(
46
- `\x1b[2m[${ts}]\x1b[0m \x1b[33mtool_call:\x1b[0m ${name}(${String(args)})\n`
47
- );
48
- } else if (etype === 'tool_result') {
49
- let output = data.output ?? data.result ?? data.content ?? '';
50
- if (typeof output === 'string') {
51
- output = truncateStr(output, 100);
52
- } else {
53
- output = JSON.stringify(output);
54
- }
55
- process.stdout.write(
56
- `\x1b[2m[${ts}]\x1b[0m \x1b[32mtool_result:\x1b[0m ${String(output)}\n`
57
- );
58
- } else if (etype === 'status') {
59
- const st = typeof data.status === 'string' ? data.status : '';
60
- process.stdout.write(`\x1b[2m[${ts}]\x1b[0m \x1b[34mstatus:\x1b[0m ${st}\n`);
61
- } else if (etype === 'error') {
62
- process.stdout.write(
63
- `\x1b[2m[${ts}]\x1b[0m \x1b[1m\x1b[31merror:\x1b[0m ${content}\n`
64
- );
65
- } else {
66
- const snippet = JSON.stringify(data).slice(0, 120);
67
- process.stdout.write(`\x1b[2m[${ts}]\x1b[0m \x1b[2m${etype}:\x1b[0m ${snippet}\n`);
68
- }
69
- }
70
-
71
- /** Completion summary after streaming (`waiters._print_summary`). */
72
- export function printSummary(result: Record<string, unknown>): void {
73
- const status = String(result.status ?? '');
74
- const durationMs = Number(result.duration_ms ?? 0) / 1000;
75
- const eventsCount = result.events_count;
76
-
77
- if (status === 'succeeded') {
78
- process.stdout.write(
79
- `\n\x1b[1m\x1b[32m✓\x1b[0m run completed in ${durationMs.toFixed(1)}s (${eventsCount} events)\n`
80
- );
81
- } else if (status === 'failed') {
82
- const err = String(result.error ?? '');
83
- process.stdout.write(
84
- `\n\x1b[1m\x1b[31m✗\x1b[0m run failed in ${durationMs.toFixed(1)}s: ${err}\n`
85
- );
86
- } else if (status === 'disconnected') {
87
- process.stdout.write(
88
- `\n\x1b[2mdisconnected after ${durationMs.toFixed(1)}s (${eventsCount} events)\x1b[0m\n`
89
- );
90
- }
91
- }
92
-
93
- export function shouldPrintStreamSummary(streamOutput: boolean, jsonMode: boolean): boolean {
94
- return streamOutput && !jsonMode && isTTY();
95
- }
@@ -1,169 +0,0 @@
1
- import { CliError, ExitCode } from './errors.js';
2
- import { isJsonMode } from './output.js';
3
- import {
4
- printEventHuman,
5
- printSummary,
6
- shouldPrintStreamSummary,
7
- } from './waiters-output.js';
8
- import { streamSse, type SseEvent } from './sse.js';
9
-
10
- export interface WaitForRunOptions {
11
- endpoint: string;
12
- token: string;
13
- agent_run_id: string;
14
- timeout?: number | null;
15
- stream_output?: boolean;
16
- json_mode?: boolean;
17
- debug?: boolean;
18
- event_filter?: Set<string> | null;
19
- on_event?: ((e: SseEvent) => void) | null;
20
- /** Test hook: custom event stream instead of `streamSse`. */
21
- eventStream?: AsyncIterable<SseEvent> | null;
22
- }
23
-
24
- function outputEvent(
25
- event: SseEvent,
26
- jsonMode: boolean,
27
- eventFilter: Set<string> | null | undefined
28
- ): void {
29
- if (eventFilter && !eventFilter.has(event.event_type)) {
30
- return;
31
- }
32
- if (jsonMode) {
33
- if (typeof event.data === 'object' && event.data !== null) {
34
- const line = { ts: event.timestamp, ...event.data };
35
- process.stdout.write(`${JSON.stringify(line)}\n`);
36
- } else {
37
- process.stdout.write(
38
- `${JSON.stringify({ ts: event.timestamp, type: event.event_type, raw: event.data })}\n`
39
- );
40
- }
41
- } else {
42
- printEventHuman(event);
43
- }
44
- }
45
-
46
- /**
47
- * Wait for an agent run via SSE (`waiters.wait_for_run`).
48
- * Exit semantics via `CliError`: failed run → BUSINESS_ERROR; timeout from stream → TIMEOUT.
49
- */
50
- export async function waitForRun(options: WaitForRunOptions): Promise<Record<string, unknown>> {
51
- const start = Date.now();
52
- const events: { type: string; data: unknown }[] = [];
53
- let finalStatus = 'unknown';
54
- let errorMessage: string | null = null;
55
- const assistantTextParts: string[] = [];
56
- const toolCalls: Record<string, unknown>[] = [];
57
-
58
- const streamOutput = options.stream_output ?? false;
59
- const jsonMode = options.json_mode ?? isJsonMode();
60
- const eventFilter = options.event_filter ?? null;
61
- const debug = options.debug ?? false;
62
-
63
- const useBuiltinStream = options.eventStream == null;
64
- const ac = useBuiltinStream ? new AbortController() : null;
65
- const onSigint = (): void => {
66
- ac?.abort();
67
- };
68
- if (useBuiltinStream) {
69
- process.once('SIGINT', onSigint);
70
- }
71
-
72
- const path = `/api/agent-run/${options.agent_run_id}/stream`;
73
- const iterable: AsyncIterable<SseEvent> =
74
- options.eventStream ??
75
- streamSse({
76
- endpoint: options.endpoint,
77
- path,
78
- token: options.token,
79
- timeoutSec: options.timeout ?? undefined,
80
- debug,
81
- signal: ac?.signal,
82
- });
83
-
84
- try {
85
- try {
86
- for await (const event of iterable) {
87
- events.push({ type: event.event_type, data: event.data });
88
- options.on_event?.(event);
89
- if (streamOutput) {
90
- outputEvent(event, jsonMode, eventFilter);
91
- }
92
- if (typeof event.data === 'object' && event.data !== null) {
93
- const d = event.data as Record<string, unknown>;
94
- const etype = event.event_type;
95
- if (etype === 'status') {
96
- const st = typeof d.status === 'string' ? d.status : '';
97
- if (st === 'completed') {
98
- finalStatus = 'succeeded';
99
- break;
100
- }
101
- if (st === 'failed' || st === 'error') {
102
- finalStatus = 'failed';
103
- errorMessage =
104
- (typeof d.content === 'string' ? d.content : null) ??
105
- (typeof d.error === 'string' ? d.error : null) ??
106
- null;
107
- break;
108
- }
109
- } else if (etype === 'error') {
110
- finalStatus = 'failed';
111
- errorMessage =
112
- (typeof d.content === 'string' ? d.content : null) ?? String(event.data);
113
- break;
114
- } else if (etype === 'assistant' || etype === 'text' || etype === 'message') {
115
- const c = typeof d.content === 'string' ? d.content : '';
116
- if (c) {
117
- assistantTextParts.push(c);
118
- }
119
- } else if (etype === 'tool_call' || etype === 'tool_result') {
120
- toolCalls.push(d);
121
- }
122
- }
123
- }
124
- } catch (e) {
125
- if (e !== null && typeof e === 'object' && (e as Error).name === 'AbortError') {
126
- if (!jsonMode) {
127
- process.stderr.write(
128
- '\n\x1b[2mDisconnected from stream (run continues in background)\x1b[0m\n'
129
- );
130
- }
131
- finalStatus = 'disconnected';
132
- } else {
133
- throw e;
134
- }
135
- }
136
- } finally {
137
- if (useBuiltinStream) {
138
- process.off('SIGINT', onSigint);
139
- }
140
- }
141
-
142
- const duration_ms = Date.now() - start;
143
- const result: Record<string, unknown> = {
144
- status: finalStatus,
145
- agent_run_id: options.agent_run_id,
146
- duration_ms,
147
- events_count: events.length,
148
- };
149
-
150
- if (assistantTextParts.length > 0) {
151
- result.assistant_message = assistantTextParts.join('');
152
- }
153
- if (toolCalls.length > 0) {
154
- result.tool_calls = toolCalls;
155
- }
156
- if (errorMessage) {
157
- result.error = errorMessage;
158
- }
159
-
160
- if (shouldPrintStreamSummary(streamOutput, jsonMode)) {
161
- printSummary(result);
162
- }
163
-
164
- if (finalStatus === 'failed') {
165
- throw new CliError(`Run failed: ${errorMessage ?? 'unknown error'}`, ExitCode.BUSINESS_ERROR, result);
166
- }
167
-
168
- return result;
169
- }