@mitsein-ai/cli 0.1.5 → 0.2.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.
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.5"
49
+ "version": "0.2.0"
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
  }
@@ -80,9 +80,47 @@ export function registerAuth(program: Command): void {
80
80
  setJsonMode(Boolean(g.json));
81
81
  void opts.endpoint;
82
82
  const profile = opts.profile ?? 'default';
83
+
84
+ // Check env token first (MITSEIN_TOKEN or --token)
85
+ if (g.token) {
86
+ // Try to parse dev token format: #{"userid":"...", ...}
87
+ const raw = g.token.startsWith('#') ? g.token.slice(1) : g.token;
88
+ try {
89
+ const parsed = JSON.parse(raw) as Record<string, string>;
90
+ emit(
91
+ {
92
+ email: parsed.email ?? 'unknown',
93
+ user_id: parsed.userid ?? 'unknown',
94
+ username: parsed.username ?? 'unknown',
95
+ source: 'env/flag',
96
+ profile,
97
+ },
98
+ (payload) => {
99
+ const d = payload as Record<string, string>;
100
+ consola.success(`Logged in as ${d.email}`);
101
+ consola.log(` User ID: ${d.user_id}`);
102
+ consola.log(` Username: ${d.username}`);
103
+ consola.log(` Source: ${d.source}`);
104
+ }
105
+ );
106
+ return;
107
+ } catch {
108
+ // Not a parseable dev token, show raw info
109
+ emit(
110
+ { token: '***', source: 'env/flag', profile },
111
+ (payload) => {
112
+ const d = payload as Record<string, string>;
113
+ consola.success('Authenticated via token');
114
+ consola.log(` Source: ${d.source}`);
115
+ }
116
+ );
117
+ return;
118
+ }
119
+ }
120
+
83
121
  const oauthPath = getOauthPath(profile);
84
122
  if (!existsSync(oauthPath)) {
85
- throw new CliError('Not logged in. Run `mitsein auth login`.', ExitCode.USAGE_ERROR);
123
+ throw new CliError('Not logged in. Run `mitsein auth login` or set MITSEIN_TOKEN.', ExitCode.USAGE_ERROR);
86
124
  }
87
125
  const data = JSON.parse(readFileSync(oauthPath, 'utf8')) as Record<string, string>;
88
126
  emit(
@@ -91,6 +129,7 @@ export function registerAuth(program: Command): void {
91
129
  user_id: data.user_id ?? 'unknown',
92
130
  profile,
93
131
  endpoint: data.endpoint ?? '',
132
+ source: 'profile',
94
133
  },
95
134
  (payload) => {
96
135
  const d = payload as Record<string, string>;
@@ -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
+ }
@@ -73,7 +73,7 @@ export function registerThread(program: Command): void {
73
73
  timeoutSec: httpTimeoutSec(g),
74
74
  debug: g.debug,
75
75
  });
76
- const result = await client.post('/api/thread/create', { title: title ?? '' });
76
+ const result = await client.post('/api/threads', { title: title ?? '' });
77
77
  emit(result);
78
78
  })
79
79
  );
@@ -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
- }