@bctrl/cli 0.1.3 → 0.1.5

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.
Files changed (50) hide show
  1. package/README.md +95 -95
  2. package/dist/api/auth.d.ts +5 -6
  3. package/dist/api/auth.js +4 -5
  4. package/dist/api/client.d.ts +1 -0
  5. package/dist/api/client.js +3 -0
  6. package/dist/api/errors.js +2 -2
  7. package/dist/bin/bctrl.js +0 -0
  8. package/dist/commands/ai/index.js +71 -106
  9. package/dist/commands/{browser → api-key}/index.d.ts +1 -1
  10. package/dist/commands/api-key/index.js +47 -0
  11. package/dist/commands/auth/login.js +2 -1
  12. package/dist/commands/auth/status.js +5 -0
  13. package/dist/commands/browser-extension/index.d.ts +3 -0
  14. package/dist/commands/browser-extension/index.js +85 -0
  15. package/dist/commands/file/index.js +26 -21
  16. package/dist/commands/{invocation → help}/index.d.ts +1 -1
  17. package/dist/commands/help/index.js +17 -0
  18. package/dist/commands/proxy/index.d.ts +3 -0
  19. package/dist/commands/proxy/index.js +72 -0
  20. package/dist/commands/run/index.js +227 -160
  21. package/dist/commands/runtime/index.js +343 -118
  22. package/dist/commands/shared/help.d.ts +1 -0
  23. package/dist/commands/shared/help.js +10 -6
  24. package/dist/commands/shared/operation.d.ts +83 -0
  25. package/dist/commands/shared/{rest.js → operation.js} +81 -32
  26. package/dist/commands/space/index.js +62 -68
  27. package/dist/commands/space/list.d.ts +1 -11
  28. package/dist/commands/space/list.js +12 -20
  29. package/dist/commands/subaccount/index.js +56 -125
  30. package/dist/commands/tool/index.js +28 -22
  31. package/dist/commands/tool-call/index.js +11 -6
  32. package/dist/commands/toolset/index.js +16 -15
  33. package/dist/commands/usage/index.d.ts +3 -0
  34. package/dist/commands/usage/index.js +13 -0
  35. package/dist/commands/vault/index.js +115 -34
  36. package/dist/config/auth-store.d.ts +10 -12
  37. package/dist/config/auth-store.js +4 -5
  38. package/dist/generated/help.d.ts +5992 -71
  39. package/dist/generated/help.js +7918 -123
  40. package/dist/generated/openapi-routes.d.ts +391 -0
  41. package/dist/generated/openapi-routes.js +100 -0
  42. package/dist/generated/openapi-types.d.ts +13755 -0
  43. package/dist/generated/openapi-types.js +5 -0
  44. package/dist/openapi.d.ts +20 -0
  45. package/dist/openapi.js +1 -0
  46. package/dist/root.js +10 -4
  47. package/package.json +47 -45
  48. package/dist/commands/browser/index.js +0 -53
  49. package/dist/commands/invocation/index.js +0 -36
  50. package/dist/commands/shared/rest.d.ts +0 -54
@@ -2,16 +2,18 @@ import { Command } from 'commander';
2
2
  import { readBlob, readJsonFile, writeBinary } from '../shared/io.js';
3
3
  import { parsePositiveInteger } from '../shared/options.js';
4
4
  import { addOutputFlags, outputData } from '../shared/output.js';
5
- import { createDeleteCommand, createJsonBodyCommand, createListCommand, createViewCommand, } from '../shared/rest.js';
5
+ import { createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, downloadOperation, uploadOperationFile, } from '../shared/operation.js';
6
6
  export function createFileCommand(factory) {
7
7
  const command = new Command('file').description('Upload, download, and manage BCTRL files');
8
- command.addCommand(createListCommand(factory, {
8
+ command.addCommand(createOperationListCommand(factory, {
9
+ operationId: 'files.list',
9
10
  description: 'List files',
10
- path: '/files',
11
11
  configure: (cmd) => cmd
12
12
  .option('--space <id>', 'Filter by space id')
13
13
  .option('--source <source>', 'Filter by file source')
14
14
  .option('--prefix <path>', 'Filter by path prefix')
15
+ .option('--folders', 'Directory view: direct files plus subfolder rollups')
16
+ .option('--created-after <iso>', 'Only files created after this timestamp')
15
17
  .option('--query <text>', 'Search query')
16
18
  .option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
17
19
  .option('--cursor <cursor>', 'Pagination cursor'),
@@ -19,26 +21,28 @@ export function createFileCommand(factory) {
19
21
  spaceId: typeof options.space === 'string' ? options.space : undefined,
20
22
  source: typeof options.source === 'string' ? options.source : undefined,
21
23
  prefix: typeof options.prefix === 'string' ? options.prefix : undefined,
22
- query: typeof options.query === 'string' ? options.query : undefined,
24
+ include: options.folders === true ? 'folders' : undefined,
25
+ createdAfter: typeof options.createdAfter === 'string' ? options.createdAfter : undefined,
26
+ // The public query param is `q` (was sent as `query`, which the API ignores).
27
+ q: typeof options.query === 'string' ? options.query : undefined,
23
28
  limit: typeof options.limit === 'number' ? options.limit : undefined,
24
29
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
25
30
  }),
26
31
  }));
27
- command.addCommand(createViewCommand(factory, {
32
+ command.addCommand(createOperationViewCommand(factory, {
33
+ operationId: 'files.get',
34
+ name: 'get',
28
35
  description: 'View file metadata',
29
- path: '/files/{fileId}',
30
36
  argName: 'fileId',
31
37
  }));
32
38
  command.addCommand(addOutputFlags(new Command('upload')
33
39
  .description('Upload a file')
34
40
  .argument('<path>')
35
- .requiredOption('--space <id>', 'Space id')
41
+ .option('--space <id>', 'Space id; omitted uses caller default space')
36
42
  .option('--path <storagePath>', 'Storage path')
37
- .option('--name <name>', 'Display name'))
38
- .action(async (path, options) => {
39
- const client = await factory.apiClient();
43
+ .option('--name <name>', 'Display name')).action(async (path, options) => {
40
44
  const file = await readBlob(path);
41
- const result = await client.uploadFile('/files', {
45
+ const result = await uploadOperationFile(factory, 'files.upload', {
42
46
  file: file.blob,
43
47
  fileName: options.name ?? file.fileName,
44
48
  query: { spaceId: options.space },
@@ -54,23 +58,24 @@ export function createFileCommand(factory) {
54
58
  .argument('<id>')
55
59
  .requiredOption('--to <path>', 'Output path, or - for stdout')
56
60
  .action(async (id, options) => {
57
- const client = await factory.apiClient();
58
- const data = await client.download(`/files/${encodeURIComponent(id)}/content`);
61
+ const data = await downloadOperation(factory, 'files.content', {
62
+ pathParams: { fileId: id },
63
+ });
59
64
  await writeBinary(options.to, data);
60
65
  }));
61
- command.addCommand(createJsonBodyCommand(factory, {
62
- name: 'edit',
66
+ command.addCommand(createOperationJsonBodyCommand(factory, {
67
+ operationId: 'files.update',
68
+ name: 'patch',
63
69
  description: 'Edit file metadata',
64
- method: 'patch',
65
- path: '/files/{fileId}',
66
70
  argNames: ['fileId'],
67
71
  configure: (cmd) => cmd
68
72
  .option('--name <name>', 'Display name')
69
73
  .option('--metadata-file <path>', 'Metadata JSON file')
70
74
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
71
75
  body: async (_args, options) => {
72
- if (typeof options.input === 'string')
73
- return readJsonFile(options.input);
76
+ if (typeof options.input === 'string') {
77
+ return (await readJsonFile(options.input));
78
+ }
74
79
  return {
75
80
  name: options.name,
76
81
  metadata: typeof options.metadataFile === 'string'
@@ -79,9 +84,9 @@ export function createFileCommand(factory) {
79
84
  };
80
85
  },
81
86
  }));
82
- command.addCommand(createDeleteCommand(factory, {
87
+ command.addCommand(createOperationDeleteCommand(factory, {
88
+ operationId: 'files.delete',
83
89
  description: 'Delete a file',
84
- path: '/files/{fileId}',
85
90
  argNames: ['fileId'],
86
91
  }));
87
92
  return command;
@@ -1,3 +1,3 @@
1
1
  import { Command } from 'commander';
2
2
  import type { Factory } from '../../factory.js';
3
- export declare function createInvocationCommand(factory: Factory): Command;
3
+ export declare function createHelpCommand(factory: Factory): Command;
@@ -0,0 +1,17 @@
1
+ import { Command } from 'commander';
2
+ import { addOutputFlags } from '../shared/output.js';
3
+ import { outputFlags, requestOperationAndPrint } from '../shared/operation.js';
4
+ export function createHelpCommand(factory) {
5
+ return addOutputFlags(new Command('help')
6
+ .description('Get BCTRL API help')
7
+ .option('--topic <topic>', 'Help topic')
8
+ .option('--audience <audience>', 'Help audience')).action(async (options) => {
9
+ await requestOperationAndPrint(factory, 'help', {
10
+ query: {
11
+ topic: options.topic,
12
+ audience: options.audience,
13
+ },
14
+ output: outputFlags(options),
15
+ });
16
+ });
17
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { Factory } from '../../factory.js';
3
+ export declare function createProxyCommand(factory: Factory): Command;
@@ -0,0 +1,72 @@
1
+ import { Command } from 'commander';
2
+ import { addOutputFlags } from '../shared/output.js';
3
+ import { addPaginationFlags, createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, outputFlags, requestOperationAndPrint, } from '../shared/operation.js';
4
+ export function createProxyCommand(factory) {
5
+ const command = new Command('proxy').description('Manage proxies');
6
+ command.addCommand(createOperationListCommand(factory, {
7
+ operationId: 'proxies.list',
8
+ description: 'List proxies',
9
+ }));
10
+ command.addCommand(createOperationViewCommand(factory, {
11
+ operationId: 'proxies.get',
12
+ name: 'get',
13
+ description: 'Get a proxy',
14
+ argName: 'proxyId',
15
+ }));
16
+ command.addCommand(createOperationJsonBodyCommand(factory, {
17
+ operationId: 'proxies.create',
18
+ name: 'create',
19
+ description: 'Create a proxy',
20
+ }));
21
+ command.addCommand(createOperationJsonBodyCommand(factory, {
22
+ operationId: 'proxies.update',
23
+ name: 'patch',
24
+ description: 'Update a proxy',
25
+ argNames: ['proxyId'],
26
+ }));
27
+ command.addCommand(addOutputFlags(new Command('test').description('Test a proxy').argument('<proxyId>')).action(async (proxyId, options) => {
28
+ await requestOperationAndPrint(factory, 'proxies.test', {
29
+ pathParams: { proxyId },
30
+ output: outputFlags(options),
31
+ });
32
+ }));
33
+ command.addCommand(createOperationDeleteCommand(factory, {
34
+ operationId: 'proxies.delete',
35
+ description: 'Delete a proxy',
36
+ argNames: ['proxyId'],
37
+ }));
38
+ command.addCommand(createProxyPoolCommand(factory));
39
+ return command;
40
+ }
41
+ function createProxyPoolCommand(factory) {
42
+ const command = new Command('pool').description('Inspect managed proxy pools');
43
+ command.addCommand(createOperationListCommand(factory, {
44
+ operationId: 'proxies.pools.list',
45
+ description: 'List proxy pools',
46
+ configure: (cmd) => addPaginationFlags(cmd)
47
+ .option('--country <country>', 'Filter by ISO country code')
48
+ .option('--category <category>', 'Filter by category')
49
+ .option('--available <boolean>', 'Filter by availability'),
50
+ query: (options) => ({
51
+ country: typeof options.country === 'string' ? options.country : undefined,
52
+ category: typeof options.category === 'string' ? options.category : undefined,
53
+ available: typeof options.available === 'string' ? parseBooleanFlag(options.available) : undefined,
54
+ limit: typeof options.limit === 'number' ? options.limit : undefined,
55
+ cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
56
+ }),
57
+ }));
58
+ command.addCommand(createOperationViewCommand(factory, {
59
+ operationId: 'proxies.pools.get',
60
+ name: 'get',
61
+ description: 'Get a proxy pool',
62
+ argName: 'poolId',
63
+ }));
64
+ return command;
65
+ }
66
+ function parseBooleanFlag(value) {
67
+ if (value === 'true')
68
+ return true;
69
+ if (value === 'false')
70
+ return false;
71
+ return undefined;
72
+ }
@@ -3,57 +3,242 @@ import { CliError } from '../../runtime/errors.js';
3
3
  import { readJsonFile, writeBinary } from '../shared/io.js';
4
4
  import { parsePositiveInteger } from '../shared/options.js';
5
5
  import { addOutputFlags } from '../shared/output.js';
6
- import { addPaginationFlags, createJsonBodyCommand, createListCommand, createViewCommand, requestAndPrint, } from '../shared/rest.js';
6
+ import { addPaginationFlags, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, downloadOperation, outputFlags, requestOperation, requestOperationAndPrint, streamOperationText, } from '../shared/operation.js';
7
7
  export function createRunCommand(factory) {
8
- const command = new Command('run').description('Inspect and stream BCTRL run history');
9
- command.addCommand(createListCommand(factory, {
8
+ const command = new Command('run').description('Inspect and stream runs');
9
+ command.addCommand(createOperationListCommand(factory, {
10
+ operationId: 'runs.list',
10
11
  description: 'List runs',
11
- path: '/runs',
12
12
  configure: (cmd) => addPaginationFlags(cmd)
13
13
  .option('--space <id>', 'Filter by space id')
14
14
  .option('--runtime <id>', 'Filter by runtime id')
15
- .option('--status <status>', 'Filter by run status'),
15
+ .option('--status <status>', 'Filter by run status')
16
+ .option('--subaccount <id>', 'Filter by subaccount id'),
16
17
  query: (options) => ({
17
18
  spaceId: typeof options.space === 'string' ? options.space : undefined,
18
19
  runtimeId: typeof options.runtime === 'string' ? options.runtime : undefined,
19
20
  status: typeof options.status === 'string' ? options.status : undefined,
21
+ subaccountId: typeof options.subaccount === 'string' ? options.subaccount : undefined,
20
22
  limit: typeof options.limit === 'number' ? options.limit : undefined,
21
23
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
22
24
  }),
23
25
  }));
24
- command.addCommand(createViewCommand(factory, {
25
- description: 'View a run',
26
- path: '/runs/{id}',
26
+ command.addCommand(createOperationViewCommand(factory, {
27
+ operationId: 'runs.get',
28
+ name: 'get',
29
+ description: 'Get a run',
30
+ argName: 'runId',
27
31
  }));
32
+ command.addCommand(createRunActivityCommand(factory));
28
33
  command.addCommand(createRunEventsCommand(factory));
29
- command.addCommand(createRunCommandsCommand(factory));
30
34
  command.addCommand(createRunFilesCommand(factory));
31
- command.addCommand(createRunExportCommand(factory));
32
- command.addCommand(createRunWatchCommand(factory));
33
- command.addCommand(createRunWaitCommand(factory));
34
- command.addCommand(createRunViewerCommand(factory, 'live', 'Mint a live viewer URL'));
35
- command.addCommand(createRunViewerCommand(factory, 'recording', 'Mint a recording viewer URL'));
35
+ command.addCommand(createRunInvocationsCommand(factory));
36
+ command.addCommand(createRunInvocationCommand(factory));
37
+ command.addCommand(createRunViewerCommand(factory, 'live', 'Create a live viewer URL'));
38
+ command.addCommand(createRunViewerCommand(factory, 'recording', 'Create a recording viewer URL'));
36
39
  return command;
37
40
  }
38
- function createRunWatchCommand(factory) {
39
- return new Command('watch')
40
- .description('Stream run events')
41
- .argument('<id>')
42
- .option('--type <type...>', 'Filter by event type')
43
- .option('--category <category...>', 'Filter by event category')
44
- .option('--connection <id>', 'Filter by connection id')
45
- .option('-L, --limit <number>', 'Maximum number of events per poll', parsePositiveInteger)
46
- .option('--cursor <cursor>', 'Start cursor')
47
- .action(async (id, options) => {
48
- const client = await factory.apiClient();
49
- const stream = await client.streamText(`/runs/${encodeURIComponent(id)}/events/stream`, {
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
+ }),
65
+ }));
66
+ return command;
67
+ }
68
+ function createRunActivityListCommand(factory) {
69
+ return addOutputFlags(addPaginationFlags(new Command('list').description('List run activities').argument('<runId>'))
70
+ .option('--view <view>', 'Activity view')
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')).action(async (runId, options) => {
76
+ await requestOperationAndPrint(factory, 'runs.activity.list', {
77
+ pathParams: { runId },
78
+ query: {
79
+ view: options.view,
80
+ type: options.type,
81
+ category: options.category,
82
+ severity: options.severity,
83
+ invocationId: options.invocation,
84
+ fileId: options.file,
85
+ limit: options.limit,
86
+ cursor: options.cursor,
87
+ },
88
+ output: outputFlags(options),
89
+ });
90
+ });
91
+ }
92
+ function createRunEventsCommand(factory) {
93
+ const command = new Command('events').description('Inspect run events');
94
+ command.addCommand(createRunEventsListCommand(factory));
95
+ command.addCommand(createSseCommand(factory, {
96
+ name: 'stream',
97
+ description: 'Stream run events',
98
+ operationId: 'runs.events.stream',
99
+ configure: (cmd) => addPaginationFlags(cmd)
100
+ .option('--type <type>', 'Filter by event type')
101
+ .option('--status <status>', 'Filter by event status')
102
+ .option('--page-id <id>', 'Filter by page id')
103
+ .option('--context-id <id>', 'Filter by browser context id'),
104
+ query: (options) => ({
105
+ type: typeof options.type === 'string' ? options.type : undefined,
106
+ status: typeof options.status === 'string' ? options.status : undefined,
107
+ pageId: typeof options.pageId === 'string' ? options.pageId : undefined,
108
+ contextId: typeof options.contextId === 'string' ? options.contextId : undefined,
109
+ limit: typeof options.limit === 'number' ? options.limit : undefined,
110
+ cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
111
+ }),
112
+ }));
113
+ return command;
114
+ }
115
+ function createRunEventsListCommand(factory) {
116
+ return addOutputFlags(addPaginationFlags(new Command('list').description('List run events').argument('<runId>'))
117
+ .option('--type <type>', 'Filter by event type')
118
+ .option('--status <status>', 'Filter by event status')
119
+ .option('--page-id <id>', 'Filter by page id')
120
+ .option('--context-id <id>', 'Filter by browser context id')).action(async (runId, options) => {
121
+ await requestOperationAndPrint(factory, 'runs.events.list', {
122
+ pathParams: { runId },
123
+ query: {
124
+ type: options.type,
125
+ status: options.status,
126
+ pageId: options.pageId,
127
+ contextId: options.contextId,
128
+ limit: options.limit,
129
+ cursor: options.cursor,
130
+ },
131
+ output: outputFlags(options),
132
+ });
133
+ });
134
+ }
135
+ function createRunFilesCommand(factory) {
136
+ const command = new Command('files').description('Inspect run files');
137
+ command.addCommand(createRunFilesListCommand(factory));
138
+ command.addCommand(createRunFilesExportCommand(factory));
139
+ return command;
140
+ }
141
+ function createRunFilesListCommand(factory) {
142
+ return addOutputFlags(addPaginationFlags(new Command('list').description('List run files').argument('<runId>')).option('--type <type>', 'Filter by file type')).action(async (runId, options) => {
143
+ await requestOperationAndPrint(factory, 'runs.files.list', {
144
+ pathParams: { runId },
145
+ query: {
146
+ type: options.type,
147
+ limit: options.limit,
148
+ cursor: options.cursor,
149
+ },
150
+ output: outputFlags(options),
151
+ });
152
+ });
153
+ }
154
+ function createRunFilesExportCommand(factory) {
155
+ return addOutputFlags(new Command('export')
156
+ .description('Export run files as an archive')
157
+ .argument('<runId>')
158
+ .requiredOption('--to <path>', 'Output archive path, or - for stdout')
159
+ .option('--name <name>', 'Export file name', 'run-files.zip')
160
+ .option('--type <type...>', 'Filter exported file types')
161
+ .option('--input <path>', 'Read JSON request body from file, or - for stdin')).action(async (runId, options) => {
162
+ const body = typeof options.input === 'string'
163
+ ? await readJsonFile(options.input)
164
+ : {
165
+ format: 'zip',
166
+ name: options.name,
167
+ filter: options.type ? { type: options.type } : undefined,
168
+ };
169
+ const result = (await requestOperation(factory, 'runs.files.export', {
170
+ pathParams: { runId },
171
+ body: body,
172
+ }));
173
+ const fileId = result.fileId ?? result.id;
174
+ if (!fileId) {
175
+ throw new CliError('Expected run export response to include fileId');
176
+ }
177
+ const data = await downloadOperation(factory, 'files.content', {
178
+ pathParams: { fileId },
179
+ });
180
+ await writeBinary(options.to, data);
181
+ });
182
+ }
183
+ function createRunInvocationsCommand(factory) {
184
+ const command = addOutputFlags(addPaginationFlags(new Command('invocations').description('List run invocations').argument('<runId>'))
185
+ .option('--status <status>', 'Filter by invocation status')
186
+ .option('--action <action>', 'Filter by invocation action'));
187
+ command.action(async (runId, options) => {
188
+ await requestOperationAndPrint(factory, 'runs.invocations.list', {
189
+ pathParams: { runId },
50
190
  query: {
51
- type: options.type?.join(','),
52
- category: options.category?.join(','),
53
- connectionId: options.connection,
191
+ status: options.status,
192
+ action: options.action,
54
193
  limit: options.limit,
55
194
  cursor: options.cursor,
56
195
  },
196
+ output: outputFlags(options),
197
+ });
198
+ });
199
+ return command;
200
+ }
201
+ function createRunInvocationCommand(factory) {
202
+ const command = new Command('invocation').description('Inspect run invocations');
203
+ command.addCommand(addOutputFlags(new Command('get')
204
+ .description('Get a run invocation')
205
+ .argument('<runId>')
206
+ .argument('<invocationId>')).action(async (runId, invocationId, options) => {
207
+ await requestOperationAndPrint(factory, 'runs.invocations.get', {
208
+ pathParams: { runId, invocationId },
209
+ output: outputFlags(options),
210
+ });
211
+ }));
212
+ return command;
213
+ }
214
+ function createRunViewerCommand(factory, name, description) {
215
+ return createOperationJsonBodyCommand(factory, {
216
+ operationId: name === 'live' ? 'runs.live' : 'runs.recording',
217
+ name,
218
+ description,
219
+ argNames: ['runId'],
220
+ configure: (cmd) => cmd
221
+ .option('--expires-in-seconds <seconds>', 'Viewer expiration in seconds', parsePositiveInteger)
222
+ .option('--control <none|input>', 'Live viewer control mode')
223
+ .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
224
+ body: async (_args, options) => {
225
+ if (typeof options.input === 'string') {
226
+ return (await readJsonFile(options.input));
227
+ }
228
+ return {
229
+ expiresInSeconds: typeof options.expiresInSeconds === 'number' ? options.expiresInSeconds : undefined,
230
+ control: name === 'live' ? options.control : undefined,
231
+ };
232
+ },
233
+ });
234
+ }
235
+ function createSseCommand(factory, config) {
236
+ let command = new Command(config.name).description(config.description).argument('<runId>');
237
+ command = config.configure(command);
238
+ return command.action(async (runId, options) => {
239
+ const stream = await streamOperationText(factory, config.operationId, {
240
+ pathParams: { runId },
241
+ query: config.query(options),
57
242
  });
58
243
  await renderSseStream(factory, stream);
59
244
  });
@@ -110,138 +295,20 @@ function formatRunEvent(value) {
110
295
  if (!value || typeof value !== 'object')
111
296
  return JSON.stringify(value);
112
297
  const event = value;
113
- const time = typeof event.time === 'string' ? event.time : '';
298
+ const time = typeof event.time === 'string'
299
+ ? event.time
300
+ : typeof event.createdAt === 'string'
301
+ ? event.createdAt
302
+ : '';
114
303
  const label = [event.category, event.type].filter((part) => typeof part === 'string').join('/');
115
304
  const status = typeof event.status === 'string' ? ` ${event.status}` : '';
116
- const name = typeof event.name === 'string' ? ` ${event.name}` : '';
117
- const suffix = event.data && Object.keys(event.data).length > 0 ? ` ${JSON.stringify(event.data)}` : '';
305
+ const name = typeof event.title === 'string'
306
+ ? ` ${event.title}`
307
+ : typeof event.name === 'string'
308
+ ? ` ${event.name}`
309
+ : '';
310
+ const suffix = event.data && Object.keys(event.data).length > 0
311
+ ? ` ${JSON.stringify(event.data)}`
312
+ : '';
118
313
  return [time, label || 'event'].filter(Boolean).join(' ') + status + name + suffix;
119
314
  }
120
- function createRunEventsCommand(factory) {
121
- return addOutputFlags(new Command('events')
122
- .description('List run events')
123
- .argument('<id>')
124
- .option('--type <type...>', 'Filter by event type')
125
- .option('--category <category...>', 'Filter by event category')
126
- .option('--connection <id>', 'Filter by connection id')
127
- .option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
128
- .option('--cursor <cursor>', 'Pagination cursor'))
129
- .action(async (id, options) => {
130
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(id)}/events`, {
131
- query: {
132
- type: options.type?.join(','),
133
- category: options.category?.join(','),
134
- connectionId: options.connection,
135
- limit: options.limit,
136
- cursor: options.cursor,
137
- },
138
- output: options,
139
- });
140
- });
141
- }
142
- function createRunCommandsCommand(factory) {
143
- return addOutputFlags(new Command('commands')
144
- .description('List run commands')
145
- .argument('<id>')
146
- .option('--type <type>', 'Filter by command type')
147
- .option('--connection <id>', 'Filter by connection id')
148
- .option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
149
- .option('--cursor <cursor>', 'Pagination cursor'))
150
- .action(async (id, options) => {
151
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(id)}/commands`, {
152
- query: {
153
- type: options.type,
154
- connectionId: options.connection,
155
- limit: options.limit,
156
- cursor: options.cursor,
157
- },
158
- output: options,
159
- });
160
- });
161
- }
162
- function createRunFilesCommand(factory) {
163
- return addOutputFlags(new Command('files')
164
- .description('List run files')
165
- .argument('<id>')
166
- .option('--type <type>', 'Filter by file type')
167
- .option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
168
- .option('--cursor <cursor>', 'Pagination cursor'))
169
- .action(async (id, options) => {
170
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(id)}/files`, {
171
- query: { type: options.type, limit: options.limit, cursor: options.cursor },
172
- output: options,
173
- });
174
- });
175
- }
176
- function createRunExportCommand(factory) {
177
- return addOutputFlags(new Command('export')
178
- .description('Export run files as an archive')
179
- .argument('<id>')
180
- .requiredOption('--to <path>', 'Output archive path, or - for stdout')
181
- .option('--name <name>', 'Export file name', 'run-files.zip')
182
- .option('--type <type...>', 'Filter exported file types'))
183
- .action(async (id, options) => {
184
- const client = await factory.apiClient();
185
- const result = await client.post(`/runs/${encodeURIComponent(id)}/files/export`, {
186
- body: {
187
- format: 'zip',
188
- name: options.name,
189
- filter: options.type ? { type: options.type } : undefined,
190
- },
191
- });
192
- const fileId = result.fileId ?? result.id;
193
- if (!fileId) {
194
- throw new CliError('Expected run export response to include fileId');
195
- }
196
- const data = await client.download(`/files/${encodeURIComponent(fileId)}/content`);
197
- await writeBinary(options.to, data);
198
- });
199
- }
200
- function createRunWaitCommand(factory) {
201
- return createJsonBodyCommand(factory, {
202
- name: 'wait',
203
- description: 'Wait for a run event',
204
- method: 'post',
205
- path: '/runs/{id}/events/wait',
206
- argNames: ['id'],
207
- configure: (cmd) => cmd
208
- .option('--type <type...>', 'Event type')
209
- .option('--category <category...>', 'Event category')
210
- .option('--connection <id>', 'Connection id')
211
- .option('--cursor <cursor>', 'Cursor')
212
- .option('--timeout-ms <ms>', 'Timeout in milliseconds')
213
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
214
- body: async (_args, options) => {
215
- if (typeof options.input === 'string')
216
- return readJsonFile(options.input);
217
- return {
218
- type: options.type,
219
- category: options.category,
220
- connectionId: options.connection,
221
- cursor: options.cursor,
222
- timeoutMs: options.timeoutMs ? Number(options.timeoutMs) : undefined,
223
- };
224
- },
225
- });
226
- }
227
- function createRunViewerCommand(factory, name, description) {
228
- return createJsonBodyCommand(factory, {
229
- name,
230
- description,
231
- method: 'post',
232
- path: `/runs/{id}/${name}`,
233
- argNames: ['id'],
234
- configure: (cmd) => cmd
235
- .option('--expires-in-seconds <seconds>', 'Viewer expiration in seconds')
236
- .option('--control <none|input>', 'Live viewer control mode')
237
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
238
- body: async (_args, options) => {
239
- if (typeof options.input === 'string')
240
- return readJsonFile(options.input);
241
- return {
242
- expiresInSeconds: options.expiresInSeconds ? Number(options.expiresInSeconds) : undefined,
243
- control: name === 'live' ? options.control : undefined,
244
- };
245
- },
246
- });
247
- }