@guildai/cli 0.5.10 → 0.5.11
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/commands/agent/init.js +24 -3
- package/dist/commands/container/destroy.d.ts +3 -0
- package/dist/commands/container/destroy.js +48 -0
- package/dist/commands/container/events.d.ts +3 -0
- package/dist/commands/container/events.js +44 -0
- package/dist/commands/container/exec.d.ts +3 -0
- package/dist/commands/container/exec.js +64 -0
- package/dist/commands/container/get.d.ts +3 -0
- package/dist/commands/container/get.js +33 -0
- package/dist/commands/container/list.d.ts +3 -0
- package/dist/commands/container/list.js +48 -0
- package/dist/commands/container-image/create.d.ts +3 -0
- package/dist/commands/container-image/create.js +41 -0
- package/dist/commands/container-image/get.d.ts +3 -0
- package/dist/commands/container-image/get.js +33 -0
- package/dist/commands/container-image/list.d.ts +3 -0
- package/dist/commands/container-image/list.js +44 -0
- package/dist/commands/job/get.d.ts +3 -0
- package/dist/commands/job/get.js +44 -0
- package/dist/commands/job/step-get.d.ts +3 -0
- package/dist/commands/job/step-get.js +40 -0
- package/dist/commands/trigger/create.js +4 -0
- package/dist/commands/trigger/update.js +4 -0
- package/dist/index.js +28 -0
- package/dist/lib/api-types.d.ts +135 -0
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +1 -0
- package/dist/lib/output.d.ts +21 -16
- package/dist/lib/output.js +184 -1
- package/dist/lib/session-events.d.ts +1 -0
- package/dist/lib/table.d.ts +22 -0
- package/dist/lib/table.js +60 -0
- package/package.json +2 -3
|
@@ -34,18 +34,30 @@ function slugify(text) {
|
|
|
34
34
|
.replace(/[^a-z0-9]+/g, '-')
|
|
35
35
|
.replace(/^-+|-+$/g, '');
|
|
36
36
|
}
|
|
37
|
+
const NAME_RULES = 'Lowercase letters, digits, hyphens, and underscores only (1-100 characters)';
|
|
38
|
+
function isValidAgentName(name) {
|
|
39
|
+
return name.length >= 1 && name.length <= 100 && /^[a-z0-9\-_.]+$/.test(name);
|
|
40
|
+
}
|
|
37
41
|
async function promptForName(defaultName) {
|
|
38
42
|
const rl = readline.createInterface({
|
|
39
43
|
input: process.stdin,
|
|
40
44
|
output: process.stdout,
|
|
41
45
|
});
|
|
42
|
-
|
|
46
|
+
const ask = () => new Promise((resolve) => {
|
|
43
47
|
rl.question(`Agent name [${defaultName}]: `, (answer) => {
|
|
44
|
-
rl.close();
|
|
45
48
|
const trimmed = answer.trim();
|
|
46
|
-
|
|
49
|
+
const name = trimmed || defaultName;
|
|
50
|
+
if (!isValidAgentName(name)) {
|
|
51
|
+
console.error(`Invalid name "${name}": ${NAME_RULES}`);
|
|
52
|
+
resolve(ask());
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
rl.close();
|
|
56
|
+
resolve(name);
|
|
57
|
+
}
|
|
47
58
|
});
|
|
48
59
|
});
|
|
60
|
+
return ask();
|
|
49
61
|
}
|
|
50
62
|
async function promptForTemplate() {
|
|
51
63
|
const { template } = await inquirer.prompt([
|
|
@@ -120,6 +132,15 @@ export function createAgentInitCommand() {
|
|
|
120
132
|
process.exit(1);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
135
|
+
// Validate name (catches --name flag and non-interactive paths)
|
|
136
|
+
if (!isValidAgentName(agentName)) {
|
|
137
|
+
console.error(`Error: Invalid agent name "${agentName}"`);
|
|
138
|
+
console.error('');
|
|
139
|
+
console.error(`Agent names must use ${NAME_RULES}`);
|
|
140
|
+
console.error('');
|
|
141
|
+
console.error('Examples: my-agent, my_agent');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
123
144
|
// Determine template: use --template option, prompt if interactive, or error
|
|
124
145
|
let template = options.template;
|
|
125
146
|
if (!template) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
9
|
+
export function createContainerDestroyCommand() {
|
|
10
|
+
const cmd = new Command('destroy');
|
|
11
|
+
cmd
|
|
12
|
+
.description('Destroy a running container')
|
|
13
|
+
.argument('<container-id>', 'Container ID')
|
|
14
|
+
.action(async (containerId) => {
|
|
15
|
+
const output = createOutputWriter();
|
|
16
|
+
try {
|
|
17
|
+
const token = await getAuthToken();
|
|
18
|
+
if (!token) {
|
|
19
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const client = new GuildAPIClient();
|
|
23
|
+
const response = await client.post(`/containers/${containerId}/destroy`, {});
|
|
24
|
+
if (getOutputMode() === 'json') {
|
|
25
|
+
console.log(JSON.stringify(response, null, 2));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (response.status === 'SUCCESS') {
|
|
29
|
+
output.success(`Container ${containerId} destroyed`);
|
|
30
|
+
}
|
|
31
|
+
else if (response.status === 'ERROR') {
|
|
32
|
+
output.error(`Failed to destroy container: ${response.error || 'unknown error'}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
output.progress(`Container destruction ${response.status.toLowerCase()}...`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const formattedError = handleAxiosError(error);
|
|
42
|
+
output.error(`Failed to destroy container: ${formattedError.details}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return cmd;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=destroy.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatContainerEventTable } from '../../lib/output.js';
|
|
9
|
+
export function createContainerEventsCommand() {
|
|
10
|
+
const cmd = new Command('events');
|
|
11
|
+
cmd
|
|
12
|
+
.description('List events for a container')
|
|
13
|
+
.argument('<container-id>', 'Container ID')
|
|
14
|
+
.option('--limit <number>', 'Number of results to return', '20')
|
|
15
|
+
.option('--offset <number>', 'Offset for pagination', '0')
|
|
16
|
+
.action(async (containerId, options) => {
|
|
17
|
+
const output = createOutputWriter();
|
|
18
|
+
try {
|
|
19
|
+
const token = await getAuthToken();
|
|
20
|
+
if (!token) {
|
|
21
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const client = new GuildAPIClient();
|
|
25
|
+
const params = new URLSearchParams();
|
|
26
|
+
params.append('limit', options.limit);
|
|
27
|
+
params.append('offset', options.offset);
|
|
28
|
+
const response = await client.get(`/containers/${containerId}/events?${params.toString()}`);
|
|
29
|
+
if (getOutputMode() === 'json') {
|
|
30
|
+
console.log(JSON.stringify(response, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
formatContainerEventTable(response.items, response.pagination);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const formattedError = handleAxiosError(error);
|
|
38
|
+
output.error(`Failed to list container events: ${formattedError.details}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return cmd;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
6
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
7
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
8
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
9
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
10
|
+
export function createContainerExecCommand() {
|
|
11
|
+
const cmd = new Command('exec');
|
|
12
|
+
cmd
|
|
13
|
+
.description('Execute a command in a running container')
|
|
14
|
+
.argument('<container-id>', 'Container ID')
|
|
15
|
+
.argument('<command>', 'Command to execute')
|
|
16
|
+
.action(async (containerId, command) => {
|
|
17
|
+
const output = createOutputWriter();
|
|
18
|
+
try {
|
|
19
|
+
const token = await getAuthToken();
|
|
20
|
+
if (!token) {
|
|
21
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const client = new GuildAPIClient();
|
|
25
|
+
const response = await client.post(`/containers/${containerId}/command`, { command });
|
|
26
|
+
if (getOutputMode() === 'json') {
|
|
27
|
+
console.log(JSON.stringify(response, null, 2));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Display return code
|
|
31
|
+
if (response.return_code !== null) {
|
|
32
|
+
const codeColor = response.return_code === 0 ? chalk.green : chalk.red;
|
|
33
|
+
console.error(chalk.dim('Return code:'), codeColor(String(response.return_code)));
|
|
34
|
+
}
|
|
35
|
+
// Display stdout
|
|
36
|
+
if (response.stdout) {
|
|
37
|
+
console.log(response.stdout);
|
|
38
|
+
}
|
|
39
|
+
// Display stderr
|
|
40
|
+
if (response.stderr) {
|
|
41
|
+
console.error(chalk.red(response.stderr));
|
|
42
|
+
}
|
|
43
|
+
// Display error from event
|
|
44
|
+
if (response.status === 'ERROR' && response.error) {
|
|
45
|
+
console.error(chalk.red(`Error: ${response.error}`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Exit with the command's return code if available
|
|
49
|
+
if (response.return_code !== null && response.return_code !== 0) {
|
|
50
|
+
process.exit(response.return_code);
|
|
51
|
+
}
|
|
52
|
+
if (response.status === 'ERROR') {
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const formattedError = handleAxiosError(error);
|
|
58
|
+
output.error(`Failed to execute command: ${formattedError.details}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return cmd;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
8
|
+
export function createContainerGetCommand() {
|
|
9
|
+
const cmd = new Command('get');
|
|
10
|
+
cmd
|
|
11
|
+
.description('Get container details')
|
|
12
|
+
.argument('<container-id>', 'Container ID')
|
|
13
|
+
.action(async (containerId) => {
|
|
14
|
+
const output = createOutputWriter();
|
|
15
|
+
try {
|
|
16
|
+
const token = await getAuthToken();
|
|
17
|
+
if (!token) {
|
|
18
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const client = new GuildAPIClient();
|
|
22
|
+
const response = await client.get(`/containers/${containerId}`);
|
|
23
|
+
output.data(response);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const formattedError = handleAxiosError(error);
|
|
27
|
+
output.error(`Failed to get container: ${formattedError.details}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return cmd;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=get.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatContainerTable } from '../../lib/output.js';
|
|
9
|
+
export function createContainerListCommand() {
|
|
10
|
+
const cmd = new Command('list');
|
|
11
|
+
cmd
|
|
12
|
+
.description('List containers for an account')
|
|
13
|
+
.requiredOption('--account <id-or-name>', 'Account ID or name')
|
|
14
|
+
.option('--status <status>', 'Filter by status: STARTING, RUNNING, ERRORED, DESTROYED')
|
|
15
|
+
.option('--limit <number>', 'Number of results to return', '20')
|
|
16
|
+
.option('--offset <number>', 'Offset for pagination', '0')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const output = createOutputWriter();
|
|
19
|
+
try {
|
|
20
|
+
const token = await getAuthToken();
|
|
21
|
+
if (!token) {
|
|
22
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const client = new GuildAPIClient();
|
|
26
|
+
const params = new URLSearchParams();
|
|
27
|
+
params.append('limit', options.limit);
|
|
28
|
+
params.append('offset', options.offset);
|
|
29
|
+
if (options.status) {
|
|
30
|
+
params.append('statuses', options.status.toUpperCase());
|
|
31
|
+
}
|
|
32
|
+
const response = await client.get(`/accounts/${options.account}/containers?${params.toString()}`);
|
|
33
|
+
if (getOutputMode() === 'json') {
|
|
34
|
+
console.log(JSON.stringify(response, null, 2));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
formatContainerTable(response.items, response.pagination);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const formattedError = handleAxiosError(error);
|
|
42
|
+
output.error(`Failed to list containers: ${formattedError.details}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return cmd;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
8
|
+
export function createContainerImageCreateCommand() {
|
|
9
|
+
const cmd = new Command('create');
|
|
10
|
+
cmd
|
|
11
|
+
.description('Create a container image')
|
|
12
|
+
.requiredOption('--account <id-or-name>', 'Account or organization ID or name')
|
|
13
|
+
.requiredOption('--name <name>', 'Image name (1-100 characters)')
|
|
14
|
+
.requiredOption('--image <image>', 'Container image path (e.g. registry.example.com/org/image)')
|
|
15
|
+
.requiredOption('--tag <tag>', 'Image tag (e.g. latest)')
|
|
16
|
+
.action(async (opts) => {
|
|
17
|
+
const output = createOutputWriter();
|
|
18
|
+
try {
|
|
19
|
+
const token = await getAuthToken();
|
|
20
|
+
if (!token) {
|
|
21
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const client = new GuildAPIClient();
|
|
25
|
+
const response = await client.post('/container-images', {
|
|
26
|
+
name: opts.name,
|
|
27
|
+
image_name: opts.image,
|
|
28
|
+
tag: opts.tag,
|
|
29
|
+
owner_id_or_name: opts.account,
|
|
30
|
+
});
|
|
31
|
+
output.data(response);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const formattedError = handleAxiosError(error);
|
|
35
|
+
output.error(`Failed to create container image: ${formattedError.details}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return cmd;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
8
|
+
export function createContainerImageGetCommand() {
|
|
9
|
+
const cmd = new Command('get');
|
|
10
|
+
cmd
|
|
11
|
+
.description('Get container image details')
|
|
12
|
+
.argument('<image-id-or-name>', 'Container image ID or name (owner~name)')
|
|
13
|
+
.action(async (imageIdOrName) => {
|
|
14
|
+
const output = createOutputWriter();
|
|
15
|
+
try {
|
|
16
|
+
const token = await getAuthToken();
|
|
17
|
+
if (!token) {
|
|
18
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const client = new GuildAPIClient();
|
|
22
|
+
const response = await client.get(`/container-images/${imageIdOrName}`);
|
|
23
|
+
output.data(response);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const formattedError = handleAxiosError(error);
|
|
27
|
+
output.error(`Failed to get container image: ${formattedError.details}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return cmd;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=get.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatContainerImageTable } from '../../lib/output.js';
|
|
9
|
+
export function createContainerImageListCommand() {
|
|
10
|
+
const cmd = new Command('list');
|
|
11
|
+
cmd
|
|
12
|
+
.description('List container images for an account')
|
|
13
|
+
.requiredOption('--account <id-or-name>', 'Account ID or name')
|
|
14
|
+
.option('--limit <number>', 'Number of results to return', '20')
|
|
15
|
+
.option('--offset <number>', 'Offset for pagination', '0')
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
const output = createOutputWriter();
|
|
18
|
+
try {
|
|
19
|
+
const token = await getAuthToken();
|
|
20
|
+
if (!token) {
|
|
21
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const client = new GuildAPIClient();
|
|
25
|
+
const params = new URLSearchParams();
|
|
26
|
+
params.append('limit', options.limit);
|
|
27
|
+
params.append('offset', options.offset);
|
|
28
|
+
const response = await client.get(`/accounts/${options.account}/container-images?${params.toString()}`);
|
|
29
|
+
if (getOutputMode() === 'json') {
|
|
30
|
+
console.log(JSON.stringify(response, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
formatContainerImageTable(response.items, response.pagination);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const formattedError = handleAxiosError(error);
|
|
38
|
+
output.error(`Failed to list container images: ${formattedError.details}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return cmd;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatJobStepTable } from '../../lib/output.js';
|
|
9
|
+
export function createJobGetCommand() {
|
|
10
|
+
const cmd = new Command('get');
|
|
11
|
+
cmd
|
|
12
|
+
.description('Get job details and steps')
|
|
13
|
+
.argument('<job-id>', 'Job ID')
|
|
14
|
+
.action(async (jobId) => {
|
|
15
|
+
const output = createOutputWriter();
|
|
16
|
+
try {
|
|
17
|
+
const token = await getAuthToken();
|
|
18
|
+
if (!token) {
|
|
19
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const client = new GuildAPIClient();
|
|
23
|
+
const [job, stepsResponse] = await Promise.all([
|
|
24
|
+
client.get(`/jobs/${jobId}`),
|
|
25
|
+
client.get(`/jobs/${jobId}/steps`),
|
|
26
|
+
]);
|
|
27
|
+
if (getOutputMode() === 'json') {
|
|
28
|
+
console.log(JSON.stringify({ ...job, steps: stepsResponse.steps }, null, 2));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
output.data(job);
|
|
32
|
+
console.log();
|
|
33
|
+
formatJobStepTable(stepsResponse.steps);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const formattedError = handleAxiosError(error);
|
|
38
|
+
output.error(`Failed to get job: ${formattedError.details}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return cmd;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=get.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
8
|
+
export function createJobStepGetCommand() {
|
|
9
|
+
const cmd = new Command('step-get');
|
|
10
|
+
cmd
|
|
11
|
+
.description('Get details of a step in a job')
|
|
12
|
+
.argument('<job-id>', 'Job ID')
|
|
13
|
+
.argument('<step-name>', 'Step name')
|
|
14
|
+
.action(async (jobId, stepName) => {
|
|
15
|
+
const output = createOutputWriter();
|
|
16
|
+
try {
|
|
17
|
+
const token = await getAuthToken();
|
|
18
|
+
if (!token) {
|
|
19
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const client = new GuildAPIClient();
|
|
23
|
+
const stepsResponse = await client.get(`/jobs/${jobId}/steps`);
|
|
24
|
+
const step = stepsResponse.steps.find((s) => s.name === stepName);
|
|
25
|
+
if (!step) {
|
|
26
|
+
const available = stepsResponse.steps.map((s) => s.name).join(', ');
|
|
27
|
+
output.error(`Step "${stepName}" not found. Available steps: ${available || 'none'}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
output.data(step);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const formattedError = handleAxiosError(error);
|
|
34
|
+
output.error(`Failed to get job step: ${formattedError.details}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return cmd;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=step-get.js.map
|
|
@@ -24,6 +24,7 @@ export function createTriggerCreateCommand() {
|
|
|
24
24
|
.option('--time <time>', 'Time of day in HH:MM format')
|
|
25
25
|
.option('--days-of-week <days>', 'Days of week (comma-separated: MONDAY,TUESDAY,...)')
|
|
26
26
|
.option('--days-of-month <days>', 'Days of month (comma-separated: 1,15,-1)')
|
|
27
|
+
.option('--minutes-of-hour <minutes>', 'Minutes of hour, comma-separated (0-59)')
|
|
27
28
|
.option('--input <json>', 'Agent input as JSON object')
|
|
28
29
|
.action(async (options) => {
|
|
29
30
|
const output = createOutputWriter();
|
|
@@ -130,6 +131,9 @@ export function createTriggerCreateCommand() {
|
|
|
130
131
|
if (options.daysOfMonth) {
|
|
131
132
|
body.days_of_month = options.daysOfMonth;
|
|
132
133
|
}
|
|
134
|
+
if (options.minutesOfHour) {
|
|
135
|
+
body.minutes_of_hour = options.minutesOfHour;
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
const response = await client.post(`/workspaces/${workspaceId}/triggers`, body);
|
|
135
139
|
output.data(response);
|
|
@@ -20,6 +20,7 @@ export function createTriggerUpdateCommand() {
|
|
|
20
20
|
.option('--time <time>', 'Time of day in HH:MM format (for time triggers)')
|
|
21
21
|
.option('--days-of-week <days>', 'Days of week, comma-separated (for time triggers)')
|
|
22
22
|
.option('--days-of-month <days>', 'Days of month, comma-separated (for time triggers)')
|
|
23
|
+
.option('--minutes-of-hour <minutes>', 'Minutes of hour, comma-separated (0-59, for HOURLY triggers)')
|
|
23
24
|
.option('--input <json>', 'Agent input as JSON object (for time triggers)')
|
|
24
25
|
.action(async (triggerId, options) => {
|
|
25
26
|
const output = createOutputWriter();
|
|
@@ -66,6 +67,9 @@ export function createTriggerUpdateCommand() {
|
|
|
66
67
|
if (options.daysOfMonth !== undefined) {
|
|
67
68
|
body.days_of_month = options.daysOfMonth;
|
|
68
69
|
}
|
|
70
|
+
if (options.minutesOfHour !== undefined) {
|
|
71
|
+
body.minutes_of_hour = options.minutesOfHour;
|
|
72
|
+
}
|
|
69
73
|
if (options.input !== undefined) {
|
|
70
74
|
try {
|
|
71
75
|
body.agent_input = JSON.parse(options.input);
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,16 @@ import { createSessionEventsCommand } from './commands/session/events.js';
|
|
|
60
60
|
import { createSessionTasksCommand } from './commands/session/tasks.js';
|
|
61
61
|
import { createSessionCreateCommand } from './commands/session/create.js';
|
|
62
62
|
import { createSessionSendCommand } from './commands/session/send.js';
|
|
63
|
+
import { createContainerGetCommand } from './commands/container/get.js';
|
|
64
|
+
import { createContainerListCommand } from './commands/container/list.js';
|
|
65
|
+
import { createContainerEventsCommand } from './commands/container/events.js';
|
|
66
|
+
import { createContainerExecCommand } from './commands/container/exec.js';
|
|
67
|
+
import { createContainerDestroyCommand } from './commands/container/destroy.js';
|
|
68
|
+
import { createContainerImageListCommand } from './commands/container-image/list.js';
|
|
69
|
+
import { createContainerImageGetCommand } from './commands/container-image/get.js';
|
|
70
|
+
import { createContainerImageCreateCommand } from './commands/container-image/create.js';
|
|
71
|
+
import { createJobGetCommand } from './commands/job/get.js';
|
|
72
|
+
import { createJobStepGetCommand } from './commands/job/step-get.js';
|
|
63
73
|
import { createConfigListCommand } from './commands/config/list.js';
|
|
64
74
|
import { createConfigGetCommand } from './commands/config/get.js';
|
|
65
75
|
import { createConfigSetCommand } from './commands/config/set.js';
|
|
@@ -208,6 +218,20 @@ triggerCmd.addCommand(createTriggerUpdateCommand());
|
|
|
208
218
|
triggerCmd.addCommand(createTriggerActivateCommand());
|
|
209
219
|
triggerCmd.addCommand(createTriggerDeactivateCommand());
|
|
210
220
|
triggerCmd.addCommand(createTriggerSessionsCommand());
|
|
221
|
+
// Container command group
|
|
222
|
+
const containerCmd = program.command('container').description('Container management');
|
|
223
|
+
containerCmd.addCommand(createContainerListCommand());
|
|
224
|
+
containerCmd.addCommand(createContainerGetCommand());
|
|
225
|
+
containerCmd.addCommand(createContainerEventsCommand());
|
|
226
|
+
containerCmd.addCommand(createContainerExecCommand());
|
|
227
|
+
containerCmd.addCommand(createContainerDestroyCommand());
|
|
228
|
+
// Container image command group
|
|
229
|
+
const containerImageCmd = program
|
|
230
|
+
.command('container-image')
|
|
231
|
+
.description('Container image management');
|
|
232
|
+
containerImageCmd.addCommand(createContainerImageCreateCommand());
|
|
233
|
+
containerImageCmd.addCommand(createContainerImageListCommand());
|
|
234
|
+
containerImageCmd.addCommand(createContainerImageGetCommand());
|
|
211
235
|
// Session command group
|
|
212
236
|
const sessionCmd = program.command('session').description('Session management');
|
|
213
237
|
sessionCmd.addCommand(createSessionListCommand());
|
|
@@ -216,6 +240,10 @@ sessionCmd.addCommand(createSessionEventsCommand());
|
|
|
216
240
|
sessionCmd.addCommand(createSessionTasksCommand());
|
|
217
241
|
sessionCmd.addCommand(createSessionCreateCommand());
|
|
218
242
|
sessionCmd.addCommand(createSessionSendCommand());
|
|
243
|
+
// Job command group
|
|
244
|
+
const jobCmd = program.command('job').description('Job management');
|
|
245
|
+
jobCmd.addCommand(createJobGetCommand());
|
|
246
|
+
jobCmd.addCommand(createJobStepGetCommand());
|
|
219
247
|
// Config command group
|
|
220
248
|
const configCmd = program
|
|
221
249
|
.command('config')
|
package/dist/lib/api-types.d.ts
CHANGED
|
@@ -315,6 +315,141 @@ export interface Organization {
|
|
|
315
315
|
viewer_role: 'ADMIN' | 'MEMBER' | null;
|
|
316
316
|
}
|
|
317
317
|
export type OrganizationListResponse = PaginatedResponse<Organization>;
|
|
318
|
+
/**
|
|
319
|
+
* Session entity from API responses.
|
|
320
|
+
* Used by: session list, container list (as owner)
|
|
321
|
+
*/
|
|
322
|
+
export interface Session {
|
|
323
|
+
id: string;
|
|
324
|
+
session_type: 'chat' | 'agent_test' | 'time' | 'webhook';
|
|
325
|
+
name?: string;
|
|
326
|
+
workspace?: {
|
|
327
|
+
id: string;
|
|
328
|
+
name: string;
|
|
329
|
+
};
|
|
330
|
+
created_at: string;
|
|
331
|
+
updated_at: string;
|
|
332
|
+
}
|
|
333
|
+
export type ContainerStatus = 'STARTING' | 'RUNNING' | 'ERRORED' | 'DESTROYED';
|
|
334
|
+
/**
|
|
335
|
+
* Container image entity from the API.
|
|
336
|
+
*/
|
|
337
|
+
export interface ContainerImage {
|
|
338
|
+
id: string;
|
|
339
|
+
name: string;
|
|
340
|
+
image_name: string;
|
|
341
|
+
tag: string;
|
|
342
|
+
is_public: boolean;
|
|
343
|
+
created_at: string;
|
|
344
|
+
updated_at: string;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The actor that created the container (user or task agent).
|
|
348
|
+
*/
|
|
349
|
+
export interface ContainerActor {
|
|
350
|
+
id: string;
|
|
351
|
+
entity_type: string;
|
|
352
|
+
name?: string;
|
|
353
|
+
full_name?: string;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Container entity from the API.
|
|
357
|
+
* Used by: container list
|
|
358
|
+
*/
|
|
359
|
+
export interface Container {
|
|
360
|
+
id: string;
|
|
361
|
+
status: ContainerStatus;
|
|
362
|
+
owner: Session;
|
|
363
|
+
created_by: ContainerActor;
|
|
364
|
+
image: ContainerImage;
|
|
365
|
+
created_at: string;
|
|
366
|
+
updated_at: string;
|
|
367
|
+
}
|
|
368
|
+
export type ContainerListResponse = PaginatedResponse<Container>;
|
|
369
|
+
/**
|
|
370
|
+
* Container image as returned in list responses.
|
|
371
|
+
*/
|
|
372
|
+
export interface ContainerImageListItem {
|
|
373
|
+
id: string;
|
|
374
|
+
name: string;
|
|
375
|
+
image_name: string;
|
|
376
|
+
tag: string;
|
|
377
|
+
is_public: boolean;
|
|
378
|
+
created_at: string;
|
|
379
|
+
updated_at: string;
|
|
380
|
+
}
|
|
381
|
+
export type ContainerImageListResponse = PaginatedResponse<ContainerImageListItem>;
|
|
382
|
+
export type ContainerEventStatus = 'PENDING' | 'ERROR' | 'SUCCESS';
|
|
383
|
+
/**
|
|
384
|
+
* Base fields shared by all container events.
|
|
385
|
+
*/
|
|
386
|
+
export interface ContainerEventBase {
|
|
387
|
+
id: string;
|
|
388
|
+
entity_type: string;
|
|
389
|
+
actor: ContainerActor;
|
|
390
|
+
status: ContainerEventStatus;
|
|
391
|
+
completed_at: string | null;
|
|
392
|
+
error: string | null;
|
|
393
|
+
created_at: string;
|
|
394
|
+
updated_at: string;
|
|
395
|
+
}
|
|
396
|
+
export interface ContainerEventCreate extends ContainerEventBase {
|
|
397
|
+
entity_type: 'EntContainerEventCreate';
|
|
398
|
+
is_local: boolean;
|
|
399
|
+
}
|
|
400
|
+
export interface ContainerEventDestroy extends ContainerEventBase {
|
|
401
|
+
entity_type: 'EntContainerEventDestroy';
|
|
402
|
+
found: boolean | null;
|
|
403
|
+
}
|
|
404
|
+
export interface ContainerEventCommand extends ContainerEventBase {
|
|
405
|
+
entity_type: 'EntContainerEventCommand';
|
|
406
|
+
command: string;
|
|
407
|
+
return_code: number | null;
|
|
408
|
+
stdout: string | null;
|
|
409
|
+
stderr: string | null;
|
|
410
|
+
}
|
|
411
|
+
export type ContainerEvent = ContainerEventCreate | ContainerEventDestroy | ContainerEventCommand;
|
|
412
|
+
export type ContainerEventListResponse = PaginatedResponse<ContainerEvent>;
|
|
413
|
+
export type JobType = 'VALIDATION' | 'PUBLISH' | 'PUBLISH_PRE_CHECK';
|
|
414
|
+
export type JobStatus = 'PENDING' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'ERRORED' | 'CANCELLED';
|
|
415
|
+
export type JobStepStatus = 'PENDING' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'SKIPPED' | 'ERRORED';
|
|
416
|
+
/**
|
|
417
|
+
* Job entity from the API.
|
|
418
|
+
* Used by: job get
|
|
419
|
+
*/
|
|
420
|
+
export interface Job {
|
|
421
|
+
id: string;
|
|
422
|
+
target_id: string;
|
|
423
|
+
job_type: JobType;
|
|
424
|
+
status: JobStatus;
|
|
425
|
+
started_at: string | null;
|
|
426
|
+
completed_at: string | null;
|
|
427
|
+
error_message: string | null;
|
|
428
|
+
created_at: string;
|
|
429
|
+
updated_at: string;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Job step entity from the API.
|
|
433
|
+
* Used by: job get (steps table)
|
|
434
|
+
*/
|
|
435
|
+
export interface JobStep {
|
|
436
|
+
id: string;
|
|
437
|
+
job: Job;
|
|
438
|
+
name: string;
|
|
439
|
+
status: JobStepStatus;
|
|
440
|
+
started_at: string | null;
|
|
441
|
+
completed_at: string | null;
|
|
442
|
+
content: string | null;
|
|
443
|
+
created_at: string;
|
|
444
|
+
updated_at: string;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Response from GET /jobs/:id/steps.
|
|
448
|
+
*/
|
|
449
|
+
export interface JobStepsResponse {
|
|
450
|
+
job_id: string;
|
|
451
|
+
steps: JobStep[];
|
|
452
|
+
}
|
|
318
453
|
/**
|
|
319
454
|
* Contents of guild.json file in agent directories.
|
|
320
455
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const WEBHOOK_SERVICES: readonly ["AZURE_DEVOPS", "BITBUCKET", "CYPRESS", "GITHUB", "GOOGLE_DOCS", "GOOGLE_COMPUTE", "GOOGLE_LOGGING", "JIRA", "LINEAR", "NEWRELIC", "NOTION", "SLACK", "TESTRAIL", "ZENDESK"];
|
|
2
2
|
export type WebhookService = (typeof WEBHOOK_SERVICES)[number];
|
|
3
|
-
export declare const TIME_TRIGGER_FREQUENCIES: readonly ["HOURLY", "DAILY", "WEEKLY", "MONTHLY"];
|
|
3
|
+
export declare const TIME_TRIGGER_FREQUENCIES: readonly ["HOURLY", "DAILY", "WEEKLY", "MONTHLY", "CRON"];
|
|
4
4
|
export type TimeTriggerFrequency = (typeof TIME_TRIGGER_FREQUENCIES)[number];
|
|
5
5
|
//# sourceMappingURL=generated-types.d.ts.map
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import { type Spinner } from './progress.js';
|
|
2
|
-
import type { Agent, AgentVersion, Context, Pagination, Workspace, WorkspaceAgent, Trigger } from './api-types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Session type from API responses
|
|
5
|
-
*/
|
|
6
|
-
interface Session {
|
|
7
|
-
id: string;
|
|
8
|
-
session_type: 'chat' | 'agent_test' | 'time' | 'webhook';
|
|
9
|
-
name?: string;
|
|
10
|
-
workspace?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
created_at: string;
|
|
15
|
-
updated_at: string;
|
|
16
|
-
}
|
|
2
|
+
import type { Agent, AgentVersion, Container, ContainerEvent, ContainerImageListItem, Context, JobStep, Pagination, Session, Workspace, WorkspaceAgent, Trigger } from './api-types.js';
|
|
17
3
|
/**
|
|
18
4
|
* Format an agent list as a human-readable table.
|
|
19
5
|
* Shared by agent list and agent search commands.
|
|
@@ -49,6 +35,26 @@ export declare function formatSessionTable(sessions: Session[], pagination: Pagi
|
|
|
49
35
|
* Used by trigger list command.
|
|
50
36
|
*/
|
|
51
37
|
export declare function formatTriggerTable(triggers: Trigger[], pagination: Pagination): void;
|
|
38
|
+
/**
|
|
39
|
+
* Format a container list as a human-readable table.
|
|
40
|
+
* Used by container list command.
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatContainerTable(containers: Container[], pagination: Pagination): void;
|
|
43
|
+
/**
|
|
44
|
+
* Format a container image list as a human-readable table.
|
|
45
|
+
* Used by container-image list command.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatContainerImageTable(images: ContainerImageListItem[], pagination: Pagination): void;
|
|
48
|
+
/**
|
|
49
|
+
* Format a container event list as a human-readable table.
|
|
50
|
+
* Used by container events command.
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatContainerEventTable(events: ContainerEvent[], pagination: Pagination): void;
|
|
53
|
+
/**
|
|
54
|
+
* Format job steps as a human-readable table.
|
|
55
|
+
* Used by job get command.
|
|
56
|
+
*/
|
|
57
|
+
export declare function formatJobStepTable(steps: JobStep[]): void;
|
|
52
58
|
/**
|
|
53
59
|
* Output writer interface for consistent CLI output
|
|
54
60
|
*
|
|
@@ -108,5 +114,4 @@ export declare class JSONOutputWriter implements OutputWriter {
|
|
|
108
114
|
* Create output writer based on current mode
|
|
109
115
|
*/
|
|
110
116
|
export declare function createOutputWriter(): OutputWriter;
|
|
111
|
-
export {};
|
|
112
117
|
//# sourceMappingURL=output.d.ts.map
|
package/dist/lib/output.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { Table } from '
|
|
4
|
+
import { Table } from './table.js';
|
|
5
5
|
import { getOutputMode, isQuietMode } from './output-mode.js';
|
|
6
6
|
import { createSpinner } from './progress.js';
|
|
7
7
|
import { brand, hyperlink } from './colors.js';
|
|
@@ -313,6 +313,189 @@ export function formatTriggerTable(triggers, pagination) {
|
|
|
313
313
|
console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} triggers`));
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Format a container list as a human-readable table.
|
|
318
|
+
* Used by container list command.
|
|
319
|
+
*/
|
|
320
|
+
export function formatContainerTable(containers, pagination) {
|
|
321
|
+
if (containers.length === 0) {
|
|
322
|
+
console.log(chalk.dim('No containers found'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const table = new Table({
|
|
326
|
+
columns: [
|
|
327
|
+
{ name: 'id', title: 'ID', alignment: 'left' },
|
|
328
|
+
{ name: 'status', title: 'STATUS', alignment: 'left' },
|
|
329
|
+
{ name: 'image', title: 'IMAGE', alignment: 'left' },
|
|
330
|
+
{ name: 'tag', title: 'TAG', alignment: 'left' },
|
|
331
|
+
{ name: 'created', title: 'CREATED', alignment: 'left' },
|
|
332
|
+
],
|
|
333
|
+
});
|
|
334
|
+
containers.forEach((container) => {
|
|
335
|
+
const statusColor = container.status === 'RUNNING'
|
|
336
|
+
? chalk.green
|
|
337
|
+
: container.status === 'ERRORED'
|
|
338
|
+
? chalk.red
|
|
339
|
+
: container.status === 'DESTROYED'
|
|
340
|
+
? chalk.dim
|
|
341
|
+
: chalk.yellow;
|
|
342
|
+
table.addRow({
|
|
343
|
+
id: container.id,
|
|
344
|
+
status: statusColor(container.status),
|
|
345
|
+
image: container.image?.name || '-',
|
|
346
|
+
tag: container.image?.tag || '-',
|
|
347
|
+
created: container.created_at ? formatRelativeTime(container.created_at) : '',
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
table.printTable();
|
|
351
|
+
const showing = Math.min(pagination.limit, containers.length);
|
|
352
|
+
if (pagination.has_more) {
|
|
353
|
+
const nextOffset = pagination.offset + pagination.limit;
|
|
354
|
+
console.log(`\nShowing ${showing} of ${pagination.total_count} containers. ` +
|
|
355
|
+
chalk.dim(`Use --offset ${nextOffset} to see more.`));
|
|
356
|
+
}
|
|
357
|
+
else if (pagination.total_count > showing) {
|
|
358
|
+
console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} containers`));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Format a container image list as a human-readable table.
|
|
363
|
+
* Used by container-image list command.
|
|
364
|
+
*/
|
|
365
|
+
export function formatContainerImageTable(images, pagination) {
|
|
366
|
+
if (images.length === 0) {
|
|
367
|
+
console.log(chalk.dim('No container images found'));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const table = new Table({
|
|
371
|
+
columns: [
|
|
372
|
+
{ name: 'id', title: 'ID', alignment: 'left' },
|
|
373
|
+
{ name: 'name', title: 'NAME', alignment: 'left' },
|
|
374
|
+
{ name: 'image', title: 'IMAGE', alignment: 'left' },
|
|
375
|
+
{ name: 'tag', title: 'TAG', alignment: 'left' },
|
|
376
|
+
{ name: 'public', title: 'PUBLIC', alignment: 'left' },
|
|
377
|
+
],
|
|
378
|
+
});
|
|
379
|
+
images.forEach((image) => {
|
|
380
|
+
table.addRow({
|
|
381
|
+
id: image.id,
|
|
382
|
+
name: image.name,
|
|
383
|
+
image: image.image_name,
|
|
384
|
+
tag: image.tag,
|
|
385
|
+
public: image.is_public ? chalk.green('yes') : chalk.dim('no'),
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
table.printTable();
|
|
389
|
+
const showing = Math.min(pagination.limit, images.length);
|
|
390
|
+
if (pagination.has_more) {
|
|
391
|
+
const nextOffset = pagination.offset + pagination.limit;
|
|
392
|
+
console.log(`\nShowing ${showing} of ${pagination.total_count} images. ` +
|
|
393
|
+
chalk.dim(`Use --offset ${nextOffset} to see more.`));
|
|
394
|
+
}
|
|
395
|
+
else if (pagination.total_count > showing) {
|
|
396
|
+
console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} images`));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Derive a human-readable event type label from entity_type.
|
|
401
|
+
*/
|
|
402
|
+
function formatEventType(entityType) {
|
|
403
|
+
if (entityType === 'EntContainerEventCreate')
|
|
404
|
+
return 'create';
|
|
405
|
+
if (entityType === 'EntContainerEventDestroy')
|
|
406
|
+
return 'destroy';
|
|
407
|
+
if (entityType === 'EntContainerEventCommand')
|
|
408
|
+
return 'command';
|
|
409
|
+
return entityType;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Format a container event list as a human-readable table.
|
|
413
|
+
* Used by container events command.
|
|
414
|
+
*/
|
|
415
|
+
export function formatContainerEventTable(events, pagination) {
|
|
416
|
+
if (events.length === 0) {
|
|
417
|
+
console.log(chalk.dim('No events found'));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const table = new Table({
|
|
421
|
+
columns: [
|
|
422
|
+
{ name: 'id', title: 'ID', alignment: 'left' },
|
|
423
|
+
{ name: 'type', title: 'TYPE', alignment: 'left', color: 'cyan' },
|
|
424
|
+
{ name: 'status', title: 'STATUS', alignment: 'left' },
|
|
425
|
+
{ name: 'actor', title: 'ACTOR', alignment: 'left' },
|
|
426
|
+
{ name: 'detail', title: 'DETAIL', alignment: 'left' },
|
|
427
|
+
{ name: 'created', title: 'CREATED', alignment: 'left' },
|
|
428
|
+
],
|
|
429
|
+
});
|
|
430
|
+
events.forEach((event) => {
|
|
431
|
+
const statusColor = event.status === 'SUCCESS'
|
|
432
|
+
? chalk.green
|
|
433
|
+
: event.status === 'ERROR'
|
|
434
|
+
? chalk.red
|
|
435
|
+
: chalk.yellow;
|
|
436
|
+
let detail = '';
|
|
437
|
+
if (event.entity_type === 'EntContainerEventCommand') {
|
|
438
|
+
detail = truncate(event.command, 30);
|
|
439
|
+
}
|
|
440
|
+
else if (event.status === 'ERROR' && event.error) {
|
|
441
|
+
detail = truncate(event.error, 30);
|
|
442
|
+
}
|
|
443
|
+
table.addRow({
|
|
444
|
+
id: event.id,
|
|
445
|
+
type: formatEventType(event.entity_type),
|
|
446
|
+
status: statusColor(event.status),
|
|
447
|
+
actor: event.actor?.name || event.actor?.full_name || '-',
|
|
448
|
+
detail,
|
|
449
|
+
created: event.created_at ? formatRelativeTime(event.created_at) : '',
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
table.printTable();
|
|
453
|
+
const showing = Math.min(pagination.limit, events.length);
|
|
454
|
+
if (pagination.has_more) {
|
|
455
|
+
const nextOffset = pagination.offset + pagination.limit;
|
|
456
|
+
console.log(`\nShowing ${showing} of ${pagination.total_count} events. ` +
|
|
457
|
+
chalk.dim(`Use --offset ${nextOffset} to see more.`));
|
|
458
|
+
}
|
|
459
|
+
else if (pagination.total_count > showing) {
|
|
460
|
+
console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} events`));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Format job steps as a human-readable table.
|
|
465
|
+
* Used by job get command.
|
|
466
|
+
*/
|
|
467
|
+
export function formatJobStepTable(steps) {
|
|
468
|
+
if (steps.length === 0) {
|
|
469
|
+
console.log(chalk.dim('No steps found'));
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const table = new Table({
|
|
473
|
+
columns: [
|
|
474
|
+
{ name: 'name', title: 'STEP', alignment: 'left' },
|
|
475
|
+
{ name: 'status', title: 'STATUS', alignment: 'left' },
|
|
476
|
+
{ name: 'started', title: 'STARTED', alignment: 'left' },
|
|
477
|
+
{ name: 'completed', title: 'COMPLETED', alignment: 'left' },
|
|
478
|
+
],
|
|
479
|
+
});
|
|
480
|
+
steps.forEach((step) => {
|
|
481
|
+
const statusColor = step.status === 'SUCCEEDED'
|
|
482
|
+
? chalk.green
|
|
483
|
+
: step.status === 'FAILED' || step.status === 'ERRORED'
|
|
484
|
+
? chalk.red
|
|
485
|
+
: step.status === 'RUNNING'
|
|
486
|
+
? chalk.yellow
|
|
487
|
+
: step.status === 'SKIPPED'
|
|
488
|
+
? chalk.dim
|
|
489
|
+
: chalk.white;
|
|
490
|
+
table.addRow({
|
|
491
|
+
name: step.name,
|
|
492
|
+
status: statusColor(step.status),
|
|
493
|
+
started: step.started_at ? formatRelativeTime(step.started_at) : '-',
|
|
494
|
+
completed: step.completed_at ? formatRelativeTime(step.completed_at) : '-',
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
table.printTable();
|
|
498
|
+
}
|
|
316
499
|
/**
|
|
317
500
|
* Human-friendly output writer
|
|
318
501
|
*
|
|
@@ -85,6 +85,7 @@ export interface RuntimeWaitingEvent extends BaseEvent {
|
|
|
85
85
|
export interface RuntimeErrorEvent extends BaseEvent {
|
|
86
86
|
type: 'runtime_error';
|
|
87
87
|
content: string;
|
|
88
|
+
stack: string | null;
|
|
88
89
|
}
|
|
89
90
|
export interface RuntimeDoneEvent extends BaseEvent {
|
|
90
91
|
type: 'runtime_done';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface ColumnConfig {
|
|
2
|
+
name: string;
|
|
3
|
+
title: string;
|
|
4
|
+
alignment?: 'left';
|
|
5
|
+
color?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Minimal table renderer using string-width@7 for ANSI-aware column sizing.
|
|
9
|
+
* Handles OSC8 hyperlink sequences correctly (string-width uses strip-ansi@7).
|
|
10
|
+
*/
|
|
11
|
+
export declare class Table {
|
|
12
|
+
private columns;
|
|
13
|
+
private rows;
|
|
14
|
+
constructor(config: {
|
|
15
|
+
columns: ColumnConfig[];
|
|
16
|
+
});
|
|
17
|
+
addRow(row: Record<string, string>): void;
|
|
18
|
+
render(): string;
|
|
19
|
+
printTable(): void;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import stringWidth from 'string-width';
|
|
5
|
+
/**
|
|
6
|
+
* Minimal table renderer using string-width@7 for ANSI-aware column sizing.
|
|
7
|
+
* Handles OSC8 hyperlink sequences correctly (string-width uses strip-ansi@7).
|
|
8
|
+
*/
|
|
9
|
+
export class Table {
|
|
10
|
+
columns;
|
|
11
|
+
rows = [];
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.columns = config.columns;
|
|
14
|
+
}
|
|
15
|
+
addRow(row) {
|
|
16
|
+
const sanitized = {};
|
|
17
|
+
for (const [k, v] of Object.entries(row)) {
|
|
18
|
+
sanitized[k] = v.replace(/[\r\n]+/g, ' ').trim();
|
|
19
|
+
}
|
|
20
|
+
this.rows.push(sanitized);
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
const widths = this.columns.map((col) => {
|
|
24
|
+
const headerWidth = stringWidth(col.title);
|
|
25
|
+
const maxData = this.rows.reduce((max, row) => Math.max(max, stringWidth(row[col.name] ?? '')), 0);
|
|
26
|
+
return Math.max(headerWidth, maxData);
|
|
27
|
+
});
|
|
28
|
+
const pad = (text, width) => {
|
|
29
|
+
const visible = stringWidth(text);
|
|
30
|
+
return visible >= width ? text : text + ' '.repeat(width - visible);
|
|
31
|
+
};
|
|
32
|
+
const top = '┌' + widths.map((w) => '─'.repeat(w + 2)).join('┬') + '┐';
|
|
33
|
+
const mid = '├' + widths.map((w) => '─'.repeat(w + 2)).join('┼') + '┤';
|
|
34
|
+
const bot = '└' + widths.map((w) => '─'.repeat(w + 2)).join('┴') + '┘';
|
|
35
|
+
const header = '│' +
|
|
36
|
+
this.columns
|
|
37
|
+
.map((col, i) => ' ' + chalk.bold(pad(col.title, widths[i])) + ' ')
|
|
38
|
+
.join('│') +
|
|
39
|
+
'│';
|
|
40
|
+
const dataRows = this.rows.map((row) => '│' +
|
|
41
|
+
this.columns
|
|
42
|
+
.map((col, i) => {
|
|
43
|
+
const val = row[col.name] ?? '';
|
|
44
|
+
const padded = pad(val, widths[i]);
|
|
45
|
+
if (col.color && col.color in chalk) {
|
|
46
|
+
return (' ' +
|
|
47
|
+
chalk[col.color](padded) +
|
|
48
|
+
' ');
|
|
49
|
+
}
|
|
50
|
+
return ' ' + padded + ' ';
|
|
51
|
+
})
|
|
52
|
+
.join('│') +
|
|
53
|
+
'│');
|
|
54
|
+
return [top, header, mid, ...dataRows, bot].join('\n');
|
|
55
|
+
}
|
|
56
|
+
printTable() {
|
|
57
|
+
console.log(this.render());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=table.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guildai/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"description": "Guild.ai CLI - Build, test, and deploy AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -59,9 +59,7 @@
|
|
|
59
59
|
"@napi-rs/keyring": "^1.2.0",
|
|
60
60
|
"axios": "^1.13.2",
|
|
61
61
|
"chalk": "^5.3.0",
|
|
62
|
-
"cli-table3": "^0.6.5",
|
|
63
62
|
"commander": "^12.1.0",
|
|
64
|
-
"console-table-printer": "^2.15.0",
|
|
65
63
|
"dotenv": "^16.4.7",
|
|
66
64
|
"execa": "^9.6.1",
|
|
67
65
|
"ink": "^5.0.1",
|
|
@@ -76,6 +74,7 @@
|
|
|
76
74
|
"react": "^18.3.1",
|
|
77
75
|
"sharp": "^0.34.5",
|
|
78
76
|
"shell-quote": "^1.8.2",
|
|
77
|
+
"string-width": "^7.2.0",
|
|
79
78
|
"svg-pathdata": "^8.0.0",
|
|
80
79
|
"svgdom": "^0.1.22",
|
|
81
80
|
"ws": "^8.18.0",
|