@mitsein-ai/cli 0.1.6 → 0.2.1

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.
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "ofetch": "^1.4.1",
9
9
  "undici": "^8.0.2"
10
10
  },
11
- "description": "Mitsein CLI dev tooling, API helpers, and workflow automation",
11
+ "description": "Mitsein CLI \u2014 dev tooling, API helpers, and workflow automation",
12
12
  "devDependencies": {
13
13
  "@types/bun": "latest",
14
14
  "typescript": "^5.3.3"
@@ -46,5 +46,5 @@
46
46
  "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "type": "module",
49
- "version": "0.1.6"
49
+ "version": "0.2.1"
50
50
  }
@@ -88,4 +88,130 @@ export function registerAgent(program: Command): void {
88
88
  }
89
89
  })
90
90
  );
91
+
92
+ /* ── running sub-group (formerly top-level `run` command) ── */
93
+ const running = agent.command('running').description('Agent run management');
94
+
95
+ running
96
+ .command('list <thread_id>')
97
+ .description('List agent runs for a thread')
98
+ .action(
99
+ handleErrors(async function runListAction(this: Command, threadId: string) {
100
+ const g = readGlobals(this);
101
+ setJsonMode(Boolean(g.json));
102
+ const client = ApiClient.fromOptions({
103
+ token: g.token,
104
+ endpoint: g.endpoint,
105
+ profile: g.profile ?? 'e2e',
106
+ real: g.real,
107
+ timeoutSec: httpTimeoutSec(g),
108
+ debug: g.debug,
109
+ });
110
+ const result = await client.post(`/api/thread/${threadId}/agent-runs`);
111
+ emit(result);
112
+ })
113
+ );
114
+
115
+ running
116
+ .command('status <run_id>')
117
+ .description('Get agent run status')
118
+ .action(
119
+ handleErrors(async function runStatusAction(this: Command, runId: string) {
120
+ const g = readGlobals(this);
121
+ setJsonMode(Boolean(g.json));
122
+ const client = ApiClient.fromOptions({
123
+ token: g.token,
124
+ endpoint: g.endpoint,
125
+ profile: g.profile ?? 'e2e',
126
+ real: g.real,
127
+ timeoutSec: httpTimeoutSec(g),
128
+ debug: g.debug,
129
+ });
130
+ const result = await client.post(`/api/agent-run/${runId}`);
131
+ emit(result);
132
+ })
133
+ );
134
+
135
+ running
136
+ .command('tail <run_id>')
137
+ .description('Stream agent run events (tail -f)')
138
+ .option('--filter <types>', 'Comma-separated event types')
139
+ .option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
140
+ .action(
141
+ handleErrors(async function runTailAction(this: Command, runId: string) {
142
+ const g = readGlobals(this);
143
+ const opts = this.opts() as { filter?: string; timeout?: string };
144
+ setJsonMode(Boolean(g.json));
145
+ const creds = resolveCredentials({
146
+ token: g.token,
147
+ endpoint: g.endpoint,
148
+ profile: g.profile ?? 'e2e',
149
+ real: g.real,
150
+ });
151
+ const rawT = opts.timeout ?? '120';
152
+ const tNum = Number.parseFloat(rawT);
153
+ const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
154
+ await waitForRun({
155
+ endpoint: creds.endpoint,
156
+ token: creds.token,
157
+ agent_run_id: runId,
158
+ timeout: effectiveTimeout ?? undefined,
159
+ stream_output: true,
160
+ json_mode: Boolean(g.json),
161
+ debug: g.debug,
162
+ event_filter: commaSet(opts.filter),
163
+ });
164
+ })
165
+ );
166
+
167
+ running
168
+ .command('wait <run_id>')
169
+ .description('Block until agent run completes')
170
+ .option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
171
+ .action(
172
+ handleErrors(async function runWaitAction(this: Command, runId: string) {
173
+ const g = readGlobals(this);
174
+ const opts = this.opts() as { timeout?: string };
175
+ setJsonMode(Boolean(g.json));
176
+ const creds = resolveCredentials({
177
+ token: g.token,
178
+ endpoint: g.endpoint,
179
+ profile: g.profile ?? 'e2e',
180
+ real: g.real,
181
+ });
182
+ const rawT = opts.timeout ?? '120';
183
+ const tNum = Number.parseFloat(rawT);
184
+ const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
185
+ const result = await waitForRun({
186
+ endpoint: creds.endpoint,
187
+ token: creds.token,
188
+ agent_run_id: runId,
189
+ timeout: effectiveTimeout ?? undefined,
190
+ stream_output: false,
191
+ json_mode: Boolean(g.json),
192
+ debug: g.debug,
193
+ });
194
+ emit(result);
195
+ })
196
+ );
197
+
198
+ running
199
+ .command('cancel <run_id>')
200
+ .description('Cancel a running agent')
201
+ .action(
202
+ handleErrors(async function runCancelAction(this: Command, runId: string) {
203
+ const g = readGlobals(this);
204
+ setJsonMode(Boolean(g.json));
205
+ const client = ApiClient.fromOptions({
206
+ token: g.token,
207
+ endpoint: g.endpoint,
208
+ profile: g.profile ?? 'e2e',
209
+ real: g.real,
210
+ timeoutSec: httpTimeoutSec(g),
211
+ debug: g.debug,
212
+ });
213
+ const result = await client.post(`/api/agent-run/${runId}/stop`);
214
+ emit(result);
215
+ })
216
+ );
91
217
  }
@@ -15,7 +15,7 @@ function firstQuery(q: ParsedUrlQuery, key: string): string | undefined {
15
15
 
16
16
  export async function loginBrowserFlow(
17
17
  endpoint: string,
18
- provider: string,
18
+ provider: string | undefined,
19
19
  profile: string,
20
20
  jsonOutput: boolean
21
21
  ): Promise<void> {
@@ -92,7 +92,12 @@ export async function loginBrowserFlow(
92
92
  timer = setTimeout(() => finish(), 300_000);
93
93
 
94
94
  server.listen(LOCALHOST_PORT, '127.0.0.1', () => {
95
- const loginUrl = `${endpoint}/api/auth/cognito/login?provider=${encodeURIComponent(provider)}`;
95
+ const redirectUri = `http://127.0.0.1:${LOCALHOST_PORT}/callback`;
96
+ const params = new URLSearchParams({ redirect_uri: redirectUri });
97
+ if (provider) {
98
+ params.set('provider', provider);
99
+ }
100
+ const loginUrl = `${endpoint}/api/auth/cognito/login?${params.toString()}`;
96
101
  if (!jsonOutput) {
97
102
  consola.log('\nOpening browser for login...');
98
103
  consola.log(`If browser does not open, visit: ${loginUrl}\n`);
@@ -16,7 +16,7 @@ export function registerAuth(program: Command): void {
16
16
  .command('login')
17
17
  .description('Log in (browser callback on localhost, or --device-code)')
18
18
  .option('--device-code', 'Device code flow', false)
19
- .option('--provider <name>', 'OAuth provider: google or msa', 'google')
19
+ .option('--provider <name>', 'OAuth provider (e.g. google, msa). Omit to let the server show a chooser')
20
20
  .option('--endpoint <url>', 'API base URL')
21
21
  .option('--profile <name>', 'Profile to save credentials', 'default')
22
22
  .action(
@@ -33,7 +33,7 @@ export function registerAuth(program: Command): void {
33
33
  if (opts.deviceCode) {
34
34
  await loginDeviceCodeFlow(ep, opts.profile ?? 'default', Boolean(g.json));
35
35
  } else {
36
- await loginBrowserFlow(ep, opts.provider ?? 'google', opts.profile ?? 'default', Boolean(g.json));
36
+ await loginBrowserFlow(ep, opts.provider, opts.profile ?? 'default', Boolean(g.json));
37
37
  }
38
38
  })
39
39
  );
@@ -0,0 +1,216 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { basename, resolve } from 'node:path';
3
+ import { writeFile } from 'node:fs/promises';
4
+ import type { Command } from 'commander';
5
+ import { ApiClient } from '../core/client.js';
6
+ import { CliError, ExitCode, handleErrors } from '../core/errors.js';
7
+ import { emit, setJsonMode } from '../core/output.js';
8
+ import { httpTimeoutSec, readGlobals } from './command-opts.js';
9
+
10
+ function makeClient(cmd: Command): ApiClient {
11
+ const g = readGlobals(cmd);
12
+ setJsonMode(Boolean(g.json));
13
+ return ApiClient.fromOptions({
14
+ token: g.token,
15
+ endpoint: g.endpoint,
16
+ profile: g.profile ?? 'e2e',
17
+ real: g.real,
18
+ timeoutSec: httpTimeoutSec(g),
19
+ debug: g.debug,
20
+ });
21
+ }
22
+
23
+ export function registerFiles(program: Command): void {
24
+ const files = program.command('files').description('Sandbox file operations');
25
+
26
+ files
27
+ .command('list <thread_id>')
28
+ .description('List files in a thread sandbox')
29
+ .option('--path <dir>', 'Directory path', '')
30
+ .option('--filter <glob>', 'Glob filter', '*')
31
+ .action(
32
+ handleErrors(async function filesListAction(this: Command, threadId: string) {
33
+ const client = makeClient(this);
34
+ const opts = this.opts() as { path: string; filter: string };
35
+ const result = await client.get(`/api/files/${threadId}/list`, {
36
+ path: opts.path,
37
+ filter: opts.filter,
38
+ });
39
+ emit(result);
40
+ })
41
+ );
42
+
43
+ files
44
+ .command('read <thread_id> <path>')
45
+ .description('Read file content from sandbox')
46
+ .action(
47
+ handleErrors(async function filesReadAction(this: Command, threadId: string, filePath: string) {
48
+ const client = makeClient(this);
49
+ const result = await client.get(`/api/files/${threadId}/read`, {
50
+ path: filePath,
51
+ });
52
+ emit(result);
53
+ })
54
+ );
55
+
56
+ files
57
+ .command('info <thread_id> <path>')
58
+ .description('Get file metadata')
59
+ .action(
60
+ handleErrors(async function filesInfoAction(this: Command, threadId: string, filePath: string) {
61
+ const client = makeClient(this);
62
+ const result = await client.get(`/api/files/${threadId}/info`, {
63
+ path: filePath,
64
+ });
65
+ emit(result);
66
+ })
67
+ );
68
+
69
+ files
70
+ .command('upload <thread_id> <local_path>')
71
+ .description('Upload a local file to the sandbox')
72
+ .option('--remote-path <path>', 'Remote destination path (default: filename)')
73
+ .action(
74
+ handleErrors(async function filesUploadAction(
75
+ this: Command,
76
+ threadId: string,
77
+ localPath: string,
78
+ ) {
79
+ const client = makeClient(this);
80
+ const opts = this.opts() as { remotePath?: string };
81
+ const absPath = resolve(localPath);
82
+ const bytes = await readFile(absPath);
83
+ const fileName = basename(absPath);
84
+ const remotePath = opts.remotePath ?? fileName;
85
+ const file = new File([bytes], fileName);
86
+ const result = await client.postMultipart(`/api/files/${threadId}/upload`, {
87
+ file,
88
+ path: remotePath,
89
+ });
90
+ emit(result);
91
+ })
92
+ );
93
+
94
+ files
95
+ .command('write <thread_id> <remote_path>')
96
+ .description('Write content to a file in the sandbox')
97
+ .option('--from <local_path>', 'Local file to read (reads stdin if omitted)')
98
+ .action(
99
+ handleErrors(async function filesWriteAction(
100
+ this: Command,
101
+ threadId: string,
102
+ remotePath: string,
103
+ ) {
104
+ const client = makeClient(this);
105
+ const opts = this.opts() as { from?: string };
106
+ let bytes: Buffer;
107
+ if (opts.from) {
108
+ bytes = await readFile(resolve(opts.from));
109
+ } else {
110
+ bytes = await readStdin();
111
+ }
112
+ const fileName = basename(remotePath);
113
+ const content = new File([bytes], fileName);
114
+ const result = await client.postMultipart(`/api/files/${threadId}/write`, {
115
+ content,
116
+ path: remotePath,
117
+ });
118
+ emit(result);
119
+ })
120
+ );
121
+
122
+ files
123
+ .command('mkdir <thread_id> <path>')
124
+ .description('Create a directory in the sandbox')
125
+ .action(
126
+ handleErrors(async function filesMkdirAction(this: Command, threadId: string, dirPath: string) {
127
+ const client = makeClient(this);
128
+ const result = await client.post(`/api/files/${threadId}/folder`, {
129
+ path: dirPath,
130
+ });
131
+ emit(result);
132
+ })
133
+ );
134
+
135
+ files
136
+ .command('delete <thread_id> <path>')
137
+ .description('Delete a file from the sandbox')
138
+ .action(
139
+ handleErrors(async function filesDeleteAction(
140
+ this: Command,
141
+ threadId: string,
142
+ filePath: string,
143
+ ) {
144
+ const client = makeClient(this);
145
+ const result = await client.delete(`/api/files/${threadId}/delete`, { path: filePath });
146
+ emit(result);
147
+ })
148
+ );
149
+
150
+ files
151
+ .command('download <thread_id>')
152
+ .description('Download file(s) from the sandbox')
153
+ .option('--path <path>', 'Single file path to download')
154
+ .option('--paths <paths...>', 'Multiple file paths (batch download)')
155
+ .option('--out <dir>', 'Output directory', '.')
156
+ .action(
157
+ handleErrors(async function filesDownloadAction(this: Command, threadId: string) {
158
+ const client = makeClient(this);
159
+ const opts = this.opts() as {
160
+ path?: string;
161
+ paths?: string[];
162
+ out: string;
163
+ };
164
+
165
+ if (!opts.path && (!opts.paths || opts.paths.length === 0)) {
166
+ throw new CliError(
167
+ 'Either --path or --paths is required',
168
+ ExitCode.USAGE_ERROR,
169
+ );
170
+ }
171
+
172
+ if (opts.path && opts.paths && opts.paths.length > 0) {
173
+ throw new CliError(
174
+ '--path and --paths are mutually exclusive',
175
+ ExitCode.USAGE_ERROR,
176
+ );
177
+ }
178
+
179
+ if (opts.path) {
180
+ const result = await client.get(
181
+ `/api/files/${threadId}/read-binary`,
182
+ { path: opts.path },
183
+ );
184
+ const outPath = resolve(opts.out, basename(opts.path));
185
+ if (result instanceof ArrayBuffer || Buffer.isBuffer(result)) {
186
+ await writeFile(outPath, Buffer.from(result as ArrayBuffer));
187
+ } else {
188
+ await writeFile(outPath, String(result));
189
+ }
190
+ emit({ downloaded: outPath });
191
+ } else {
192
+ const result = await client.post(
193
+ `/api/files/${threadId}/batch-download`,
194
+ { paths: opts.paths },
195
+ );
196
+ emit(result);
197
+ }
198
+ })
199
+ );
200
+ }
201
+
202
+ async function readStdin(timeoutMs = 30_000): Promise<Buffer> {
203
+ const chunks: Buffer[] = [];
204
+ const timer = setTimeout(() => {
205
+ process.stderr.write('error: stdin read timed out (no input received)\n');
206
+ process.exit(1);
207
+ }, timeoutMs);
208
+ try {
209
+ for await (const chunk of process.stdin) {
210
+ chunks.push(Buffer.from(chunk as Uint8Array));
211
+ }
212
+ } finally {
213
+ clearTimeout(timer);
214
+ }
215
+ return Buffer.concat(chunks);
216
+ }
@@ -168,6 +168,66 @@ export class ApiClient {
168
168
  }
169
169
  }
170
170
 
171
+ async postMultipart(
172
+ path: string,
173
+ fields: Record<string, string | File | Blob>,
174
+ ): Promise<unknown> {
175
+ const p = path.startsWith('/') ? path : `/${path}`;
176
+ const formData = new FormData();
177
+ for (const [k, v] of Object.entries(fields)) {
178
+ formData.append(k, v);
179
+ }
180
+ if (this.debug) {
181
+ logDebug(this.debug, `POST ${p} (multipart)`);
182
+ logDebug(this.debug, `fields: ${Object.keys(fields).join(', ')}`);
183
+ }
184
+ const url = `${this.baseUrl}${p}`;
185
+ const controller = new AbortController();
186
+ const ms = this.timeoutMs;
187
+ const timer =
188
+ ms === undefined ? null : setTimeout(() => controller.abort(), ms);
189
+ try {
190
+ const r = await fetch(url, {
191
+ method: 'POST',
192
+ headers: {
193
+ Authorization: `Bearer ${this.bearerToken}`,
194
+ },
195
+ body: formData,
196
+ signal: controller.signal,
197
+ });
198
+ logDebug(this.debug, `→ ${r.status} (${r.headers.get('content-length') ?? '?'} bytes)`);
199
+ if (r.ok) {
200
+ const ct = r.headers.get('content-type') ?? '';
201
+ if (ct.includes('application/json')) {
202
+ return r.json();
203
+ }
204
+ return r.text();
205
+ }
206
+ let message = `HTTP ${r.status}`;
207
+ let detail: unknown;
208
+ try {
209
+ const j = (await r.json()) as Record<string, unknown>;
210
+ message = typeof j.message === 'string' ? j.message : message;
211
+ detail = j.detail ?? j.error;
212
+ } catch {
213
+ /* ignore */
214
+ }
215
+ throw new HttpError(r.status, message, detail);
216
+ } catch (e) {
217
+ if (e instanceof HttpError) {
218
+ throw e;
219
+ }
220
+ if (e instanceof Error && e.name === 'AbortError') {
221
+ throw new HttpError(599, 'Request aborted or timed out');
222
+ }
223
+ throw e;
224
+ } finally {
225
+ if (timer) {
226
+ clearTimeout(timer);
227
+ }
228
+ }
229
+ }
230
+
171
231
  async patch(path: string, jsonBody?: unknown | null): Promise<unknown> {
172
232
  const p = path.startsWith('/') ? path : `/${path}`;
173
233
  if (this.debug && jsonBody !== undefined && jsonBody !== null) {
@@ -194,8 +254,8 @@ export class ApiClient {
194
254
  );
195
255
  }
196
256
 
197
- async delete(path: string): Promise<unknown> {
257
+ async delete(path: string, query?: Record<string, unknown> | null): Promise<unknown> {
198
258
  const p = path.startsWith('/') ? path : `/${path}`;
199
- return handleFetch(this.fetchImpl(p, { method: 'DELETE' }));
259
+ return handleFetch(this.fetchImpl(p, { method: 'DELETE', query: query ?? undefined }));
200
260
  }
201
261
  }
package/src/index.ts CHANGED
@@ -6,9 +6,9 @@ import { registerAgent } from './commands/agent.js';
6
6
  import { registerApi } from './commands/api-auto.js';
7
7
  import { registerAuth } from './commands/auth.js';
8
8
  import { registerDev } from './commands/dev.js';
9
+ import { registerFiles } from './commands/files.js';
9
10
  import { registerMessages } from './commands/messages.js';
10
11
  import { registerProject } from './commands/project.js';
11
- import { registerRun } from './commands/run.js';
12
12
  import { registerThread } from './commands/thread.js';
13
13
  import { runVersion } from './commands/version.js';
14
14
  import { CliError } from './core/errors.js';
@@ -49,12 +49,28 @@ program
49
49
  registerDev(program);
50
50
  registerApi(program);
51
51
  registerThread(program);
52
- registerRun(program);
53
52
  registerMessages(program);
54
53
  registerAgent(program);
55
54
  registerProject(program);
55
+ registerFiles(program);
56
56
  registerAuth(program);
57
57
 
58
+ // Deprecated: `mitsein run` was merged into `mitsein agent running` in v0.2.0
59
+ program
60
+ .command('run', { hidden: true })
61
+ .description('[deprecated] Use `mitsein agent running` instead')
62
+ .allowUnknownOption(true)
63
+ .action(() => {
64
+ process.stderr.write(
65
+ 'error: `mitsein run` has been removed in v0.2.0.\n' +
66
+ 'Use `mitsein agent running <subcommand>` instead.\n' +
67
+ 'Examples:\n' +
68
+ ' mitsein run list → mitsein agent running list\n' +
69
+ ' mitsein run status → mitsein agent running status\n',
70
+ );
71
+ process.exit(1);
72
+ });
73
+
58
74
  try {
59
75
  await program.parseAsync(process.argv, { from: 'node' });
60
76
  } catch (e) {
@@ -1,134 +0,0 @@
1
- import type { Command } from 'commander';
2
- import { ApiClient } from '../core/client.js';
3
- import { resolveCredentials } from '../core/credentials.js';
4
- import { handleErrors } from '../core/errors.js';
5
- import { emit, setJsonMode } from '../core/output.js';
6
- import { waitForRun } from '../core/waiters.js';
7
- import { commaSet, httpTimeoutSec, readGlobals } from './command-opts.js';
8
-
9
- export function registerRun(program: Command): void {
10
- const run = program.command('run').description('Agent run management');
11
-
12
- run
13
- .command('list <thread_id>')
14
- .description('List agent runs for a thread')
15
- .action(
16
- handleErrors(async function runListAction(this: Command, threadId: string) {
17
- const g = readGlobals(this);
18
- setJsonMode(Boolean(g.json));
19
- const client = ApiClient.fromOptions({
20
- token: g.token,
21
- endpoint: g.endpoint,
22
- profile: g.profile ?? 'e2e',
23
- real: g.real,
24
- timeoutSec: httpTimeoutSec(g),
25
- debug: g.debug,
26
- });
27
- const result = await client.post(`/api/thread/${threadId}/agent-runs`);
28
- emit(result);
29
- })
30
- );
31
-
32
- run
33
- .command('get <run_id>')
34
- .description('Get agent run status')
35
- .action(
36
- handleErrors(async function runGetAction(this: Command, runId: string) {
37
- const g = readGlobals(this);
38
- setJsonMode(Boolean(g.json));
39
- const client = ApiClient.fromOptions({
40
- token: g.token,
41
- endpoint: g.endpoint,
42
- profile: g.profile ?? 'e2e',
43
- real: g.real,
44
- timeoutSec: httpTimeoutSec(g),
45
- debug: g.debug,
46
- });
47
- const result = await client.post(`/api/agent-run/${runId}`);
48
- emit(result);
49
- })
50
- );
51
-
52
- run
53
- .command('tail <run_id>')
54
- .description('Stream agent run events (tail -f)')
55
- .option('--filter <types>', 'Comma-separated event types')
56
- .option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
57
- .action(
58
- handleErrors(async function runTailAction(this: Command, runId: string) {
59
- const g = readGlobals(this);
60
- const opts = this.opts() as { filter?: string; timeout?: string };
61
- setJsonMode(Boolean(g.json));
62
- const creds = resolveCredentials({
63
- token: g.token,
64
- endpoint: g.endpoint,
65
- profile: g.profile ?? 'e2e',
66
- real: g.real,
67
- });
68
- const rawT = opts.timeout ?? '120';
69
- const tNum = Number.parseFloat(rawT);
70
- const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
71
- await waitForRun({
72
- endpoint: creds.endpoint,
73
- token: creds.token,
74
- agent_run_id: runId,
75
- timeout: effectiveTimeout ?? undefined,
76
- stream_output: true,
77
- json_mode: Boolean(g.json),
78
- debug: g.debug,
79
- event_filter: commaSet(opts.filter),
80
- });
81
- })
82
- );
83
-
84
- run
85
- .command('wait <run_id>')
86
- .description('Block until agent run completes')
87
- .option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
88
- .action(
89
- handleErrors(async function runWaitAction(this: Command, runId: string) {
90
- const g = readGlobals(this);
91
- const opts = this.opts() as { timeout?: string };
92
- setJsonMode(Boolean(g.json));
93
- const creds = resolveCredentials({
94
- token: g.token,
95
- endpoint: g.endpoint,
96
- profile: g.profile ?? 'e2e',
97
- real: g.real,
98
- });
99
- const rawT = opts.timeout ?? '120';
100
- const tNum = Number.parseFloat(rawT);
101
- const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
102
- const result = await waitForRun({
103
- endpoint: creds.endpoint,
104
- token: creds.token,
105
- agent_run_id: runId,
106
- timeout: effectiveTimeout ?? undefined,
107
- stream_output: false,
108
- json_mode: Boolean(g.json),
109
- debug: g.debug,
110
- });
111
- emit(result);
112
- })
113
- );
114
-
115
- run
116
- .command('cancel <run_id>')
117
- .description('Cancel a running agent')
118
- .action(
119
- handleErrors(async function runCancelAction(this: Command, runId: string) {
120
- const g = readGlobals(this);
121
- setJsonMode(Boolean(g.json));
122
- const client = ApiClient.fromOptions({
123
- token: g.token,
124
- endpoint: g.endpoint,
125
- profile: g.profile ?? 'e2e',
126
- real: g.real,
127
- timeoutSec: httpTimeoutSec(g),
128
- debug: g.debug,
129
- });
130
- const result = await client.post(`/api/agent-run/${runId}/stop`);
131
- emit(result);
132
- })
133
- );
134
- }