@bctrl/cli 0.1.7 → 0.1.9
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/README.md +17 -89
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.js +1 -0
- package/dist/api/errors.js +2 -2
- package/dist/bin/bctrl.js +0 -0
- package/dist/commands/{vault → account}/index.d.ts +1 -1
- package/dist/commands/account/index.js +21 -0
- package/dist/commands/ai/index.js +13 -8
- package/dist/commands/api-key/index.js +1 -1
- package/dist/commands/browser-extension/index.js +8 -8
- package/dist/commands/conversation/index.d.ts +3 -0
- package/dist/commands/conversation/index.js +51 -0
- package/dist/commands/file/index.js +8 -3
- package/dist/commands/run/index.js +23 -292
- package/dist/commands/runtime/index.js +32 -453
- package/dist/commands/shared/operation.d.ts +3 -0
- package/dist/commands/shared/operation.js +20 -7
- package/dist/commands/space/index.js +0 -25
- package/dist/commands/subaccount/index.js +2 -2
- package/dist/commands/tool/index.js +15 -5
- package/dist/commands/tool-call/index.js +22 -6
- package/dist/commands/toolset/index.js +1 -1
- package/dist/commands/view/index.d.ts +3 -0
- package/dist/commands/view/index.js +26 -0
- package/dist/commands/webhook/index.d.ts +3 -0
- package/dist/commands/webhook/index.js +64 -0
- package/dist/generated/help.d.ts +4896 -5977
- package/dist/generated/help.js +6208 -7532
- package/dist/generated/openapi-routes.d.ts +114 -142
- package/dist/generated/openapi-routes.js +37 -44
- package/dist/generated/openapi-types.d.ts +4451 -5302
- package/dist/root.js +8 -2
- package/package.json +14 -12
- package/dist/commands/vault/index.js +0 -171
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
import { writeBinary } from '../shared/io.js';
|
|
4
|
-
import { parsePositiveInteger } from '../shared/options.js';
|
|
5
|
-
import { addOutputFlags } from '../shared/output.js';
|
|
6
|
-
import { actingSubaccountOption, addPaginationFlags, buildOperationInput, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, downloadOperation, outputFlags, requestOperation, requestOperationAndPrint, streamOperationText, } from '../shared/operation.js';
|
|
2
|
+
import { createOperationListCommand, createOperationViewCommand, streamOperationText, } from '../shared/operation.js';
|
|
7
3
|
export function createRunCommand(factory) {
|
|
8
|
-
const command = new Command('
|
|
4
|
+
const command = new Command('runs').description('Inspect unified run observability');
|
|
9
5
|
command.addCommand(createOperationListCommand(factory, {
|
|
10
6
|
operationId: 'runs.list',
|
|
11
7
|
description: 'List runs',
|
|
12
|
-
configure: (cmd) => addPaginationFlags(cmd)
|
|
13
|
-
.option('--space <id>', 'Filter by space id')
|
|
14
|
-
.option('--runtime <id>', 'Filter by runtime id')
|
|
15
|
-
.option('--status <status>', 'Filter by run status')
|
|
16
|
-
.option('--subaccount <id>', 'Filter by subaccount id'),
|
|
17
|
-
query: (options) => ({
|
|
18
|
-
spaceId: typeof options.space === 'string' ? options.space : undefined,
|
|
19
|
-
runtimeId: typeof options.runtime === 'string' ? options.runtime : undefined,
|
|
20
|
-
status: typeof options.status === 'string' ? options.status : undefined,
|
|
21
|
-
limit: typeof options.limit === 'number' ? options.limit : undefined,
|
|
22
|
-
cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
|
|
23
|
-
}),
|
|
24
|
-
actingSubaccountId: actingSubaccountOption('subaccount'),
|
|
25
8
|
}));
|
|
26
9
|
command.addCommand(createOperationViewCommand(factory, {
|
|
27
10
|
operationId: 'runs.get',
|
|
@@ -29,285 +12,33 @@ export function createRunCommand(factory) {
|
|
|
29
12
|
description: 'Get a run',
|
|
30
13
|
argName: 'runId',
|
|
31
14
|
}));
|
|
32
|
-
command.addCommand(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
command.addCommand(createRunViewerCommand(factory, 'live', 'Create a live viewer URL'));
|
|
38
|
-
command.addCommand(createRunViewerCommand(factory, 'recording', 'Create a recording viewer URL'));
|
|
39
|
-
return command;
|
|
40
|
-
}
|
|
41
|
-
function createRunActivityCommand(factory) {
|
|
42
|
-
const command = new Command('activity').description('Inspect run activities');
|
|
43
|
-
command.addCommand(createRunActivityListCommand(factory));
|
|
44
|
-
command.addCommand(createSseCommand(factory, {
|
|
45
|
-
name: 'stream',
|
|
46
|
-
description: 'Stream run activities',
|
|
47
|
-
operationId: 'runs.activity.stream',
|
|
48
|
-
configure: (cmd) => addPaginationFlags(cmd)
|
|
49
|
-
.option('--view <view>', 'Activity view')
|
|
50
|
-
.option('--type <type>', 'Filter by activity type')
|
|
51
|
-
.option('--category <category>', 'Filter by activity category')
|
|
52
|
-
.option('--severity <severity>', 'Filter by severity')
|
|
53
|
-
.option('--invocation <id>', 'Filter by invocation id')
|
|
54
|
-
.option('--file <id>', 'Filter by file id'),
|
|
55
|
-
query: (options) => ({
|
|
56
|
-
view: typeof options.view === 'string' ? options.view : undefined,
|
|
57
|
-
type: typeof options.type === 'string' ? options.type : undefined,
|
|
58
|
-
category: typeof options.category === 'string' ? options.category : undefined,
|
|
59
|
-
severity: typeof options.severity === 'string' ? options.severity : undefined,
|
|
60
|
-
invocationId: typeof options.invocation === 'string' ? options.invocation : undefined,
|
|
61
|
-
fileId: typeof options.file === 'string' ? options.file : undefined,
|
|
62
|
-
limit: typeof options.limit === 'number' ? options.limit : undefined,
|
|
63
|
-
cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
|
|
64
|
-
}),
|
|
15
|
+
command.addCommand(createOperationListCommand(factory, {
|
|
16
|
+
operationId: 'runs.trace.list',
|
|
17
|
+
name: 'trace',
|
|
18
|
+
description: 'List run trace spans',
|
|
19
|
+
argNames: ['runId'],
|
|
65
20
|
}));
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.option('--type <type>', 'Filter by activity type')
|
|
72
|
-
.option('--category <category>', 'Filter by activity category')
|
|
73
|
-
.option('--severity <severity>', 'Filter by severity')
|
|
74
|
-
.option('--invocation <id>', 'Filter by invocation id')
|
|
75
|
-
.option('--file <id>', 'Filter by file id')
|
|
76
|
-
.option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
|
|
77
|
-
await requestOperationAndPrint(factory, 'runs.activity.list', await buildOperationInput('runs.activity.list', options, {
|
|
78
|
-
pathParams: { runId },
|
|
79
|
-
query: {
|
|
80
|
-
view: options.view,
|
|
81
|
-
type: options.type,
|
|
82
|
-
category: options.category,
|
|
83
|
-
severity: options.severity,
|
|
84
|
-
invocationId: options.invocation,
|
|
85
|
-
fileId: options.file,
|
|
86
|
-
limit: options.limit,
|
|
87
|
-
cursor: options.cursor,
|
|
88
|
-
},
|
|
89
|
-
output: outputFlags(options),
|
|
90
|
-
}));
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
function createRunEventsCommand(factory) {
|
|
94
|
-
const command = new Command('events').description('Inspect run events');
|
|
95
|
-
command.addCommand(createRunEventsListCommand(factory));
|
|
96
|
-
command.addCommand(createSseCommand(factory, {
|
|
97
|
-
name: 'stream',
|
|
98
|
-
description: 'Stream run events',
|
|
99
|
-
operationId: 'runs.events.stream',
|
|
100
|
-
configure: (cmd) => addPaginationFlags(cmd)
|
|
101
|
-
.option('--type <type>', 'Filter by event type')
|
|
102
|
-
.option('--status <status>', 'Filter by event status')
|
|
103
|
-
.option('--page-id <id>', 'Filter by page id')
|
|
104
|
-
.option('--context-id <id>', 'Filter by browser context id'),
|
|
105
|
-
query: (options) => ({
|
|
106
|
-
type: typeof options.type === 'string' ? options.type : undefined,
|
|
107
|
-
status: typeof options.status === 'string' ? options.status : undefined,
|
|
108
|
-
pageId: typeof options.pageId === 'string' ? options.pageId : undefined,
|
|
109
|
-
contextId: typeof options.contextId === 'string' ? options.contextId : undefined,
|
|
110
|
-
limit: typeof options.limit === 'number' ? options.limit : undefined,
|
|
111
|
-
cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
|
|
112
|
-
}),
|
|
21
|
+
command.addCommand(createOperationListCommand(factory, {
|
|
22
|
+
operationId: 'runs.events.list',
|
|
23
|
+
name: 'events',
|
|
24
|
+
description: 'List run events',
|
|
25
|
+
argNames: ['runId'],
|
|
113
26
|
}));
|
|
27
|
+
command.addCommand(createRunStreamCommand(factory));
|
|
114
28
|
return command;
|
|
115
29
|
}
|
|
116
|
-
function
|
|
117
|
-
return
|
|
118
|
-
.
|
|
119
|
-
.option('--status <status>', 'Filter by event status')
|
|
120
|
-
.option('--page-id <id>', 'Filter by page id')
|
|
121
|
-
.option('--context-id <id>', 'Filter by browser context id')
|
|
122
|
-
.option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
|
|
123
|
-
await requestOperationAndPrint(factory, 'runs.events.list', await buildOperationInput('runs.events.list', options, {
|
|
124
|
-
pathParams: { runId },
|
|
125
|
-
query: {
|
|
126
|
-
type: options.type,
|
|
127
|
-
status: options.status,
|
|
128
|
-
pageId: options.pageId,
|
|
129
|
-
contextId: options.contextId,
|
|
130
|
-
limit: options.limit,
|
|
131
|
-
cursor: options.cursor,
|
|
132
|
-
},
|
|
133
|
-
output: outputFlags(options),
|
|
134
|
-
}));
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
function createRunFilesCommand(factory) {
|
|
138
|
-
const command = new Command('files').description('Inspect run files');
|
|
139
|
-
command.addCommand(createRunFilesListCommand(factory));
|
|
140
|
-
command.addCommand(createRunFilesExportCommand(factory));
|
|
141
|
-
return command;
|
|
142
|
-
}
|
|
143
|
-
function createRunFilesListCommand(factory) {
|
|
144
|
-
return addOutputFlags(addPaginationFlags(new Command('list').description('List run files').argument('<runId>'))
|
|
145
|
-
.option('--type <type>', 'Filter by file type')
|
|
146
|
-
.option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
|
|
147
|
-
await requestOperationAndPrint(factory, 'runs.files.list', await buildOperationInput('runs.files.list', options, {
|
|
148
|
-
pathParams: { runId },
|
|
149
|
-
query: {
|
|
150
|
-
type: options.type,
|
|
151
|
-
limit: options.limit,
|
|
152
|
-
cursor: options.cursor,
|
|
153
|
-
},
|
|
154
|
-
output: outputFlags(options),
|
|
155
|
-
}));
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function createRunFilesExportCommand(factory) {
|
|
159
|
-
return addOutputFlags(new Command('export')
|
|
160
|
-
.description('Export run files as an archive')
|
|
30
|
+
function createRunStreamCommand(factory) {
|
|
31
|
+
return new Command('stream')
|
|
32
|
+
.description('Stream trace spans and events from a run')
|
|
161
33
|
.argument('<runId>')
|
|
162
|
-
.
|
|
163
|
-
.option('--
|
|
164
|
-
.
|
|
165
|
-
const
|
|
166
|
-
format: 'zip',
|
|
167
|
-
name: options.name,
|
|
168
|
-
filter: options.type ? { type: options.type } : undefined,
|
|
169
|
-
};
|
|
170
|
-
const result = (await requestOperation(factory, 'runs.files.export', {
|
|
34
|
+
.option('--after <cursor>', 'Resume after a stream cursor')
|
|
35
|
+
.option('--include <kind>', 'trace or events')
|
|
36
|
+
.action(async (runId, options) => {
|
|
37
|
+
const stream = await streamOperationText(factory, 'runs.stream', {
|
|
171
38
|
pathParams: { runId },
|
|
172
|
-
|
|
173
|
-
}));
|
|
174
|
-
const fileId = result.fileId ?? result.id;
|
|
175
|
-
if (!fileId) {
|
|
176
|
-
throw new CliError('Expected run export response to include fileId');
|
|
177
|
-
}
|
|
178
|
-
const data = await downloadOperation(factory, 'files.content', {
|
|
179
|
-
pathParams: { fileId },
|
|
39
|
+
query: { after: options.after, include: options.include },
|
|
180
40
|
});
|
|
181
|
-
await
|
|
41
|
+
for await (const chunk of stream)
|
|
42
|
+
factory.io.writeOut(chunk);
|
|
182
43
|
});
|
|
183
44
|
}
|
|
184
|
-
function createRunInvocationsCommand(factory) {
|
|
185
|
-
const command = addOutputFlags(addPaginationFlags(new Command('invocations').description('List run invocations').argument('<runId>'))
|
|
186
|
-
.option('--status <status>', 'Filter by invocation status')
|
|
187
|
-
.option('--action <action>', 'Filter by invocation action')
|
|
188
|
-
.option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)'));
|
|
189
|
-
command.action(async (runId, options) => {
|
|
190
|
-
await requestOperationAndPrint(factory, 'runs.invocations.list', await buildOperationInput('runs.invocations.list', options, {
|
|
191
|
-
pathParams: { runId },
|
|
192
|
-
query: {
|
|
193
|
-
status: options.status,
|
|
194
|
-
action: options.action,
|
|
195
|
-
limit: options.limit,
|
|
196
|
-
cursor: options.cursor,
|
|
197
|
-
},
|
|
198
|
-
output: outputFlags(options),
|
|
199
|
-
}));
|
|
200
|
-
});
|
|
201
|
-
return command;
|
|
202
|
-
}
|
|
203
|
-
function createRunInvocationCommand(factory) {
|
|
204
|
-
const command = new Command('invocation').description('Inspect run invocations');
|
|
205
|
-
command.addCommand(addOutputFlags(new Command('get')
|
|
206
|
-
.description('Get a run invocation')
|
|
207
|
-
.argument('<runId>')
|
|
208
|
-
.argument('<invocationId>')
|
|
209
|
-
.option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, invocationId, options) => {
|
|
210
|
-
await requestOperationAndPrint(factory, 'runs.invocations.get', await buildOperationInput('runs.invocations.get', options, {
|
|
211
|
-
pathParams: { runId, invocationId },
|
|
212
|
-
output: outputFlags(options),
|
|
213
|
-
}));
|
|
214
|
-
}));
|
|
215
|
-
return command;
|
|
216
|
-
}
|
|
217
|
-
function createRunViewerCommand(factory, name, description) {
|
|
218
|
-
return createOperationJsonBodyCommand(factory, {
|
|
219
|
-
operationId: name === 'live' ? 'runs.live' : 'runs.recording',
|
|
220
|
-
name,
|
|
221
|
-
description,
|
|
222
|
-
argNames: ['runId'],
|
|
223
|
-
configure: (cmd) => cmd
|
|
224
|
-
.option('--expires-in-seconds <seconds>', 'Viewer expiration in seconds', parsePositiveInteger)
|
|
225
|
-
.option('--control <none|input>', 'Live viewer control mode'),
|
|
226
|
-
body: async (_args, options) => {
|
|
227
|
-
return {
|
|
228
|
-
expiresInSeconds: typeof options.expiresInSeconds === 'number' ? options.expiresInSeconds : undefined,
|
|
229
|
-
control: name === 'live' ? options.control : undefined,
|
|
230
|
-
};
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
function createSseCommand(factory, config) {
|
|
235
|
-
let command = new Command(config.name).description(config.description).argument('<runId>');
|
|
236
|
-
command = config.configure(command);
|
|
237
|
-
return command.action(async (runId, options) => {
|
|
238
|
-
const stream = await streamOperationText(factory, config.operationId, {
|
|
239
|
-
pathParams: { runId },
|
|
240
|
-
query: config.query(options),
|
|
241
|
-
});
|
|
242
|
-
await renderSseStream(factory, stream);
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
async function renderSseStream(factory, stream) {
|
|
246
|
-
let buffer = '';
|
|
247
|
-
for await (const chunk of stream) {
|
|
248
|
-
buffer += chunk;
|
|
249
|
-
let frameEnd = buffer.indexOf('\n\n');
|
|
250
|
-
while (frameEnd !== -1) {
|
|
251
|
-
const frame = buffer.slice(0, frameEnd);
|
|
252
|
-
buffer = buffer.slice(frameEnd + 2);
|
|
253
|
-
renderSseFrame(factory, frame);
|
|
254
|
-
frameEnd = buffer.indexOf('\n\n');
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (buffer.trim())
|
|
258
|
-
renderSseFrame(factory, buffer);
|
|
259
|
-
}
|
|
260
|
-
function renderSseFrame(factory, frame) {
|
|
261
|
-
let eventName = 'message';
|
|
262
|
-
const dataLines = [];
|
|
263
|
-
for (const line of frame.split(/\r?\n/)) {
|
|
264
|
-
if (line.startsWith(':'))
|
|
265
|
-
continue;
|
|
266
|
-
const separator = line.indexOf(':');
|
|
267
|
-
const field = separator === -1 ? line : line.slice(0, separator);
|
|
268
|
-
const value = separator === -1 ? '' : line.slice(separator + 1).trimStart();
|
|
269
|
-
if (field === 'event')
|
|
270
|
-
eventName = value;
|
|
271
|
-
if (field === 'data')
|
|
272
|
-
dataLines.push(value);
|
|
273
|
-
}
|
|
274
|
-
if (eventName === 'heartbeat')
|
|
275
|
-
return;
|
|
276
|
-
const rawData = dataLines.join('\n');
|
|
277
|
-
if (!rawData)
|
|
278
|
-
return;
|
|
279
|
-
let parsed;
|
|
280
|
-
try {
|
|
281
|
-
parsed = JSON.parse(rawData);
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
factory.io.writeOut(`${rawData}\n`);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (!factory.io.isStdoutTTY()) {
|
|
288
|
-
factory.io.writeOut(`${JSON.stringify(parsed)}\n`);
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
factory.io.writeOut(`${formatRunEvent(parsed)}\n`);
|
|
292
|
-
}
|
|
293
|
-
function formatRunEvent(value) {
|
|
294
|
-
if (!value || typeof value !== 'object')
|
|
295
|
-
return JSON.stringify(value);
|
|
296
|
-
const event = value;
|
|
297
|
-
const time = typeof event.time === 'string'
|
|
298
|
-
? event.time
|
|
299
|
-
: typeof event.createdAt === 'string'
|
|
300
|
-
? event.createdAt
|
|
301
|
-
: '';
|
|
302
|
-
const label = [event.category, event.type].filter((part) => typeof part === 'string').join('/');
|
|
303
|
-
const status = typeof event.status === 'string' ? ` ${event.status}` : '';
|
|
304
|
-
const name = typeof event.title === 'string'
|
|
305
|
-
? ` ${event.title}`
|
|
306
|
-
: typeof event.name === 'string'
|
|
307
|
-
? ` ${event.name}`
|
|
308
|
-
: '';
|
|
309
|
-
const suffix = event.data && Object.keys(event.data).length > 0
|
|
310
|
-
? ` ${JSON.stringify(event.data)}`
|
|
311
|
-
: '';
|
|
312
|
-
return [time, label || 'event'].filter(Boolean).join(' ') + status + name + suffix;
|
|
313
|
-
}
|