@bctrl/cli 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -89
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.js +1 -0
- package/dist/api/errors.js +2 -2
- package/dist/bin/bctrl.js +0 -0
- package/dist/commands/{vault → account}/index.d.ts +1 -1
- package/dist/commands/account/index.js +21 -0
- package/dist/commands/ai/index.js +13 -8
- package/dist/commands/api-key/index.js +1 -1
- package/dist/commands/browser-extension/index.js +8 -8
- package/dist/commands/conversation/index.d.ts +3 -0
- package/dist/commands/conversation/index.js +51 -0
- package/dist/commands/file/index.js +8 -3
- package/dist/commands/run/index.js +23 -292
- package/dist/commands/runtime/index.js +32 -453
- package/dist/commands/shared/operation.d.ts +3 -0
- package/dist/commands/shared/operation.js +20 -7
- package/dist/commands/space/index.js +0 -25
- package/dist/commands/subaccount/index.js +2 -2
- package/dist/commands/tool/index.js +15 -5
- package/dist/commands/tool-call/index.js +22 -6
- package/dist/commands/toolset/index.js +1 -1
- package/dist/commands/view/index.d.ts +3 -0
- package/dist/commands/view/index.js +26 -0
- package/dist/commands/webhook/index.d.ts +3 -0
- package/dist/commands/webhook/index.js +64 -0
- package/dist/generated/help.d.ts +4896 -5977
- package/dist/generated/help.js +6208 -7532
- package/dist/generated/openapi-routes.d.ts +114 -142
- package/dist/generated/openapi-routes.js +37 -44
- package/dist/generated/openapi-types.d.ts +4451 -5302
- package/dist/root.js +8 -2
- package/package.json +14 -12
- package/dist/commands/vault/index.js +0 -171
package/README.md
CHANGED
|
@@ -1,109 +1,37 @@
|
|
|
1
1
|
# BCTRL CLI
|
|
2
2
|
|
|
3
|
-
CLI for controlling BCTRL runtimes, files, runs, vault secrets, tools, and account resources from scripts or AI agents.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
3
|
```bash
|
|
8
4
|
npm install -g @bctrl/cli
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Auth
|
|
12
|
-
|
|
13
|
-
The CLI authenticates with either a browser-approved CLI session or a BCTRL API key.
|
|
14
|
-
|
|
15
|
-
For a local terminal, start a browser approval and let the CLI poll until you approve it:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
bctrl auth login --url --wait
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
For agents that need to hand the URL to a human, split the flow into two commands:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
bctrl auth login --url
|
|
25
|
-
# approve the printed URL
|
|
26
5
|
bctrl auth login
|
|
27
6
|
```
|
|
28
7
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
export BCTRL_API_KEY="bctrl_..."
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
`BCTRL_API_KEY` takes precedence over stored credentials.
|
|
36
|
-
|
|
37
|
-
Check the active auth:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
bctrl auth status
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Point contributors at a local or staging stack with `BCTRL_API_URL`:
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
BCTRL_API_URL=http://127.0.0.1:8787/v1 bctrl auth login --url --wait
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Browser Runtime Flow
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
bctrl space list
|
|
53
|
-
bctrl runtime create --space sp_123 --name checkout-test
|
|
54
|
-
bctrl runtime start rt_123
|
|
55
|
-
bctrl runtime stop rt_123
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## JSON Input
|
|
59
|
-
|
|
60
|
-
Use `--input` for full request bodies:
|
|
8
|
+
Create and start runtimes with `bctrl runtime`. Automation is exposed through a
|
|
9
|
+
small generic Tool interface:
|
|
61
10
|
|
|
62
11
|
```bash
|
|
63
|
-
bctrl
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Use `-` to read from stdin:
|
|
12
|
+
bctrl tools call stagehand.act \
|
|
13
|
+
--body '{"runtimeId":"rt_...","instruction":"Click Continue"}'
|
|
67
14
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| bctrl runtime create --input -
|
|
15
|
+
bctrl tools start captcha.solve --body '{"runtimeId":"rt_..."}'
|
|
16
|
+
bctrl tool-calls result call_... --params '{"waitSeconds":60}'
|
|
71
17
|
```
|
|
72
18
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Print full JSON:
|
|
19
|
+
Use persistent conversations for long-running agents:
|
|
76
20
|
|
|
77
21
|
```bash
|
|
78
|
-
bctrl
|
|
22
|
+
bctrl conversations create \
|
|
23
|
+
--body '{"agent":"browser-use","runtimeId":"rt_..."}'
|
|
24
|
+
bctrl conversations message conv_... --body '{"text":"Complete checkout"}'
|
|
25
|
+
bctrl conversations stream conv_...
|
|
79
26
|
```
|
|
80
27
|
|
|
81
|
-
|
|
28
|
+
Every automation path contributes to one Run:
|
|
82
29
|
|
|
83
30
|
```bash
|
|
84
|
-
bctrl
|
|
31
|
+
bctrl runs trace run_...
|
|
32
|
+
bctrl runs events run_...
|
|
33
|
+
bctrl runs stream run_...
|
|
85
34
|
```
|
|
86
35
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
bctrl runtime list --space sp_123 --json --jq '.data[] | select(.status == "active")'
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Render with a template:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
bctrl runtime list \
|
|
97
|
-
--space sp_123 \
|
|
98
|
-
--json \
|
|
99
|
-
--template '{{#each data}}{{id}} {{status}}{{newline}}{{/each}}'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Help
|
|
103
|
-
|
|
104
|
-
Use command help for exact input and output fields:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
bctrl runtime create --help
|
|
108
|
-
bctrl runtime start --help
|
|
109
|
-
```
|
|
36
|
+
All JSON-body commands accept inline JSON, `@file.json`, or `-` for stdin through
|
|
37
|
+
`--body`. Use `--json`, `--jq`, or `--template` to control output.
|
package/dist/api/client.d.ts
CHANGED
package/dist/api/client.js
CHANGED
|
@@ -120,6 +120,7 @@ function requestHeaders(token, options, accept) {
|
|
|
120
120
|
authorization: `Bearer ${token}`,
|
|
121
121
|
'user-agent': 'BCTRL CLI',
|
|
122
122
|
...(options?.actingSubaccountId ? { 'BCTRL-Subaccount-Id': options.actingSubaccountId } : {}),
|
|
123
|
+
...(options?.runtimeId ? { 'BCTRL-Runtime-Id': options.runtimeId } : {}),
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
126
|
function buildUrl(baseUrl, path, query) {
|
package/dist/api/errors.js
CHANGED
|
@@ -77,8 +77,8 @@ function suggestionForApiError(error) {
|
|
|
77
77
|
return commandWithOptionalSpace('bctrl toolset list', error.details);
|
|
78
78
|
case 'tool_call.not_found':
|
|
79
79
|
return 'bctrl tool-call list';
|
|
80
|
-
case '
|
|
81
|
-
return 'bctrl
|
|
80
|
+
case 'tool.not_found':
|
|
81
|
+
return 'bctrl tools list';
|
|
82
82
|
case 'browser_extension.not_found':
|
|
83
83
|
return 'bctrl browser extension list';
|
|
84
84
|
case 'ai_credential.not_found':
|
package/dist/bin/bctrl.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { addOutputFlags } from '../shared/output.js';
|
|
3
|
+
import { createOperationJsonBodyCommand, outputFlags, requestOperationAndPrint, } from '../shared/operation.js';
|
|
4
|
+
export function createAccountCommand(factory) {
|
|
5
|
+
const command = new Command('account').description('Manage organization settings');
|
|
6
|
+
command.addCommand(addOutputFlags(new Command('get').description('Get organization settings')).action(async (options) => {
|
|
7
|
+
await requestOperationAndPrint(factory, 'account.get', {
|
|
8
|
+
output: outputFlags(options),
|
|
9
|
+
});
|
|
10
|
+
}));
|
|
11
|
+
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
12
|
+
operationId: 'account.update',
|
|
13
|
+
name: 'patch',
|
|
14
|
+
description: 'Update organization settings (use --body for branding merge-patch JSON)',
|
|
15
|
+
configure: (cmd) => cmd.option('--dry-run', 'Validate and resolve without persisting'),
|
|
16
|
+
query: (_args, options) => ({
|
|
17
|
+
dryRun: options.dryRun === true,
|
|
18
|
+
}),
|
|
19
|
+
}));
|
|
20
|
+
return command;
|
|
21
|
+
}
|
|
@@ -70,14 +70,19 @@ function createAiCredentialWriteCommand(factory, name, operationId, argNames = [
|
|
|
70
70
|
name,
|
|
71
71
|
description: name === 'create' ? 'Create an AI credential' : 'Update an AI credential',
|
|
72
72
|
argNames,
|
|
73
|
-
configure: (cmd) =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
configure: (cmd) => {
|
|
74
|
+
const configured = cmd
|
|
75
|
+
.option('--name <name>', 'Credential name')
|
|
76
|
+
.option('--provider <provider>', 'Provider key')
|
|
77
|
+
.option('--api-key <key>', 'Provider API key')
|
|
78
|
+
.option('--status <status>', 'Credential status')
|
|
79
|
+
.option('--default-model <model>', 'Default model')
|
|
80
|
+
.option('--base-url <url>', 'Custom provider base URL');
|
|
81
|
+
if (name === 'create') {
|
|
82
|
+
configured.option('--subaccount-id <id>', 'Use this Subaccount account context with an Organization key');
|
|
83
|
+
}
|
|
84
|
+
return configured;
|
|
85
|
+
},
|
|
81
86
|
body: async (_args, options) => {
|
|
82
87
|
return {
|
|
83
88
|
name: options.name,
|
|
@@ -23,7 +23,7 @@ export function createApiKeyCommand(factory) {
|
|
|
23
23
|
description: 'Create an API key',
|
|
24
24
|
configure: (cmd) => cmd
|
|
25
25
|
.option('--name <name>', 'API key name')
|
|
26
|
-
.option('--subaccount-id <id>', 'Create a
|
|
26
|
+
.option('--subaccount-id <id>', 'Create a key confined to this Subaccount')
|
|
27
27
|
.option('--expires-at <iso>', 'Expiration timestamp'),
|
|
28
28
|
body: async (_args, options) => {
|
|
29
29
|
return {
|
|
@@ -5,7 +5,7 @@ import { actingSubaccountOption, createOperationDeleteCommand, createOperationJs
|
|
|
5
5
|
export function createBrowserExtensionCommand(factory) {
|
|
6
6
|
const command = new Command('browser-extension').description('Manage browser extensions');
|
|
7
7
|
command.addCommand(createOperationListCommand(factory, {
|
|
8
|
-
operationId: 'browser
|
|
8
|
+
operationId: 'browser.extensions.list',
|
|
9
9
|
description: 'List browser extensions',
|
|
10
10
|
configure: (cmd) => cmd
|
|
11
11
|
.option('--subaccount-id <id>', 'Filter by subaccount id')
|
|
@@ -20,7 +20,7 @@ export function createBrowserExtensionCommand(factory) {
|
|
|
20
20
|
actingSubaccountId: actingSubaccountOption(),
|
|
21
21
|
}));
|
|
22
22
|
command.addCommand(createOperationViewCommand(factory, {
|
|
23
|
-
operationId: 'browser
|
|
23
|
+
operationId: 'browser.extensions.get',
|
|
24
24
|
name: 'get',
|
|
25
25
|
description: 'Get a browser extension',
|
|
26
26
|
argName: 'extensionId',
|
|
@@ -29,9 +29,9 @@ export function createBrowserExtensionCommand(factory) {
|
|
|
29
29
|
.description('Upload a browser extension package')
|
|
30
30
|
.argument('<path>')
|
|
31
31
|
.option('--name <name>', 'Display name')
|
|
32
|
-
.option('--subaccount-id <id>', 'Create
|
|
32
|
+
.option('--subaccount-id <id>', 'Create in this Subaccount account context with an Organization key')).action(async (path, options) => {
|
|
33
33
|
const file = await readBlob(path);
|
|
34
|
-
const result = await uploadOperationFile(factory, 'browser
|
|
34
|
+
const result = await uploadOperationFile(factory, 'browser.extensions.create', {
|
|
35
35
|
file: file.blob,
|
|
36
36
|
fileName: file.fileName,
|
|
37
37
|
fields: {
|
|
@@ -42,13 +42,13 @@ export function createBrowserExtensionCommand(factory) {
|
|
|
42
42
|
await outputData(factory.io, result, options);
|
|
43
43
|
}));
|
|
44
44
|
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
45
|
-
operationId: 'browser
|
|
45
|
+
operationId: 'browser.extensions.create',
|
|
46
46
|
name: 'import',
|
|
47
47
|
description: 'Import a browser extension from a URL',
|
|
48
48
|
configure: (cmd) => cmd
|
|
49
49
|
.option('--url <url>', 'Extension URL')
|
|
50
50
|
.option('--name <name>', 'Display name')
|
|
51
|
-
.option('--subaccount-id <id>', 'Create
|
|
51
|
+
.option('--subaccount-id <id>', 'Create in this Subaccount account context with an Organization key'),
|
|
52
52
|
body: async (_args, options) => {
|
|
53
53
|
return {
|
|
54
54
|
url: options.url,
|
|
@@ -58,7 +58,7 @@ export function createBrowserExtensionCommand(factory) {
|
|
|
58
58
|
actingSubaccountId: (_args, options) => actingSubaccountOption()(options),
|
|
59
59
|
}));
|
|
60
60
|
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
61
|
-
operationId: 'browser
|
|
61
|
+
operationId: 'browser.extensions.update',
|
|
62
62
|
name: 'patch',
|
|
63
63
|
description: 'Update a browser extension',
|
|
64
64
|
argNames: ['extensionId'],
|
|
@@ -69,7 +69,7 @@ export function createBrowserExtensionCommand(factory) {
|
|
|
69
69
|
},
|
|
70
70
|
}));
|
|
71
71
|
command.addCommand(createOperationDeleteCommand(factory, {
|
|
72
|
-
operationId: 'browser
|
|
72
|
+
operationId: 'browser.extensions.delete',
|
|
73
73
|
description: 'Delete a browser extension',
|
|
74
74
|
argNames: ['extensionId'],
|
|
75
75
|
}));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createOperationJsonBodyCommand, createOperationListCommand, createOperationViewCommand, streamOperationText, } from '../shared/operation.js';
|
|
3
|
+
export function createConversationCommand(factory) {
|
|
4
|
+
const command = new Command('conversations').description('Converse with runtime agents');
|
|
5
|
+
command.addCommand(createOperationListCommand(factory, {
|
|
6
|
+
operationId: 'conversations.list',
|
|
7
|
+
description: 'List conversations',
|
|
8
|
+
}));
|
|
9
|
+
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
10
|
+
operationId: 'conversations.create',
|
|
11
|
+
name: 'create',
|
|
12
|
+
description: 'Create a conversation',
|
|
13
|
+
}));
|
|
14
|
+
command.addCommand(createOperationViewCommand(factory, {
|
|
15
|
+
operationId: 'conversations.get',
|
|
16
|
+
name: 'get',
|
|
17
|
+
description: 'Get a conversation and its messages',
|
|
18
|
+
argName: 'conversationId',
|
|
19
|
+
}));
|
|
20
|
+
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
21
|
+
operationId: 'conversations.update',
|
|
22
|
+
name: 'patch',
|
|
23
|
+
description: 'Update the default agent, model, toolset, or title',
|
|
24
|
+
argNames: ['conversationId'],
|
|
25
|
+
}));
|
|
26
|
+
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
27
|
+
operationId: 'conversations.messages.create',
|
|
28
|
+
name: 'message',
|
|
29
|
+
description: 'Send a message and start an agent turn',
|
|
30
|
+
argNames: ['conversationId'],
|
|
31
|
+
}));
|
|
32
|
+
command.addCommand(createOperationJsonBodyCommand(factory, {
|
|
33
|
+
operationId: 'conversations.cancel',
|
|
34
|
+
name: 'cancel',
|
|
35
|
+
description: 'Cancel the active agent turn',
|
|
36
|
+
argNames: ['conversationId'],
|
|
37
|
+
}));
|
|
38
|
+
command.addCommand(new Command('stream')
|
|
39
|
+
.description('Stream normalized conversation events')
|
|
40
|
+
.argument('<conversationId>')
|
|
41
|
+
.option('--after <cursor>', 'Resume after a stream cursor')
|
|
42
|
+
.action(async (conversationId, options) => {
|
|
43
|
+
const stream = await streamOperationText(factory, 'conversations.stream', {
|
|
44
|
+
pathParams: { conversationId },
|
|
45
|
+
query: { after: options.after },
|
|
46
|
+
});
|
|
47
|
+
for await (const chunk of stream)
|
|
48
|
+
factory.io.writeOut(chunk);
|
|
49
|
+
}));
|
|
50
|
+
return command;
|
|
51
|
+
}
|
|
@@ -10,21 +10,26 @@ export function createFileCommand(factory) {
|
|
|
10
10
|
description: 'List files',
|
|
11
11
|
configure: (cmd) => cmd
|
|
12
12
|
.option('--space <id>', 'Filter by space id')
|
|
13
|
+
.option('--run <id>', 'Filter by run id')
|
|
14
|
+
.option('--runtime <id>', 'Filter by runtime id')
|
|
15
|
+
.option('--type <type...>', 'Filter by file artifact type')
|
|
13
16
|
.option('--source <source>', 'Filter by file source')
|
|
14
17
|
.option('--prefix <path>', 'Filter by path prefix')
|
|
15
18
|
.option('--folders', 'Directory view: direct files plus subfolder rollups')
|
|
16
19
|
.option('--created-after <iso>', 'Only files created after this timestamp')
|
|
17
|
-
.option('--
|
|
20
|
+
.option('--q <text>', 'Search query')
|
|
18
21
|
.option('-L, --limit <number>', 'Maximum number of results to return', parsePositiveInteger)
|
|
19
22
|
.option('--cursor <cursor>', 'Pagination cursor'),
|
|
20
23
|
query: (options) => ({
|
|
21
24
|
spaceId: typeof options.space === 'string' ? options.space : undefined,
|
|
25
|
+
runId: typeof options.run === 'string' ? options.run : undefined,
|
|
26
|
+
runtimeId: typeof options.runtime === 'string' ? options.runtime : undefined,
|
|
27
|
+
type: Array.isArray(options.type) ? options.type : undefined,
|
|
22
28
|
source: typeof options.source === 'string' ? options.source : undefined,
|
|
23
29
|
prefix: typeof options.prefix === 'string' ? options.prefix : undefined,
|
|
24
30
|
include: options.folders === true ? 'folders' : undefined,
|
|
25
31
|
createdAfter: typeof options.createdAfter === 'string' ? options.createdAfter : undefined,
|
|
26
|
-
|
|
27
|
-
q: typeof options.query === 'string' ? options.query : undefined,
|
|
32
|
+
q: typeof options.q === 'string' ? options.q : undefined,
|
|
28
33
|
limit: typeof options.limit === 'number' ? options.limit : undefined,
|
|
29
34
|
cursor: typeof options.cursor === 'string' ? options.cursor : undefined,
|
|
30
35
|
}),
|