@guildai/cli 0.8.1 → 0.9.1
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.d.ts +1 -0
- package/dist/commands/agent/chat.js +24 -1
- package/dist/commands/agent/init.js +1 -1
- package/dist/commands/agent/test.d.ts +1 -0
- package/dist/commands/agent/test.js +80 -28
- package/dist/commands/chat.d.ts +1 -0
- package/dist/commands/chat.js +178 -46
- package/dist/commands/integration/connect.js +1 -1
- package/dist/commands/integration/create.js +36 -36
- package/dist/commands/integration/operation/create.js +2 -1
- package/dist/commands/integration/operation/list.js +23 -15
- package/dist/commands/job/get-step.d.ts +3 -0
- package/dist/commands/job/{step-get.js → get-step.js} +3 -3
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/session/create.js +1 -1
- package/dist/commands/session/events.js +16 -7
- package/dist/commands/session/list.js +1 -1
- package/dist/commands/session/send.js +1 -1
- package/dist/commands/workspace/agent/add.js +16 -3
- package/dist/commands/workspace/agent/list.js +14 -1
- package/dist/commands/workspace/agent/remove.js +14 -1
- package/dist/commands/workspace/clear.d.ts +3 -0
- package/dist/commands/workspace/clear.js +45 -0
- package/dist/commands/workspace/select.js +3 -1
- package/dist/index.js +58 -8
- package/dist/lib/api-types.d.ts +7 -0
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +1 -0
- package/dist/lib/guild-config.d.ts +13 -0
- package/dist/lib/guild-config.js +19 -0
- package/dist/lib/npmrc.js +6 -2
- package/dist/lib/output.js +25 -99
- package/dist/lib/polling.d.ts +7 -0
- package/dist/lib/polling.js +12 -3
- package/dist/lib/session-events.d.ts +32 -16
- package/dist/lib/session-events.js +22 -0
- package/dist/lib/session-polling.d.ts +4 -3
- package/dist/lib/session-polling.js +75 -17
- package/dist/lib/session-resume.js +4 -1
- package/dist/lib/stdin.d.ts +4 -0
- package/dist/lib/stdin.js +23 -0
- package/dist/lib/validate-input-schema.d.ts +19 -0
- package/dist/lib/validate-input-schema.js +208 -0
- package/dist/lib/version-helpers.js +38 -0
- package/dist/lib/workspace-helpers.d.ts +20 -0
- package/dist/lib/workspace-helpers.js +49 -0
- package/dist/mcp/tools.js +8 -52
- package/docs/CLI_WORKFLOW.md +1 -1
- package/docs/skills/agent-dev.md +192 -129
- package/docs/skills/integrations.md +1 -1
- package/package.json +2 -1
- package/dist/commands/job/step-get.d.ts +0 -3
package/dist/lib/stdin.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export declare function isInteractive(): boolean;
|
|
|
8
8
|
* accidentally pipe input without --mode get clear guidance instead of a hang.
|
|
9
9
|
*/
|
|
10
10
|
export declare function ensureInteractiveStdin(command: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Read raw text from stdin with a timeout.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readStdinAsText(): Promise<string>;
|
|
11
15
|
/**
|
|
12
16
|
* Read JSON from stdin with a timeout.
|
|
13
17
|
*/
|
package/dist/lib/stdin.js
CHANGED
|
@@ -24,6 +24,29 @@ export function ensureInteractiveStdin(command) {
|
|
|
24
24
|
process.exit(1);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Read raw text from stdin with a timeout.
|
|
29
|
+
*/
|
|
30
|
+
export function readStdinAsText() {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let input = '';
|
|
33
|
+
const timeout = setTimeout(() => {
|
|
34
|
+
reject(new Error('Timeout waiting for stdin'));
|
|
35
|
+
}, 5000);
|
|
36
|
+
process.stdin.setEncoding('utf8');
|
|
37
|
+
process.stdin.on('data', (chunk) => {
|
|
38
|
+
input += chunk;
|
|
39
|
+
});
|
|
40
|
+
process.stdin.on('end', () => {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
resolve(input);
|
|
43
|
+
});
|
|
44
|
+
process.stdin.on('error', (error) => {
|
|
45
|
+
clearTimeout(timeout);
|
|
46
|
+
reject(error);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
27
50
|
/**
|
|
28
51
|
* Read JSON from stdin with a timeout.
|
|
29
52
|
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface ValidationSuccess {
|
|
2
|
+
valid: true;
|
|
3
|
+
}
|
|
4
|
+
interface ValidationFailure {
|
|
5
|
+
valid: false;
|
|
6
|
+
errors: Array<{
|
|
7
|
+
message: string;
|
|
8
|
+
path?: string[];
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
interface ValidationSkipped {
|
|
12
|
+
valid: true;
|
|
13
|
+
skipped: true;
|
|
14
|
+
reason: string;
|
|
15
|
+
}
|
|
16
|
+
export type ValidationResult = ValidationSuccess | ValidationFailure | ValidationSkipped;
|
|
17
|
+
export declare function validateInputSchema(agentDir: string, inputs: unknown[]): Promise<ValidationResult>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=validate-input-schema.d.ts.map
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
import { execFile } from 'child_process';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
import { debug } from './errors.js';
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
function resolveSourceEntryPoint(agentDir, packageJson) {
|
|
11
|
+
const exports = packageJson.exports;
|
|
12
|
+
if (exports?.['.']) {
|
|
13
|
+
const distPath = exports['.'];
|
|
14
|
+
const sourcePath = distPath
|
|
15
|
+
.replace(/^\.\/dist\//, '')
|
|
16
|
+
.replace(/\.compiled\.js$/, '.ts')
|
|
17
|
+
.replace(/\.js$/, '.ts');
|
|
18
|
+
return path.join(agentDir, sourcePath);
|
|
19
|
+
}
|
|
20
|
+
return path.join(agentDir, 'agent.ts');
|
|
21
|
+
}
|
|
22
|
+
async function fileExists(filePath) {
|
|
23
|
+
return fs
|
|
24
|
+
.access(filePath)
|
|
25
|
+
.then(() => true)
|
|
26
|
+
.catch(() => false);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract a single `const <name> = z.<call>(...)` declaration from source,
|
|
30
|
+
* starting at the given regex match. Uses balanced parenthesis matching
|
|
31
|
+
* with string literal awareness.
|
|
32
|
+
*/
|
|
33
|
+
function extractZodDeclaration(source, match) {
|
|
34
|
+
const startIndex = match.index;
|
|
35
|
+
const afterEquals = match.index + match[0].length;
|
|
36
|
+
let depth = 0;
|
|
37
|
+
let foundOpen = false;
|
|
38
|
+
let endIndex = afterEquals;
|
|
39
|
+
let inString = null;
|
|
40
|
+
for (let charIndex = afterEquals; charIndex < source.length; charIndex++) {
|
|
41
|
+
const char = source[charIndex];
|
|
42
|
+
if (inString) {
|
|
43
|
+
if (char === '\\') {
|
|
44
|
+
charIndex++;
|
|
45
|
+
}
|
|
46
|
+
else if (char === inString) {
|
|
47
|
+
inString = null;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
52
|
+
inString = char;
|
|
53
|
+
}
|
|
54
|
+
else if (char === '(') {
|
|
55
|
+
depth++;
|
|
56
|
+
foundOpen = true;
|
|
57
|
+
}
|
|
58
|
+
else if (char === ')') {
|
|
59
|
+
depth--;
|
|
60
|
+
if (foundOpen && depth === 0) {
|
|
61
|
+
endIndex = charIndex + 1;
|
|
62
|
+
if (source[endIndex] === ';')
|
|
63
|
+
endIndex++;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!foundOpen)
|
|
69
|
+
return null;
|
|
70
|
+
return source.substring(startIndex, endIndex);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract inputSchema and any Zod schema dependencies from agent source.
|
|
74
|
+
* Finds all `const <name> = z.<call>(...)` declarations that appear before
|
|
75
|
+
* inputSchema (which may be referenced inside it), plus inputSchema itself.
|
|
76
|
+
* Returns them in source order so dependencies are defined before use.
|
|
77
|
+
*/
|
|
78
|
+
function extractInputSchema(source) {
|
|
79
|
+
const inputPattern = /const\s+inputSchema\s*=\s*/;
|
|
80
|
+
const inputMatch = inputPattern.exec(source);
|
|
81
|
+
if (!inputMatch)
|
|
82
|
+
return null;
|
|
83
|
+
const inputBlock = extractZodDeclaration(source, inputMatch);
|
|
84
|
+
if (!inputBlock)
|
|
85
|
+
return null;
|
|
86
|
+
const blocks = [];
|
|
87
|
+
const zodDeclPattern = /const\s+\w+\s*=\s*z\s*\.\s*/g;
|
|
88
|
+
let declMatch;
|
|
89
|
+
while ((declMatch = zodDeclPattern.exec(source)) !== null) {
|
|
90
|
+
if (declMatch.index >= inputMatch.index)
|
|
91
|
+
break;
|
|
92
|
+
const block = extractZodDeclaration(source, declMatch);
|
|
93
|
+
if (block)
|
|
94
|
+
blocks.push(block);
|
|
95
|
+
}
|
|
96
|
+
blocks.push(inputBlock);
|
|
97
|
+
return blocks.join('\n');
|
|
98
|
+
}
|
|
99
|
+
export async function validateInputSchema(agentDir, inputs) {
|
|
100
|
+
const nodeModulesPath = path.join(agentDir, 'node_modules');
|
|
101
|
+
if (!(await fileExists(nodeModulesPath))) {
|
|
102
|
+
return {
|
|
103
|
+
valid: true,
|
|
104
|
+
skipped: true,
|
|
105
|
+
reason: 'run npm install for pre-build input validation',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const pkgJsonPath = path.join(agentDir, 'package.json');
|
|
109
|
+
if (!(await fileExists(pkgJsonPath))) {
|
|
110
|
+
return {
|
|
111
|
+
valid: true,
|
|
112
|
+
skipped: true,
|
|
113
|
+
reason: 'no package.json found',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'));
|
|
117
|
+
let entryPoint = resolveSourceEntryPoint(agentDir, pkgJson);
|
|
118
|
+
if (!(await fileExists(entryPoint))) {
|
|
119
|
+
const fallback = path.join(agentDir, 'agent.ts');
|
|
120
|
+
if (await fileExists(fallback)) {
|
|
121
|
+
entryPoint = fallback;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return {
|
|
125
|
+
valid: true,
|
|
126
|
+
skipped: true,
|
|
127
|
+
reason: `could not find agent source file (tried ${path.basename(entryPoint)})`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const agentSource = await fs.readFile(entryPoint, 'utf-8');
|
|
132
|
+
const schemaBlock = extractInputSchema(agentSource);
|
|
133
|
+
if (!schemaBlock) {
|
|
134
|
+
return {
|
|
135
|
+
valid: true,
|
|
136
|
+
skipped: true,
|
|
137
|
+
reason: 'could not find inputSchema definition in agent source',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'guild-validate-'));
|
|
141
|
+
try {
|
|
142
|
+
const extractorSource = [
|
|
143
|
+
`import { z } from "zod";`,
|
|
144
|
+
schemaBlock,
|
|
145
|
+
`const inputs = JSON.parse(process.argv[2]);`,
|
|
146
|
+
`const errors = [];`,
|
|
147
|
+
`for (let i = 0; i < inputs.length; i++) {`,
|
|
148
|
+
` try {`,
|
|
149
|
+
` inputSchema.parse(inputs[i]);`,
|
|
150
|
+
` } catch (e) {`,
|
|
151
|
+
` const issues = e.issues || [{ message: e.message }];`,
|
|
152
|
+
` errors.push({ index: i, issues: issues.map(iss => ({ message: iss.message, path: iss.path })) });`,
|
|
153
|
+
` }`,
|
|
154
|
+
`}`,
|
|
155
|
+
`if (errors.length > 0) {`,
|
|
156
|
+
` console.log(JSON.stringify({ valid: false, errors }));`,
|
|
157
|
+
`} else {`,
|
|
158
|
+
` console.log(JSON.stringify({ valid: true }));`,
|
|
159
|
+
`}`,
|
|
160
|
+
].join('\n');
|
|
161
|
+
const extractorPath = path.join(tmpDir, 'validate.mjs');
|
|
162
|
+
await fs.writeFile(extractorPath, extractorSource);
|
|
163
|
+
const { build } = await import('esbuild');
|
|
164
|
+
const bundlePath = path.join(tmpDir, 'validate-bundle.mjs');
|
|
165
|
+
await build({
|
|
166
|
+
entryPoints: [extractorPath],
|
|
167
|
+
bundle: true,
|
|
168
|
+
format: 'esm',
|
|
169
|
+
target: 'esnext',
|
|
170
|
+
platform: 'node',
|
|
171
|
+
outfile: bundlePath,
|
|
172
|
+
logLevel: 'silent',
|
|
173
|
+
nodePaths: [path.join(agentDir, 'node_modules')],
|
|
174
|
+
});
|
|
175
|
+
const inputArg = JSON.stringify(inputs);
|
|
176
|
+
const { stdout } = await execFileAsync('node', [bundlePath, inputArg], {
|
|
177
|
+
cwd: agentDir,
|
|
178
|
+
timeout: 10000,
|
|
179
|
+
});
|
|
180
|
+
const result = JSON.parse(stdout.trim());
|
|
181
|
+
if (result.valid) {
|
|
182
|
+
return { valid: true };
|
|
183
|
+
}
|
|
184
|
+
const flatErrors = [];
|
|
185
|
+
for (const entry of result.errors) {
|
|
186
|
+
for (const issue of entry.issues) {
|
|
187
|
+
const prefix = inputs.length > 1 ? `Line ${entry.index + 1}: ` : '';
|
|
188
|
+
flatErrors.push({
|
|
189
|
+
message: `${prefix}${issue.message}`,
|
|
190
|
+
path: issue.path,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { valid: false, errors: flatErrors };
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
debug('Local input validation failed: %s', error);
|
|
198
|
+
return {
|
|
199
|
+
valid: true,
|
|
200
|
+
skipped: true,
|
|
201
|
+
reason: 'local validation encountered an error (server-side validation will still run)',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=validate-input-schema.js.map
|
|
@@ -2,12 +2,41 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { GuildAPIClient } from './api-client.js';
|
|
4
4
|
import { pollUntilComplete } from './polling.js';
|
|
5
|
+
/**
|
|
6
|
+
* Format a list of validation steps into a compact single-line status string
|
|
7
|
+
* suitable for spinner text, e.g. `✓ build ⟳ test ○ deploy`.
|
|
8
|
+
* Returns null when the steps array is empty so callers can fall back to the
|
|
9
|
+
* default attempt-counter text.
|
|
10
|
+
*/
|
|
11
|
+
function formatStepProgress(steps) {
|
|
12
|
+
if (steps.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
const tokens = steps.map((step) => {
|
|
15
|
+
let prefix;
|
|
16
|
+
switch (step.status) {
|
|
17
|
+
case 'SUCCEEDED':
|
|
18
|
+
prefix = '✓';
|
|
19
|
+
break;
|
|
20
|
+
case 'RUNNING':
|
|
21
|
+
prefix = '⟳';
|
|
22
|
+
break;
|
|
23
|
+
case 'FAILED':
|
|
24
|
+
prefix = '✗';
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
prefix = '○';
|
|
28
|
+
}
|
|
29
|
+
return `${prefix} ${step.name}`;
|
|
30
|
+
});
|
|
31
|
+
return tokens.join(' ');
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* Poll until a version's validation completes, then check the result.
|
|
7
35
|
* Exits the process on timeout or validation failure.
|
|
8
36
|
* Returns the updated version on success.
|
|
9
37
|
*/
|
|
10
38
|
export async function waitForValidation(versionId, output) {
|
|
39
|
+
const client = new GuildAPIClient();
|
|
11
40
|
const pollResult = await pollUntilComplete({
|
|
12
41
|
resourceId: versionId,
|
|
13
42
|
endpoint: `/versions/${versionId}`,
|
|
@@ -18,6 +47,10 @@ export async function waitForValidation(versionId, output) {
|
|
|
18
47
|
timeoutMessage: 'Validation timed out',
|
|
19
48
|
maxAttempts: 120,
|
|
20
49
|
delayMs: 1000,
|
|
50
|
+
onPoll: async () => {
|
|
51
|
+
const r = await client.get(`/versions/${versionId}/validation/steps`);
|
|
52
|
+
return formatStepProgress(r.steps);
|
|
53
|
+
},
|
|
21
54
|
});
|
|
22
55
|
if (!pollResult.success || !pollResult.response) {
|
|
23
56
|
output.error('Validation did not complete in time', 'Check status manually:\n guild agent versions');
|
|
@@ -64,6 +97,7 @@ async function fetchValidationFailureDetails(versionId) {
|
|
|
64
97
|
* Returns the updated version on success.
|
|
65
98
|
*/
|
|
66
99
|
export async function waitForPublish(versionId, output) {
|
|
100
|
+
const client = new GuildAPIClient();
|
|
67
101
|
const pollResult = await pollUntilComplete({
|
|
68
102
|
resourceId: versionId,
|
|
69
103
|
endpoint: `/versions/${versionId}`,
|
|
@@ -73,6 +107,10 @@ export async function waitForPublish(versionId, output) {
|
|
|
73
107
|
timeoutMessage: 'Publish timed out',
|
|
74
108
|
maxAttempts: 60,
|
|
75
109
|
delayMs: 1000,
|
|
110
|
+
onPoll: async () => {
|
|
111
|
+
const r = await client.get(`/versions/${versionId}/publish/steps`);
|
|
112
|
+
return formatStepProgress(r.steps);
|
|
113
|
+
},
|
|
76
114
|
});
|
|
77
115
|
if (!pollResult.success || pollResult.response?.status !== 'PUBLISHED') {
|
|
78
116
|
output.error('Publish did not complete in time', 'Check status manually:\n guild agent versions');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace resolution helper for workspace subcommands.
|
|
3
|
+
*
|
|
4
|
+
* Resolves a workspace identifier (UUID or human-readable name) to a UUID.
|
|
5
|
+
* Used by `guild workspace agent add`, `list`, and `remove`.
|
|
6
|
+
*/
|
|
7
|
+
import { GuildAPIClient } from './api-client.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a workspace ID or name to a workspace UUID.
|
|
10
|
+
*
|
|
11
|
+
* UUID inputs pass through unchanged. Name inputs are resolved by searching
|
|
12
|
+
* the user's workspaces via `GET /me/workspaces?filter=all&search=...` first,
|
|
13
|
+
* with a `fetchAll` fallback for IDs and names not matched by the search.
|
|
14
|
+
*
|
|
15
|
+
* Throws an `Error` with message `Workspace "<idOrName>" not found` if
|
|
16
|
+
* nothing resolves — callers should catch this and call `output.error` +
|
|
17
|
+
* `process.exit(1)`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveWorkspaceId(client: GuildAPIClient, idOrName: string): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=workspace-helpers.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4
|
+
/**
|
|
5
|
+
* Match a workspace against a search argument (case-insensitive name, full_name, or exact ID).
|
|
6
|
+
*/
|
|
7
|
+
function matchesWorkspaceArg(w, arg) {
|
|
8
|
+
return (w.id === arg ||
|
|
9
|
+
w.name === arg ||
|
|
10
|
+
w.name.toLowerCase() === arg.toLowerCase() ||
|
|
11
|
+
w.full_name === arg ||
|
|
12
|
+
w.full_name?.toLowerCase() === arg.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a workspace ID or name to a workspace UUID.
|
|
16
|
+
*
|
|
17
|
+
* UUID inputs pass through unchanged. Name inputs are resolved by searching
|
|
18
|
+
* the user's workspaces via `GET /me/workspaces?filter=all&search=...` first,
|
|
19
|
+
* with a `fetchAll` fallback for IDs and names not matched by the search.
|
|
20
|
+
*
|
|
21
|
+
* Throws an `Error` with message `Workspace "<idOrName>" not found` if
|
|
22
|
+
* nothing resolves — callers should catch this and call `output.error` +
|
|
23
|
+
* `process.exit(1)`.
|
|
24
|
+
*/
|
|
25
|
+
export async function resolveWorkspaceId(client, idOrName) {
|
|
26
|
+
// Short-circuit UUID inputs — pass through unchanged
|
|
27
|
+
if (UUID_RE.test(idOrName)) {
|
|
28
|
+
return idOrName;
|
|
29
|
+
}
|
|
30
|
+
// Use server-side search first. Extract just the name part for full_name
|
|
31
|
+
// inputs (e.g. "owner/workspace-name") since the backend searches by name.
|
|
32
|
+
const searchTerm = idOrName.includes('/')
|
|
33
|
+
? (idOrName.split('/').pop() ?? idOrName)
|
|
34
|
+
: idOrName;
|
|
35
|
+
const searchResults = await client.get(`/me/workspaces?filter=all&search=${encodeURIComponent(searchTerm)}&limit=100`);
|
|
36
|
+
const directMatch = searchResults.items.find((w) => matchesWorkspaceArg(w, idOrName));
|
|
37
|
+
if (directMatch) {
|
|
38
|
+
return directMatch.id;
|
|
39
|
+
}
|
|
40
|
+
// Fall back to fetching all workspaces (handles IDs and names not returned
|
|
41
|
+
// by search, e.g. when the backend's ILIKE search doesn't match full_name).
|
|
42
|
+
const allWorkspaces = await client.fetchAll('/me/workspaces?filter=all');
|
|
43
|
+
const fallbackMatch = allWorkspaces.find((w) => matchesWorkspaceArg(w, idOrName));
|
|
44
|
+
if (fallbackMatch) {
|
|
45
|
+
return fallbackMatch.id;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Workspace "${idOrName}" not found`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=workspace-helpers.js.map
|
package/dist/mcp/tools.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import { pollForResponse as pollForSessionResponse } from '../lib/session-polling.js';
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Constants
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
|
-
const POLL_INTERVAL_MS = 2000;
|
|
8
8
|
const POLL_TIMEOUT_MS = 120_000;
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Helpers
|
|
@@ -14,55 +14,6 @@ function debugLog(debug, message) {
|
|
|
14
14
|
process.stderr.write(`[guild-mcp] ${message}\n`);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Poll session events until runtime_done or runtime_error.
|
|
19
|
-
* Returns collected agent messages.
|
|
20
|
-
*/
|
|
21
|
-
async function pollForResponse(apiClient, sessionId, debug) {
|
|
22
|
-
const startTime = Date.now();
|
|
23
|
-
const messages = [];
|
|
24
|
-
let lastEventId;
|
|
25
|
-
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
26
|
-
try {
|
|
27
|
-
let url = `/sessions/${sessionId}/events`;
|
|
28
|
-
if (lastEventId) {
|
|
29
|
-
url += `?from_id=${lastEventId}`;
|
|
30
|
-
}
|
|
31
|
-
const response = await apiClient.get(url);
|
|
32
|
-
const events = response.events || [];
|
|
33
|
-
for (const event of events) {
|
|
34
|
-
debugLog(debug, `Event: ${event.event_type}`);
|
|
35
|
-
if (event.event_type === 'agent_notification_message') {
|
|
36
|
-
const data = extractEventText(event);
|
|
37
|
-
if (data) {
|
|
38
|
-
messages.push(data);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (event.event_type === 'runtime_error') {
|
|
42
|
-
const errorText = extractEventText(event);
|
|
43
|
-
if (errorText) {
|
|
44
|
-
return `Error: ${errorText}`;
|
|
45
|
-
}
|
|
46
|
-
return 'Error: Agent encountered an error';
|
|
47
|
-
}
|
|
48
|
-
if (event.event_type === 'runtime_done') {
|
|
49
|
-
debugLog(debug, 'Runtime done');
|
|
50
|
-
return messages.join('\n\n') || 'Agent completed without output.';
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (events.length > 0) {
|
|
54
|
-
lastEventId = events[events.length - 1].id;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
debugLog(debug, `Poll error: ${String(error)}`);
|
|
59
|
-
}
|
|
60
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
61
|
-
}
|
|
62
|
-
return messages.length > 0
|
|
63
|
-
? messages.join('\n\n') + '\n\n(Timed out waiting for completion)'
|
|
64
|
-
: 'Timed out waiting for agent response.';
|
|
65
|
-
}
|
|
66
17
|
function extractEventText(event) {
|
|
67
18
|
if (!event.content)
|
|
68
19
|
return undefined;
|
|
@@ -74,6 +25,11 @@ function extractEventText(event) {
|
|
|
74
25
|
}
|
|
75
26
|
return undefined;
|
|
76
27
|
}
|
|
28
|
+
async function pollForMcpResponse(apiClient, sessionId, debug) {
|
|
29
|
+
debugLog(debug, `Polling response for session ${sessionId}`);
|
|
30
|
+
const result = await pollForSessionResponse(apiClient, sessionId, undefined, POLL_TIMEOUT_MS);
|
|
31
|
+
return result.response || 'Agent completed without output.';
|
|
32
|
+
}
|
|
77
33
|
function errText(action, error) {
|
|
78
34
|
return `Failed to ${action}: ${error instanceof Error ? error.message : String(error)}`;
|
|
79
35
|
}
|
|
@@ -504,7 +460,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
|
|
|
504
460
|
}
|
|
505
461
|
const session = await apiClient.post(`/workspaces/${wsId}/sessions`, body);
|
|
506
462
|
debugLog(debug, `Session created: ${session.id}`);
|
|
507
|
-
const response = await
|
|
463
|
+
const response = await pollForMcpResponse(apiClient, session.id, debug);
|
|
508
464
|
return {
|
|
509
465
|
content: [{ type: 'text', text: response }],
|
|
510
466
|
};
|
|
@@ -526,7 +482,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
|
|
|
526
482
|
event_type: 'user_message',
|
|
527
483
|
content: { type: 'text', data: message },
|
|
528
484
|
});
|
|
529
|
-
const response = await
|
|
485
|
+
const response = await pollForMcpResponse(apiClient, session_id, debug);
|
|
530
486
|
return {
|
|
531
487
|
content: [{ type: 'text', text: response }],
|
|
532
488
|
};
|
package/docs/CLI_WORKFLOW.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: guild-cli-workflow
|
|
3
3
|
description: Agent development using the Guild CLI. Activated when user mentions guild agent commands, saving/publishing agents, clone/pull workflow, or agent testing. Covers CLI commands and common workflows.
|
|
4
4
|
---
|
|
5
5
|
|