@bctrl/cli 0.1.6 → 0.1.8

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 (61) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +113 -95
  3. package/dist/api/auth.d.ts +10 -1
  4. package/dist/api/auth.js +10 -1
  5. package/dist/api/client.d.ts +2 -1
  6. package/dist/api/client.js +37 -37
  7. package/dist/api/device-auth.d.ts +39 -0
  8. package/dist/api/device-auth.js +93 -0
  9. package/dist/api/errors.d.ts +1 -0
  10. package/dist/api/errors.js +8 -0
  11. package/dist/commands/ai/index.js +10 -11
  12. package/dist/commands/api-key/index.js +3 -8
  13. package/dist/commands/auth/device-login.d.ts +6 -0
  14. package/dist/commands/auth/device-login.js +19 -0
  15. package/dist/commands/auth/login.d.ts +11 -0
  16. package/dist/commands/auth/login.js +140 -20
  17. package/dist/commands/auth/logout.d.ts +2 -0
  18. package/dist/commands/auth/logout.js +19 -2
  19. package/dist/commands/auth/status.d.ts +1 -0
  20. package/dist/commands/auth/status.js +22 -5
  21. package/dist/commands/auth/token.js +6 -0
  22. package/dist/commands/browser-extension/index.js +7 -15
  23. package/dist/commands/file/index.js +8 -7
  24. package/dist/commands/help/index.js +5 -4
  25. package/dist/commands/notification-recipient/index.d.ts +3 -0
  26. package/dist/commands/notification-recipient/index.js +72 -0
  27. package/dist/commands/proxy/index.js +6 -4
  28. package/dist/commands/run/index.js +31 -32
  29. package/dist/commands/runtime/index.js +185 -106
  30. package/dist/commands/shared/help.d.ts +26 -3
  31. package/dist/commands/shared/help.js +87 -23
  32. package/dist/commands/shared/io.d.ts +1 -0
  33. package/dist/commands/shared/io.js +4 -2
  34. package/dist/commands/shared/operation.d.ts +50 -3
  35. package/dist/commands/shared/operation.js +171 -25
  36. package/dist/commands/shared/output.js +3 -7
  37. package/dist/commands/space/index.js +35 -32
  38. package/dist/commands/space/list.js +6 -5
  39. package/dist/commands/subaccount/index.js +12 -14
  40. package/dist/commands/usage/index.js +9 -5
  41. package/dist/commands/vault/index.js +15 -21
  42. package/dist/config/auth-store.d.ts +75 -9
  43. package/dist/config/auth-store.js +133 -27
  44. package/dist/config/config.d.ts +2 -2
  45. package/dist/config/config.js +9 -17
  46. package/dist/config/paths.d.ts +16 -0
  47. package/dist/config/paths.js +34 -0
  48. package/dist/config/pending-auth.d.ts +19 -0
  49. package/dist/config/pending-auth.js +72 -0
  50. package/dist/config/secret-store.d.ts +24 -0
  51. package/dist/config/secret-store.js +123 -0
  52. package/dist/factory.js +2 -1
  53. package/dist/generated/help.d.ts +7424 -2768
  54. package/dist/generated/help.js +10116 -4014
  55. package/dist/generated/openapi-routes.d.ts +52 -8
  56. package/dist/generated/openapi-routes.js +20 -9
  57. package/dist/generated/openapi-types.d.ts +2688 -883
  58. package/dist/root.js +2 -0
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +4 -0
  61. package/package.json +58 -47
@@ -1,9 +1,9 @@
1
1
  import { Command } from 'commander';
2
2
  import { CliError } from '../../runtime/errors.js';
3
- import { readJsonFile, writeBinary } from '../shared/io.js';
3
+ import { writeBinary } from '../shared/io.js';
4
4
  import { parsePositiveInteger } from '../shared/options.js';
5
5
  import { addOutputFlags } from '../shared/output.js';
6
- import { addPaginationFlags, createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, downloadOperation, outputFlags, requestOperation, requestOperationAndPrint, streamOperationText, } from '../shared/operation.js';
6
+ import { actingSubaccountOption, addPaginationFlags, buildOperationInput, 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
9
  command.addCommand(createOperationListCommand(factory, {
@@ -18,10 +18,10 @@ export function createRunCommand(factory) {
18
18
  spaceId: typeof options.space === 'string' ? options.space : undefined,
19
19
  runtimeId: typeof options.runtime === 'string' ? options.runtime : undefined,
20
20
  status: typeof options.status === 'string' ? options.status : undefined,
21
- subaccountId: typeof options.subaccount === 'string' ? options.subaccount : undefined,
22
21
  limit: typeof options.limit === 'number' ? options.limit : undefined,
23
22
  cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
24
23
  }),
24
+ actingSubaccountId: actingSubaccountOption('subaccount'),
25
25
  }));
26
26
  command.addCommand(createOperationViewCommand(factory, {
27
27
  operationId: 'runs.get',
@@ -72,8 +72,9 @@ function createRunActivityListCommand(factory) {
72
72
  .option('--category <category>', 'Filter by activity category')
73
73
  .option('--severity <severity>', 'Filter by severity')
74
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', {
75
+ .option('--file <id>', 'Filter by file id')
76
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
77
+ await requestOperationAndPrint(factory, 'runs.activity.list', await buildOperationInput('runs.activity.list', options, {
77
78
  pathParams: { runId },
78
79
  query: {
79
80
  view: options.view,
@@ -86,7 +87,7 @@ function createRunActivityListCommand(factory) {
86
87
  cursor: options.cursor,
87
88
  },
88
89
  output: outputFlags(options),
89
- });
90
+ }));
90
91
  });
91
92
  }
92
93
  function createRunEventsCommand(factory) {
@@ -117,8 +118,9 @@ function createRunEventsListCommand(factory) {
117
118
  .option('--type <type>', 'Filter by event type')
118
119
  .option('--status <status>', 'Filter by event status')
119
120
  .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', {
121
+ .option('--context-id <id>', 'Filter by browser context id')
122
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
123
+ await requestOperationAndPrint(factory, 'runs.events.list', await buildOperationInput('runs.events.list', options, {
122
124
  pathParams: { runId },
123
125
  query: {
124
126
  type: options.type,
@@ -129,7 +131,7 @@ function createRunEventsListCommand(factory) {
129
131
  cursor: options.cursor,
130
132
  },
131
133
  output: outputFlags(options),
132
- });
134
+ }));
133
135
  });
134
136
  }
135
137
  function createRunFilesCommand(factory) {
@@ -139,8 +141,10 @@ function createRunFilesCommand(factory) {
139
141
  return command;
140
142
  }
141
143
  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
+ return addOutputFlags(addPaginationFlags(new Command('list').description('List run files').argument('<runId>'))
145
+ .option('--type <type>', 'Filter by file type')
146
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, options) => {
147
+ await requestOperationAndPrint(factory, 'runs.files.list', await buildOperationInput('runs.files.list', options, {
144
148
  pathParams: { runId },
145
149
  query: {
146
150
  type: options.type,
@@ -148,7 +152,7 @@ function createRunFilesListCommand(factory) {
148
152
  cursor: options.cursor,
149
153
  },
150
154
  output: outputFlags(options),
151
- });
155
+ }));
152
156
  });
153
157
  }
154
158
  function createRunFilesExportCommand(factory) {
@@ -157,15 +161,12 @@ function createRunFilesExportCommand(factory) {
157
161
  .argument('<runId>')
158
162
  .requiredOption('--to <path>', 'Output archive path, or - for stdout')
159
163
  .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
- };
164
+ .option('--type <type...>', 'Filter exported file types')).action(async (runId, options) => {
165
+ const body = {
166
+ format: 'zip',
167
+ name: options.name,
168
+ filter: options.type ? { type: options.type } : undefined,
169
+ };
169
170
  const result = (await requestOperation(factory, 'runs.files.export', {
170
171
  pathParams: { runId },
171
172
  body: body,
@@ -183,9 +184,10 @@ function createRunFilesExportCommand(factory) {
183
184
  function createRunInvocationsCommand(factory) {
184
185
  const command = addOutputFlags(addPaginationFlags(new Command('invocations').description('List run invocations').argument('<runId>'))
185
186
  .option('--status <status>', 'Filter by invocation status')
186
- .option('--action <action>', 'Filter by invocation action'));
187
+ .option('--action <action>', 'Filter by invocation action')
188
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)'));
187
189
  command.action(async (runId, options) => {
188
- await requestOperationAndPrint(factory, 'runs.invocations.list', {
190
+ await requestOperationAndPrint(factory, 'runs.invocations.list', await buildOperationInput('runs.invocations.list', options, {
189
191
  pathParams: { runId },
190
192
  query: {
191
193
  status: options.status,
@@ -194,7 +196,7 @@ function createRunInvocationsCommand(factory) {
194
196
  cursor: options.cursor,
195
197
  },
196
198
  output: outputFlags(options),
197
- });
199
+ }));
198
200
  });
199
201
  return command;
200
202
  }
@@ -203,11 +205,12 @@ function createRunInvocationCommand(factory) {
203
205
  command.addCommand(addOutputFlags(new Command('get')
204
206
  .description('Get a run invocation')
205
207
  .argument('<runId>')
206
- .argument('<invocationId>')).action(async (runId, invocationId, options) => {
207
- await requestOperationAndPrint(factory, 'runs.invocations.get', {
208
+ .argument('<invocationId>')
209
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runId, invocationId, options) => {
210
+ await requestOperationAndPrint(factory, 'runs.invocations.get', await buildOperationInput('runs.invocations.get', options, {
208
211
  pathParams: { runId, invocationId },
209
212
  output: outputFlags(options),
210
- });
213
+ }));
211
214
  }));
212
215
  return command;
213
216
  }
@@ -219,12 +222,8 @@ function createRunViewerCommand(factory, name, description) {
219
222
  argNames: ['runId'],
220
223
  configure: (cmd) => cmd
221
224
  .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'),
225
+ .option('--control <none|input>', 'Live viewer control mode'),
224
226
  body: async (_args, options) => {
225
- if (typeof options.input === 'string') {
226
- return (await readJsonFile(options.input));
227
- }
228
227
  return {
229
228
  expiresInSeconds: typeof options.expiresInSeconds === 'number' ? options.expiresInSeconds : undefined,
230
229
  control: name === 'live' ? options.control : undefined,
@@ -1,9 +1,10 @@
1
1
  import { Command, Option } from 'commander';
2
- import { readBlob, readJsonFile } from '../shared/io.js';
3
- import { addPaginationFlags, createOperationJsonBodyCommand, outputFlags, requestOperationAndPrint, uploadOperationFile, } from '../shared/operation.js';
2
+ import { parseJsonString, readBlob, readJsonFile } from '../shared/io.js';
3
+ import { addPaginationFlags, buildOperationInput, createOperationJsonBodyCommand, outputFlags, requestOperationAndPrint, uploadOperationFile, } from '../shared/operation.js';
4
4
  import { parsePositiveInteger } from '../shared/options.js';
5
5
  import { addOutputFlags, outputData } from '../shared/output.js';
6
6
  import { CliError } from '../../runtime/errors.js';
7
+ import { addCliOperationHelp } from '../shared/help.js';
7
8
  export function createRuntimeCommand(factory) {
8
9
  const command = new Command('runtime').description('Manage runtimes');
9
10
  command.addCommand(addOutputFlags(addPaginationFlags(new Command('list').description('List runtimes'))
@@ -12,8 +13,9 @@ export function createRuntimeCommand(factory) {
12
13
  'active',
13
14
  'failed',
14
15
  'stopped',
15
- ]))).action(async (options) => {
16
- await requestOperationAndPrint(factory, 'runtimes.list', {
16
+ ]))
17
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (options) => {
18
+ await requestOperationAndPrint(factory, 'runtimes.list', await buildOperationInput('runtimes.list', options, {
17
19
  query: {
18
20
  spaceId: options.space,
19
21
  status: options.status ? [options.status] : undefined,
@@ -21,13 +23,16 @@ export function createRuntimeCommand(factory) {
21
23
  cursor: options.cursor,
22
24
  },
23
25
  output: outputFlags(options),
24
- });
26
+ }));
25
27
  }));
26
- command.addCommand(addOutputFlags(new Command('get').description('Get a runtime').argument('<runtimeId>')).action(async (runtimeId, options) => {
27
- await requestOperationAndPrint(factory, 'runtimes.get', {
28
+ command.addCommand(addOutputFlags(new Command('get')
29
+ .description('Get a runtime')
30
+ .argument('<runtimeId>')
31
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
32
+ await requestOperationAndPrint(factory, 'runtimes.get', await buildOperationInput('runtimes.get', options, {
28
33
  pathParams: { runtimeId },
29
34
  output: outputFlags(options),
30
- });
35
+ }));
31
36
  }));
32
37
  command.addCommand(createRuntimeCreateCommand(factory));
33
38
  command.addCommand(createRuntimePatchCommand(factory));
@@ -35,84 +40,101 @@ export function createRuntimeCommand(factory) {
35
40
  .description('Delete a runtime and its browser state')
36
41
  .argument('<runtimeId>')
37
42
  .option('-y, --yes', 'Confirm deletion')
38
- .option('--force', 'Stop the runtime first if it is still active')).action(async (runtimeId, options) => {
43
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
39
44
  if (options.yes !== true) {
40
45
  throw new CliError('Refusing to delete without --yes');
41
46
  }
42
- await requestOperationAndPrint(factory, 'runtimes.delete', {
47
+ await requestOperationAndPrint(factory, 'runtimes.delete', await buildOperationInput('runtimes.delete', options, {
43
48
  pathParams: { runtimeId },
44
- query: { force: options.force === true ? true : undefined },
45
49
  output: outputFlags(options),
46
- });
50
+ }));
47
51
  }));
48
52
  command.addCommand(createRuntimeStartCommand(factory));
49
- command.addCommand(addOutputFlags(new Command('stop').description('Stop a runtime').argument('<runtimeId>')).action(async (runtimeId, options) => {
50
- await requestOperationAndPrint(factory, 'runtimes.stop', {
53
+ command.addCommand(addOutputFlags(new Command('stop')
54
+ .description('Stop a runtime')
55
+ .argument('<runtimeId>')
56
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
57
+ await requestOperationAndPrint(factory, 'runtimes.stop', await buildOperationInput('runtimes.stop', options, {
51
58
  pathParams: { runtimeId },
52
59
  output: outputFlags(options),
53
- });
60
+ }));
54
61
  }));
55
62
  command.addCommand(createRuntimeFileCommand(factory));
56
63
  command.addCommand(createRuntimeInvocationCommand(factory));
64
+ command.addCommand(createRuntimeHumanActionCommand(factory));
57
65
  command.addCommand(createRuntimeTargetCommand(factory));
58
66
  return command;
59
67
  }
60
68
  function createRuntimeTargetCommand(factory) {
61
69
  const command = new Command('target').description('Manage runtime targets (tabs)');
62
- command.addCommand(addOutputFlags(new Command('list').description('List targets').argument('<runtimeId>')).action(async (runtimeId, options) => {
63
- await requestOperationAndPrint(factory, 'runtimes.targets.list', {
70
+ command.addCommand(addOutputFlags(new Command('list')
71
+ .description('List targets')
72
+ .argument('<runtimeId>')
73
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
74
+ await requestOperationAndPrint(factory, 'runtimes.targets.list', await buildOperationInput('runtimes.targets.list', options, {
64
75
  pathParams: { runtimeId },
65
76
  output: outputFlags(options),
66
- });
77
+ }));
67
78
  }));
68
79
  command.addCommand(addOutputFlags(new Command('create')
69
80
  .description('Open a new target')
70
81
  .argument('<runtimeId>')
71
82
  .option('--uri <uri>', 'Navigate the new target to this URI')
72
- .option('--activate', 'Focus the new target')).action(async (runtimeId, options) => {
73
- await requestOperationAndPrint(factory, 'runtimes.targets.create', {
83
+ .option('--activate', 'Focus the new target')
84
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')
85
+ .option('--body <json>', 'Request body as JSON (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
86
+ await requestOperationAndPrint(factory, 'runtimes.targets.create', await buildOperationInput('runtimes.targets.create', options, {
74
87
  pathParams: { runtimeId },
75
88
  body: {
76
89
  ...(options.uri ? { uri: options.uri } : {}),
77
90
  ...(options.activate ? { activate: true } : {}),
78
91
  },
79
92
  output: outputFlags(options),
80
- });
93
+ }));
81
94
  }));
82
- command.addCommand(addOutputFlags(new Command('get').description('Get a target').argument('<runtimeId>').argument('<targetId>')).action(async (runtimeId, targetId, options) => {
83
- await requestOperationAndPrint(factory, 'runtimes.targets.get', {
95
+ command.addCommand(addOutputFlags(new Command('get')
96
+ .description('Get a target')
97
+ .argument('<runtimeId>')
98
+ .argument('<targetId>')
99
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, targetId, options) => {
100
+ await requestOperationAndPrint(factory, 'runtimes.targets.get', await buildOperationInput('runtimes.targets.get', options, {
84
101
  pathParams: { runtimeId, targetId },
85
102
  output: outputFlags(options),
86
- });
103
+ }));
87
104
  }));
88
105
  command.addCommand(addOutputFlags(new Command('activate')
89
106
  .description('Focus a target')
90
107
  .argument('<runtimeId>')
91
- .argument('<targetId>')).action(async (runtimeId, targetId, options) => {
92
- await requestOperationAndPrint(factory, 'runtimes.targets.activate', {
108
+ .argument('<targetId>')
109
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, targetId, options) => {
110
+ await requestOperationAndPrint(factory, 'runtimes.targets.activate', await buildOperationInput('runtimes.targets.activate', options, {
93
111
  pathParams: { runtimeId, targetId },
94
112
  output: outputFlags(options),
95
- });
113
+ }));
96
114
  }));
97
115
  command.addCommand(addOutputFlags(new Command('delete')
98
116
  .description('Close a target')
99
117
  .argument('<runtimeId>')
100
- .argument('<targetId>')).action(async (runtimeId, targetId, options) => {
101
- await requestOperationAndPrint(factory, 'runtimes.targets.delete', {
118
+ .argument('<targetId>')
119
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, targetId, options) => {
120
+ await requestOperationAndPrint(factory, 'runtimes.targets.delete', await buildOperationInput('runtimes.targets.delete', options, {
102
121
  pathParams: { runtimeId, targetId },
103
122
  output: outputFlags(options),
104
- });
123
+ }));
105
124
  }));
106
125
  return command;
107
126
  }
108
127
  function createRuntimeCreateCommand(factory) {
109
- return addOutputFlags(new Command('create')
128
+ return addOutputFlags(addCliOperationHelp(new Command('create')
110
129
  .description('Create a runtime')
111
130
  .option('--space <id>', 'Space id; omitted uses caller default space')
112
131
  .option('--name <name>', 'Runtime name')
113
132
  .option('--profile', 'Persist browser state across runtime starts')
114
133
  .option('--proxy <id-or-url>', 'Saved proxy id or inline custom proxy URL')
115
- .addOption(new Option('--device <device>', 'Fingerprint device filter').choices(['desktop', 'mobile']))
134
+ .addOption(new Option('--device <device>', 'Fingerprint device filter').choices([
135
+ 'desktop',
136
+ 'mobile',
137
+ ]))
116
138
  .addOption(new Option('--os <os>', 'Fingerprint OS filter').choices([
117
139
  'windows',
118
140
  'macos',
@@ -128,24 +150,26 @@ function createRuntimeCreateCommand(factory) {
128
150
  .option('--locale <locale>', 'Fingerprint locale/language filter; repeat to set an ordered language stack', collectLocale, [])
129
151
  .option('--config-file <path>', 'Runtime config JSON file')
130
152
  .option('--metadata-file <path>', 'Runtime metadata JSON file')
131
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin')).action(async (options) => {
153
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')
154
+ .option('--body <json>', 'Full request body as JSON (inline, @file, or - for stdin)'), 'runtimes.create')).action(async (options) => {
155
+ // --body (raw JSON) is the complete body; otherwise build it from
156
+ // the curated flags. --params can still add query params. The control-plane
157
+ // validates the body against the runtimes.create schema either way.
132
158
  const config = typeof options.configFile === 'string'
133
159
  ? await readJsonFile(options.configFile, '--config-file')
134
160
  : undefined;
135
- await requestOperationAndPrint(factory, 'runtimes.create', {
136
- body: typeof options.input === 'string'
137
- ? (await readJsonFile(options.input))
138
- : {
139
- type: 'browser',
140
- spaceId: options.space,
141
- name: options.name,
142
- config: buildRuntimeCreateConfig(config, options),
143
- metadata: typeof options.metadataFile === 'string'
144
- ? await readJsonFile(options.metadataFile, '--metadata-file')
145
- : undefined,
146
- },
161
+ await requestOperationAndPrint(factory, 'runtimes.create', await buildOperationInput('runtimes.create', options, {
162
+ body: {
163
+ type: 'browser',
164
+ spaceId: options.space,
165
+ name: options.name,
166
+ config: buildRuntimeCreateConfig(config, options),
167
+ metadata: typeof options.metadataFile === 'string'
168
+ ? await readJsonFile(options.metadataFile, '--metadata-file')
169
+ : undefined,
170
+ },
147
171
  output: outputFlags(options),
148
- });
172
+ }));
149
173
  });
150
174
  }
151
175
  function collectLocale(value, previous) {
@@ -195,34 +219,34 @@ function createRuntimePatchCommand(factory) {
195
219
  .description('Update a runtime')
196
220
  .argument('<runtimeId>')
197
221
  .option('--name <name>', 'Runtime name')
198
- .option('--idle-timeout-minutes <minutes>', 'Idle timeout in minutes', parsePositiveInteger)
222
+ .option('--idle-timeout-seconds <seconds>', 'Idle timeout in seconds', parsePositiveInteger)
199
223
  .option('--config-file <path>', 'Runtime config JSON file')
200
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin')).action(async (runtimeId, options) => {
201
- await requestOperationAndPrint(factory, 'runtimes.update', {
224
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')
225
+ .option('--body <json>', 'Request body as JSON (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
226
+ await requestOperationAndPrint(factory, 'runtimes.update', await buildOperationInput('runtimes.update', options, {
202
227
  pathParams: { runtimeId },
203
- body: typeof options.input === 'string'
204
- ? (await readJsonFile(options.input))
205
- : {
206
- name: options.name,
207
- idleTimeoutMinutes: options.idleTimeoutMinutes,
208
- config: typeof options.configFile === 'string'
209
- ? await readJsonFile(options.configFile, '--config-file')
210
- : undefined,
211
- },
228
+ body: {
229
+ name: options.name,
230
+ idleTimeoutSeconds: options.idleTimeoutSeconds,
231
+ config: typeof options.configFile === 'string'
232
+ ? await readJsonFile(options.configFile, '--config-file')
233
+ : undefined,
234
+ },
212
235
  output: outputFlags(options),
213
- });
236
+ }));
214
237
  });
215
238
  }
216
239
  function createRuntimeStartCommand(factory) {
217
240
  return addOutputFlags(new Command('start')
218
241
  .description('Start a runtime')
219
242
  .argument('<runtimeId>')
220
- .option('--idempotency-key <key>', 'Idempotency key for retry-safe start')).action(async (runtimeId, options) => {
221
- await requestOperationAndPrint(factory, 'runtimes.start', {
243
+ .option('--idempotency-key <key>', 'Idempotency key for retry-safe start')
244
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
245
+ await requestOperationAndPrint(factory, 'runtimes.start', await buildOperationInput('runtimes.start', options, {
222
246
  pathParams: { runtimeId },
223
247
  idempotencyKey: options.idempotencyKey,
224
248
  output: outputFlags(options),
225
- });
249
+ }));
226
250
  });
227
251
  }
228
252
  function createRuntimeInvocationCommand(factory) {
@@ -232,11 +256,12 @@ function createRuntimeInvocationCommand(factory) {
232
256
  command.addCommand(addOutputFlags(new Command('cancel')
233
257
  .description('Cancel an invocation')
234
258
  .argument('<runtimeId>')
235
- .argument('<invocationId>')).action(async (runtimeId, invocationId, options) => {
236
- await requestOperationAndPrint(factory, 'runtimes.invocations.cancel', {
259
+ .argument('<invocationId>')
260
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, invocationId, options) => {
261
+ await requestOperationAndPrint(factory, 'runtimes.invocations.cancel', await buildOperationInput('runtimes.invocations.cancel', options, {
237
262
  pathParams: { runtimeId, invocationId },
238
263
  output: outputFlags(options),
239
- });
264
+ }));
240
265
  }));
241
266
  return command;
242
267
  }
@@ -250,30 +275,29 @@ function createRuntimeInvocationCreateCommand(factory) {
250
275
  .option('--model <model>', 'Model id')
251
276
  .option('--tool <id...>', 'Inline tool id')
252
277
  .option('--toolset <id>', 'Toolset id')
253
- .option('--timeout-ms <ms>', 'Invocation timeout in milliseconds', parsePositiveInteger)
278
+ .option('--timeout-seconds <seconds>', 'Invocation timeout in seconds', parsePositiveInteger)
254
279
  .option('--idempotency-key <key>', 'Idempotency key for retry-safe create')
255
- .option('--input <path>', 'Read full JSON request body from file, or - for stdin')).action(async (runtimeId, options) => {
256
- const body = typeof options.input === 'string'
257
- ? await readJsonFile(options.input)
258
- : {
259
- action: options.action,
260
- instruction: options.instruction,
261
- target: options.target === undefined
262
- ? undefined
263
- : options.target === 'active' || options.target === 'new'
264
- ? options.target
265
- : { id: options.target },
266
- model: options.model,
267
- toolIds: options.tool,
268
- toolsetId: options.toolset,
269
- timeoutMs: options.timeoutMs,
270
- };
271
- await requestOperationAndPrint(factory, 'runtimes.invocations.create', {
280
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')
281
+ .option('--body <json>', 'Request body as JSON (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
282
+ const body = {
283
+ action: options.action,
284
+ instruction: options.instruction,
285
+ target: options.target === undefined
286
+ ? undefined
287
+ : options.target === 'active' || options.target === 'new'
288
+ ? options.target
289
+ : { id: options.target },
290
+ model: options.model,
291
+ toolIds: options.tool,
292
+ toolsetId: options.toolset,
293
+ timeoutSeconds: options.timeoutSeconds,
294
+ };
295
+ await requestOperationAndPrint(factory, 'runtimes.invocations.create', await buildOperationInput('runtimes.invocations.create', options, {
272
296
  pathParams: { runtimeId },
273
297
  body: body,
274
298
  idempotencyKey: options.idempotencyKey,
275
299
  output: outputFlags(options),
276
- });
300
+ }));
277
301
  });
278
302
  }
279
303
  function createRuntimeInvocationWaitCommand(factory) {
@@ -283,20 +307,80 @@ function createRuntimeInvocationWaitCommand(factory) {
283
307
  description: 'Wait for an invocation',
284
308
  argNames: ['runtimeId', 'invocationId'],
285
309
  configure: (cmd) => cmd
286
- .option('--timeout-ms <ms>', 'Wait timeout in milliseconds', parsePositiveInteger)
287
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
310
+ .option('--timeout-seconds <seconds>', 'Wait timeout in seconds', parsePositiveInteger),
288
311
  body: async (_args, options) => {
289
- if (typeof options.input === 'string') {
290
- return (await readJsonFile(options.input));
291
- }
292
- return { timeoutMs: options.timeoutMs };
312
+ return {
313
+ timeoutSeconds: options.timeoutSeconds,
314
+ };
293
315
  },
294
316
  });
295
317
  }
318
+ function createRuntimeHumanActionCommand(factory) {
319
+ const command = new Command('human-action').description('Manage runtime human actions');
320
+ command.addCommand(createOperationJsonBodyCommand(factory, {
321
+ operationId: 'runtimes.human-actions.create',
322
+ name: 'create',
323
+ description: 'Request human action',
324
+ argNames: ['runtimeId'],
325
+ configure: (cmd) => cmd
326
+ .option('--message <message>', 'Instructions for the human')
327
+ .option('--timeout-seconds <seconds>', 'Human action timeout in seconds', parsePositiveInteger),
328
+ body: async (_args, options) => {
329
+ return {
330
+ message: options.message,
331
+ timeoutSeconds: options.timeoutSeconds,
332
+ };
333
+ },
334
+ }));
335
+ command.addCommand(addOutputFlags(addCliOperationHelp(new Command('get')
336
+ .description('Get a human action')
337
+ .argument('<runtimeId>')
338
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)'), 'runtimes.human-actions.get')).action(async (runtimeId, options) => {
339
+ await requestOperationAndPrint(factory, 'runtimes.human-actions.get', await buildOperationInput('runtimes.human-actions.get', options, {
340
+ pathParams: { runtimeId },
341
+ output: outputFlags(options),
342
+ }));
343
+ }));
344
+ command.addCommand(addOutputFlags(addCliOperationHelp(new Command('complete')
345
+ .description('Complete a human action')
346
+ .argument('<runtimeId>')
347
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)'), 'runtimes.human-actions.complete')).action(async (runtimeId, options) => {
348
+ await requestOperationAndPrint(factory, 'runtimes.human-actions.complete', await buildOperationInput('runtimes.human-actions.complete', options, {
349
+ pathParams: { runtimeId },
350
+ output: outputFlags(options),
351
+ }));
352
+ }));
353
+ command.addCommand(addOutputFlags(addCliOperationHelp(new Command('cancel')
354
+ .description('Cancel a human action')
355
+ .argument('<runtimeId>')
356
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)'), 'runtimes.human-actions.cancel')).action(async (runtimeId, options) => {
357
+ await requestOperationAndPrint(factory, 'runtimes.human-actions.cancel', await buildOperationInput('runtimes.human-actions.cancel', options, {
358
+ pathParams: { runtimeId },
359
+ output: outputFlags(options),
360
+ }));
361
+ }));
362
+ command.addCommand(addOutputFlags(addCliOperationHelp(new Command('wait')
363
+ .description('Wait for a human action')
364
+ .argument('<runtimeId>')
365
+ .option('--timeout-seconds <seconds>', 'Long-poll timeout', parsePositiveInteger)
366
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')
367
+ .option('--body <json>', 'Request body as JSON (inline, @file, or - for stdin)'), 'runtimes.human-actions.wait')).action(async (runtimeId, options) => {
368
+ await requestOperationAndPrint(factory, 'runtimes.human-actions.wait', await buildOperationInput('runtimes.human-actions.wait', options, {
369
+ pathParams: { runtimeId },
370
+ body: {
371
+ timeoutSeconds: options.timeoutSeconds,
372
+ },
373
+ output: outputFlags(options),
374
+ }));
375
+ }));
376
+ return command;
377
+ }
296
378
  function createRuntimeFileCommand(factory) {
297
379
  const command = new Command('file').description('Move files into and out of runtimes');
298
- command.addCommand(addOutputFlags(addPaginationFlags(new Command('list').description('List runtime files').argument('<runtimeId>')).option('--type <type>', 'Filter by file type')).action(async (runtimeId, options) => {
299
- await requestOperationAndPrint(factory, 'runtimes.files.list', {
380
+ command.addCommand(addOutputFlags(addPaginationFlags(new Command('list').description('List runtime files').argument('<runtimeId>'))
381
+ .option('--type <type>', 'Filter by file type')
382
+ .option('--params <json>', 'Path/query parameters as a JSON object (inline, @file, or - for stdin)')).action(async (runtimeId, options) => {
383
+ await requestOperationAndPrint(factory, 'runtimes.files.list', await buildOperationInput('runtimes.files.list', options, {
300
384
  pathParams: { runtimeId },
301
385
  query: {
302
386
  type: options.type,
@@ -304,7 +388,7 @@ function createRuntimeFileCommand(factory) {
304
388
  cursor: options.cursor,
305
389
  },
306
390
  output: outputFlags(options),
307
- });
391
+ }));
308
392
  }));
309
393
  command.addCommand(createRuntimeFileUploadCommand(factory));
310
394
  command.addCommand(createRuntimeFileStageCommand(factory));
@@ -319,11 +403,14 @@ function createRuntimeFileUploadCommand(factory) {
319
403
  .option('--destination-path <path>', 'Durable BCTRL storage destination')
320
404
  .option('--runtime-path <path>', 'Runtime workspace destination')
321
405
  .option('--name <name>', 'Display name')
406
+ .option('--metadata <json>', 'Metadata as inline JSON (overrides --metadata-file)')
322
407
  .option('--metadata-file <path>', 'Metadata JSON file')).action(async (runtimeId, localPath, options) => {
323
408
  const file = await readBlob(localPath);
324
- const metadata = typeof options.metadataFile === 'string'
325
- ? JSON.stringify(await readJsonFile(options.metadataFile, '--metadata-file'))
326
- : undefined;
409
+ const metadata = options.metadata !== undefined
410
+ ? JSON.stringify(parseJsonString(options.metadata, '--metadata'))
411
+ : typeof options.metadataFile === 'string'
412
+ ? JSON.stringify(await readJsonFile(options.metadataFile, '--metadata-file'))
413
+ : undefined;
327
414
  const result = await uploadOperationFile(factory, 'runtimes.files.upload', {
328
415
  pathParams: { runtimeId },
329
416
  file: file.blob,
@@ -347,12 +434,8 @@ function createRuntimeFileStageCommand(factory) {
347
434
  configure: (cmd) => cmd
348
435
  .option('--file <id>', 'Durable file id')
349
436
  .option('--runtime-path <path>', 'Runtime workspace destination')
350
- .option('--name <name>', 'Runtime-local display name')
351
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
437
+ .option('--name <name>', 'Runtime-local display name'),
352
438
  body: async (_args, options) => {
353
- if (typeof options.input === 'string') {
354
- return (await readJsonFile(options.input));
355
- }
356
439
  return {
357
440
  fileId: options.file,
358
441
  runtimePath: options.runtimePath,
@@ -370,12 +453,8 @@ function createRuntimeFileCollectCommand(factory) {
370
453
  configure: (cmd) => cmd
371
454
  .requiredOption('--runtime-path <path>', 'Runtime workspace source')
372
455
  .option('--name <name>', 'Durable file name')
373
- .option('--destination-path <path>', 'Durable BCTRL storage destination')
374
- .option('--input <path>', 'Read JSON request body from file, or - for stdin'),
456
+ .option('--destination-path <path>', 'Durable BCTRL storage destination'),
375
457
  body: async (_args, options) => {
376
- if (typeof options.input === 'string') {
377
- return (await readJsonFile(options.input));
378
- }
379
458
  return {
380
459
  runtimePath: options.runtimePath,
381
460
  name: options.name,