@guildai/cli 0.9.1 → 0.11.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 +10 -7
- package/dist/commands/agent/clone.js +2 -0
- package/dist/commands/agent/fork.js +3 -0
- package/dist/commands/agent/init.js +58 -44
- package/dist/commands/agent/list.js +5 -4
- package/dist/commands/agent/logs.d.ts +3 -0
- package/dist/commands/agent/logs.js +62 -0
- package/dist/commands/agent/owners.js +3 -3
- package/dist/commands/agent/pull.js +8 -12
- package/dist/commands/agent/save.js +5 -6
- package/dist/commands/agent/search.js +5 -4
- package/dist/commands/agent/test.js +9 -6
- package/dist/commands/agent/update.js +9 -1
- package/dist/commands/agent/versions.js +5 -4
- package/dist/commands/agent/workspaces.js +5 -4
- package/dist/commands/auth/login.js +1 -3
- package/dist/commands/chat.d.ts +9 -0
- package/dist/commands/chat.js +136 -32
- package/dist/commands/config/get.js +4 -4
- package/dist/commands/config/list.js +2 -3
- package/dist/commands/config/path.js +2 -3
- package/dist/commands/config/set.js +12 -12
- package/dist/commands/credentials/endpoint-list.js +5 -4
- package/dist/commands/credentials/list.js +5 -4
- package/dist/commands/credentials/policy-list.js +5 -4
- package/dist/commands/doctor.js +5 -5
- package/dist/commands/integration/connect.js +2 -2
- package/dist/commands/integration/create.js +2 -2
- package/dist/commands/integration/get.js +2 -2
- package/dist/commands/integration/list.js +5 -4
- package/dist/commands/integration/operation/create.js +4 -4
- package/dist/commands/integration/operation/list.js +5 -4
- package/dist/commands/integration/update.js +2 -2
- package/dist/commands/integration/version/build.js +2 -2
- package/dist/commands/integration/version/create.js +2 -2
- package/dist/commands/integration/version/get.js +2 -2
- package/dist/commands/integration/version/list.js +5 -4
- package/dist/commands/integration/version/publish.js +2 -2
- package/dist/commands/integration/version/test.js +2 -2
- package/dist/commands/job/get.js +2 -2
- package/dist/commands/session/create.js +1 -1
- package/dist/commands/session/events.js +3 -2
- package/dist/commands/session/list.js +5 -4
- package/dist/commands/session/tasks.js +5 -4
- package/dist/commands/setup.d.ts +16 -0
- package/dist/commands/setup.js +76 -46
- package/dist/commands/trigger/list.js +5 -4
- package/dist/commands/trigger/sessions.js +3 -2
- package/dist/commands/workspace/agent/list.js +5 -4
- package/dist/commands/workspace/context/list.js +5 -4
- package/dist/commands/workspace/list.js +5 -4
- package/dist/index.js +15 -4
- package/dist/lib/api-types.d.ts +4 -0
- package/dist/lib/api-types.js +4 -0
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +2 -2
- package/dist/lib/output-mode.d.ts +9 -2
- package/dist/lib/output-mode.js +23 -2
- package/dist/lib/output.d.ts +7 -1
- package/dist/lib/output.js +36 -5
- package/dist/lib/owner-helpers.d.ts +3 -0
- package/dist/lib/owner-helpers.js +17 -10
- package/dist/lib/session-events.d.ts +13 -2
- package/dist/lib/session-events.js +15 -1
- package/dist/lib/session-polling.js +9 -3
- package/dist/lib/session-resume.d.ts +15 -1
- package/dist/lib/session-resume.js +149 -16
- package/dist/lib/splash.js +3 -2
- package/dist/lib/stdin.d.ts +5 -1
- package/dist/lib/stdin.js +8 -1
- package/dist/lib/version-helpers.js +24 -8
- package/package.json +1 -1
|
@@ -6,14 +6,15 @@ import { getGuildcoreUrl } from '../../lib/config.js';
|
|
|
6
6
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
7
7
|
import { getAgentId, resolveAgentRef } from '../../lib/agent-helpers.js';
|
|
8
8
|
import { createOutputWriter, formatWorkspaceTable } from '../../lib/output.js';
|
|
9
|
-
import {
|
|
9
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
10
|
+
import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
|
|
10
11
|
export function createAgentWorkspacesCommand() {
|
|
11
12
|
const cmd = new Command('workspaces');
|
|
12
13
|
cmd
|
|
13
14
|
.description('List workspaces that use an agent')
|
|
14
15
|
.argument('[identifier]', 'Agent ID or full name (e.g., owner~agent-name)')
|
|
15
|
-
.option('--limit <number>',
|
|
16
|
-
.option('--offset <number>', 'Number of workspaces to skip', '0')
|
|
16
|
+
.option('--limit <number>', `Maximum number of workspaces to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
|
|
17
|
+
.option('--offset <number>', 'Number of workspaces to skip (default: 0)', '0')
|
|
17
18
|
.action(async (agentIdArg, options) => {
|
|
18
19
|
const output = createOutputWriter();
|
|
19
20
|
const { agentId } = await getAgentId(agentIdArg);
|
|
@@ -24,7 +25,7 @@ export function createAgentWorkspacesCommand() {
|
|
|
24
25
|
try {
|
|
25
26
|
const resolvedId = await resolveAgentRef(client, agentId);
|
|
26
27
|
const result = await client.get(`/agents/${resolvedId}/workspaces?limit=${limit}&offset=${offset}`);
|
|
27
|
-
if (
|
|
28
|
+
if (isMachineReadable()) {
|
|
28
29
|
output.data(result);
|
|
29
30
|
}
|
|
30
31
|
else {
|
|
@@ -11,11 +11,9 @@ export function createAuthLoginCommand() {
|
|
|
11
11
|
.description('Login to Guild.ai')
|
|
12
12
|
.option('--return-url <url>', 'Custom URL to redirect to after authentication')
|
|
13
13
|
.option('--return-label <text>', 'Friendly label for return button (e.g., "VSCode")')
|
|
14
|
-
.option('--non-interactive', 'Skip interactive prompts (for use with coding agents)')
|
|
15
14
|
.action(async (options) => {
|
|
16
15
|
const output = createOutputWriter();
|
|
17
|
-
const
|
|
18
|
-
const success = await login(options.returnUrl, options.returnLabel, nonInteractive);
|
|
16
|
+
const success = await login(options.returnUrl, options.returnLabel);
|
|
19
17
|
if (success) {
|
|
20
18
|
try {
|
|
21
19
|
await configureNpmrc();
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -2,6 +2,15 @@ import React from 'react';
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { GuildAPIClient } from '../lib/api-client.js';
|
|
4
4
|
import { SessionEvent, Session } from '../lib/session-events.js';
|
|
5
|
+
/** Thrown when no workspace is configured (no --workspace flag, no config). */
|
|
6
|
+
export declare class WorkspaceNotConfiguredError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
/** Thrown when the specified workspace ID is not found in the backend. */
|
|
10
|
+
export declare class WorkspaceNotFoundError extends Error {
|
|
11
|
+
readonly workspaceId: string;
|
|
12
|
+
constructor(workspaceId: string);
|
|
13
|
+
}
|
|
5
14
|
/**
|
|
6
15
|
* ChatApp wrapper component that shows splash animation during connection
|
|
7
16
|
* and then transitions to ChatUIWithConnection once connected.
|
package/dist/commands/chat.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import React, { useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { Box, Text, Static, render, useInput, useApp } from 'ink';
|
|
5
|
-
import { Command } from 'commander';
|
|
5
|
+
import { Command, Option } from 'commander';
|
|
6
6
|
import { getAuthToken } from '../lib/auth.js';
|
|
7
7
|
import { GuildAPIClient } from '../lib/api-client.js';
|
|
8
8
|
import { handleAxiosError, ErrorCodes, debug, isDebugMode, retry, } from '../lib/errors.js';
|
|
@@ -13,8 +13,8 @@ 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, getAgentName, getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
|
|
17
|
-
import { printResumeHint, fetchSession, fetchSessionEvents,
|
|
16
|
+
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, getAgentNotificationText, applyResponseStreamText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
|
|
17
|
+
import { printResumeHint, fetchSession, fetchSessionEvents, prepareSessionResumeDisplay, } 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';
|
|
20
20
|
import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
|
|
@@ -31,6 +31,28 @@ import { getOutputMode, isQuietMode } from '../lib/output-mode.js';
|
|
|
31
31
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
32
|
// Read version from package.json
|
|
33
33
|
const packageJson = JSON.parse(readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Workspace error types
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/** Thrown when no workspace is configured (no --workspace flag, no config). */
|
|
38
|
+
export class WorkspaceNotConfiguredError extends Error {
|
|
39
|
+
constructor() {
|
|
40
|
+
super('No workspace configured.');
|
|
41
|
+
this.name = 'WorkspaceNotConfiguredError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Thrown when the specified workspace ID is not found in the backend. */
|
|
45
|
+
export class WorkspaceNotFoundError extends Error {
|
|
46
|
+
workspaceId;
|
|
47
|
+
constructor(workspaceId) {
|
|
48
|
+
super(`Workspace ${workspaceId} not found.`);
|
|
49
|
+
this.name = 'WorkspaceNotFoundError';
|
|
50
|
+
this.workspaceId = workspaceId;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** User-facing error messages for workspace resolution failures. */
|
|
54
|
+
const WORKSPACE_NOT_CONFIGURED_MSG = 'No workspace configured. Pass a --workspace <id_or_name> argument or run guild workspace select';
|
|
55
|
+
const WORKSPACE_NOT_FOUND_MSG = "The workspace doesn't exist.";
|
|
34
56
|
// Configure marked for terminal
|
|
35
57
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
58
|
marked.use(markedTerminal({}, { theme: {} }));
|
|
@@ -90,6 +112,11 @@ function renderAssistantMessage(text, taskName) {
|
|
|
90
112
|
const rendered = fixListItemMarkdown(marked.parse(text));
|
|
91
113
|
return `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
|
|
92
114
|
}
|
|
115
|
+
function applyResponseStreamContentsInSequence(contents) {
|
|
116
|
+
return [...contents]
|
|
117
|
+
.sort((left, right) => left.sequence - right.sequence)
|
|
118
|
+
.reduce((currentText, content) => applyResponseStreamText(currentText, content), '');
|
|
119
|
+
}
|
|
93
120
|
/**
|
|
94
121
|
* Output the result of a --once mode session.
|
|
95
122
|
* Handles both JSON and human-readable output formats.
|
|
@@ -102,9 +129,18 @@ async function outputOnceResult(sessionId, events, mode) {
|
|
|
102
129
|
const finalAgentMessages = events.filter((e) => e.type === 'agent_notification_message' &&
|
|
103
130
|
isRootTaskEvent(e) &&
|
|
104
131
|
!isResponseStreamEvent(e));
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
const responseStreamContents = new Map();
|
|
133
|
+
const doneStreamIds = [];
|
|
134
|
+
for (const event of events) {
|
|
135
|
+
if (!isResponseStreamEvent(event) || !isRootTaskEvent(event))
|
|
136
|
+
continue;
|
|
137
|
+
const streamContents = responseStreamContents.get(event.content.stream_id) ?? [];
|
|
138
|
+
streamContents.push(event.content);
|
|
139
|
+
responseStreamContents.set(event.content.stream_id, streamContents);
|
|
140
|
+
if (isDoneResponseStreamEvent(event)) {
|
|
141
|
+
doneStreamIds.push(event.content.stream_id);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
108
144
|
if (finalAgentMessages.length > 0) {
|
|
109
145
|
const messageContent = extractMessageText(getAgentNotificationText(finalAgentMessages[finalAgentMessages.length - 1]));
|
|
110
146
|
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
@@ -120,9 +156,12 @@ async function outputOnceResult(sessionId, events, mode) {
|
|
|
120
156
|
return;
|
|
121
157
|
}
|
|
122
158
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
159
|
+
const lastDoneStreamId = doneStreamIds[doneStreamIds.length - 1];
|
|
160
|
+
const streamFallbackText = lastDoneStreamId !== undefined
|
|
161
|
+
? applyResponseStreamContentsInSequence(responseStreamContents.get(lastDoneStreamId) ?? [])
|
|
162
|
+
: null;
|
|
163
|
+
if (streamFallbackText !== null) {
|
|
164
|
+
const rendered = fixListItemMarkdown(await marked(streamFallbackText));
|
|
126
165
|
console.log(rendered.trim());
|
|
127
166
|
}
|
|
128
167
|
}
|
|
@@ -264,8 +303,11 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
|
|
|
264
303
|
formattedError.code === ErrorCodes.AUTH_TOKEN_INVALID) {
|
|
265
304
|
format.error('Not authenticated. Run: guild auth login');
|
|
266
305
|
}
|
|
267
|
-
else if (
|
|
268
|
-
format.error(
|
|
306
|
+
else if (error instanceof WorkspaceNotConfiguredError) {
|
|
307
|
+
format.error(WORKSPACE_NOT_CONFIGURED_MSG);
|
|
308
|
+
}
|
|
309
|
+
else if (error instanceof WorkspaceNotFoundError) {
|
|
310
|
+
format.error(WORKSPACE_NOT_FOUND_MSG);
|
|
269
311
|
}
|
|
270
312
|
else if (details.includes('agent')) {
|
|
271
313
|
format.error('Agent not found in workspace.');
|
|
@@ -278,6 +320,7 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
|
|
|
278
320
|
};
|
|
279
321
|
connect();
|
|
280
322
|
}, [workspaceId, initialPrompt, versionId]);
|
|
323
|
+
const chatInstanceKey = `${connectedSession?.id ?? 'pending'}:${resumeEvents?.[resumeEvents.length - 1]?.id ?? 'live'}`;
|
|
281
324
|
// Render both splash and chat, but only show one at a time
|
|
282
325
|
// ChatUIWithConnection is always mounted (when connected) so it can stream events in background
|
|
283
326
|
return (React.createElement(React.Fragment, null,
|
|
@@ -291,7 +334,7 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
|
|
|
291
334
|
}
|
|
292
335
|
// If not connected yet, ignore escape (let connection complete)
|
|
293
336
|
} })),
|
|
294
|
-
phase === 'chat' && (React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand, eventFilter: eventFilter })),
|
|
337
|
+
phase === 'chat' && (React.createElement(ChatUIWithConnection, { key: chatInstanceKey, initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand, eventFilter: eventFilter })),
|
|
295
338
|
(phase === 'splash' || phase === 'finalizing') &&
|
|
296
339
|
connectedSession &&
|
|
297
340
|
connectedClient && (React.createElement(Box, { display: "none" },
|
|
@@ -311,9 +354,9 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
311
354
|
// Only include initial prompt when active (not during splash)
|
|
312
355
|
// Static component writes to stdout even with display="none"
|
|
313
356
|
// When resuming, show past events instead of initial prompt
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
357
|
+
const [resumeDisplay] = useState(() => resumeEvents ? prepareSessionResumeDisplay(resumeEvents) : null);
|
|
358
|
+
const resumeDisplayMessages = resumeDisplay?.displayMessages ?? null;
|
|
359
|
+
const responseStreamResumeState = resumeDisplay?.responseStreamState ?? null;
|
|
317
360
|
const sessionLinkMessage = isActive && preConnectedSession?.session_url
|
|
318
361
|
? [
|
|
319
362
|
{
|
|
@@ -477,23 +520,53 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
477
520
|
const isPolling = useRef(false);
|
|
478
521
|
const receivedResponseSinceLastInput = useRef(false);
|
|
479
522
|
const firstMessageNotified = useRef(!!resumeEvents);
|
|
480
|
-
const responseStreamKeys = useRef(new Map());
|
|
481
|
-
const
|
|
482
|
-
const
|
|
483
|
-
const
|
|
523
|
+
const responseStreamKeys = useRef(responseStreamResumeState?.keys ?? new Map());
|
|
524
|
+
const responseStreamContents = useRef(responseStreamResumeState?.contents ?? new Map());
|
|
525
|
+
const responseStreamTexts = useRef(responseStreamResumeState?.texts ?? new Map());
|
|
526
|
+
const responseStreamTimestamps = useRef(responseStreamResumeState?.timestamps ?? new Map());
|
|
527
|
+
const responseStreamStatuses = useRef(responseStreamResumeState?.statuses ??
|
|
528
|
+
new Map());
|
|
529
|
+
const responseStreamKeysByTask = useRef(responseStreamResumeState?.keysByTask ?? new Map());
|
|
530
|
+
const clearResponseStreamsForTask = (taskId, options = {}) => {
|
|
484
531
|
if (!taskId)
|
|
485
532
|
return;
|
|
486
533
|
const keys = responseStreamKeysByTask.current.get(taskId);
|
|
487
534
|
if (!keys?.size)
|
|
488
535
|
return;
|
|
536
|
+
// Removed keys disappear from the transcript; detached keys stay rendered
|
|
537
|
+
// but are no longer tracked as active streams for future cleanup.
|
|
538
|
+
const removedKeys = new Set();
|
|
539
|
+
const detachedKeys = new Set();
|
|
489
540
|
for (const [streamId, key] of responseStreamKeys.current.entries()) {
|
|
490
541
|
if (keys.has(key)) {
|
|
542
|
+
if (options.preserveContinued &&
|
|
543
|
+
responseStreamStatuses.current.get(streamId) === 'continued' &&
|
|
544
|
+
responseStreamTexts.current.get(streamId) !== options.finalText) {
|
|
545
|
+
responseStreamKeys.current.delete(streamId);
|
|
546
|
+
responseStreamContents.current.delete(streamId);
|
|
547
|
+
responseStreamTexts.current.delete(streamId);
|
|
548
|
+
responseStreamTimestamps.current.delete(streamId);
|
|
549
|
+
responseStreamStatuses.current.delete(streamId);
|
|
550
|
+
detachedKeys.add(key);
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
491
553
|
responseStreamKeys.current.delete(streamId);
|
|
554
|
+
responseStreamContents.current.delete(streamId);
|
|
555
|
+
responseStreamTexts.current.delete(streamId);
|
|
492
556
|
responseStreamTimestamps.current.delete(streamId);
|
|
557
|
+
responseStreamStatuses.current.delete(streamId);
|
|
558
|
+
removedKeys.add(key);
|
|
493
559
|
}
|
|
494
560
|
}
|
|
495
|
-
|
|
496
|
-
|
|
561
|
+
const inactiveKeys = new Set([...removedKeys, ...detachedKeys]);
|
|
562
|
+
const remainingKeys = new Set([...keys].filter((key) => !inactiveKeys.has(key)));
|
|
563
|
+
if (remainingKeys.size > 0) {
|
|
564
|
+
responseStreamKeysByTask.current.set(taskId, remainingKeys);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
responseStreamKeysByTask.current.delete(taskId);
|
|
568
|
+
}
|
|
569
|
+
setMessages((prev) => prev.filter((message) => !removedKeys.has(message.key)));
|
|
497
570
|
};
|
|
498
571
|
const upsertResponseStreamMessage = (event) => {
|
|
499
572
|
if (!isResponseStreamEvent(event))
|
|
@@ -504,9 +577,12 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
504
577
|
const taskId = event.task?.id;
|
|
505
578
|
const existingKey = responseStreamKeys.current.get(streamId);
|
|
506
579
|
if (event.content.status === 'aborted') {
|
|
580
|
+
responseStreamContents.current.delete(streamId);
|
|
581
|
+
responseStreamTexts.current.delete(streamId);
|
|
582
|
+
responseStreamTimestamps.current.delete(streamId);
|
|
583
|
+
responseStreamStatuses.current.delete(streamId);
|
|
507
584
|
if (existingKey) {
|
|
508
585
|
responseStreamKeys.current.delete(streamId);
|
|
509
|
-
responseStreamTimestamps.current.delete(streamId);
|
|
510
586
|
if (taskId) {
|
|
511
587
|
const keys = responseStreamKeysByTask.current.get(taskId);
|
|
512
588
|
keys?.delete(existingKey);
|
|
@@ -517,11 +593,22 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
517
593
|
}
|
|
518
594
|
return;
|
|
519
595
|
}
|
|
520
|
-
const
|
|
596
|
+
const streamContents = responseStreamContents.current.get(streamId) ?? [];
|
|
597
|
+
const existingContentIndex = streamContents.findIndex((content) => content.sequence === event.content.sequence);
|
|
598
|
+
if (existingContentIndex === -1) {
|
|
599
|
+
streamContents.push(event.content);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
streamContents[existingContentIndex] = event.content;
|
|
603
|
+
}
|
|
604
|
+
responseStreamContents.current.set(streamId, streamContents);
|
|
605
|
+
const text = applyResponseStreamContentsInSequence(streamContents);
|
|
521
606
|
if (!text.trim())
|
|
522
607
|
return;
|
|
523
608
|
const key = existingKey ?? `response-stream-${streamId}`;
|
|
524
609
|
responseStreamKeys.current.set(streamId, key);
|
|
610
|
+
responseStreamTexts.current.set(streamId, text);
|
|
611
|
+
responseStreamStatuses.current.set(streamId, event.content.status);
|
|
525
612
|
if (taskId) {
|
|
526
613
|
const keys = responseStreamKeysByTask.current.get(taskId) ?? new Set();
|
|
527
614
|
keys.add(key);
|
|
@@ -773,7 +860,10 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
773
860
|
}
|
|
774
861
|
const text = extractMessageText(getAgentNotificationText(event));
|
|
775
862
|
if (text.trim()) {
|
|
776
|
-
clearResponseStreamsForTask(taskInfo?.id
|
|
863
|
+
clearResponseStreamsForTask(taskInfo?.id, {
|
|
864
|
+
preserveContinued: true,
|
|
865
|
+
finalText: text,
|
|
866
|
+
});
|
|
777
867
|
const taskName = agentName || 'assistant';
|
|
778
868
|
const messageContent = renderAssistantMessage(text, taskName);
|
|
779
869
|
setMessages((prev) => [
|
|
@@ -842,13 +932,16 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
842
932
|
event.content !== undefined &&
|
|
843
933
|
taskInfo &&
|
|
844
934
|
'agent' in taskInfo) {
|
|
845
|
-
clearResponseStreamsForTask(taskInfo.id);
|
|
846
935
|
// One-shot agents may complete with runtime_done without sending
|
|
847
936
|
// agent_notification_message. Display the output if we haven't
|
|
848
937
|
// already shown a response for this input cycle.
|
|
849
938
|
const contentStr = typeof event.content === 'string'
|
|
850
939
|
? event.content
|
|
851
940
|
: JSON.stringify(event.content);
|
|
941
|
+
clearResponseStreamsForTask(taskInfo.id, {
|
|
942
|
+
preserveContinued: true,
|
|
943
|
+
finalText: contentStr,
|
|
944
|
+
});
|
|
852
945
|
if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
|
|
853
946
|
const rendered = fixListItemMarkdown(marked.parse(contentStr));
|
|
854
947
|
const taskName = agentName || 'assistant';
|
|
@@ -1108,7 +1201,7 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
|
|
|
1108
1201
|
}
|
|
1109
1202
|
}
|
|
1110
1203
|
if (!workspaceId) {
|
|
1111
|
-
throw new
|
|
1204
|
+
throw new WorkspaceNotConfiguredError();
|
|
1112
1205
|
}
|
|
1113
1206
|
progress('Creating session');
|
|
1114
1207
|
const sessionData = {
|
|
@@ -1127,7 +1220,7 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
|
|
|
1127
1220
|
catch (error) {
|
|
1128
1221
|
const err = handleAxiosError(error);
|
|
1129
1222
|
if (err.code === ErrorCodes.NOT_FOUND) {
|
|
1130
|
-
throw new
|
|
1223
|
+
throw new WorkspaceNotFoundError(workspaceId);
|
|
1131
1224
|
}
|
|
1132
1225
|
throw error;
|
|
1133
1226
|
}
|
|
@@ -1145,11 +1238,13 @@ export function createChatCommand() {
|
|
|
1145
1238
|
.argument('[prompt...]', 'Optional initial prompt (multiple words)')
|
|
1146
1239
|
.option('--agent <identifier>', 'Agent ID or full name, e.g., foo~bar (default: assistant)')
|
|
1147
1240
|
.option('--once', 'One-shot mode: send message, wait for response, exit (non-interactive)')
|
|
1148
|
-
.option('--mode <format>', 'Machine-readable output format: json or jsonl')
|
|
1149
1241
|
.option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
|
|
1150
1242
|
.option('--no-splash', 'Skip the splash screen animation')
|
|
1151
1243
|
.option('--resume <session-id>', 'Resume an existing session')
|
|
1152
1244
|
.option('--events <types>', 'Event types to show (default: user). Shorthands: none, user, system, all, or comma-separated type names (e.g. agent_console,llm_start)')
|
|
1245
|
+
// Accept --mode so `guild chat --mode json` works when re-parsed.
|
|
1246
|
+
// The actual value is read from process.argv by getOutputMode().
|
|
1247
|
+
.addOption(new Option('--mode <format>').hideHelp())
|
|
1153
1248
|
.addHelpText('after', '\nTo chat with a local agent under development: guild agent chat')
|
|
1154
1249
|
.action(async (promptArgs, options) => {
|
|
1155
1250
|
const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
|
|
@@ -1220,7 +1315,8 @@ export function createChatCommand() {
|
|
|
1220
1315
|
if (hasRootTaskError) {
|
|
1221
1316
|
debug('Found error event from root agent, exiting --once mode');
|
|
1222
1317
|
const errorEvents = allEvents.filter((e) => e.type === 'runtime_error' || e.type === 'agent_notification_error');
|
|
1223
|
-
|
|
1318
|
+
const outputMode = getOutputMode();
|
|
1319
|
+
if (errorEvents.length > 0 && outputMode === 'interactive') {
|
|
1224
1320
|
const lastError = errorEvents[errorEvents.length - 1];
|
|
1225
1321
|
const content = lastError.content;
|
|
1226
1322
|
if (content?.data) {
|
|
@@ -1230,7 +1326,7 @@ export function createChatCommand() {
|
|
|
1230
1326
|
console.error(chalk.red('Agent failed to start'));
|
|
1231
1327
|
}
|
|
1232
1328
|
}
|
|
1233
|
-
else if (
|
|
1329
|
+
else if (outputMode === 'json') {
|
|
1234
1330
|
console.log(JSON.stringify({
|
|
1235
1331
|
session_id: session.id,
|
|
1236
1332
|
events: allEvents,
|
|
@@ -1241,14 +1337,14 @@ export function createChatCommand() {
|
|
|
1241
1337
|
}
|
|
1242
1338
|
if (hasRootTaskDone || hasAgentMessage || hasUIPromptMessage) {
|
|
1243
1339
|
debug('Found completion event from root agent, exiting --once mode');
|
|
1244
|
-
await outputOnceResult(session.id, allEvents,
|
|
1340
|
+
await outputOnceResult(session.id, allEvents, getOutputMode());
|
|
1245
1341
|
process.exit(0);
|
|
1246
1342
|
}
|
|
1247
1343
|
// Timeout if no activity for too long
|
|
1248
1344
|
if (inactivityCounter >= maxInactivityAttempts) {
|
|
1249
1345
|
debug(`Inactivity timeout reached (${maxInactivityAttempts} attempts with no new events)`);
|
|
1250
1346
|
debug(`Exiting with ${allEvents.length} events total`);
|
|
1251
|
-
await outputOnceResult(session.id, allEvents,
|
|
1347
|
+
await outputOnceResult(session.id, allEvents, getOutputMode());
|
|
1252
1348
|
process.exit(0);
|
|
1253
1349
|
}
|
|
1254
1350
|
}
|
|
@@ -1256,6 +1352,14 @@ export function createChatCommand() {
|
|
|
1256
1352
|
catch (error) {
|
|
1257
1353
|
spinner.fail('Connection failed');
|
|
1258
1354
|
console.error('');
|
|
1355
|
+
if (error instanceof WorkspaceNotConfiguredError) {
|
|
1356
|
+
format.error(WORKSPACE_NOT_CONFIGURED_MSG);
|
|
1357
|
+
process.exit(1);
|
|
1358
|
+
}
|
|
1359
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
1360
|
+
format.error(WORKSPACE_NOT_FOUND_MSG);
|
|
1361
|
+
process.exit(1);
|
|
1362
|
+
}
|
|
1259
1363
|
const formattedError = handleAxiosError(error);
|
|
1260
1364
|
console.error(`Error: ${formattedError.error}`);
|
|
1261
1365
|
console.error(formattedError.details);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { loadConfig, } from '../../lib/guild-config.js';
|
|
5
|
-
import {
|
|
5
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
6
6
|
import { createOutputWriter } from '../../lib/output.js';
|
|
7
7
|
/**
|
|
8
8
|
* All valid config keys across global and local configs.
|
|
@@ -25,7 +25,7 @@ export function createConfigGetCommand() {
|
|
|
25
25
|
.action(async (key) => {
|
|
26
26
|
const output = createOutputWriter();
|
|
27
27
|
const config = await loadConfig();
|
|
28
|
-
const
|
|
28
|
+
const jsonMode = isMachineReadable();
|
|
29
29
|
if (!ALL_VALID_KEYS.includes(key)) {
|
|
30
30
|
output.error(`Unknown config key: ${key}\n\nValid keys:\n Global: ${VALID_GLOBAL_KEYS.join(', ')}\n Local: ${VALID_LOCAL_KEYS.join(', ')}`);
|
|
31
31
|
process.exit(1);
|
|
@@ -44,7 +44,7 @@ export function createConfigGetCommand() {
|
|
|
44
44
|
source = 'local';
|
|
45
45
|
}
|
|
46
46
|
if (value === undefined) {
|
|
47
|
-
if (
|
|
47
|
+
if (jsonMode) {
|
|
48
48
|
output.data({ key, value: null, source: null });
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
@@ -52,7 +52,7 @@ export function createConfigGetCommand() {
|
|
|
52
52
|
}
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
-
if (
|
|
55
|
+
if (jsonMode) {
|
|
56
56
|
output.data({ key, value, source });
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { loadConfig } from '../../lib/guild-config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
7
7
|
import { createOutputWriter } from '../../lib/output.js';
|
|
8
8
|
export function createConfigListCommand() {
|
|
9
9
|
const cmd = new Command('list');
|
|
10
10
|
cmd.description('Show all configuration values').action(async () => {
|
|
11
11
|
const output = createOutputWriter();
|
|
12
12
|
const config = await loadConfig();
|
|
13
|
-
|
|
14
|
-
if (mode === 'json') {
|
|
13
|
+
if (isMachineReadable()) {
|
|
15
14
|
output.data({
|
|
16
15
|
global: config.global || null,
|
|
17
16
|
local: config.local || null,
|
|
@@ -4,7 +4,7 @@ import { Command } from 'commander';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
6
|
import { getGlobalConfigPath, getLocalConfigPath } from '../../lib/guild-config.js';
|
|
7
|
-
import {
|
|
7
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
8
8
|
import { createOutputWriter } from '../../lib/output.js';
|
|
9
9
|
async function fileExists(filePath) {
|
|
10
10
|
return fs
|
|
@@ -18,10 +18,9 @@ export function createConfigPathCommand() {
|
|
|
18
18
|
const output = createOutputWriter();
|
|
19
19
|
const globalPath = getGlobalConfigPath();
|
|
20
20
|
const localPath = getLocalConfigPath();
|
|
21
|
-
const mode = getOutputMode();
|
|
22
21
|
const globalExists = await fileExists(globalPath);
|
|
23
22
|
const localExists = await fileExists(localPath);
|
|
24
|
-
if (
|
|
23
|
+
if (isMachineReadable()) {
|
|
25
24
|
output.data({
|
|
26
25
|
global: { path: globalPath, exists: globalExists },
|
|
27
26
|
local: { path: localPath, exists: localExists },
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { saveGlobalConfig } from '../../lib/guild-config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
7
7
|
import { createOutputWriter } from '../../lib/output.js';
|
|
8
8
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
9
9
|
import { debug } from '../../lib/errors.js';
|
|
@@ -46,8 +46,8 @@ async function resolveWorkspaceName(workspaceId) {
|
|
|
46
46
|
return undefined;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
function printResult(key, value,
|
|
50
|
-
if (
|
|
49
|
+
function printResult(key, value, jsonMode, output) {
|
|
50
|
+
if (jsonMode) {
|
|
51
51
|
output.data({ key, value });
|
|
52
52
|
}
|
|
53
53
|
else {
|
|
@@ -62,7 +62,7 @@ export function createConfigSetCommand() {
|
|
|
62
62
|
.argument('<value>', 'Value to set')
|
|
63
63
|
.action(async (key, value) => {
|
|
64
64
|
const output = createOutputWriter();
|
|
65
|
-
const
|
|
65
|
+
const jsonMode = isMachineReadable();
|
|
66
66
|
if (!VALID_GLOBAL_KEYS.includes(key)) {
|
|
67
67
|
output.error(`Unknown config key: ${key}\n\nValid keys:\n${VALID_GLOBAL_KEYS.map((k) => ` ${k}`).join('\n')}`);
|
|
68
68
|
process.exit(1);
|
|
@@ -71,7 +71,7 @@ export function createConfigSetCommand() {
|
|
|
71
71
|
if (BOOLEAN_KEYS.has(typedKey)) {
|
|
72
72
|
const boolValue = parseBoolean(value, key, output);
|
|
73
73
|
await saveGlobalConfig({ [typedKey]: boolValue });
|
|
74
|
-
printResult(key, boolValue,
|
|
74
|
+
printResult(key, boolValue, jsonMode, output);
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
if (typedKey === 'default_workspace') {
|
|
@@ -80,14 +80,14 @@ export function createConfigSetCommand() {
|
|
|
80
80
|
default_workspace: value,
|
|
81
81
|
default_workspace_name: name,
|
|
82
82
|
});
|
|
83
|
-
printResult(key, value,
|
|
83
|
+
printResult(key, value, jsonMode, output);
|
|
84
84
|
if (name) {
|
|
85
|
-
if (
|
|
85
|
+
if (!jsonMode) {
|
|
86
86
|
output.progress(` Workspace name: ${name}`);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
|
-
if (
|
|
90
|
+
if (!jsonMode) {
|
|
91
91
|
output.error('Could not resolve workspace name (not authenticated?)');
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -111,21 +111,21 @@ export function createConfigSetCommand() {
|
|
|
111
111
|
default_owner: ownerId,
|
|
112
112
|
default_owner_name: ownerName,
|
|
113
113
|
});
|
|
114
|
-
printResult(key, ownerId,
|
|
114
|
+
printResult(key, ownerId, jsonMode, output);
|
|
115
115
|
if (ownerName) {
|
|
116
|
-
if (
|
|
116
|
+
if (!jsonMode) {
|
|
117
117
|
output.progress(` Owner name: ${ownerName}`);
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
121
|
-
if (
|
|
121
|
+
if (!jsonMode) {
|
|
122
122
|
output.error('Could not resolve owner name (not authenticated?)');
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
await saveGlobalConfig({ [typedKey]: value });
|
|
128
|
-
printResult(key, value,
|
|
128
|
+
printResult(key, value, jsonMode, output);
|
|
129
129
|
});
|
|
130
130
|
return cmd;
|
|
131
131
|
}
|
|
@@ -5,8 +5,9 @@ import chalk from 'chalk';
|
|
|
5
5
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
6
6
|
import { getAuthToken } from '../../lib/auth.js';
|
|
7
7
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
8
|
-
import {
|
|
8
|
+
import { isMachineReadable } from '../../lib/output-mode.js';
|
|
9
9
|
import { createOutputWriter } from '../../lib/output.js';
|
|
10
|
+
import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
|
|
10
11
|
import { Table } from '../../lib/table.js';
|
|
11
12
|
export function createCredentialsEndpointListCommand() {
|
|
12
13
|
const cmd = new Command('list');
|
|
@@ -15,8 +16,8 @@ export function createCredentialsEndpointListCommand() {
|
|
|
15
16
|
.argument('<credential-id>', 'Credential ID')
|
|
16
17
|
.option('--include-previous-versions', 'Include endpoints from previous versions')
|
|
17
18
|
.option('--search <query>', 'Search by operation name')
|
|
18
|
-
.option('--limit <number>',
|
|
19
|
-
.option('--offset <number>', 'Offset for pagination', '0')
|
|
19
|
+
.option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
|
|
20
|
+
.option('--offset <number>', 'Offset for pagination (default: 0)', '0')
|
|
20
21
|
.action(async (credentialId, options) => {
|
|
21
22
|
const output = createOutputWriter();
|
|
22
23
|
try {
|
|
@@ -36,7 +37,7 @@ export function createCredentialsEndpointListCommand() {
|
|
|
36
37
|
params.append('search', options.search);
|
|
37
38
|
}
|
|
38
39
|
const response = await client.get(`/credentials/${credentialId}/endpoints?${params.toString()}`);
|
|
39
|
-
if (
|
|
40
|
+
if (isMachineReadable()) {
|
|
40
41
|
console.log(JSON.stringify(response, null, 2));
|
|
41
42
|
}
|
|
42
43
|
else {
|
|
@@ -4,16 +4,17 @@ 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 { isMachineReadable } from '../../lib/output-mode.js';
|
|
8
8
|
import { createOutputWriter, formatCredentialsTable } from '../../lib/output.js';
|
|
9
|
+
import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
|
|
9
10
|
export function createCredentialsListCommand() {
|
|
10
11
|
const cmd = new Command('list');
|
|
11
12
|
cmd
|
|
12
13
|
.description('List credentials for an account')
|
|
13
14
|
.requiredOption('--owner <account>', 'Account name or ID')
|
|
14
15
|
.option('--search <query>', 'Filter by integration name')
|
|
15
|
-
.option('--limit <number>',
|
|
16
|
-
.option('--offset <number>', 'Offset for pagination', '0')
|
|
16
|
+
.option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
|
|
17
|
+
.option('--offset <number>', 'Offset for pagination (default: 0)', '0')
|
|
17
18
|
.action(async (options) => {
|
|
18
19
|
const output = createOutputWriter();
|
|
19
20
|
try {
|
|
@@ -31,7 +32,7 @@ export function createCredentialsListCommand() {
|
|
|
31
32
|
params.append('search', options.search);
|
|
32
33
|
}
|
|
33
34
|
const response = await client.get(`/accounts/${accountId}/credentials?${params.toString()}`);
|
|
34
|
-
if (
|
|
35
|
+
if (isMachineReadable()) {
|
|
35
36
|
console.log(JSON.stringify(response, null, 2));
|
|
36
37
|
}
|
|
37
38
|
else {
|