@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
@@ -1,11 +1,10 @@
1
+ import { CLI_OPENAPI_ROUTES } from '../../generated/openapi-routes.js';
1
2
  import { Command } from 'commander';
2
- import { CliError } from '../../runtime/errors.js';
3
3
  import { readJsonFile } from './io.js';
4
4
  import { parsePositiveInteger } from './options.js';
5
5
  import { addOutputFlags, outputData } from './output.js';
6
- export function pathTemplate(template, params) {
7
- return template.replace(/\{(\w+)\}/g, (_, key) => encodeURIComponent(params[key] ?? ''));
8
- }
6
+ import { CliError } from '../../runtime/errors.js';
7
+ import { addCliOperationHelp } from './help.js';
9
8
  export function addPaginationFlags(command) {
10
9
  return command
11
10
  .option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
@@ -14,72 +13,125 @@ export function addPaginationFlags(command) {
14
13
  export function addJsonBodyFlag(command) {
15
14
  return command.option('--input <path>', 'Read JSON request body from file, or - for stdin');
16
15
  }
17
- export async function requestAndPrint(deps, method, path, options) {
16
+ export function outputFlags(options) {
17
+ return {
18
+ ...(typeof options.json === 'string' || typeof options.json === 'boolean'
19
+ ? { json: options.json }
20
+ : {}),
21
+ ...(typeof options.jq === 'string' ? { jq: options.jq } : {}),
22
+ ...(typeof options.template === 'string' ? { template: options.template } : {}),
23
+ };
24
+ }
25
+ export function operationPath(operationId, pathParams) {
26
+ const route = CLI_OPENAPI_ROUTES[operationId];
27
+ return route.path.replace(/\{([^}]+)\}/g, (_match, key) => {
28
+ const value = pathParams?.[key];
29
+ if (value === undefined) {
30
+ throw new Error(`Missing path parameter "${key}" for ${operationId}`);
31
+ }
32
+ return encodeURIComponent(String(value));
33
+ });
34
+ }
35
+ export async function requestOperationAndPrint(deps, operationId, input) {
36
+ const result = await requestOperation(deps, operationId, input);
37
+ await outputData(deps.io, result, input.output);
38
+ }
39
+ export async function requestOperation(deps, operationId, input) {
40
+ const route = CLI_OPENAPI_ROUTES[operationId];
18
41
  const client = await deps.apiClient();
19
- const result = method === 'get'
42
+ const path = operationPath(operationId, input.pathParams);
43
+ const requestOptions = {
44
+ ...('query' in input && input.query !== undefined ? { query: input.query } : {}),
45
+ ...('body' in input && input.body !== undefined ? { body: input.body } : {}),
46
+ ...(input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}),
47
+ };
48
+ const options = Object.keys(requestOptions).length > 0 ? requestOptions : undefined;
49
+ const result = route.method === 'get'
20
50
  ? await client.get(path, options)
21
- : method === 'post'
51
+ : route.method === 'post'
22
52
  ? await client.post(path, options)
23
- : method === 'patch'
53
+ : route.method === 'patch'
24
54
  ? await client.patch(path, options)
25
- : method === 'put'
55
+ : route.method === 'put'
26
56
  ? await client.put(path, options)
27
57
  : await client.delete(path, options);
28
- await outputData(deps.io, result, options?.output);
58
+ return result;
29
59
  }
30
- export async function bodyFromInputOption(path) {
31
- return path ? readJsonFile(path) : {};
60
+ export async function uploadOperationFile(deps, operationId, input) {
61
+ const client = await deps.apiClient();
62
+ return client.uploadFile(operationPath(operationId, input.pathParams), {
63
+ file: input.file,
64
+ fileName: input.fileName,
65
+ ...('query' in input && input.query !== undefined ? { query: input.query } : {}),
66
+ ...(input.fields ? { fields: input.fields } : {}),
67
+ });
32
68
  }
33
- export function createListCommand(factory, config) {
69
+ export async function downloadOperation(deps, operationId, input) {
70
+ const client = await deps.apiClient();
71
+ return client.download(operationPath(operationId, input.pathParams));
72
+ }
73
+ export async function streamOperationText(deps, operationId, input) {
74
+ const client = await deps.apiClient();
75
+ return client.streamText(operationPath(operationId, input.pathParams), {
76
+ ...('query' in input && input.query !== undefined ? { query: input.query } : {}),
77
+ });
78
+ }
79
+ export function createOperationListCommand(factory, config) {
34
80
  let command = new Command(config.name ?? 'list').description(config.description);
81
+ command = addCliOperationHelp(command, config.operationId);
35
82
  command = config.configure ? config.configure(command) : addPaginationFlags(command);
36
83
  command = addOutputFlags(command);
37
84
  return command.action(async (options) => {
38
- await requestAndPrint(factory, 'get', config.path, {
85
+ await requestOperationAndPrint(factory, config.operationId, {
39
86
  query: config.query ? config.query(options) : defaultListQuery(options),
40
87
  output: outputFlags(options),
41
88
  });
42
89
  });
43
90
  }
44
- export function createViewCommand(factory, config) {
91
+ export function createOperationViewCommand(factory, config) {
45
92
  const argName = config.argName ?? 'id';
46
93
  let command = new Command(config.name ?? 'view')
47
94
  .description(config.description)
48
95
  .argument(`<${argName}>`);
96
+ command = addCliOperationHelp(command, config.operationId);
49
97
  command = config.configure ? config.configure(command) : command;
50
98
  command = addOutputFlags(command);
51
99
  return command.action(async (id, options) => {
52
- await requestAndPrint(factory, 'get', pathTemplate(config.path, { [argName]: id, id }), {
100
+ await requestOperationAndPrint(factory, config.operationId, {
101
+ pathParams: { [argName]: id, id },
53
102
  query: config.query ? config.query(id, options) : undefined,
54
103
  output: outputFlags(options),
55
104
  });
56
105
  });
57
106
  }
58
- export function createDeleteCommand(factory, config) {
107
+ export function createOperationDeleteCommand(factory, config) {
59
108
  const argNames = config.argNames ?? ['id'];
60
109
  let command = new Command(config.name ?? 'delete').description(config.description);
110
+ command = addCliOperationHelp(command, config.operationId);
61
111
  for (const arg of argNames)
62
112
  command = command.argument(`<${arg}>`);
63
113
  command = command.option('-y, --yes', 'Confirm deletion');
64
114
  command = config.configure ? config.configure(command) : command;
65
115
  command = addOutputFlags(command);
66
- return command.action(async (...args) => {
67
- const options = getActionOptions(args);
116
+ return command.action(async (...actionArgs) => {
117
+ const options = getActionOptions(actionArgs);
68
118
  if (options.yes !== true) {
69
119
  throw new CliError('Refusing to delete without --yes');
70
120
  }
71
121
  const params = {};
72
122
  for (let i = 0; i < argNames.length; i += 1)
73
- params[argNames[i]] = String(args[i]);
74
- await requestAndPrint(factory, 'delete', pathTemplate(config.path, params), {
123
+ params[argNames[i]] = String(actionArgs[i]);
124
+ await requestOperationAndPrint(factory, config.operationId, {
125
+ pathParams: params,
75
126
  query: config.query ? config.query(params, options) : undefined,
76
127
  output: outputFlags(options),
77
128
  });
78
129
  });
79
130
  }
80
- export function createJsonBodyCommand(factory, config) {
131
+ export function createOperationJsonBodyCommand(factory, config) {
81
132
  const argNames = config.argNames ?? [];
82
133
  let command = new Command(config.name).description(config.description);
134
+ command = addCliOperationHelp(command, config.operationId);
83
135
  for (const arg of argNames)
84
136
  command = command.argument(`<${arg}>`);
85
137
  command = config.configure ? config.configure(command) : addJsonBodyFlag(command);
@@ -89,8 +141,11 @@ export function createJsonBodyCommand(factory, config) {
89
141
  const args = {};
90
142
  for (let i = 0; i < argNames.length; i += 1)
91
143
  args[argNames[i]] = String(actionArgs[i]);
92
- const body = config.body ? await config.body(args, options) : await bodyFromInputOption(String(options.input ?? ''));
93
- await requestAndPrint(factory, config.method, pathTemplate(config.path, args), {
144
+ const body = config.body
145
+ ? await config.body(args, options)
146
+ : await bodyFromInputOption(String(options.input ?? ''));
147
+ await requestOperationAndPrint(factory, config.operationId, {
148
+ pathParams: args,
94
149
  body,
95
150
  query: config.query ? config.query(args, options) : undefined,
96
151
  output: outputFlags(options),
@@ -103,14 +158,8 @@ function defaultListQuery(options) {
103
158
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
104
159
  };
105
160
  }
106
- export function outputFlags(options) {
107
- return {
108
- ...(typeof options.json === 'string' || typeof options.json === 'boolean'
109
- ? { json: options.json }
110
- : {}),
111
- ...(typeof options.jq === 'string' ? { jq: options.jq } : {}),
112
- ...(typeof options.template === 'string' ? { template: options.template } : {}),
113
- };
161
+ async function bodyFromInputOption(path) {
162
+ return path ? readJsonFile(path) : {};
114
163
  }
115
164
  function getActionOptions(args) {
116
165
  const candidate = args.at(-2);
@@ -1,86 +1,80 @@
1
1
  import { Command } from 'commander';
2
- import { createJsonBodyCommand, createViewCommand } from '../shared/rest.js';
3
2
  import { readJsonFile } from '../shared/io.js';
4
- import { addOutputFlags, outputData } from '../shared/output.js';
3
+ import { outputFlags, requestOperationAndPrint } from '../shared/operation.js';
4
+ import { addOutputFlags } from '../shared/output.js';
5
+ import { CliError } from '../../runtime/errors.js';
5
6
  import { createSpaceListCommand } from './list.js';
6
7
  export function createSpaceCommand(factory) {
7
8
  const command = new Command('space').description('Manage BCTRL spaces');
8
9
  command.addCommand(createSpaceListCommand(factory));
9
- command.addCommand(createViewCommand(factory, {
10
- description: 'View a space',
11
- path: '/spaces/{id}',
10
+ command.addCommand(addOutputFlags(new Command('get').description('View a space').argument('<spaceId>')).action(async (spaceId, options) => {
11
+ await requestOperationAndPrint(factory, 'spaces.get', {
12
+ pathParams: { spaceId },
13
+ output: outputFlags(options),
14
+ });
12
15
  }));
13
- command.addCommand(createJsonBodyCommand(factory, {
14
- name: 'create',
15
- description: 'Create a space',
16
- method: 'post',
17
- path: '/spaces',
18
- configure: (cmd) => cmd
19
- .option('--name <name>', 'Space name')
20
- .option('--region <region>', 'Space region')
21
- .option('--subaccount-id <id>', 'Subaccount id')
22
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
23
- body: async (_args, options) => {
24
- if (typeof options.input === 'string')
25
- return readJsonFile(options.input);
26
- return {
27
- name: options.name,
28
- region: options.region,
29
- subaccountId: options.subaccountId,
30
- };
31
- },
16
+ command.addCommand(addOutputFlags(new Command('create')
17
+ .description('Create a space')
18
+ .option('--name <name>', 'Space name')
19
+ .option('--subaccount-id <id>', 'Subaccount id')
20
+ .option('--input <path>', 'Read JSON request body from file, or - for stdin')).action(async (options) => {
21
+ await requestOperationAndPrint(factory, 'spaces.create', {
22
+ body: typeof options.input === 'string'
23
+ ? (await readJsonFile(options.input))
24
+ : {
25
+ name: options.name,
26
+ subaccountId: options.subaccountId,
27
+ },
28
+ output: outputFlags(options),
29
+ });
32
30
  }));
33
- command.addCommand(createJsonBodyCommand(factory, {
34
- name: 'edit',
35
- description: 'Edit a space',
36
- method: 'patch',
37
- path: '/spaces/{id}',
38
- argNames: ['id'],
39
- configure: (cmd) => cmd
40
- .option('--name <name>', 'Space name')
41
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
42
- body: async (_args, options) => {
43
- if (typeof options.input === 'string')
44
- return readJsonFile(options.input);
45
- return { name: options.name };
46
- },
31
+ command.addCommand(addOutputFlags(new Command('patch')
32
+ .description('Edit a space')
33
+ .argument('<spaceId>')
34
+ .option('--name <name>', 'Space name')
35
+ .option('--input <path>', 'Read JSON request body from file, or - for stdin')).action(async (spaceId, options) => {
36
+ await requestOperationAndPrint(factory, 'spaces.update', {
37
+ pathParams: { spaceId },
38
+ body: typeof options.input === 'string'
39
+ ? (await readJsonFile(options.input))
40
+ : { name: options.name },
41
+ output: outputFlags(options),
42
+ });
43
+ }));
44
+ command.addCommand(addOutputFlags(new Command('delete')
45
+ .description('Delete a space')
46
+ .argument('<spaceId>')
47
+ .option('-y, --yes', 'Confirm deletion')).action(async (spaceId, options) => {
48
+ if (options.yes !== true) {
49
+ throw new CliError('Refusing to delete without --yes');
50
+ }
51
+ await requestOperationAndPrint(factory, 'spaces.delete', {
52
+ pathParams: { spaceId },
53
+ output: outputFlags(options),
54
+ });
47
55
  }));
48
56
  command.addCommand(createSpaceEnvironmentCommand(factory));
49
- command.addCommand(createSpaceAgentContextCommand(factory));
50
57
  return command;
51
58
  }
52
59
  function createSpaceEnvironmentCommand(factory) {
53
60
  const command = new Command('env').description('Manage a space environment');
54
- command.addCommand(createViewCommand(factory, {
55
- name: 'get',
56
- description: 'Get a space environment',
57
- path: '/spaces/{id}/environment',
61
+ command.addCommand(addOutputFlags(new Command('get').description('Get a space environment').argument('<spaceId>')).action(async (spaceId, options) => {
62
+ await requestOperationAndPrint(factory, 'spaces.environment.get', {
63
+ pathParams: { spaceId },
64
+ output: outputFlags(options),
65
+ });
58
66
  }));
59
- command.addCommand(createJsonBodyCommand(factory, {
60
- name: 'set',
61
- description: 'Update a space environment',
62
- method: 'patch',
63
- path: '/spaces/{id}/environment',
64
- argNames: ['id'],
67
+ command.addCommand(addOutputFlags(new Command('patch')
68
+ .description('Update a space environment')
69
+ .argument('<spaceId>')
70
+ .option('--input <path>', 'Read JSON request body from file, or - for stdin')).action(async (spaceId, options) => {
71
+ await requestOperationAndPrint(factory, 'spaces.environment.update', {
72
+ pathParams: { spaceId },
73
+ body: typeof options.input === 'string'
74
+ ? (await readJsonFile(options.input))
75
+ : {},
76
+ output: outputFlags(options),
77
+ });
65
78
  }));
66
79
  return command;
67
80
  }
68
- function createSpaceAgentContextCommand(factory) {
69
- return addOutputFlags(new Command('agent-context')
70
- .description('Get machine-readable context for coding agents')
71
- .argument('<id>')
72
- .option('--runtime <id>', 'Runtime id')
73
- .option('--topic <topic>', 'Context topic')
74
- .option('--detail <level>', 'Detail level'))
75
- .action(async (id, options) => {
76
- const client = await factory.apiClient();
77
- const result = await client.get(`/spaces/${encodeURIComponent(id)}/agent-context`, {
78
- query: {
79
- runtimeId: options.runtime,
80
- topic: options.topic,
81
- detail: options.detail,
82
- },
83
- });
84
- await outputData(factory.io, result, options);
85
- });
86
- }
@@ -1,13 +1,3 @@
1
1
  import { Command } from 'commander';
2
- import type { BctrlApiClient } from '../../api/client.js';
3
2
  import type { Factory } from '../../factory.js';
4
- import type { IOStreams } from '../../io/streams.js';
5
- import { type OutputFlags } from '../shared/output.js';
6
- export type SpaceListOptions = {
7
- io: IOStreams;
8
- apiClient: () => Promise<BctrlApiClient>;
9
- limit?: number;
10
- output?: OutputFlags;
11
- };
12
- export declare function createSpaceListCommand(factory: Factory, run?: (options: SpaceListOptions) => Promise<void>): Command;
13
- export declare function spaceListRun(options: SpaceListOptions): Promise<void>;
3
+ export declare function createSpaceListCommand(factory: Factory): Command;
@@ -1,25 +1,17 @@
1
1
  import { Command } from 'commander';
2
- import { addOutputFlags, outputData } from '../shared/output.js';
3
- export function createSpaceListCommand(factory, run = spaceListRun) {
4
- return addOutputFlags(new Command('list')
2
+ import { addPaginationFlags, outputFlags, requestOperationAndPrint } from '../shared/operation.js';
3
+ import { addOutputFlags } from '../shared/output.js';
4
+ export function createSpaceListCommand(factory) {
5
+ return addOutputFlags(addPaginationFlags(new Command('list')
5
6
  .description('List spaces')
6
- .option('--limit <number>', 'Maximum number of spaces to return', parsePositiveInteger))
7
- .action(async (options) => {
8
- await run({
9
- io: factory.io,
10
- apiClient: factory.apiClient,
11
- limit: options.limit,
12
- output: options,
7
+ .option('--subaccount <id>', 'Filter by subaccount id'))).action(async (options) => {
8
+ await requestOperationAndPrint(factory, 'spaces.list', {
9
+ query: {
10
+ subaccountId: options.subaccount,
11
+ limit: options.limit,
12
+ cursor: options.cursor,
13
+ },
14
+ output: outputFlags(options),
13
15
  });
14
16
  });
15
17
  }
16
- export async function spaceListRun(options) {
17
- const client = await options.apiClient();
18
- const result = await client.get('/spaces', {
19
- query: {
20
- limit: options.limit,
21
- },
22
- });
23
- await outputData(options.io, result, options.output);
24
- }
25
- import { parsePositiveInteger } from '../shared/options.js';
@@ -2,54 +2,65 @@ import { Command } from 'commander';
2
2
  import { readJsonFile } from '../shared/io.js';
3
3
  import { parsePositiveInteger } from '../shared/options.js';
4
4
  import { addOutputFlags } from '../shared/output.js';
5
- import { createDeleteCommand, createJsonBodyCommand, createListCommand, createViewCommand, requestAndPrint, } from '../shared/rest.js';
5
+ import { addPaginationFlags, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, outputFlags, requestOperationAndPrint, } from '../shared/operation.js';
6
6
  export function createSubaccountCommand(factory) {
7
- const command = new Command('subaccount').description('Manage subaccounts, keys, limits, and usage');
8
- command.addCommand(createListCommand(factory, {
7
+ const command = new Command('subaccount').description('Manage subaccounts');
8
+ command.addCommand(createOperationListCommand(factory, {
9
+ operationId: 'subaccounts.list',
9
10
  description: 'List subaccounts',
10
- path: '/subaccounts',
11
- configure: (cmd) => cmd,
11
+ query: (options) => ({
12
+ limit: typeof options.limit === 'number' ? options.limit : undefined,
13
+ cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
14
+ include: options.includeUsage === true ? 'usage' : undefined,
15
+ status: typeof options.status === 'string' ? options.status : undefined,
16
+ externalId: typeof options.externalId === 'string' ? options.externalId : undefined,
17
+ query: typeof options.query === 'string' ? options.query : undefined,
18
+ }),
19
+ configure: (cmd) => addPaginationFlags(cmd)
20
+ .option('--include-usage', 'Inline current usage')
21
+ .option('--status <status>', 'Filter by status')
22
+ .option('--external-id <id>', 'Filter by external id')
23
+ .option('--query <text>', 'Search by id, name, or external id'),
12
24
  }));
13
- command.addCommand(createViewCommand(factory, {
14
- description: 'View a subaccount',
15
- path: '/subaccounts/{id}',
25
+ command.addCommand(createOperationViewCommand(factory, {
26
+ operationId: 'subaccounts.get',
27
+ name: 'get',
28
+ description: 'Get a subaccount',
29
+ argName: 'subaccountId',
30
+ configure: (cmd) => cmd.option('--include <value>', 'Include related data, for example usage'),
31
+ query: (_id, options) => ({
32
+ include: typeof options.include === 'string' ? options.include : undefined,
33
+ }),
16
34
  }));
17
- command.addCommand(createJsonBodyCommand(factory, {
18
- name: 'create',
19
- description: 'Create a subaccount',
20
- method: 'post',
21
- path: '/subaccounts',
22
- configure: (cmd) => cmd
23
- .option('--name <name>', 'Subaccount name')
24
- .option('--external-id <id>', 'External id')
25
- .option('--metadata-file <path>', 'Metadata JSON file')
26
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
27
- body: async (_args, options) => {
28
- if (typeof options.input === 'string')
29
- return readJsonFile(options.input);
30
- return {
31
- name: options.name,
32
- externalId: options.externalId,
33
- metadata: typeof options.metadataFile === 'string'
34
- ? await readJsonFile(options.metadataFile, '--metadata-file')
35
- : undefined,
36
- };
37
- },
35
+ command.addCommand(createSubaccountWriteCommand(factory, 'create', 'subaccounts.create'));
36
+ command.addCommand(createSubaccountWriteCommand(factory, 'patch', 'subaccounts.update', ['subaccountId']));
37
+ command.addCommand(addOutputFlags(new Command('archive')
38
+ .description('Archive a subaccount')
39
+ .argument('<subaccountId>')
40
+ .requiredOption('-y, --yes', 'Confirm archive')).action(async (subaccountId, options) => {
41
+ await requestOperationAndPrint(factory, 'subaccounts.archive', {
42
+ pathParams: { subaccountId },
43
+ output: outputFlags(options),
44
+ });
38
45
  }));
39
- command.addCommand(createJsonBodyCommand(factory, {
40
- name: 'edit',
41
- description: 'Edit a subaccount',
42
- method: 'patch',
43
- path: '/subaccounts/{id}',
44
- argNames: ['id'],
46
+ command.addCommand(createSubaccountUsageCommand(factory));
47
+ return command;
48
+ }
49
+ function createSubaccountWriteCommand(factory, name, operationId, argNames = []) {
50
+ return createOperationJsonBodyCommand(factory, {
51
+ operationId,
52
+ name,
53
+ description: name === 'create' ? 'Create a subaccount' : 'Update a subaccount',
54
+ argNames,
45
55
  configure: (cmd) => cmd
46
56
  .option('--name <name>', 'Subaccount name')
47
57
  .option('--external-id <id>', 'External id')
48
58
  .option('--metadata-file <path>', 'Metadata JSON file')
49
59
  .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
50
60
  body: async (_args, options) => {
51
- if (typeof options.input === 'string')
52
- return readJsonFile(options.input);
61
+ if (typeof options.input === 'string') {
62
+ return (await readJsonFile(options.input));
63
+ }
53
64
  return {
54
65
  name: options.name,
55
66
  externalId: options.externalId,
@@ -58,105 +69,25 @@ export function createSubaccountCommand(factory) {
58
69
  : undefined,
59
70
  };
60
71
  },
61
- }));
62
- command.addCommand(addOutputFlags(new Command('archive')
63
- .description('Archive a subaccount')
64
- .argument('<id>')
65
- .requiredOption('-y, --yes', 'Confirm archive'))
66
- .action(async (id, options) => {
67
- await requestAndPrint(factory, 'post', `/subaccounts/${encodeURIComponent(id)}/archive`, {
68
- output: options,
69
- });
70
- }));
71
- command.addCommand(createSubaccountUsageCommand(factory));
72
- command.addCommand(createSubaccountLimitsCommand(factory));
73
- command.addCommand(createSubaccountKeyCommand(factory));
74
- return command;
72
+ });
75
73
  }
76
74
  function createSubaccountUsageCommand(factory) {
77
75
  return addOutputFlags(new Command('usage')
78
76
  .description('Show subaccount usage')
79
77
  .argument('[id]')
80
78
  .option('-L, --limit <number>', 'Maximum number of usage records to return', parsePositiveInteger)
81
- .option('--cursor <cursor>', 'Pagination cursor'))
82
- .action(async (id, options) => {
79
+ .option('--cursor <cursor>', 'Pagination cursor')).action(async (id, options) => {
83
80
  if (id) {
84
- await requestAndPrint(factory, 'get', `/subaccounts/${encodeURIComponent(id)}/usage`, {
85
- output: options,
81
+ await requestOperationAndPrint(factory, 'subaccounts.get', {
82
+ pathParams: { subaccountId: id },
83
+ query: { include: 'usage' },
84
+ output: outputFlags(options),
86
85
  });
87
86
  return;
88
87
  }
89
- await requestAndPrint(factory, 'get', '/subaccounts/usage', {
88
+ await requestOperationAndPrint(factory, 'subaccounts.usage.list', {
90
89
  query: { limit: options.limit, cursor: options.cursor },
91
- output: options,
90
+ output: outputFlags(options),
92
91
  });
93
92
  });
94
93
  }
95
- function createSubaccountLimitsCommand(factory) {
96
- const command = new Command('limits').description('Manage subaccount limits');
97
- command.addCommand(addOutputFlags(new Command('get')
98
- .description('View subaccount limits')
99
- .argument('<id>'))
100
- .action(async (id, options) => {
101
- await requestAndPrint(factory, 'get', `/subaccounts/${encodeURIComponent(id)}/limits`, {
102
- output: options,
103
- });
104
- }));
105
- command.addCommand(createJsonBodyCommand(factory, {
106
- name: 'set',
107
- description: 'Update subaccount limits',
108
- method: 'patch',
109
- path: '/subaccounts/{id}/limits',
110
- argNames: ['id'],
111
- configure: (cmd) => cmd
112
- .option('--max-spaces <number>', 'Maximum spaces')
113
- .option('--max-active-runs <number>', 'Maximum active runs')
114
- .option('--monthly-spend-micro-usd <number>', 'Monthly spend limit in micro USD')
115
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
116
- body: async (_args, options) => {
117
- if (typeof options.input === 'string')
118
- return readJsonFile(options.input);
119
- return {
120
- spaces: options.maxSpaces ? { max: Number(options.maxSpaces) } : undefined,
121
- runs: options.maxActiveRuns ? { maxActive: Number(options.maxActiveRuns) } : undefined,
122
- spend: options.monthlySpendMicroUsd
123
- ? { monthlyLimitMicroUsd: Number(options.monthlySpendMicroUsd) }
124
- : undefined,
125
- };
126
- },
127
- }));
128
- return command;
129
- }
130
- function createSubaccountKeyCommand(factory) {
131
- const command = new Command('key').description('Manage subaccount API keys');
132
- command.addCommand(addOutputFlags(new Command('list')
133
- .description('List subaccount API keys')
134
- .argument('<id>'))
135
- .action(async (id, options) => {
136
- await requestAndPrint(factory, 'get', `/subaccounts/${encodeURIComponent(id)}/api-keys`, {
137
- output: options,
138
- });
139
- }));
140
- command.addCommand(createJsonBodyCommand(factory, {
141
- name: 'create',
142
- description: 'Create a subaccount API key',
143
- method: 'post',
144
- path: '/subaccounts/{id}/api-keys',
145
- argNames: ['id'],
146
- configure: (cmd) => cmd
147
- .option('--name <name>', 'API key name')
148
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin'),
149
- body: async (_args, options) => {
150
- if (typeof options.input === 'string')
151
- return readJsonFile(options.input);
152
- return { name: options.name };
153
- },
154
- }));
155
- command.addCommand(createDeleteCommand(factory, {
156
- name: 'revoke',
157
- description: 'Revoke a subaccount API key',
158
- path: '/subaccounts/{id}/api-keys/{keyId}',
159
- argNames: ['id', 'keyId'],
160
- }));
161
- return command;
162
- }