@guildai/cli 0.7.0 → 0.8.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/commands/agent/chat.js +29 -62
- package/dist/commands/agent/clone.js +1 -1
- package/dist/commands/agent/code.js +1 -1
- package/dist/commands/agent/fork.js +2 -2
- package/dist/commands/agent/get.js +1 -1
- package/dist/commands/agent/grep.js +2 -2
- package/dist/commands/agent/init.js +11 -4
- package/dist/commands/agent/list.js +9 -1
- package/dist/commands/agent/publish.js +1 -1
- package/dist/commands/agent/revalidate.js +1 -1
- package/dist/commands/agent/save.js +23 -2
- package/dist/commands/agent/test.js +91 -92
- package/dist/commands/agent/unpublish.js +1 -1
- package/dist/commands/agent/update.js +1 -1
- package/dist/commands/agent/versions.js +1 -1
- package/dist/commands/agent/workspaces.js +1 -1
- package/dist/commands/chat.js +28 -15
- package/dist/commands/config/list.js +2 -2
- package/dist/commands/integration/operation/create.js +2 -2
- package/dist/commands/integration/operation/list.js +2 -2
- package/dist/commands/integration/update.js +1 -1
- package/dist/commands/integration/version/get.js +2 -2
- package/dist/commands/integration/version/publish.js +2 -2
- package/dist/commands/integration/version/test.js +2 -2
- package/dist/commands/session/events.js +7 -3
- package/dist/commands/session/tasks.js +8 -2
- package/dist/commands/workspace/get.js +1 -1
- package/dist/commands/workspace/list.js +28 -6
- package/dist/commands/workspace/select.js +40 -9
- package/dist/components/TaskView.js +2 -2
- package/dist/lib/agent-helpers.d.ts +74 -2
- package/dist/lib/agent-helpers.js +222 -8
- package/dist/lib/alternate-screen.js +2 -0
- package/dist/lib/api-client.js +2 -1
- package/dist/lib/api-types.d.ts +37 -0
- package/dist/lib/config.d.ts +3 -0
- package/dist/lib/config.js +33 -0
- package/dist/lib/output.d.ts +6 -1
- package/dist/lib/output.js +50 -0
- package/dist/lib/session-events.d.ts +1 -1
- package/dist/lib/session-events.js +5 -3
- package/dist/lib/session-polling.d.ts +8 -0
- package/dist/lib/session-polling.js +49 -0
- package/dist/lib/spinners.js +4 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ export function createAgentUnpublishCommand() {
|
|
|
9
9
|
const cmd = new Command('unpublish');
|
|
10
10
|
cmd
|
|
11
11
|
.description('Unpublish the latest published version of an agent')
|
|
12
|
-
.argument('[identifier]', 'Agent ID or full name (e.g., owner
|
|
12
|
+
.argument('[identifier]', 'Agent ID or full name (e.g., owner~agent-name)')
|
|
13
13
|
.action(async (agentIdArg) => {
|
|
14
14
|
const output = createOutputWriter();
|
|
15
15
|
try {
|
|
@@ -26,7 +26,7 @@ export function createAgentUpdateCommand() {
|
|
|
26
26
|
const cmd = new Command('update');
|
|
27
27
|
cmd
|
|
28
28
|
.description('Update agent properties (visibility)')
|
|
29
|
-
.argument('[identifier]', 'Agent ID or full name (e.g., owner
|
|
29
|
+
.argument('[identifier]', 'Agent ID or full name (e.g., owner~agent-name)')
|
|
30
30
|
.option('--public', 'Make the agent public (visible to everyone)')
|
|
31
31
|
.option('--private', 'Make the agent private (only visible to owner)')
|
|
32
32
|
.option('--yes', 'Skip confirmation prompt for visibility changes')
|
|
@@ -11,7 +11,7 @@ export function createAgentVersionsCommand() {
|
|
|
11
11
|
const cmd = new Command('versions');
|
|
12
12
|
cmd
|
|
13
13
|
.description('List all versions of an agent')
|
|
14
|
-
.argument('[identifier]', 'Agent ID or full name (e.g., owner
|
|
14
|
+
.argument('[identifier]', 'Agent ID or full name (e.g., owner~agent-name)')
|
|
15
15
|
.option('--limit <number>', 'Maximum number of versions to return', '20')
|
|
16
16
|
.option('--offset <number>', 'Number of versions to skip', '0')
|
|
17
17
|
.action(async (agentIdArg, options) => {
|
|
@@ -11,7 +11,7 @@ export function createAgentWorkspacesCommand() {
|
|
|
11
11
|
const cmd = new Command('workspaces');
|
|
12
12
|
cmd
|
|
13
13
|
.description('List workspaces that use an agent')
|
|
14
|
-
.argument('[identifier]', 'Agent ID or full name (e.g., owner
|
|
14
|
+
.argument('[identifier]', 'Agent ID or full name (e.g., owner~agent-name)')
|
|
15
15
|
.option('--limit <number>', 'Maximum number of workspaces to return', '20')
|
|
16
16
|
.option('--offset <number>', 'Number of workspaces to skip', '0')
|
|
17
17
|
.action(async (agentIdArg, options) => {
|
package/dist/commands/chat.js
CHANGED
|
@@ -13,7 +13,7 @@ import chalk from 'chalk';
|
|
|
13
13
|
import { readFileSync } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
|
-
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName,
|
|
16
|
+
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, } from '../lib/session-events.js';
|
|
17
17
|
import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
|
|
18
18
|
import { DEFAULT_EVENT_TYPES, parseEventFilter, shouldShowEvent, } from '../lib/event-filter.js';
|
|
19
19
|
import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
|
|
@@ -1087,24 +1087,37 @@ export function createChatCommand() {
|
|
|
1087
1087
|
else {
|
|
1088
1088
|
inactivityCounter++;
|
|
1089
1089
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
const targetAgent = options.agent || 'assistant';
|
|
1095
|
-
const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' &&
|
|
1096
|
-
matchesAgent(e.task?.agent, targetAgent) &&
|
|
1097
|
-
!e.task?.parent_task_id // Root task has no parent
|
|
1098
|
-
);
|
|
1099
|
-
// Also check for agent_notification_message events from the root agent
|
|
1100
|
-
const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
1101
|
-
matchesAgent(e.task?.agent, targetAgent) &&
|
|
1102
|
-
!e.task?.parent_task_id);
|
|
1090
|
+
const isRootTask = (e) => !e.task?.parent_task;
|
|
1091
|
+
const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' && isRootTask(e));
|
|
1092
|
+
const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' && isRootTask(e));
|
|
1093
|
+
const hasRootTaskError = allEvents.some((e) => e.type === 'runtime_error' && isRootTask(e));
|
|
1103
1094
|
// Check for a ui_prompt request... that ends the game.
|
|
1104
1095
|
const hasUIPromptMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
1105
1096
|
e.task?.tool_name === 'ui_prompt');
|
|
1097
|
+
if (hasRootTaskError) {
|
|
1098
|
+
debug('Found error event from root agent, exiting --once mode');
|
|
1099
|
+
const errorEvents = allEvents.filter((e) => e.type === 'runtime_error' || e.type === 'agent_notification_error');
|
|
1100
|
+
if (errorEvents.length > 0 && !options.mode) {
|
|
1101
|
+
const lastError = errorEvents[errorEvents.length - 1];
|
|
1102
|
+
const content = lastError.content;
|
|
1103
|
+
if (content?.data) {
|
|
1104
|
+
console.error(chalk.red(`Error: ${content.data}`));
|
|
1105
|
+
}
|
|
1106
|
+
else {
|
|
1107
|
+
console.error(chalk.red('Agent failed to start'));
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
else if (options.mode === 'json') {
|
|
1111
|
+
console.log(JSON.stringify({
|
|
1112
|
+
session_id: session.id,
|
|
1113
|
+
events: allEvents,
|
|
1114
|
+
error: true,
|
|
1115
|
+
}, null, 2));
|
|
1116
|
+
}
|
|
1117
|
+
process.exit(1);
|
|
1118
|
+
}
|
|
1106
1119
|
if (hasRootTaskDone || hasAgentMessage || hasUIPromptMessage) {
|
|
1107
|
-
debug(
|
|
1120
|
+
debug('Found completion event from root agent, exiting --once mode');
|
|
1108
1121
|
await outputOnceResult(session.id, allEvents, options.mode);
|
|
1109
1122
|
process.exit(0);
|
|
1110
1123
|
}
|
|
@@ -27,13 +27,13 @@ export function createConfigListCommand() {
|
|
|
27
27
|
output.progress(chalk.dim(' guild workspace select'));
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
if (hasGlobal) {
|
|
30
|
+
if (config.global && hasGlobal) {
|
|
31
31
|
output.progress(chalk.bold('Global config') + chalk.dim(' (~/.guild/config.json):'));
|
|
32
32
|
for (const [key, value] of Object.entries(config.global)) {
|
|
33
33
|
output.progress(` ${key}: ${chalk.cyan(String(value))}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
if (hasLocal) {
|
|
36
|
+
if (config.local && hasLocal) {
|
|
37
37
|
if (hasGlobal)
|
|
38
38
|
output.progress('');
|
|
39
39
|
output.progress(chalk.bold('Local config') + chalk.dim(' (guild.json):'));
|
|
@@ -17,7 +17,7 @@ export function createIntegrationOperationCreateCommand() {
|
|
|
17
17
|
cmd
|
|
18
18
|
.description('Create operation(s) for an integration version')
|
|
19
19
|
.argument('<id_or_name>', 'Integration ID or name (owner~name)')
|
|
20
|
-
.option('--version <semver>', 'Specific version, e.g. 1.0.0')
|
|
20
|
+
.option('--version-number <semver>', 'Specific version, e.g. 1.0.0')
|
|
21
21
|
.option('--operation <name>', 'Operation identifier, e.g. list_users')
|
|
22
22
|
.option('--method <method>', 'HTTP method: GET, POST, PUT, PATCH, DELETE')
|
|
23
23
|
.option('--path <path>', 'REST path, e.g. /{owner}/{repo}/issues')
|
|
@@ -35,7 +35,7 @@ export function createIntegrationOperationCreateCommand() {
|
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
const client = new GuildAPIClient();
|
|
38
|
-
const versionId = await resolveVersionId(client, identifier, options.
|
|
38
|
+
const versionId = await resolveVersionId(client, identifier, options.versionNumber);
|
|
39
39
|
if (options.openapi) {
|
|
40
40
|
// OpenAPI mode
|
|
41
41
|
if (!existsSync(options.openapi)) {
|
|
@@ -14,7 +14,7 @@ export function createIntegrationOperationListCommand() {
|
|
|
14
14
|
cmd
|
|
15
15
|
.description('List operations for an integration version')
|
|
16
16
|
.argument('<id_or_name>', 'Integration ID or name (owner~name)')
|
|
17
|
-
.option('--version <semver>', 'Specific version, e.g. 1.0.0')
|
|
17
|
+
.option('--version-number <semver>', 'Specific version, e.g. 1.0.0')
|
|
18
18
|
.option('--limit <number>', 'Number of results to return', '100')
|
|
19
19
|
.option('--offset <number>', 'Offset for pagination', '0')
|
|
20
20
|
.action(async (identifier, options) => {
|
|
@@ -26,7 +26,7 @@ export function createIntegrationOperationListCommand() {
|
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
28
|
const client = new GuildAPIClient();
|
|
29
|
-
const versionId = await resolveVersionId(client, identifier, options.
|
|
29
|
+
const versionId = await resolveVersionId(client, identifier, options.versionNumber);
|
|
30
30
|
const params = new URLSearchParams();
|
|
31
31
|
params.append('limit', options.limit);
|
|
32
32
|
params.append('offset', options.offset);
|
|
@@ -63,7 +63,7 @@ export function createIntegrationUpdateCommand() {
|
|
|
63
63
|
hasUpdates = true;
|
|
64
64
|
}
|
|
65
65
|
if (options.baseUrl !== undefined) {
|
|
66
|
-
body.protocol_config = { base_url: options.baseUrl };
|
|
66
|
+
body.protocol_config = { protocol: 'REST', base_url: options.baseUrl };
|
|
67
67
|
hasUpdates = true;
|
|
68
68
|
}
|
|
69
69
|
// Auth config updates need the discriminator
|
|
@@ -34,7 +34,7 @@ export function createIntegrationVersionGetCommand() {
|
|
|
34
34
|
cmd
|
|
35
35
|
.description('Get version details')
|
|
36
36
|
.argument('<id_or_name>', 'Integration ID or name (owner~name)')
|
|
37
|
-
.option('--version <semver>', 'Specific version, e.g. 1.0.0')
|
|
37
|
+
.option('--version-number <semver>', 'Specific version, e.g. 1.0.0')
|
|
38
38
|
.action(async (identifier, options) => {
|
|
39
39
|
const output = createOutputWriter();
|
|
40
40
|
try {
|
|
@@ -44,7 +44,7 @@ export function createIntegrationVersionGetCommand() {
|
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
const client = new GuildAPIClient();
|
|
47
|
-
const versionId = await resolveVersionId(client, identifier, options.
|
|
47
|
+
const versionId = await resolveVersionId(client, identifier, options.versionNumber);
|
|
48
48
|
const response = await client.get(`/integration_versions/${versionId}`);
|
|
49
49
|
if (getOutputMode() === 'json') {
|
|
50
50
|
output.data(response);
|
|
@@ -23,7 +23,7 @@ export function createIntegrationVersionPublishCommand() {
|
|
|
23
23
|
cmd
|
|
24
24
|
.description('Publish a built version')
|
|
25
25
|
.argument('<id_or_name>', 'Integration ID or name (owner~name)')
|
|
26
|
-
.option('--version <semver>', 'Specific version to publish, e.g. 1.0.0')
|
|
26
|
+
.option('--version-number <semver>', 'Specific version to publish, e.g. 1.0.0')
|
|
27
27
|
.action(async (identifier, options) => {
|
|
28
28
|
const output = createOutputWriter();
|
|
29
29
|
try {
|
|
@@ -33,7 +33,7 @@ export function createIntegrationVersionPublishCommand() {
|
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
const client = new GuildAPIClient();
|
|
36
|
-
const versionId = await resolveVersionId(client, identifier, options.
|
|
36
|
+
const versionId = await resolveVersionId(client, identifier, options.versionNumber);
|
|
37
37
|
// Get current version info for display
|
|
38
38
|
const currentVersion = await client.get(`/integration_versions/${versionId}`);
|
|
39
39
|
const versionDisplay = currentVersion.version_number || versionId;
|
|
@@ -22,7 +22,7 @@ export function createIntegrationVersionTestCommand() {
|
|
|
22
22
|
cmd
|
|
23
23
|
.description('Test an endpoint invocation')
|
|
24
24
|
.argument('<id_or_name>', 'Integration ID or name (owner~name)')
|
|
25
|
-
.option('--version <semver>', 'Specific version, e.g. 1.0.0')
|
|
25
|
+
.option('--version-number <semver>', 'Specific version, e.g. 1.0.0')
|
|
26
26
|
.requiredOption('--operation <name>', 'Operation to test, e.g. list_users')
|
|
27
27
|
.option('--account <name>', 'Account name to resolve credential from')
|
|
28
28
|
.option('--input-path <json>', 'JSON object of path parameters')
|
|
@@ -50,7 +50,7 @@ export function createIntegrationVersionTestCommand() {
|
|
|
50
50
|
body.input_body = parseJsonFlag(options.inputBody, '--input-body');
|
|
51
51
|
}
|
|
52
52
|
const client = new GuildAPIClient();
|
|
53
|
-
const versionId = await resolveVersionId(client, identifier, options.
|
|
53
|
+
const versionId = await resolveVersionId(client, identifier, options.versionNumber);
|
|
54
54
|
if (options.account) {
|
|
55
55
|
const creds = await client.get(`/accounts/${options.account}/credentials?integration=${encodeURIComponent(identifier)}`);
|
|
56
56
|
if (creds.items.length === 0) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
5
|
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { parseEventFilter } from '../../lib/event-filter.js';
|
|
6
7
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
8
|
import { createOutputWriter } from '../../lib/output.js';
|
|
8
9
|
export function createSessionEventsCommand() {
|
|
@@ -10,7 +11,7 @@ export function createSessionEventsCommand() {
|
|
|
10
11
|
cmd
|
|
11
12
|
.description('List events in a session')
|
|
12
13
|
.argument('<session-id>', 'Session ID')
|
|
13
|
-
.option('--
|
|
14
|
+
.option('--events <types>', 'Event types to show. Shorthands: none, user, system, all, or comma-separated type names')
|
|
14
15
|
.option('--limit <number>', 'Number of results to return', '100')
|
|
15
16
|
.option('--offset <number>', 'Offset for pagination', '0')
|
|
16
17
|
.action(async (sessionId, options) => {
|
|
@@ -25,8 +26,11 @@ export function createSessionEventsCommand() {
|
|
|
25
26
|
const params = new URLSearchParams();
|
|
26
27
|
params.append('limit', options.limit);
|
|
27
28
|
params.append('offset', options.offset);
|
|
28
|
-
if (options.
|
|
29
|
-
|
|
29
|
+
if (options.events) {
|
|
30
|
+
const filter = parseEventFilter(options.events);
|
|
31
|
+
if (filter.size > 0) {
|
|
32
|
+
params.append('types', [...filter].join(','));
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
const response = await client.get(`/sessions/${sessionId}/events?${params.toString()}`);
|
|
32
36
|
output.data(response);
|
|
@@ -4,7 +4,8 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
5
|
import { getAuthToken } from '../../lib/auth.js';
|
|
6
6
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatTaskTable } from '../../lib/output.js';
|
|
8
9
|
export function createSessionTasksCommand() {
|
|
9
10
|
const cmd = new Command('tasks');
|
|
10
11
|
cmd
|
|
@@ -25,7 +26,12 @@ export function createSessionTasksCommand() {
|
|
|
25
26
|
params.append('limit', options.limit);
|
|
26
27
|
params.append('offset', options.offset);
|
|
27
28
|
const response = await client.get(`/sessions/${sessionId}/tasks?${params.toString()}`);
|
|
28
|
-
|
|
29
|
+
if (getOutputMode() === 'json') {
|
|
30
|
+
console.log(JSON.stringify(response, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
formatTaskTable(response.items, response.pagination);
|
|
34
|
+
}
|
|
29
35
|
}
|
|
30
36
|
catch (error) {
|
|
31
37
|
const formattedError = handleAxiosError(error);
|
|
@@ -8,7 +8,7 @@ export function createWorkspaceGetCommand() {
|
|
|
8
8
|
const cmd = new Command('get');
|
|
9
9
|
cmd
|
|
10
10
|
.description('Get workspace details')
|
|
11
|
-
.argument('<identifier>', 'Workspace ID or full name (e.g., owner
|
|
11
|
+
.argument('<identifier>', 'Workspace ID or full name (e.g., owner~workspace-name)')
|
|
12
12
|
.action(async (id) => {
|
|
13
13
|
const output = createOutputWriter();
|
|
14
14
|
try {
|
|
@@ -11,16 +11,38 @@ export function createWorkspaceListCommand() {
|
|
|
11
11
|
.description('List workspaces')
|
|
12
12
|
.option('--limit <number>', 'Number of results to return', '20')
|
|
13
13
|
.option('--offset <number>', 'Offset for pagination', '0')
|
|
14
|
+
.option('--owner <name>', 'Filter workspaces by owner name (case-insensitive)')
|
|
14
15
|
.action(async (options) => {
|
|
15
16
|
const output = createOutputWriter();
|
|
16
17
|
try {
|
|
17
18
|
const client = new GuildAPIClient();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
let response;
|
|
20
|
+
if (options.owner) {
|
|
21
|
+
// When filtering by owner, fetch ALL workspaces first so the
|
|
22
|
+
// client-side filter is applied to the complete dataset, not just
|
|
23
|
+
// one page. The backend's `filter` param only recognises "all" vs
|
|
24
|
+
// personal; owner-name filtering is not supported server-side.
|
|
25
|
+
const allWorkspaces = await client.fetchAll('/me/workspaces?filter=all');
|
|
26
|
+
const ownerLower = options.owner.toLowerCase();
|
|
27
|
+
const filtered = allWorkspaces.filter((w) => w.owner?.name?.toLowerCase() === ownerLower);
|
|
28
|
+
response = {
|
|
29
|
+
items: filtered,
|
|
30
|
+
pagination: {
|
|
31
|
+
total_count: filtered.length,
|
|
32
|
+
limit: filtered.length,
|
|
33
|
+
offset: 0,
|
|
34
|
+
has_more: false,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const params = new URLSearchParams();
|
|
40
|
+
// Use filter=all to get workspaces from all orgs the user is a member of.
|
|
41
|
+
params.append('filter', 'all');
|
|
42
|
+
params.append('limit', options.limit);
|
|
43
|
+
params.append('offset', options.offset);
|
|
44
|
+
response = await client.get(`/me/workspaces?${params.toString()}`);
|
|
45
|
+
}
|
|
24
46
|
if (getOutputMode() === 'json') {
|
|
25
47
|
console.log(JSON.stringify(response, null, 2));
|
|
26
48
|
}
|
|
@@ -35,8 +35,10 @@ export function createWorkspaceSelectCommand() {
|
|
|
35
35
|
cmd
|
|
36
36
|
.description('Select default workspace for agent testing')
|
|
37
37
|
.argument('[workspace]', 'Workspace name or ID to select directly')
|
|
38
|
-
.
|
|
38
|
+
.option('--owner <name>', 'Filter workspaces by owner name (case-insensitive)')
|
|
39
|
+
.action(async (workspaceArg, options = {}) => {
|
|
39
40
|
const output = createOutputWriter();
|
|
41
|
+
const ownerFilter = options.owner;
|
|
40
42
|
try {
|
|
41
43
|
const client = new GuildAPIClient();
|
|
42
44
|
// If a workspace argument was provided, use server-side search to find it
|
|
@@ -45,19 +47,34 @@ export function createWorkspaceSelectCommand() {
|
|
|
45
47
|
// The backend searches only the "name" column via ILIKE, so full_name (owner/name)
|
|
46
48
|
// and ID lookups may not return results. Extract just the name part for search,
|
|
47
49
|
// and if still no match, fall back to fetching the full list.
|
|
50
|
+
// Always use filter=all — the backend's `filter` param only recognises "all"
|
|
51
|
+
// vs personal; owner-name filtering is applied client-side below.
|
|
48
52
|
const searchTerm = workspaceArg.includes('/')
|
|
49
|
-
? workspaceArg.split('/').pop()
|
|
53
|
+
? (workspaceArg.split('/').pop() ?? workspaceArg)
|
|
50
54
|
: workspaceArg;
|
|
51
55
|
const searchResults = await client.get(`/me/workspaces?filter=all&search=${encodeURIComponent(searchTerm)}&limit=100`);
|
|
52
|
-
let
|
|
56
|
+
let candidates = searchResults.items;
|
|
57
|
+
// Apply client-side owner filter on search results
|
|
58
|
+
if (ownerFilter) {
|
|
59
|
+
candidates = candidates.filter((w) => w.owner?.name?.toLowerCase() === ownerFilter.toLowerCase());
|
|
60
|
+
}
|
|
53
61
|
// Search didn't find it (could be an ID lookup, or search term didn't match).
|
|
54
62
|
// Fall back to fetching all workspaces via pagination.
|
|
55
|
-
|
|
63
|
+
const directMatch = candidates.find((w) => matchesWorkspaceArg(w, workspaceArg));
|
|
64
|
+
if (!directMatch) {
|
|
56
65
|
const allWorkspaces = await client.fetchAll('/me/workspaces?filter=all');
|
|
57
|
-
|
|
66
|
+
candidates = ownerFilter
|
|
67
|
+
? allWorkspaces.filter((w) => w.owner?.name?.toLowerCase() === ownerFilter.toLowerCase())
|
|
68
|
+
: allWorkspaces;
|
|
58
69
|
}
|
|
70
|
+
const workspace = candidates.find((w) => matchesWorkspaceArg(w, workspaceArg));
|
|
59
71
|
if (!workspace) {
|
|
60
|
-
|
|
72
|
+
if (ownerFilter) {
|
|
73
|
+
output.error(`Workspace "${workspaceArg}" not found for owner "${ownerFilter}".`, 'Run: guild workspace list');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
output.error(`Workspace "${workspaceArg}" not found.`, 'Run: guild workspace list');
|
|
77
|
+
}
|
|
61
78
|
process.exit(1);
|
|
62
79
|
}
|
|
63
80
|
const target = await saveWorkspaceConfig(workspace.id, workspace.name);
|
|
@@ -73,10 +90,20 @@ export function createWorkspaceSelectCommand() {
|
|
|
73
90
|
output.error('Interactive mode requires a terminal.', 'Provide a workspace argument:\n guild workspace select <name|id>');
|
|
74
91
|
process.exit(1);
|
|
75
92
|
}
|
|
76
|
-
// Interactive mode: fetch all workspaces across all pages
|
|
77
|
-
|
|
93
|
+
// Interactive mode: fetch all workspaces across all pages.
|
|
94
|
+
// Always use filter=all; owner-name filtering is applied client-side
|
|
95
|
+
// (the backend's `filter` param only recognises "all" vs personal).
|
|
96
|
+
let workspaces = await client.fetchAll('/me/workspaces?filter=all');
|
|
97
|
+
if (ownerFilter) {
|
|
98
|
+
workspaces = workspaces.filter((w) => w.owner?.name?.toLowerCase() === ownerFilter.toLowerCase());
|
|
99
|
+
}
|
|
78
100
|
if (workspaces.length === 0) {
|
|
79
|
-
|
|
101
|
+
if (ownerFilter) {
|
|
102
|
+
output.error(`No workspaces found for owner "${ownerFilter}".`, 'Run: guild workspace list');
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
output.error('No workspaces found.', 'Create a workspace first:\n guild workspace create <name>');
|
|
106
|
+
}
|
|
80
107
|
process.exit(1);
|
|
81
108
|
}
|
|
82
109
|
// Resolve the currently selected workspace (if any)
|
|
@@ -101,6 +128,10 @@ export function createWorkspaceSelectCommand() {
|
|
|
101
128
|
},
|
|
102
129
|
});
|
|
103
130
|
const selectedWorkspace = workspaces.find((w) => w.id === selectedId);
|
|
131
|
+
if (!selectedWorkspace) {
|
|
132
|
+
output.error('Selected workspace not found');
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
104
135
|
const target = await saveWorkspaceConfig(selectedWorkspace.id, selectedWorkspace.name);
|
|
105
136
|
if (target === 'local') {
|
|
106
137
|
output.success(`Workspace set for this agent: ${formatWorkspaceDisplay(selectedWorkspace)}`);
|
|
@@ -87,7 +87,7 @@ function calculateTaskDepths(tasks) {
|
|
|
87
87
|
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
88
88
|
function getDepth(taskId) {
|
|
89
89
|
if (depthMap.has(taskId))
|
|
90
|
-
return depthMap.get(taskId);
|
|
90
|
+
return depthMap.get(taskId) ?? 0;
|
|
91
91
|
const task = taskMap.get(taskId);
|
|
92
92
|
const parentId = task?.parent_task?.id;
|
|
93
93
|
if (!parentId) {
|
|
@@ -167,7 +167,7 @@ export function TaskView({ tasks }) {
|
|
|
167
167
|
if (!tasksByParent.has(parentId)) {
|
|
168
168
|
tasksByParent.set(parentId, []);
|
|
169
169
|
}
|
|
170
|
-
tasksByParent.get(parentId)
|
|
170
|
+
tasksByParent.get(parentId)?.push(task);
|
|
171
171
|
});
|
|
172
172
|
// Process root tasks first, then children
|
|
173
173
|
const processedIds = new Set();
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { LocalConfig } from './guild-config.js';
|
|
2
2
|
import { GuildAPIClient } from './api-client.js';
|
|
3
|
+
import type { AgentVersion } from './api-types.js';
|
|
4
|
+
/** Thrown when the ephemeral version build times out or polling fails. */
|
|
5
|
+
export declare class BuildTimeoutError extends Error {
|
|
6
|
+
constructor(message?: string);
|
|
7
|
+
}
|
|
8
|
+
/** Thrown when the ephemeral version build fails validation. */
|
|
9
|
+
export declare class BuildFailedError extends Error {
|
|
10
|
+
readonly failedSteps: {
|
|
11
|
+
name: string;
|
|
12
|
+
content?: string;
|
|
13
|
+
}[];
|
|
14
|
+
constructor(failedSteps: {
|
|
15
|
+
name: string;
|
|
16
|
+
content?: string;
|
|
17
|
+
}[]);
|
|
18
|
+
}
|
|
3
19
|
/**
|
|
4
20
|
* Resolve an agent identifier to a fully-qualified form.
|
|
5
21
|
*
|
|
6
|
-
* If the identifier already contains
|
|
7
|
-
* Otherwise, resolve the default owner and prepend `owner.name
|
|
22
|
+
* If the identifier already contains `~` or is a UUID, return it verbatim.
|
|
23
|
+
* Otherwise, resolve the default owner and prepend `owner.name~`.
|
|
8
24
|
*/
|
|
9
25
|
export declare function resolveAgentRef(client: GuildAPIClient, identifier: string): Promise<string>;
|
|
10
26
|
/**
|
|
@@ -27,4 +43,60 @@ export declare function readAgentFiles(cwd: string): Promise<{
|
|
|
27
43
|
path: string;
|
|
28
44
|
content: string;
|
|
29
45
|
}[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Compute a deterministic hash of agent files.
|
|
48
|
+
* Sorts by path to ensure consistent ordering.
|
|
49
|
+
*/
|
|
50
|
+
export declare function hashAgentFiles(files: {
|
|
51
|
+
path: string;
|
|
52
|
+
content: string;
|
|
53
|
+
}[]): string;
|
|
54
|
+
/**
|
|
55
|
+
* Get or create an ephemeral version, skipping the build if files haven't
|
|
56
|
+
* changed since the last successful ephemeral build.
|
|
57
|
+
*
|
|
58
|
+
* Returns the version (cached or newly created), a cache hit flag, and the
|
|
59
|
+
* computed hash (used by buildEphemeralVersion to write the cache on success).
|
|
60
|
+
*/
|
|
61
|
+
export declare function getOrCreateEphemeral(client: GuildAPIClient, agentId: string, files: {
|
|
62
|
+
path: string;
|
|
63
|
+
content: string;
|
|
64
|
+
}[], cwd: string, summary: string, options?: {
|
|
65
|
+
noCache?: boolean;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
version: AgentVersion;
|
|
68
|
+
cached: boolean;
|
|
69
|
+
hash: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Build an ephemeral version end-to-end: get-or-create, poll for validation,
|
|
73
|
+
* cache on success, and report build failures.
|
|
74
|
+
*
|
|
75
|
+
* Consolidates the build/poll/cache pattern used by both `guild agent test`
|
|
76
|
+
* and `guild agent chat`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildEphemeralVersion(client: GuildAPIClient, agentId: string, files: {
|
|
79
|
+
path: string;
|
|
80
|
+
content: string;
|
|
81
|
+
}[], cwd: string, summary: string, options?: {
|
|
82
|
+
noCache?: boolean;
|
|
83
|
+
}): Promise<{
|
|
84
|
+
version: AgentVersion;
|
|
85
|
+
cached: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
/** Thrown when the specified bundle file cannot be found on disk. */
|
|
88
|
+
export declare class BundleNotFoundError extends Error {
|
|
89
|
+
constructor(filePath: string);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Upload a pre-built bundle as an ephemeral version.
|
|
93
|
+
*
|
|
94
|
+
* The bundle file must be gzip+base64 encoded (the output of
|
|
95
|
+
* `esbuild ... | gzip | base64`). Source files are included for
|
|
96
|
+
* dashboard viewing, but the server skips its own build step because
|
|
97
|
+
* the ready-to-run artifact is already provided.
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildBundledVersion(client: GuildAPIClient, agentId: string, bundlePath: string, cwd: string, summary: string): Promise<{
|
|
100
|
+
version: AgentVersion;
|
|
101
|
+
}>;
|
|
30
102
|
//# sourceMappingURL=agent-helpers.d.ts.map
|