@bctrl/cli 0.1.4 → 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.
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import { readJsonFile } from '../shared/io.js';
3
3
  import { addOutputFlags } from '../shared/output.js';
4
- import { createDeleteCommand, createJsonBodyCommand, createListCommand, createViewCommand, requestAndPrint, } from '../shared/rest.js';
4
+ import { createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, outputFlags, requestOperationAndPrint, } from '../shared/operation.js';
5
5
  export function createAiCommand(factory) {
6
6
  const command = new Command('ai').description('Manage AI models and credentials');
7
7
  command.addCommand(createAiModelsCommand(factory));
@@ -10,9 +10,9 @@ export function createAiCommand(factory) {
10
10
  }
11
11
  function createAiModelsCommand(factory) {
12
12
  const command = new Command('models').description('Discover AI models');
13
- command.addCommand(createListCommand(factory, {
13
+ command.addCommand(createOperationListCommand(factory, {
14
+ operationId: 'ai.models.list',
14
15
  description: 'List AI models',
15
- path: '/ai/models',
16
16
  configure: (cmd) => cmd
17
17
  .option('--provider <provider>', 'Filter by provider')
18
18
  .option('--status <status>', 'Filter by support status')
@@ -21,7 +21,7 @@ function createAiModelsCommand(factory) {
21
21
  query: (options) => ({
22
22
  provider: typeof options.provider === 'string' ? options.provider : undefined,
23
23
  status: typeof options.status === 'string' ? options.status : undefined,
24
- managed: typeof options.managed === 'string' ? options.managed : undefined,
24
+ managed: typeof options.managed === 'string' ? parseBooleanFlag(options.managed) : undefined,
25
25
  engine: typeof options.engine === 'string' ? options.engine : undefined,
26
26
  }),
27
27
  }));
@@ -29,9 +29,9 @@ function createAiModelsCommand(factory) {
29
29
  }
30
30
  function createAiCredentialsCommand(factory) {
31
31
  const command = new Command('credentials').description('Manage BYOK AI credentials');
32
- command.addCommand(createListCommand(factory, {
32
+ command.addCommand(createOperationListCommand(factory, {
33
+ operationId: 'ai.credentials.list',
33
34
  description: 'List AI credentials',
34
- path: '/ai/credentials',
35
35
  configure: (cmd) => cmd
36
36
  .option('--provider <provider>', 'Filter by provider')
37
37
  .option('--name <name>', 'Filter by name')
@@ -42,32 +42,32 @@ function createAiCredentialsCommand(factory) {
42
42
  status: typeof options.status === 'string' ? options.status : undefined,
43
43
  }),
44
44
  }));
45
- command.addCommand(createViewCommand(factory, {
45
+ command.addCommand(createOperationViewCommand(factory, {
46
+ operationId: 'ai.credentials.get',
46
47
  name: 'get',
47
48
  description: 'Get an AI credential',
48
- path: '/ai/credentials/{credentialId}',
49
49
  argName: 'credentialId',
50
50
  }));
51
- command.addCommand(createAiCredentialWriteCommand(factory, 'create', 'post', '/ai/credentials'));
52
- command.addCommand(createAiCredentialWriteCommand(factory, 'patch', 'patch', '/ai/credentials/{credentialId}', [
53
- 'credentialId',
54
- ]));
51
+ command.addCommand(createAiCredentialWriteCommand(factory, 'create', 'ai.credentials.create'));
52
+ command.addCommand(createAiCredentialWriteCommand(factory, 'patch', 'ai.credentials.update', ['credentialId']));
55
53
  command.addCommand(addOutputFlags(new Command('test').description('Test an AI credential').argument('<credentialId>')).action(async (credentialId, options) => {
56
- await requestAndPrint(factory, 'post', `/ai/credentials/${encodeURIComponent(credentialId)}/test`, { output: options });
54
+ await requestOperationAndPrint(factory, 'ai.credentials.test', {
55
+ pathParams: { credentialId },
56
+ output: outputFlags(options),
57
+ });
57
58
  }));
58
- command.addCommand(createDeleteCommand(factory, {
59
+ command.addCommand(createOperationDeleteCommand(factory, {
60
+ operationId: 'ai.credentials.delete',
59
61
  description: 'Delete an AI credential',
60
- path: '/ai/credentials/{credentialId}',
61
62
  argNames: ['credentialId'],
62
63
  }));
63
64
  return command;
64
65
  }
65
- function createAiCredentialWriteCommand(factory, name, method, path, argNames = []) {
66
- return createJsonBodyCommand(factory, {
66
+ function createAiCredentialWriteCommand(factory, name, operationId, argNames = []) {
67
+ return createOperationJsonBodyCommand(factory, {
68
+ operationId,
67
69
  name,
68
70
  description: name === 'create' ? 'Create an AI credential' : 'Update an AI credential',
69
- method,
70
- path,
71
71
  argNames,
72
72
  configure: (cmd) => cmd
73
73
  .option('--name <name>', 'Credential name')
@@ -79,8 +79,9 @@ function createAiCredentialWriteCommand(factory, name, method, path, argNames =
79
79
  .option('--subaccount-id <id>', 'Subaccount scope when using a parent/org key')
80
80
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
81
81
  body: async (_args, options) => {
82
- if (typeof options.input === 'string')
83
- return readJsonFile(options.input);
82
+ if (typeof options.input === 'string') {
83
+ return (await readJsonFile(options.input));
84
+ }
84
85
  return {
85
86
  name: options.name,
86
87
  provider: options.provider,
@@ -93,3 +94,10 @@ function createAiCredentialWriteCommand(factory, name, method, path, argNames =
93
94
  },
94
95
  });
95
96
  }
97
+ function parseBooleanFlag(value) {
98
+ if (value === 'true')
99
+ return true;
100
+ if (value === 'false')
101
+ return false;
102
+ return undefined;
103
+ }
@@ -1,11 +1,11 @@
1
1
  import { Command } from 'commander';
2
2
  import { readJsonFile } from '../shared/io.js';
3
- import { createDeleteCommand, createJsonBodyCommand, createListCommand, } from '../shared/rest.js';
3
+ import { createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, } from '../shared/operation.js';
4
4
  export function createApiKeyCommand(factory) {
5
5
  const command = new Command('api-key').description('Manage API keys');
6
- command.addCommand(createListCommand(factory, {
6
+ command.addCommand(createOperationListCommand(factory, {
7
+ operationId: 'api-keys.list',
7
8
  description: 'List API keys',
8
- path: '/api-keys',
9
9
  configure: (cmd) => cmd
10
10
  .option('--subaccount-id <id>', 'Filter by subaccount id')
11
11
  .option('--kind <kind>', 'Filter by key kind: organization or subaccount')
@@ -18,19 +18,19 @@ export function createApiKeyCommand(factory) {
18
18
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
19
19
  }),
20
20
  }));
21
- command.addCommand(createJsonBodyCommand(factory, {
21
+ command.addCommand(createOperationJsonBodyCommand(factory, {
22
+ operationId: 'api-keys.create',
22
23
  name: 'create',
23
24
  description: 'Create an API key',
24
- method: 'post',
25
- path: '/api-keys',
26
25
  configure: (cmd) => cmd
27
26
  .option('--name <name>', 'API key name')
28
27
  .option('--subaccount-id <id>', 'Create a subaccount-scoped key')
29
28
  .option('--expires-at <iso>', 'Expiration timestamp')
30
29
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
31
30
  body: async (_args, options) => {
32
- if (typeof options.input === 'string')
33
- return readJsonFile(options.input);
31
+ if (typeof options.input === 'string') {
32
+ return (await readJsonFile(options.input));
33
+ }
34
34
  return {
35
35
  name: options.name,
36
36
  subaccountId: options.subaccountId,
@@ -38,9 +38,9 @@ export function createApiKeyCommand(factory) {
38
38
  };
39
39
  },
40
40
  }));
41
- command.addCommand(createDeleteCommand(factory, {
41
+ command.addCommand(createOperationDeleteCommand(factory, {
42
+ operationId: 'api-keys.delete',
42
43
  description: 'Delete an API key',
43
- path: '/api-keys/{keyId}',
44
44
  argNames: ['keyId'],
45
45
  }));
46
46
  return command;
@@ -1,12 +1,12 @@
1
1
  import { Command } from 'commander';
2
2
  import { readBlob, readJsonFile } from '../shared/io.js';
3
3
  import { addOutputFlags, outputData } from '../shared/output.js';
4
- import { createDeleteCommand, createJsonBodyCommand, createListCommand, createViewCommand, } from '../shared/rest.js';
4
+ import { createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, uploadOperationFile, } from '../shared/operation.js';
5
5
  export function createBrowserExtensionCommand(factory) {
6
6
  const command = new Command('browser-extension').description('Manage browser extensions');
7
- command.addCommand(createListCommand(factory, {
7
+ command.addCommand(createOperationListCommand(factory, {
8
+ operationId: 'browser-extensions.list',
8
9
  description: 'List browser extensions',
9
- path: '/browser-extensions',
10
10
  configure: (cmd) => cmd
11
11
  .option('--subaccount-id <id>', 'Filter by subaccount id')
12
12
  .option('--q <text>', 'Search query')
@@ -19,10 +19,10 @@ export function createBrowserExtensionCommand(factory) {
19
19
  source: typeof options.source === 'string' ? options.source : undefined,
20
20
  }),
21
21
  }));
22
- command.addCommand(createViewCommand(factory, {
22
+ command.addCommand(createOperationViewCommand(factory, {
23
+ operationId: 'browser-extensions.get',
23
24
  name: 'get',
24
25
  description: 'Get a browser extension',
25
- path: '/browser-extensions/{extensionId}',
26
26
  argName: 'extensionId',
27
27
  }));
28
28
  command.addCommand(addOutputFlags(new Command('upload')
@@ -30,9 +30,8 @@ export function createBrowserExtensionCommand(factory) {
30
30
  .argument('<path>')
31
31
  .option('--name <name>', 'Display name')
32
32
  .option('--subaccount-id <id>', 'Create under a subaccount when using a parent/org key')).action(async (path, options) => {
33
- const client = await factory.apiClient();
34
33
  const file = await readBlob(path);
35
- const result = await client.uploadFile('/browser-extensions/upload', {
34
+ const result = await uploadOperationFile(factory, 'browser-extensions.upload', {
36
35
  file: file.blob,
37
36
  fileName: file.fileName,
38
37
  fields: {
@@ -42,19 +41,19 @@ export function createBrowserExtensionCommand(factory) {
42
41
  });
43
42
  await outputData(factory.io, result, options);
44
43
  }));
45
- command.addCommand(createJsonBodyCommand(factory, {
44
+ command.addCommand(createOperationJsonBodyCommand(factory, {
45
+ operationId: 'browser-extensions.import',
46
46
  name: 'import',
47
47
  description: 'Import a browser extension from a URL',
48
- method: 'post',
49
- path: '/browser-extensions/import',
50
48
  configure: (cmd) => cmd
51
49
  .option('--url <url>', 'Extension URL')
52
50
  .option('--name <name>', 'Display name')
53
51
  .option('--subaccount-id <id>', 'Create under a subaccount when using a parent/org key')
54
52
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
55
53
  body: async (_args, options) => {
56
- if (typeof options.input === 'string')
57
- return readJsonFile(options.input);
54
+ if (typeof options.input === 'string') {
55
+ return (await readJsonFile(options.input));
56
+ }
58
57
  return {
59
58
  url: options.url,
60
59
  name: options.name,
@@ -62,24 +61,24 @@ export function createBrowserExtensionCommand(factory) {
62
61
  };
63
62
  },
64
63
  }));
65
- command.addCommand(createJsonBodyCommand(factory, {
64
+ command.addCommand(createOperationJsonBodyCommand(factory, {
65
+ operationId: 'browser-extensions.update',
66
66
  name: 'patch',
67
67
  description: 'Update a browser extension',
68
- method: 'patch',
69
- path: '/browser-extensions/{extensionId}',
70
68
  argNames: ['extensionId'],
71
69
  configure: (cmd) => cmd
72
70
  .option('--name <name>', 'Display name')
73
71
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
74
72
  body: async (_args, options) => {
75
- if (typeof options.input === 'string')
76
- return readJsonFile(options.input);
73
+ if (typeof options.input === 'string') {
74
+ return (await readJsonFile(options.input));
75
+ }
77
76
  return { name: options.name };
78
77
  },
79
78
  }));
80
- command.addCommand(createDeleteCommand(factory, {
79
+ command.addCommand(createOperationDeleteCommand(factory, {
80
+ operationId: 'browser-extensions.delete',
81
81
  description: 'Delete a browser extension',
82
- path: '/browser-extensions/{extensionId}',
83
82
  argNames: ['extensionId'],
84
83
  }));
85
84
  return command;
@@ -2,12 +2,12 @@ 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')
@@ -29,10 +29,10 @@ export function createFileCommand(factory) {
29
29
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
30
30
  }),
31
31
  }));
32
- command.addCommand(createViewCommand(factory, {
32
+ command.addCommand(createOperationViewCommand(factory, {
33
+ operationId: 'files.get',
33
34
  name: 'get',
34
35
  description: 'View file metadata',
35
- path: '/files/{fileId}',
36
36
  argName: 'fileId',
37
37
  }));
38
38
  command.addCommand(addOutputFlags(new Command('upload')
@@ -40,11 +40,9 @@ export function createFileCommand(factory) {
40
40
  .argument('<path>')
41
41
  .option('--space <id>', 'Space id; omitted uses caller default space')
42
42
  .option('--path <storagePath>', 'Storage path')
43
- .option('--name <name>', 'Display name'))
44
- .action(async (path, options) => {
45
- const client = await factory.apiClient();
43
+ .option('--name <name>', 'Display name')).action(async (path, options) => {
46
44
  const file = await readBlob(path);
47
- const result = await client.uploadFile('/files', {
45
+ const result = await uploadOperationFile(factory, 'files.upload', {
48
46
  file: file.blob,
49
47
  fileName: options.name ?? file.fileName,
50
48
  query: { spaceId: options.space },
@@ -60,23 +58,24 @@ export function createFileCommand(factory) {
60
58
  .argument('<id>')
61
59
  .requiredOption('--to <path>', 'Output path, or - for stdout')
62
60
  .action(async (id, options) => {
63
- const client = await factory.apiClient();
64
- const data = await client.download(`/files/${encodeURIComponent(id)}/content`);
61
+ const data = await downloadOperation(factory, 'files.content', {
62
+ pathParams: { fileId: id },
63
+ });
65
64
  await writeBinary(options.to, data);
66
65
  }));
67
- command.addCommand(createJsonBodyCommand(factory, {
66
+ command.addCommand(createOperationJsonBodyCommand(factory, {
67
+ operationId: 'files.update',
68
68
  name: 'patch',
69
69
  description: 'Edit file metadata',
70
- method: 'patch',
71
- path: '/files/{fileId}',
72
70
  argNames: ['fileId'],
73
71
  configure: (cmd) => cmd
74
72
  .option('--name <name>', 'Display name')
75
73
  .option('--metadata-file <path>', 'Metadata JSON file')
76
74
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
77
75
  body: async (_args, options) => {
78
- if (typeof options.input === 'string')
79
- return readJsonFile(options.input);
76
+ if (typeof options.input === 'string') {
77
+ return (await readJsonFile(options.input));
78
+ }
80
79
  return {
81
80
  name: options.name,
82
81
  metadata: typeof options.metadataFile === 'string'
@@ -85,9 +84,9 @@ export function createFileCommand(factory) {
85
84
  };
86
85
  },
87
86
  }));
88
- command.addCommand(createDeleteCommand(factory, {
87
+ command.addCommand(createOperationDeleteCommand(factory, {
88
+ operationId: 'files.delete',
89
89
  description: 'Delete a file',
90
- path: '/files/{fileId}',
91
90
  argNames: ['fileId'],
92
91
  }));
93
92
  return command;
@@ -1,17 +1,17 @@
1
1
  import { Command } from 'commander';
2
2
  import { addOutputFlags } from '../shared/output.js';
3
- import { requestAndPrint } from '../shared/rest.js';
3
+ import { outputFlags, requestOperationAndPrint } from '../shared/operation.js';
4
4
  export function createHelpCommand(factory) {
5
5
  return addOutputFlags(new Command('help')
6
6
  .description('Get BCTRL API help')
7
7
  .option('--topic <topic>', 'Help topic')
8
8
  .option('--audience <audience>', 'Help audience')).action(async (options) => {
9
- await requestAndPrint(factory, 'get', '/help', {
9
+ await requestOperationAndPrint(factory, 'help', {
10
10
  query: {
11
11
  topic: options.topic,
12
12
  audience: options.audience,
13
13
  },
14
- output: options,
14
+ output: outputFlags(options),
15
15
  });
16
16
  });
17
17
  }
@@ -1,39 +1,38 @@
1
1
  import { Command } from 'commander';
2
2
  import { addOutputFlags } from '../shared/output.js';
3
- import { addPaginationFlags, createDeleteCommand, createJsonBodyCommand, createListCommand, createViewCommand, requestAndPrint, } from '../shared/rest.js';
3
+ import { addPaginationFlags, createOperationDeleteCommand, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, outputFlags, requestOperationAndPrint, } from '../shared/operation.js';
4
4
  export function createProxyCommand(factory) {
5
5
  const command = new Command('proxy').description('Manage proxies');
6
- command.addCommand(createListCommand(factory, {
6
+ command.addCommand(createOperationListCommand(factory, {
7
+ operationId: 'proxies.list',
7
8
  description: 'List proxies',
8
- path: '/proxies',
9
9
  }));
10
- command.addCommand(createViewCommand(factory, {
10
+ command.addCommand(createOperationViewCommand(factory, {
11
+ operationId: 'proxies.get',
11
12
  name: 'get',
12
13
  description: 'Get a proxy',
13
- path: '/proxies/{proxyId}',
14
14
  argName: 'proxyId',
15
15
  }));
16
- command.addCommand(createJsonBodyCommand(factory, {
16
+ command.addCommand(createOperationJsonBodyCommand(factory, {
17
+ operationId: 'proxies.create',
17
18
  name: 'create',
18
19
  description: 'Create a proxy',
19
- method: 'post',
20
- path: '/proxies',
21
20
  }));
22
- command.addCommand(createJsonBodyCommand(factory, {
21
+ command.addCommand(createOperationJsonBodyCommand(factory, {
22
+ operationId: 'proxies.update',
23
23
  name: 'patch',
24
24
  description: 'Update a proxy',
25
- method: 'patch',
26
- path: '/proxies/{proxyId}',
27
25
  argNames: ['proxyId'],
28
26
  }));
29
27
  command.addCommand(addOutputFlags(new Command('test').description('Test a proxy').argument('<proxyId>')).action(async (proxyId, options) => {
30
- await requestAndPrint(factory, 'post', `/proxies/${encodeURIComponent(proxyId)}/test`, {
31
- output: options,
28
+ await requestOperationAndPrint(factory, 'proxies.test', {
29
+ pathParams: { proxyId },
30
+ output: outputFlags(options),
32
31
  });
33
32
  }));
34
- command.addCommand(createDeleteCommand(factory, {
33
+ command.addCommand(createOperationDeleteCommand(factory, {
34
+ operationId: 'proxies.delete',
35
35
  description: 'Delete a proxy',
36
- path: '/proxies/{proxyId}',
37
36
  argNames: ['proxyId'],
38
37
  }));
39
38
  command.addCommand(createProxyPoolCommand(factory));
@@ -41,9 +40,9 @@ export function createProxyCommand(factory) {
41
40
  }
42
41
  function createProxyPoolCommand(factory) {
43
42
  const command = new Command('pool').description('Inspect managed proxy pools');
44
- command.addCommand(createListCommand(factory, {
43
+ command.addCommand(createOperationListCommand(factory, {
44
+ operationId: 'proxies.pools.list',
45
45
  description: 'List proxy pools',
46
- path: '/proxies/pools',
47
46
  configure: (cmd) => addPaginationFlags(cmd)
48
47
  .option('--country <country>', 'Filter by ISO country code')
49
48
  .option('--category <category>', 'Filter by category')
@@ -51,16 +50,23 @@ function createProxyPoolCommand(factory) {
51
50
  query: (options) => ({
52
51
  country: typeof options.country === 'string' ? options.country : undefined,
53
52
  category: typeof options.category === 'string' ? options.category : undefined,
54
- available: typeof options.available === 'string' ? options.available : undefined,
53
+ available: typeof options.available === 'string' ? parseBooleanFlag(options.available) : undefined,
55
54
  limit: typeof options.limit === 'number' ? options.limit : undefined,
56
55
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
57
56
  }),
58
57
  }));
59
- command.addCommand(createViewCommand(factory, {
58
+ command.addCommand(createOperationViewCommand(factory, {
59
+ operationId: 'proxies.pools.get',
60
60
  name: 'get',
61
61
  description: 'Get a proxy pool',
62
- path: '/proxies/pools/{poolId}',
63
62
  argName: 'poolId',
64
63
  }));
65
64
  return command;
66
65
  }
66
+ function parseBooleanFlag(value) {
67
+ if (value === 'true')
68
+ return true;
69
+ if (value === 'false')
70
+ return false;
71
+ return undefined;
72
+ }
@@ -3,12 +3,12 @@ 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
8
  const command = new Command('run').description('Inspect and stream runs');
9
- command.addCommand(createListCommand(factory, {
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')
@@ -23,10 +23,10 @@ export function createRunCommand(factory) {
23
23
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
24
24
  }),
25
25
  }));
26
- command.addCommand(createViewCommand(factory, {
26
+ command.addCommand(createOperationViewCommand(factory, {
27
+ operationId: 'runs.get',
27
28
  name: 'get',
28
29
  description: 'Get a run',
29
- path: '/runs/{runId}',
30
30
  argName: 'runId',
31
31
  }));
32
32
  command.addCommand(createRunActivityCommand(factory));
@@ -44,7 +44,7 @@ function createRunActivityCommand(factory) {
44
44
  command.addCommand(createSseCommand(factory, {
45
45
  name: 'stream',
46
46
  description: 'Stream run activities',
47
- pathSuffix: 'activity/stream',
47
+ operationId: 'runs.activity.stream',
48
48
  configure: (cmd) => addPaginationFlags(cmd)
49
49
  .option('--view <view>', 'Activity view')
50
50
  .option('--type <type>', 'Filter by activity type')
@@ -73,7 +73,8 @@ function createRunActivityListCommand(factory) {
73
73
  .option('--severity <severity>', 'Filter by severity')
74
74
  .option('--invocation <id>', 'Filter by invocation id')
75
75
  .option('--file <id>', 'Filter by file id')).action(async (runId, options) => {
76
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(runId)}/activity`, {
76
+ await requestOperationAndPrint(factory, 'runs.activity.list', {
77
+ pathParams: { runId },
77
78
  query: {
78
79
  view: options.view,
79
80
  type: options.type,
@@ -84,7 +85,7 @@ function createRunActivityListCommand(factory) {
84
85
  limit: options.limit,
85
86
  cursor: options.cursor,
86
87
  },
87
- output: options,
88
+ output: outputFlags(options),
88
89
  });
89
90
  });
90
91
  }
@@ -94,7 +95,7 @@ function createRunEventsCommand(factory) {
94
95
  command.addCommand(createSseCommand(factory, {
95
96
  name: 'stream',
96
97
  description: 'Stream run events',
97
- pathSuffix: 'events/stream',
98
+ operationId: 'runs.events.stream',
98
99
  configure: (cmd) => addPaginationFlags(cmd)
99
100
  .option('--type <type>', 'Filter by event type')
100
101
  .option('--status <status>', 'Filter by event status')
@@ -117,7 +118,8 @@ function createRunEventsListCommand(factory) {
117
118
  .option('--status <status>', 'Filter by event status')
118
119
  .option('--page-id <id>', 'Filter by page id')
119
120
  .option('--context-id <id>', 'Filter by browser context id')).action(async (runId, options) => {
120
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(runId)}/events`, {
121
+ await requestOperationAndPrint(factory, 'runs.events.list', {
122
+ pathParams: { runId },
121
123
  query: {
122
124
  type: options.type,
123
125
  status: options.status,
@@ -126,7 +128,7 @@ function createRunEventsListCommand(factory) {
126
128
  limit: options.limit,
127
129
  cursor: options.cursor,
128
130
  },
129
- output: options,
131
+ output: outputFlags(options),
130
132
  });
131
133
  });
132
134
  }
@@ -138,9 +140,14 @@ function createRunFilesCommand(factory) {
138
140
  }
139
141
  function createRunFilesListCommand(factory) {
140
142
  return addOutputFlags(addPaginationFlags(new Command('list').description('List run files').argument('<runId>')).option('--type <type>', 'Filter by file type')).action(async (runId, options) => {
141
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(runId)}/files`, {
142
- query: { type: options.type, limit: options.limit, cursor: options.cursor },
143
- output: 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),
144
151
  });
145
152
  });
146
153
  }
@@ -152,7 +159,6 @@ function createRunFilesExportCommand(factory) {
152
159
  .option('--name <name>', 'Export file name', 'run-files.zip')
153
160
  .option('--type <type...>', 'Filter exported file types')
154
161
  .option('--input <path>', 'Read JSON request body from file, or - for stdin')).action(async (runId, options) => {
155
- const client = await factory.apiClient();
156
162
  const body = typeof options.input === 'string'
157
163
  ? await readJsonFile(options.input)
158
164
  : {
@@ -160,12 +166,17 @@ function createRunFilesExportCommand(factory) {
160
166
  name: options.name,
161
167
  filter: options.type ? { type: options.type } : undefined,
162
168
  };
163
- const result = await client.post(`/runs/${encodeURIComponent(runId)}/files/export`, { body });
169
+ const result = (await requestOperation(factory, 'runs.files.export', {
170
+ pathParams: { runId },
171
+ body: body,
172
+ }));
164
173
  const fileId = result.fileId ?? result.id;
165
174
  if (!fileId) {
166
175
  throw new CliError('Expected run export response to include fileId');
167
176
  }
168
- const data = await client.download(`/files/${encodeURIComponent(fileId)}/content`);
177
+ const data = await downloadOperation(factory, 'files.content', {
178
+ pathParams: { fileId },
179
+ });
169
180
  await writeBinary(options.to, data);
170
181
  });
171
182
  }
@@ -174,14 +185,15 @@ function createRunInvocationsCommand(factory) {
174
185
  .option('--status <status>', 'Filter by invocation status')
175
186
  .option('--action <action>', 'Filter by invocation action'));
176
187
  command.action(async (runId, options) => {
177
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(runId)}/invocations`, {
188
+ await requestOperationAndPrint(factory, 'runs.invocations.list', {
189
+ pathParams: { runId },
178
190
  query: {
179
191
  status: options.status,
180
192
  action: options.action,
181
193
  limit: options.limit,
182
194
  cursor: options.cursor,
183
195
  },
184
- output: options,
196
+ output: outputFlags(options),
185
197
  });
186
198
  });
187
199
  return command;
@@ -192,24 +204,27 @@ function createRunInvocationCommand(factory) {
192
204
  .description('Get a run invocation')
193
205
  .argument('<runId>')
194
206
  .argument('<invocationId>')).action(async (runId, invocationId, options) => {
195
- await requestAndPrint(factory, 'get', `/runs/${encodeURIComponent(runId)}/invocations/${encodeURIComponent(invocationId)}`, { output: options });
207
+ await requestOperationAndPrint(factory, 'runs.invocations.get', {
208
+ pathParams: { runId, invocationId },
209
+ output: outputFlags(options),
210
+ });
196
211
  }));
197
212
  return command;
198
213
  }
199
214
  function createRunViewerCommand(factory, name, description) {
200
- return createJsonBodyCommand(factory, {
215
+ return createOperationJsonBodyCommand(factory, {
216
+ operationId: name === 'live' ? 'runs.live' : 'runs.recording',
201
217
  name,
202
218
  description,
203
- method: 'post',
204
- path: `/runs/{runId}/${name}`,
205
219
  argNames: ['runId'],
206
220
  configure: (cmd) => cmd
207
221
  .option('--expires-in-seconds <seconds>', 'Viewer expiration in seconds', parsePositiveInteger)
208
222
  .option('--control <none|input>', 'Live viewer control mode')
209
223
  .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
210
224
  body: async (_args, options) => {
211
- if (typeof options.input === 'string')
212
- return readJsonFile(options.input);
225
+ if (typeof options.input === 'string') {
226
+ return (await readJsonFile(options.input));
227
+ }
213
228
  return {
214
229
  expiresInSeconds: typeof options.expiresInSeconds === 'number' ? options.expiresInSeconds : undefined,
215
230
  control: name === 'live' ? options.control : undefined,
@@ -221,8 +236,10 @@ function createSseCommand(factory, config) {
221
236
  let command = new Command(config.name).description(config.description).argument('<runId>');
222
237
  command = config.configure(command);
223
238
  return command.action(async (runId, options) => {
224
- const client = await factory.apiClient();
225
- const stream = await client.streamText(`/runs/${encodeURIComponent(runId)}/${config.pathSuffix}`, { query: config.query(options) });
239
+ const stream = await streamOperationText(factory, config.operationId, {
240
+ pathParams: { runId },
241
+ query: config.query(options),
242
+ });
226
243
  await renderSseStream(factory, stream);
227
244
  });
228
245
  }