@overlordai/developer-cli 1.0.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/dist/auth/api-client.d.ts +15 -0
- package/dist/auth/api-client.d.ts.map +1 -0
- package/dist/auth/api-client.js +79 -0
- package/dist/auth/api-client.js.map +1 -0
- package/dist/auth/token-store.d.ts +13 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +52 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/commands/attach.d.ts +3 -0
- package/dist/commands/attach.d.ts.map +1 -0
- package/dist/commands/attach.js +48 -0
- package/dist/commands/attach.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +100 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/helpers.d.ts +15 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +41 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +83 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +16 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/machine.d.ts +3 -0
- package/dist/commands/machine.d.ts.map +1 -0
- package/dist/commands/machine.js +81 -0
- package/dist/commands/machine.js.map +1 -0
- package/dist/commands/notifications.d.ts +3 -0
- package/dist/commands/notifications.d.ts.map +1 -0
- package/dist/commands/notifications.js +55 -0
- package/dist/commands/notifications.js.map +1 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +61 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +58 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.d.ts.map +1 -0
- package/dist/commands/task.js +347 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +32 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +32 -0
- package/dist/main.js.map +1 -0
- package/dist/output/formatter.d.ts +18 -0
- package/dist/output/formatter.d.ts.map +1 -0
- package/dist/output/formatter.js +63 -0
- package/dist/output/formatter.js.map +1 -0
- package/dist/terminal/pty-client.d.ts +20 -0
- package/dist/terminal/pty-client.d.ts.map +1 -0
- package/dist/terminal/pty-client.js +133 -0
- package/dist/terminal/pty-client.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getApiClient } from './helpers.js';
|
|
3
|
+
import { formatter } from '../output/formatter.js';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
const MACHINE_STATUS_COLORS = {
|
|
6
|
+
'online': chalk.green,
|
|
7
|
+
'offline': chalk.red,
|
|
8
|
+
'draining': chalk.yellow,
|
|
9
|
+
'maintenance': chalk.gray,
|
|
10
|
+
};
|
|
11
|
+
export const machineCommand = new Command('machine')
|
|
12
|
+
.description('Machine status commands');
|
|
13
|
+
machineCommand
|
|
14
|
+
.command('list')
|
|
15
|
+
.description('List cluster machines')
|
|
16
|
+
.option('--json', 'Output as JSON')
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
if (opts.json)
|
|
19
|
+
formatter.setJsonMode(true);
|
|
20
|
+
const client = getApiClient();
|
|
21
|
+
try {
|
|
22
|
+
const res = await client.get('/api/web/machines');
|
|
23
|
+
if (res.status !== 200) {
|
|
24
|
+
formatter.error(`Failed to list machines (status ${res.status}).`);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
formatter.table(res.data, [
|
|
29
|
+
{ key: 'name', label: 'Name', width: 16 },
|
|
30
|
+
{
|
|
31
|
+
key: 'status',
|
|
32
|
+
label: 'Status',
|
|
33
|
+
width: 10,
|
|
34
|
+
color: (v) => {
|
|
35
|
+
const fn = MACHINE_STATUS_COLORS[v.trim()];
|
|
36
|
+
return fn ? fn(v) : v;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{ key: 'cpuUsage', label: 'CPU %', width: 8 },
|
|
40
|
+
{ key: 'activeSlots', label: 'Used', width: 6 },
|
|
41
|
+
{ key: 'maxSlots', label: 'Total', width: 6 },
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
machineCommand
|
|
50
|
+
.command('show <id>')
|
|
51
|
+
.description('Show machine details')
|
|
52
|
+
.option('--json', 'Output as JSON')
|
|
53
|
+
.action(async (id, opts) => {
|
|
54
|
+
if (opts.json)
|
|
55
|
+
formatter.setJsonMode(true);
|
|
56
|
+
const client = getApiClient();
|
|
57
|
+
try {
|
|
58
|
+
const res = await client.get(`/api/web/machines/${id}`);
|
|
59
|
+
if (res.status !== 200) {
|
|
60
|
+
formatter.error(`Machine not found (status ${res.status}).`);
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const machine = res.data;
|
|
65
|
+
formatter.detail({
|
|
66
|
+
Name: machine.name,
|
|
67
|
+
Status: machine.status,
|
|
68
|
+
OS: machine.os,
|
|
69
|
+
IP: machine.ip,
|
|
70
|
+
'CPU Load': `${machine.cpuUsage}%`,
|
|
71
|
+
'Slots': `${machine.activeSlots}/${machine.maxSlots}`,
|
|
72
|
+
'Last Heartbeat': machine.lastHeartbeat,
|
|
73
|
+
'Current Tasks': machine.currentTasks?.map((t) => `#${t.id}`).join(', ') || '—',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/commands/machine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/commands/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,eAAO,MAAM,oBAAoB,SAgC7B,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getApiClient } from './helpers.js';
|
|
3
|
+
import { formatter } from '../output/formatter.js';
|
|
4
|
+
export const notificationsCommand = new Command('notifications')
|
|
5
|
+
.description('View notifications')
|
|
6
|
+
.option('--all', 'Show all notifications (including read)')
|
|
7
|
+
.option('--json', 'Output as JSON')
|
|
8
|
+
.action(async (opts) => {
|
|
9
|
+
if (opts.json)
|
|
10
|
+
formatter.setJsonMode(true);
|
|
11
|
+
const client = getApiClient();
|
|
12
|
+
try {
|
|
13
|
+
const res = await client.get('/api/web/notifications');
|
|
14
|
+
if (res.status !== 200) {
|
|
15
|
+
formatter.error(`Failed to fetch notifications (status ${res.status}).`);
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const notifications = res.data?.data ?? [];
|
|
20
|
+
const filtered = opts.all ? notifications : notifications.filter((n) => !n.isRead);
|
|
21
|
+
formatter.table(filtered, [
|
|
22
|
+
{ key: 'id', label: 'ID', width: 6 },
|
|
23
|
+
{ key: 'type', label: 'Type', width: 14 },
|
|
24
|
+
{ key: 'title', label: 'Title', width: 30 },
|
|
25
|
+
{ key: 'body', label: 'Body', width: 30 },
|
|
26
|
+
{ key: 'isRead', label: 'Read', width: 6 },
|
|
27
|
+
{ key: 'createdAt', label: 'Time', width: 12 },
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
notificationsCommand
|
|
36
|
+
.command('read <id>')
|
|
37
|
+
.description('Mark notification as read')
|
|
38
|
+
.action(async (id) => {
|
|
39
|
+
const client = getApiClient();
|
|
40
|
+
try {
|
|
41
|
+
const res = await client.post(`/api/web/notifications/${id}/read`);
|
|
42
|
+
if (res.status === 200) {
|
|
43
|
+
formatter.success(`Notification #${id} marked as read.`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
formatter.error(`Failed to mark notification (status ${res.status}).`);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/commands/notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,eAAO,MAAM,cAAc,SACkB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getApiClient } from './helpers.js';
|
|
3
|
+
import { formatter } from '../output/formatter.js';
|
|
4
|
+
export const projectCommand = new Command('project')
|
|
5
|
+
.description('Project management commands');
|
|
6
|
+
projectCommand
|
|
7
|
+
.command('list')
|
|
8
|
+
.description('List projects you belong to')
|
|
9
|
+
.option('--json', 'Output as JSON')
|
|
10
|
+
.action(async (opts) => {
|
|
11
|
+
if (opts.json)
|
|
12
|
+
formatter.setJsonMode(true);
|
|
13
|
+
const client = getApiClient();
|
|
14
|
+
try {
|
|
15
|
+
const res = await client.get('/api/web/projects');
|
|
16
|
+
if (res.status !== 200) {
|
|
17
|
+
formatter.error(`Failed to list projects (status ${res.status}).`);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
formatter.table(res.data, [
|
|
22
|
+
{ key: 'key', label: 'Key', width: 14 },
|
|
23
|
+
{ key: 'name', label: 'Name', width: 24 },
|
|
24
|
+
{ key: 'description', label: 'Description', width: 40 },
|
|
25
|
+
{ key: 'memberCount', label: 'Members', width: 8 },
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
projectCommand
|
|
34
|
+
.command('show <key>')
|
|
35
|
+
.description('Show project details')
|
|
36
|
+
.option('--json', 'Output as JSON')
|
|
37
|
+
.action(async (key, opts) => {
|
|
38
|
+
if (opts.json)
|
|
39
|
+
formatter.setJsonMode(true);
|
|
40
|
+
const client = getApiClient();
|
|
41
|
+
try {
|
|
42
|
+
const res = await client.get(`/api/web/projects/${key}`);
|
|
43
|
+
if (res.status !== 200) {
|
|
44
|
+
formatter.error(`Project not found (status ${res.status}).`);
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const project = res.data;
|
|
49
|
+
formatter.detail({
|
|
50
|
+
Key: project.key,
|
|
51
|
+
Name: project.name,
|
|
52
|
+
Description: project.description,
|
|
53
|
+
Members: project.members?.map((m) => `${m.name} (${m.role})`).join(', ') || '—',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,aAAa,SA6DtB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getApiClient } from './helpers.js';
|
|
3
|
+
import { formatter } from '../output/formatter.js';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
export const searchCommand = new Command('search')
|
|
6
|
+
.description('Global search across tasks, projects, and machines')
|
|
7
|
+
.argument('<query>', 'Search query')
|
|
8
|
+
.option('--json', 'Output as JSON')
|
|
9
|
+
.action(async (query, opts) => {
|
|
10
|
+
if (opts.json)
|
|
11
|
+
formatter.setJsonMode(true);
|
|
12
|
+
const client = getApiClient();
|
|
13
|
+
try {
|
|
14
|
+
const res = await client.get(`/api/web/search?q=${encodeURIComponent(query)}`);
|
|
15
|
+
if (res.status !== 200) {
|
|
16
|
+
formatter.error(`Search failed (status ${res.status}).`);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (opts.json) {
|
|
21
|
+
formatter.detail(res.data);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const { tasks, projects, machines } = res.data;
|
|
25
|
+
if (tasks && tasks.length > 0) {
|
|
26
|
+
console.log(chalk.bold('\n Tasks'));
|
|
27
|
+
formatter.table(tasks, [
|
|
28
|
+
{ key: 'id', label: 'ID', width: 6 },
|
|
29
|
+
{ key: 'description', label: 'Description', width: 40 },
|
|
30
|
+
{ key: 'status', label: 'Status', width: 12 },
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
if (projects && projects.length > 0) {
|
|
34
|
+
console.log(chalk.bold('\n Projects'));
|
|
35
|
+
formatter.table(projects, [
|
|
36
|
+
{ key: 'key', label: 'Key', width: 14 },
|
|
37
|
+
{ key: 'name', label: 'Name', width: 30 },
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
if (machines && machines.length > 0) {
|
|
41
|
+
console.log(chalk.bold('\n Machines'));
|
|
42
|
+
formatter.table(machines, [
|
|
43
|
+
{ key: 'id', label: 'ID', width: 10 },
|
|
44
|
+
{ key: 'name', label: 'Name', width: 20 },
|
|
45
|
+
{ key: 'status', label: 'Status', width: 12 },
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
const total = (tasks?.length ?? 0) + (projects?.length ?? 0) + (machines?.length ?? 0);
|
|
49
|
+
if (total === 0) {
|
|
50
|
+
formatter.info('No results found.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,eAAO,MAAM,WAAW,SACkB,CAAC"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { getApiClient, getServerUrl, getToken } from './helpers.js';
|
|
4
|
+
import { formatter } from '../output/formatter.js';
|
|
5
|
+
import { PtyClient } from '../terminal/pty-client.js';
|
|
6
|
+
import { ApiClient } from '../auth/api-client.js';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
const STATUS_COLORS = {
|
|
9
|
+
RUNNING: chalk.green,
|
|
10
|
+
QUEUED: chalk.yellow,
|
|
11
|
+
ASSIGNED: chalk.blue,
|
|
12
|
+
COMPLETED: chalk.cyan,
|
|
13
|
+
FAILED: chalk.red,
|
|
14
|
+
CANCELLED: chalk.dim,
|
|
15
|
+
SUSPENDED: chalk.magenta,
|
|
16
|
+
};
|
|
17
|
+
export const taskCommand = new Command('task')
|
|
18
|
+
.description('Task management commands');
|
|
19
|
+
taskCommand
|
|
20
|
+
.command('create')
|
|
21
|
+
.description('Create a new task')
|
|
22
|
+
.option('-d, --description <text>', 'Task description')
|
|
23
|
+
.option('-f, --file <path>', 'Create from YAML file')
|
|
24
|
+
.option('-p, --project <key>', 'Project key')
|
|
25
|
+
.option('--on <machine>', 'Target machine')
|
|
26
|
+
.option('--json', 'Output as JSON')
|
|
27
|
+
.action(async (opts) => {
|
|
28
|
+
if (opts.json)
|
|
29
|
+
formatter.setJsonMode(true);
|
|
30
|
+
const client = getApiClient();
|
|
31
|
+
let body;
|
|
32
|
+
if (opts.file) {
|
|
33
|
+
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
34
|
+
// Simple YAML-like parsing — in production, use a proper YAML library
|
|
35
|
+
body = parseSimpleYaml(raw);
|
|
36
|
+
}
|
|
37
|
+
else if (opts.description) {
|
|
38
|
+
body = {
|
|
39
|
+
description: opts.description,
|
|
40
|
+
...(opts.project && { projectKey: opts.project }),
|
|
41
|
+
...(opts.on && { machineId: opts.on }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
formatter.error('Either -d <description> or -f <file> is required.');
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (opts.project && !body['projectKey']) {
|
|
50
|
+
body['projectKey'] = opts.project;
|
|
51
|
+
}
|
|
52
|
+
if (opts.on && !body['machineId']) {
|
|
53
|
+
body['machineId'] = opts.on;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const res = await client.post('/api/web/tasks', body);
|
|
57
|
+
if (res.status === 201 || res.status === 200) {
|
|
58
|
+
formatter.success(`Task created: #${res.data.task.id}`);
|
|
59
|
+
}
|
|
60
|
+
else if (res.status === 409) {
|
|
61
|
+
formatter.error('A duplicate or conflicting task already exists. Please check existing tasks before creating a new one.');
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
formatter.error(`Failed to create task (status ${res.status}).`);
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
taskCommand
|
|
75
|
+
.command('list')
|
|
76
|
+
.description('List tasks')
|
|
77
|
+
.option('--status <status>', 'Filter by status')
|
|
78
|
+
.option('-p, --project <key>', 'Filter by project')
|
|
79
|
+
.option('--json', 'Output as JSON')
|
|
80
|
+
.action(async (opts) => {
|
|
81
|
+
if (opts.json)
|
|
82
|
+
formatter.setJsonMode(true);
|
|
83
|
+
const client = getApiClient();
|
|
84
|
+
const params = new URLSearchParams();
|
|
85
|
+
if (opts.status)
|
|
86
|
+
params.set('status', opts.status);
|
|
87
|
+
if (opts.project)
|
|
88
|
+
params.set('projectKey', opts.project);
|
|
89
|
+
const qs = params.toString();
|
|
90
|
+
const path = `/api/web/tasks${qs ? `?${qs}` : ''}`;
|
|
91
|
+
try {
|
|
92
|
+
const res = await client.get(path);
|
|
93
|
+
if (res.status !== 200) {
|
|
94
|
+
formatter.error(`Failed to list tasks (status ${res.status}).`);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// API returns { data: [...], nextCursor: ... } or a plain array
|
|
99
|
+
const rawData = res.data;
|
|
100
|
+
const tasks = Array.isArray(rawData)
|
|
101
|
+
? rawData
|
|
102
|
+
: rawData.data ?? [];
|
|
103
|
+
formatter.table(tasks, [
|
|
104
|
+
{ key: 'id', label: 'ID', width: 6 },
|
|
105
|
+
{
|
|
106
|
+
key: 'status',
|
|
107
|
+
label: 'Status',
|
|
108
|
+
width: 12,
|
|
109
|
+
color: (v) => {
|
|
110
|
+
const fn = STATUS_COLORS[v.trim()];
|
|
111
|
+
return fn ? fn(v) : v;
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{ key: 'projectKey', label: 'Project', width: 14 },
|
|
115
|
+
{ key: 'description', label: 'Description', width: 30 },
|
|
116
|
+
{ key: 'machineName', label: 'Machine', width: 14 },
|
|
117
|
+
{ key: 'createdAt', label: 'Created', width: 12 },
|
|
118
|
+
]);
|
|
119
|
+
const active = tasks.filter((t) => ['RUNNING', 'QUEUED', 'ASSIGNED', 'SUSPENDED'].includes(t.status)).length;
|
|
120
|
+
const completed = tasks.length - active;
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(chalk.dim(` ${tasks.length} tasks shown (${active} active, ${completed} completed)`));
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
taskCommand
|
|
130
|
+
.command('show <id>')
|
|
131
|
+
.description('Show task details')
|
|
132
|
+
.option('--json', 'Output as JSON')
|
|
133
|
+
.action(async (id, opts) => {
|
|
134
|
+
if (opts.json)
|
|
135
|
+
formatter.setJsonMode(true);
|
|
136
|
+
const client = getApiClient();
|
|
137
|
+
try {
|
|
138
|
+
const res = await client.get(`/api/web/tasks/${id}`);
|
|
139
|
+
if (res.status !== 200) {
|
|
140
|
+
formatter.error(`Task not found (status ${res.status}).`);
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const task = res.data;
|
|
145
|
+
formatter.detail({
|
|
146
|
+
ID: task.id,
|
|
147
|
+
Status: task.status,
|
|
148
|
+
Project: task.projectKey,
|
|
149
|
+
Description: task.description,
|
|
150
|
+
Machine: task.machineName ?? '—',
|
|
151
|
+
Stage: task.currentStage ?? '—',
|
|
152
|
+
'MR URL': task.mrUrl ?? '—',
|
|
153
|
+
Created: task.createdAt,
|
|
154
|
+
Updated: task.updatedAt,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
taskCommand
|
|
163
|
+
.command('logs <id>')
|
|
164
|
+
.description('View task logs')
|
|
165
|
+
// Note: --stage filtering is not currently supported by the server API
|
|
166
|
+
.option('--json', 'Output as JSON')
|
|
167
|
+
.action(async (id, opts) => {
|
|
168
|
+
if (opts.json)
|
|
169
|
+
formatter.setJsonMode(true);
|
|
170
|
+
const client = getApiClient();
|
|
171
|
+
try {
|
|
172
|
+
// Check task status first
|
|
173
|
+
const taskRes = await client.get(`/api/web/tasks/${id}`);
|
|
174
|
+
if (taskRes.status !== 200) {
|
|
175
|
+
formatter.error(`Task not found (status ${taskRes.status}).`);
|
|
176
|
+
process.exitCode = 1;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const task = taskRes.data;
|
|
180
|
+
const isActive = ['RUNNING', 'QUEUED', 'SUSPENDED'].includes(task.status);
|
|
181
|
+
if (isActive) {
|
|
182
|
+
// Stream logs via PTY watch mode
|
|
183
|
+
formatter.info(`Streaming logs for task #${id} (Ctrl+C to stop)...`);
|
|
184
|
+
const ptyClient = new PtyClient({
|
|
185
|
+
serverUrl: getServerUrl(),
|
|
186
|
+
taskId: id,
|
|
187
|
+
token: getToken(),
|
|
188
|
+
mode: 'watch',
|
|
189
|
+
getChannelToken: async (serverUrl, taskId, token) => {
|
|
190
|
+
const apiClient = new ApiClient(serverUrl, token);
|
|
191
|
+
const res = await apiClient.post(`/api/web/tasks/${taskId}/pty-token`);
|
|
192
|
+
if (res.status !== 200 && res.status !== 201) {
|
|
193
|
+
throw new Error(`Failed to obtain PTY channel token (status ${res.status})`);
|
|
194
|
+
}
|
|
195
|
+
return res.data.channelToken;
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
await ptyClient.connect();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Fetch historical logs
|
|
202
|
+
const params = new URLSearchParams({ limit: '100' });
|
|
203
|
+
const logsRes = await client.get(`/api/web/tasks/${id}/logs?${params.toString()}`);
|
|
204
|
+
if (logsRes.status === 200 && logsRes.data.lines) {
|
|
205
|
+
for (const line of logsRes.data.lines) {
|
|
206
|
+
console.log(line);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
formatter.info('No logs available.');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
taskCommand
|
|
220
|
+
.command('cancel <id>')
|
|
221
|
+
.description('Cancel a running task')
|
|
222
|
+
.action(async (id) => {
|
|
223
|
+
const client = getApiClient();
|
|
224
|
+
try {
|
|
225
|
+
const res = await client.post(`/api/web/tasks/${id}/cancel`);
|
|
226
|
+
if (res.status >= 200 && res.status < 300) {
|
|
227
|
+
formatter.success(`Task #${id} cancelled.`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
formatter.error(`Failed to cancel task (status ${res.status}).`);
|
|
231
|
+
process.exitCode = 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
236
|
+
process.exitCode = 1;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
taskCommand
|
|
240
|
+
.command('retry <id>')
|
|
241
|
+
.description('Retry a failed task')
|
|
242
|
+
.action(async (id) => {
|
|
243
|
+
const client = getApiClient();
|
|
244
|
+
try {
|
|
245
|
+
const res = await client.post(`/api/web/tasks/${id}/retry`);
|
|
246
|
+
if (res.status >= 200 && res.status < 300) {
|
|
247
|
+
formatter.success(`Task #${id} retry initiated.`);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
formatter.error(`Failed to retry task (status ${res.status}).`);
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
256
|
+
process.exitCode = 1;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
taskCommand
|
|
260
|
+
.command('confirm <id>')
|
|
261
|
+
.description('Confirm a suspended task stage')
|
|
262
|
+
.requiredOption('--stage <index>', 'Stage index to confirm', (val) => parseInt(val, 10))
|
|
263
|
+
.action(async (id, opts) => {
|
|
264
|
+
const client = getApiClient();
|
|
265
|
+
try {
|
|
266
|
+
const res = await client.post(`/api/web/tasks/${id}/confirm-stage`, { stageIndex: opts.stage, result: 'confirmed' });
|
|
267
|
+
if (res.status === 200) {
|
|
268
|
+
formatter.success(`Task #${id} stage ${opts.stage} confirmed.`);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
formatter.error(`Failed to confirm task stage (status ${res.status}).`);
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
taskCommand
|
|
281
|
+
.command('choose <id> <option>')
|
|
282
|
+
.description('Choose an option for a suspended task stage')
|
|
283
|
+
.requiredOption('--stage <index>', 'Stage index to respond to', (val) => parseInt(val, 10))
|
|
284
|
+
.action(async (id, option, opts) => {
|
|
285
|
+
const client = getApiClient();
|
|
286
|
+
try {
|
|
287
|
+
const res = await client.post(`/api/web/tasks/${id}/confirm-stage`, { stageIndex: opts.stage, result: option });
|
|
288
|
+
if (res.status === 200) {
|
|
289
|
+
formatter.success(`Task #${id}: option "${option}" submitted.`);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
formatter.error(`Failed to submit option (status ${res.status}).`);
|
|
293
|
+
process.exitCode = 1;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
298
|
+
process.exitCode = 1;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
taskCommand
|
|
302
|
+
.command('input <id> <value>')
|
|
303
|
+
.description('Provide input for a suspended task stage')
|
|
304
|
+
.requiredOption('--stage <index>', 'Stage index to respond to', (val) => parseInt(val, 10))
|
|
305
|
+
.action(async (id, value, opts) => {
|
|
306
|
+
const client = getApiClient();
|
|
307
|
+
try {
|
|
308
|
+
const res = await client.post(`/api/web/tasks/${id}/confirm-stage`, { stageIndex: opts.stage, result: value });
|
|
309
|
+
if (res.status === 200) {
|
|
310
|
+
formatter.success(`Task #${id}: input submitted.`);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
formatter.error(`Failed to submit input (status ${res.status}).`);
|
|
314
|
+
process.exitCode = 1;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
319
|
+
process.exitCode = 1;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
/**
|
|
323
|
+
* Simple YAML-to-object parser for task files.
|
|
324
|
+
* Handles flat key: value pairs. For full YAML support, use the `yaml` package.
|
|
325
|
+
*/
|
|
326
|
+
function parseSimpleYaml(content) {
|
|
327
|
+
const result = {};
|
|
328
|
+
for (const line of content.split('\n')) {
|
|
329
|
+
const trimmed = line.trim();
|
|
330
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
331
|
+
continue;
|
|
332
|
+
const colonIndex = trimmed.indexOf(':');
|
|
333
|
+
if (colonIndex === -1)
|
|
334
|
+
continue;
|
|
335
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
336
|
+
let value = trimmed.slice(colonIndex + 1).trim();
|
|
337
|
+
// Strip surrounding quotes
|
|
338
|
+
if (typeof value === 'string' && value.startsWith('"') && value.endsWith('"')) {
|
|
339
|
+
value = value.slice(1, -1);
|
|
340
|
+
}
|
|
341
|
+
// Convert snake_case to camelCase
|
|
342
|
+
const camelKey = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
343
|
+
result[camelKey] = value;
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=task.js.map
|