@mitsein-ai/cli 0.1.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/README.md +142 -0
- package/dist/index.js +143 -0
- package/package.json +49 -0
- package/src/commands/agent.ts +91 -0
- package/src/commands/api-auto.ts +245 -0
- package/src/commands/auth-browser.ts +126 -0
- package/src/commands/auth-device.ts +97 -0
- package/src/commands/auth-internal.ts +49 -0
- package/src/commands/auth.ts +105 -0
- package/src/commands/command-opts.ts +41 -0
- package/src/commands/dev.ts +100 -0
- package/src/commands/messages.ts +61 -0
- package/src/commands/project.ts +77 -0
- package/src/commands/run.ts +134 -0
- package/src/commands/thread.ts +187 -0
- package/src/commands/version.ts +17 -0
- package/src/core/client.ts +201 -0
- package/src/core/config.ts +26 -0
- package/src/core/credentials.ts +173 -0
- package/src/core/errors.ts +76 -0
- package/src/core/openapi.ts +182 -0
- package/src/core/output.ts +59 -0
- package/src/core/sse.ts +135 -0
- package/src/core/waiters-output.ts +95 -0
- package/src/core/waiters.ts +169 -0
- package/src/index.ts +66 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
import consola from 'consola';
|
|
4
|
+
import { getOauthPath } from '../core/config.js';
|
|
5
|
+
import { CliError, ExitCode, handleErrors } from '../core/errors.js';
|
|
6
|
+
import { emit, setJsonMode } from '../core/output.js';
|
|
7
|
+
import { loginBrowserFlow } from './auth-browser.js';
|
|
8
|
+
import { loginDeviceCodeFlow } from './auth-device.js';
|
|
9
|
+
import { normalizeEndpoint } from './auth-internal.js';
|
|
10
|
+
import { readGlobals } from './command-opts.js';
|
|
11
|
+
|
|
12
|
+
export function registerAuth(program: Command): void {
|
|
13
|
+
const auth = program.command('auth').description('Authentication management');
|
|
14
|
+
|
|
15
|
+
auth
|
|
16
|
+
.command('login')
|
|
17
|
+
.description('Log in (browser callback on localhost, or --device-code)')
|
|
18
|
+
.option('--device-code', 'Device code flow', false)
|
|
19
|
+
.option('--provider <name>', 'OAuth provider: google or msa', 'google')
|
|
20
|
+
.option('--endpoint <url>', 'API base URL')
|
|
21
|
+
.option('--profile <name>', 'Profile to save credentials', 'default')
|
|
22
|
+
.action(
|
|
23
|
+
handleErrors(async function authLoginAction(this: Command) {
|
|
24
|
+
const g = readGlobals(this);
|
|
25
|
+
const opts = this.opts() as {
|
|
26
|
+
deviceCode?: boolean;
|
|
27
|
+
provider?: string;
|
|
28
|
+
endpoint?: string;
|
|
29
|
+
profile?: string;
|
|
30
|
+
};
|
|
31
|
+
setJsonMode(Boolean(g.json));
|
|
32
|
+
const ep = normalizeEndpoint(opts.endpoint ?? g.endpoint);
|
|
33
|
+
if (opts.deviceCode) {
|
|
34
|
+
await loginDeviceCodeFlow(ep, opts.profile ?? 'default', Boolean(g.json));
|
|
35
|
+
} else {
|
|
36
|
+
await loginBrowserFlow(ep, opts.provider ?? 'google', opts.profile ?? 'default', Boolean(g.json));
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
auth
|
|
42
|
+
.command('logout')
|
|
43
|
+
.description('Remove saved credentials for a profile')
|
|
44
|
+
.option('--profile <name>', 'Profile', 'default')
|
|
45
|
+
.action(
|
|
46
|
+
handleErrors(async function authLogoutAction(this: Command) {
|
|
47
|
+
const g = readGlobals(this);
|
|
48
|
+
const opts = this.opts() as { profile?: string };
|
|
49
|
+
setJsonMode(Boolean(g.json));
|
|
50
|
+
const profile = opts.profile ?? 'default';
|
|
51
|
+
const oauthPath = getOauthPath(profile);
|
|
52
|
+
if (existsSync(oauthPath)) {
|
|
53
|
+
unlinkSync(oauthPath);
|
|
54
|
+
emit(
|
|
55
|
+
{ status: 'logged_out', profile },
|
|
56
|
+
() => {
|
|
57
|
+
consola.success(`Logged out from profile '${profile}'`);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
emit(
|
|
62
|
+
{ status: 'not_logged_in', profile },
|
|
63
|
+
() => {
|
|
64
|
+
consola.log(`Not logged in (profile '${profile}')`);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
auth
|
|
72
|
+
.command('whoami')
|
|
73
|
+
.description('Show current identity')
|
|
74
|
+
.option('--profile <name>', 'Profile', 'default')
|
|
75
|
+
.option('--endpoint <url>', 'API base URL (display only)')
|
|
76
|
+
.action(
|
|
77
|
+
handleErrors(async function authWhoamiAction(this: Command) {
|
|
78
|
+
const g = readGlobals(this);
|
|
79
|
+
const opts = this.opts() as { profile?: string; endpoint?: string };
|
|
80
|
+
setJsonMode(Boolean(g.json));
|
|
81
|
+
void opts.endpoint;
|
|
82
|
+
const profile = opts.profile ?? 'default';
|
|
83
|
+
const oauthPath = getOauthPath(profile);
|
|
84
|
+
if (!existsSync(oauthPath)) {
|
|
85
|
+
throw new CliError('Not logged in. Run `mitsein auth login`.', ExitCode.USAGE_ERROR);
|
|
86
|
+
}
|
|
87
|
+
const data = JSON.parse(readFileSync(oauthPath, 'utf8')) as Record<string, string>;
|
|
88
|
+
emit(
|
|
89
|
+
{
|
|
90
|
+
email: data.user_email ?? 'unknown',
|
|
91
|
+
user_id: data.user_id ?? 'unknown',
|
|
92
|
+
profile,
|
|
93
|
+
endpoint: data.endpoint ?? '',
|
|
94
|
+
},
|
|
95
|
+
(payload) => {
|
|
96
|
+
const d = payload as Record<string, string>;
|
|
97
|
+
consola.success(`Logged in as ${d.email}`);
|
|
98
|
+
consola.log(` User ID: ${d.user_id}`);
|
|
99
|
+
consola.log(` Profile: ${d.profile}`);
|
|
100
|
+
consola.log(` Endpoint: ${d.endpoint}`);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
|
|
3
|
+
/** Options merged from root `program` + subcommand (`optsWithGlobals`). */
|
|
4
|
+
export interface MitseinGlobalOpts {
|
|
5
|
+
endpoint?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
profile?: string;
|
|
8
|
+
real?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
debug?: boolean;
|
|
11
|
+
timeout?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function readGlobals(cmd: Command): MitseinGlobalOpts {
|
|
15
|
+
return cmd.optsWithGlobals() as MitseinGlobalOpts;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** HTTP client timeout from `--timeout` (0 = no limit). */
|
|
19
|
+
/** Comma-separated list → `Set` (trimmed, non-empty parts). */
|
|
20
|
+
export function commaSet(value: string | undefined): Set<string> | null {
|
|
21
|
+
if (!value) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const parts = value
|
|
25
|
+
.split(',')
|
|
26
|
+
.map((s) => s.trim())
|
|
27
|
+
.filter((s) => s.length > 0);
|
|
28
|
+
return parts.length > 0 ? new Set(parts) : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function httpTimeoutSec(g: MitseinGlobalOpts): number | undefined {
|
|
32
|
+
const raw = g.timeout ?? '30';
|
|
33
|
+
const n = Number.parseFloat(raw);
|
|
34
|
+
if (!Number.isFinite(n)) {
|
|
35
|
+
return 30;
|
|
36
|
+
}
|
|
37
|
+
if (n === 0) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { ApiClient } from '../core/client.js';
|
|
3
|
+
import { resolveCredentials } from '../core/credentials.js';
|
|
4
|
+
import { getOpenapiCachePath } from '../core/config.js';
|
|
5
|
+
import { handleErrors } from '../core/errors.js';
|
|
6
|
+
import { loadOpenApiSpec } from '../core/openapi.js';
|
|
7
|
+
import { emit, setJsonMode } from '../core/output.js';
|
|
8
|
+
import { httpTimeoutSec, readGlobals } from './command-opts.js';
|
|
9
|
+
|
|
10
|
+
function healthHuman(data: unknown): void {
|
|
11
|
+
const record =
|
|
12
|
+
typeof data === 'object' && data !== null ? (data as Record<string, unknown>) : null;
|
|
13
|
+
const ok = Boolean(record && (record.status === 'ok' || record.ok === true));
|
|
14
|
+
if (ok) {
|
|
15
|
+
process.stdout.write('✓ Backend is healthy\n');
|
|
16
|
+
} else {
|
|
17
|
+
process.stdout.write('✗ Backend is unhealthy\n');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function openapiHuman(data: unknown): void {
|
|
22
|
+
const d = data as { title?: string; version?: string; paths?: number; cached_at?: string };
|
|
23
|
+
process.stdout.write(`✓ ${d.title ?? ''} v${d.version ?? ''}\n`);
|
|
24
|
+
process.stdout.write(` ${d.paths ?? 0} paths\n`);
|
|
25
|
+
process.stdout.write(` Cached at: ${d.cached_at ?? ''}\n`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function registerDev(parent: Command): void {
|
|
29
|
+
const dev = parent.command('dev').description('Development utilities');
|
|
30
|
+
|
|
31
|
+
dev
|
|
32
|
+
.command('token')
|
|
33
|
+
.description('Print the resolved dev token')
|
|
34
|
+
.action(
|
|
35
|
+
handleErrors(async function devTokenAction(this: Command) {
|
|
36
|
+
const g = readGlobals(this);
|
|
37
|
+
if (g.real) {
|
|
38
|
+
process.stderr.write('⚠️ Using --real: this token accesses real data, not e2e sandbox.\n');
|
|
39
|
+
}
|
|
40
|
+
const creds = resolveCredentials({
|
|
41
|
+
token: g.token,
|
|
42
|
+
endpoint: g.endpoint,
|
|
43
|
+
profile: g.profile ?? 'e2e',
|
|
44
|
+
real: g.real,
|
|
45
|
+
});
|
|
46
|
+
emit(creds.token);
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
dev
|
|
51
|
+
.command('health')
|
|
52
|
+
.description('Check backend health (GET /health)')
|
|
53
|
+
.action(
|
|
54
|
+
handleErrors(async function devHealthAction(this: Command) {
|
|
55
|
+
const g = readGlobals(this);
|
|
56
|
+
const client = ApiClient.fromOptions({
|
|
57
|
+
token: g.token,
|
|
58
|
+
endpoint: g.endpoint,
|
|
59
|
+
profile: g.profile ?? 'e2e',
|
|
60
|
+
real: g.real,
|
|
61
|
+
timeoutSec: httpTimeoutSec(g),
|
|
62
|
+
debug: g.debug,
|
|
63
|
+
});
|
|
64
|
+
const result = await client.get('/health');
|
|
65
|
+
emit(result, healthHuman);
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
dev
|
|
70
|
+
.command('openapi')
|
|
71
|
+
.description('Fetch and cache OpenAPI spec from backend')
|
|
72
|
+
.option('--refresh', 'Force re-fetch from backend', false)
|
|
73
|
+
.action(
|
|
74
|
+
handleErrors(async function devOpenapiAction(this: Command) {
|
|
75
|
+
const g = readGlobals(this);
|
|
76
|
+
const opts = this.opts() as { refresh?: boolean };
|
|
77
|
+
setJsonMode(Boolean(g.json));
|
|
78
|
+
const spec = await loadOpenApiSpec({
|
|
79
|
+
endpoint: g.endpoint,
|
|
80
|
+
refresh: Boolean(opts.refresh),
|
|
81
|
+
});
|
|
82
|
+
const cachePath = getOpenapiCachePath();
|
|
83
|
+
const paths = spec.paths;
|
|
84
|
+
const pathsCount =
|
|
85
|
+
paths !== null && typeof paths === 'object' && !Array.isArray(paths)
|
|
86
|
+
? Object.keys(paths as object).length
|
|
87
|
+
: 0;
|
|
88
|
+
const info = (spec.info ?? {}) as Record<string, unknown>;
|
|
89
|
+
emit(
|
|
90
|
+
{
|
|
91
|
+
title: String(info.title ?? ''),
|
|
92
|
+
version: String(info.version ?? ''),
|
|
93
|
+
paths: pathsCount,
|
|
94
|
+
cached_at: cachePath,
|
|
95
|
+
},
|
|
96
|
+
openapiHuman
|
|
97
|
+
);
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { ApiClient } from '../core/client.js';
|
|
3
|
+
import { emit, setJsonMode } from '../core/output.js';
|
|
4
|
+
import { handleErrors } from '../core/errors.js';
|
|
5
|
+
import { httpTimeoutSec, readGlobals } from './command-opts.js';
|
|
6
|
+
|
|
7
|
+
export function registerMessages(program: Command): void {
|
|
8
|
+
const messages = program.command('messages').description('Message list and send (no agent)');
|
|
9
|
+
|
|
10
|
+
messages
|
|
11
|
+
.command('list <thread_id>')
|
|
12
|
+
.description('List messages for a thread')
|
|
13
|
+
.option('--offset <n>', 'Pagination offset', '0')
|
|
14
|
+
.option('--limit <n>', 'Max results', '20')
|
|
15
|
+
.action(
|
|
16
|
+
handleErrors(async function messagesListAction(this: Command, threadId: string) {
|
|
17
|
+
const g = readGlobals(this);
|
|
18
|
+
const opts = this.opts() as { offset?: string; limit?: string };
|
|
19
|
+
setJsonMode(Boolean(g.json));
|
|
20
|
+
const client = ApiClient.fromOptions({
|
|
21
|
+
token: g.token,
|
|
22
|
+
endpoint: g.endpoint,
|
|
23
|
+
profile: g.profile ?? 'e2e',
|
|
24
|
+
real: g.real,
|
|
25
|
+
timeoutSec: httpTimeoutSec(g),
|
|
26
|
+
debug: g.debug,
|
|
27
|
+
});
|
|
28
|
+
const offset = Number.parseInt(String(opts.offset ?? '0'), 10);
|
|
29
|
+
const limit = Number.parseInt(String(opts.limit ?? '20'), 10);
|
|
30
|
+
const result = await client.post('/api/message/list', {
|
|
31
|
+
thread_id: threadId,
|
|
32
|
+
limit: Number.isFinite(limit) ? limit : 20,
|
|
33
|
+
offset: Number.isFinite(offset) ? offset : 0,
|
|
34
|
+
});
|
|
35
|
+
emit(result);
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
messages
|
|
40
|
+
.command('send <thread_id> <content>')
|
|
41
|
+
.description('Add a user message (does not start the agent)')
|
|
42
|
+
.action(
|
|
43
|
+
handleErrors(async function messagesSendAction(this: Command, threadId: string, content: string) {
|
|
44
|
+
const g = readGlobals(this);
|
|
45
|
+
setJsonMode(Boolean(g.json));
|
|
46
|
+
const client = ApiClient.fromOptions({
|
|
47
|
+
token: g.token,
|
|
48
|
+
endpoint: g.endpoint,
|
|
49
|
+
profile: g.profile ?? 'e2e',
|
|
50
|
+
real: g.real,
|
|
51
|
+
timeoutSec: httpTimeoutSec(g),
|
|
52
|
+
debug: g.debug,
|
|
53
|
+
});
|
|
54
|
+
const result = await client.post('/api/message/add', {
|
|
55
|
+
thread_id: threadId,
|
|
56
|
+
content,
|
|
57
|
+
});
|
|
58
|
+
emit(result);
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { ApiClient } from '../core/client.js';
|
|
3
|
+
import { emit, setJsonMode } from '../core/output.js';
|
|
4
|
+
import { handleErrors } from '../core/errors.js';
|
|
5
|
+
import { httpTimeoutSec, readGlobals } from './command-opts.js';
|
|
6
|
+
|
|
7
|
+
export function registerProject(program: Command): void {
|
|
8
|
+
const project = program.command('project').description('Project management');
|
|
9
|
+
|
|
10
|
+
project
|
|
11
|
+
.command('list')
|
|
12
|
+
.description('List projects')
|
|
13
|
+
.option('--offset <n>', 'Pagination offset', '0')
|
|
14
|
+
.option('--limit <n>', 'Max results', '20')
|
|
15
|
+
.action(
|
|
16
|
+
handleErrors(async function projectListAction(this: Command) {
|
|
17
|
+
const g = readGlobals(this);
|
|
18
|
+
const opts = this.opts() as { offset?: string; limit?: string };
|
|
19
|
+
setJsonMode(Boolean(g.json));
|
|
20
|
+
const client = ApiClient.fromOptions({
|
|
21
|
+
token: g.token,
|
|
22
|
+
endpoint: g.endpoint,
|
|
23
|
+
profile: g.profile ?? 'e2e',
|
|
24
|
+
real: g.real,
|
|
25
|
+
timeoutSec: httpTimeoutSec(g),
|
|
26
|
+
debug: g.debug,
|
|
27
|
+
});
|
|
28
|
+
const offset = Number.parseInt(String(opts.offset ?? '0'), 10);
|
|
29
|
+
const limit = Number.parseInt(String(opts.limit ?? '20'), 10);
|
|
30
|
+
const result = await client.post('/api/project/list', {
|
|
31
|
+
offset: Number.isFinite(offset) ? offset : 0,
|
|
32
|
+
limit: Number.isFinite(limit) ? limit : 20,
|
|
33
|
+
});
|
|
34
|
+
emit(result);
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
project
|
|
39
|
+
.command('get <project_id>')
|
|
40
|
+
.description('Get project details')
|
|
41
|
+
.action(
|
|
42
|
+
handleErrors(async function projectGetAction(this: Command, projectId: string) {
|
|
43
|
+
const g = readGlobals(this);
|
|
44
|
+
setJsonMode(Boolean(g.json));
|
|
45
|
+
const client = ApiClient.fromOptions({
|
|
46
|
+
token: g.token,
|
|
47
|
+
endpoint: g.endpoint,
|
|
48
|
+
profile: g.profile ?? 'e2e',
|
|
49
|
+
real: g.real,
|
|
50
|
+
timeoutSec: httpTimeoutSec(g),
|
|
51
|
+
debug: g.debug,
|
|
52
|
+
});
|
|
53
|
+
const result = await client.post('/api/project/get', { project_id: projectId });
|
|
54
|
+
emit(result);
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
project
|
|
59
|
+
.command('delete <project_id>')
|
|
60
|
+
.description('Delete a project')
|
|
61
|
+
.action(
|
|
62
|
+
handleErrors(async function projectDeleteAction(this: Command, projectId: string) {
|
|
63
|
+
const g = readGlobals(this);
|
|
64
|
+
setJsonMode(Boolean(g.json));
|
|
65
|
+
const client = ApiClient.fromOptions({
|
|
66
|
+
token: g.token,
|
|
67
|
+
endpoint: g.endpoint,
|
|
68
|
+
profile: g.profile ?? 'e2e',
|
|
69
|
+
real: g.real,
|
|
70
|
+
timeoutSec: httpTimeoutSec(g),
|
|
71
|
+
debug: g.debug,
|
|
72
|
+
});
|
|
73
|
+
const result = await client.post('/api/project/delete', { project_id: projectId });
|
|
74
|
+
emit(result);
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
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
|
+
}
|