@agentuity/cli 0.0.104 → 0.0.106
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/bin/cli.ts +6 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -21
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/prompt/version.d.ts +1 -0
- package/dist/cmd/ai/prompt/version.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/version.js +3 -2
- package/dist/cmd/ai/prompt/version.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +179 -37
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +24 -14
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +8 -9
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/cloud/db/create.d.ts.map +1 -1
- package/dist/cmd/cloud/db/create.js +11 -2
- package/dist/cmd/cloud/db/create.js.map +1 -1
- package/dist/cmd/cloud/db/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/db/delete.js +13 -2
- package/dist/cmd/cloud/db/delete.js.map +1 -1
- package/dist/cmd/cloud/deploy.js +3 -3
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/env/delete.js +1 -1
- package/dist/cmd/cloud/env/delete.js.map +1 -1
- package/dist/cmd/cloud/env/import.js +4 -4
- package/dist/cmd/cloud/env/import.js.map +1 -1
- package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
- package/dist/cmd/cloud/env/pull.js +7 -9
- package/dist/cmd/cloud/env/pull.js.map +1 -1
- package/dist/cmd/cloud/env/push.js +2 -2
- package/dist/cmd/cloud/env/push.js.map +1 -1
- package/dist/cmd/cloud/env/set.js +3 -3
- package/dist/cmd/cloud/env/set.js.map +1 -1
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/index.js +2 -0
- package/dist/cmd/cloud/index.js.map +1 -1
- package/dist/cmd/cloud/sandbox/cp.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/cp.js +334 -0
- package/dist/cmd/cloud/sandbox/cp.js.map +1 -0
- package/dist/cmd/cloud/sandbox/create.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/create.js +105 -0
- package/dist/cmd/cloud/sandbox/create.js.map +1 -0
- package/dist/cmd/cloud/sandbox/delete.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/delete.js +72 -0
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -0
- package/dist/cmd/cloud/sandbox/exec.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/exec.js +211 -0
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/get.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/execution/get.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/get.js +96 -0
- package/dist/cmd/cloud/sandbox/execution/get.js.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/index.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/execution/index.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/index.js +24 -0
- package/dist/cmd/cloud/sandbox/execution/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/list.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/execution/list.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/execution/list.js +100 -0
- package/dist/cmd/cloud/sandbox/execution/list.js.map +1 -0
- package/dist/cmd/cloud/sandbox/get.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/get.js +95 -0
- package/dist/cmd/cloud/sandbox/get.js.map +1 -0
- package/dist/cmd/cloud/sandbox/index.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/index.js +45 -0
- package/dist/cmd/cloud/sandbox/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/list.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/list.js +120 -0
- package/dist/cmd/cloud/sandbox/list.js.map +1 -0
- package/dist/cmd/cloud/sandbox/run.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/run.js +152 -0
- package/dist/cmd/cloud/sandbox/run.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.js +65 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/delete.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/delete.js +66 -0
- package/dist/cmd/cloud/sandbox/snapshot/delete.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/get.js +154 -0
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/index.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/index.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/index.js +27 -0
- package/dist/cmd/cloud/sandbox/snapshot/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/list.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/list.js +83 -0
- package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/tag.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/snapshot/tag.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/tag.js +63 -0
- package/dist/cmd/cloud/sandbox/snapshot/tag.js.map +1 -0
- package/dist/cmd/cloud/sandbox/util.d.ts +15 -0
- package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/util.js +50 -0
- package/dist/cmd/cloud/sandbox/util.js.map +1 -0
- package/dist/cmd/cloud/secret/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/delete.js +3 -3
- package/dist/cmd/cloud/secret/delete.js.map +1 -1
- package/dist/cmd/cloud/secret/import.js +6 -6
- package/dist/cmd/cloud/secret/import.js.map +1 -1
- package/dist/cmd/cloud/secret/index.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/index.js +1 -0
- package/dist/cmd/cloud/secret/index.js.map +1 -1
- package/dist/cmd/cloud/secret/pull.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/pull.js +7 -9
- package/dist/cmd/cloud/secret/pull.js.map +1 -1
- package/dist/cmd/cloud/secret/push.js +3 -3
- package/dist/cmd/cloud/secret/push.js.map +1 -1
- package/dist/cmd/cloud/secret/set.d.ts.map +1 -1
- package/dist/cmd/cloud/secret/set.js +3 -3
- package/dist/cmd/cloud/secret/set.js.map +1 -1
- package/dist/cmd/cloud/storage/create.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/create.js +13 -2
- package/dist/cmd/cloud/storage/create.js.map +1 -1
- package/dist/cmd/cloud/storage/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/delete.js +13 -2
- package/dist/cmd/cloud/storage/delete.js.map +1 -1
- package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/list.js +2 -13
- package/dist/cmd/cloud/stream/list.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +14 -1
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/profile/create.d.ts.map +1 -1
- package/dist/cmd/profile/create.js +1 -0
- package/dist/cmd/profile/create.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +27 -10
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +0 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/env-util.d.ts +16 -8
- package/dist/env-util.d.ts.map +1 -1
- package/dist/env-util.js +46 -18
- package/dist/env-util.js.map +1 -1
- package/dist/tui.d.ts +20 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +82 -23
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +18 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +99 -21
- package/src/cmd/ai/prompt/api.md +26 -21
- package/src/cmd/ai/prompt/version.ts +3 -2
- package/src/cmd/build/ast.ts +214 -37
- package/src/cmd/build/entry-generator.ts +24 -14
- package/src/cmd/build/vite/registry-generator.ts +8 -11
- package/src/cmd/build/vite/vite-asset-server.ts +3 -1
- package/src/cmd/cloud/db/create.ts +13 -2
- package/src/cmd/cloud/db/delete.ts +15 -2
- package/src/cmd/cloud/deploy.ts +3 -3
- package/src/cmd/cloud/env/delete.ts +1 -1
- package/src/cmd/cloud/env/import.ts +4 -4
- package/src/cmd/cloud/env/pull.ts +7 -16
- package/src/cmd/cloud/env/push.ts +2 -2
- package/src/cmd/cloud/env/set.ts +3 -3
- package/src/cmd/cloud/index.ts +2 -0
- package/src/cmd/cloud/sandbox/cp.ts +531 -0
- package/src/cmd/cloud/sandbox/create.ts +114 -0
- package/src/cmd/cloud/sandbox/delete.ts +80 -0
- package/src/cmd/cloud/sandbox/exec.ts +254 -0
- package/src/cmd/cloud/sandbox/execution/get.ts +106 -0
- package/src/cmd/cloud/sandbox/execution/index.ts +25 -0
- package/src/cmd/cloud/sandbox/execution/list.ts +111 -0
- package/src/cmd/cloud/sandbox/get.ts +104 -0
- package/src/cmd/cloud/sandbox/index.ts +46 -0
- package/src/cmd/cloud/sandbox/list.ts +129 -0
- package/src/cmd/cloud/sandbox/run.ts +170 -0
- package/src/cmd/cloud/sandbox/snapshot/create.ts +71 -0
- package/src/cmd/cloud/sandbox/snapshot/delete.ts +74 -0
- package/src/cmd/cloud/sandbox/snapshot/get.ts +188 -0
- package/src/cmd/cloud/sandbox/snapshot/index.ts +28 -0
- package/src/cmd/cloud/sandbox/snapshot/list.ts +90 -0
- package/src/cmd/cloud/sandbox/snapshot/tag.ts +70 -0
- package/src/cmd/cloud/sandbox/util.ts +59 -0
- package/src/cmd/cloud/secret/delete.ts +8 -3
- package/src/cmd/cloud/secret/import.ts +6 -6
- package/src/cmd/cloud/secret/index.ts +1 -0
- package/src/cmd/cloud/secret/pull.ts +7 -16
- package/src/cmd/cloud/secret/push.ts +3 -3
- package/src/cmd/cloud/secret/set.ts +8 -3
- package/src/cmd/cloud/storage/create.ts +15 -2
- package/src/cmd/cloud/storage/delete.ts +15 -2
- package/src/cmd/cloud/stream/list.ts +2 -9
- package/src/cmd/dev/index.ts +18 -1
- package/src/cmd/profile/create.ts +1 -0
- package/src/cmd/project/template-flow.ts +29 -13
- package/src/config.ts +3 -0
- package/src/env-util.ts +52 -21
- package/src/tui.ts +131 -39
- package/src/types.ts +18 -16
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Writable } from 'node:stream';
|
|
3
|
+
import { createCommand } from '../../../types';
|
|
4
|
+
import * as tui from '../../../tui';
|
|
5
|
+
import { createSandboxClient } from './util';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { sandboxExecute, executionGet, writeAndDrain } from '@agentuity/server';
|
|
8
|
+
import type { Logger } from '@agentuity/core';
|
|
9
|
+
|
|
10
|
+
const POLL_INTERVAL_MS = 500;
|
|
11
|
+
const MAX_POLL_ATTEMPTS = 7200;
|
|
12
|
+
|
|
13
|
+
const SandboxExecResponseSchema = z.object({
|
|
14
|
+
executionId: z.string().describe('Unique execution identifier'),
|
|
15
|
+
status: z.string().describe('Execution status'),
|
|
16
|
+
exitCode: z.number().optional().describe('Exit code (if completed)'),
|
|
17
|
+
durationMs: z.number().optional().describe('Duration in milliseconds (if completed)'),
|
|
18
|
+
output: z.string().optional().describe('Combined stdout/stderr output'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const execSubcommand = createCommand({
|
|
22
|
+
name: 'exec',
|
|
23
|
+
aliases: ['execute'],
|
|
24
|
+
description: 'Execute a command in a running sandbox',
|
|
25
|
+
tags: ['slow', 'requires-auth'],
|
|
26
|
+
requires: { auth: true, region: true, org: true },
|
|
27
|
+
examples: [
|
|
28
|
+
{
|
|
29
|
+
command: getCommand('cloud sandbox exec abc123 -- echo "hello"'),
|
|
30
|
+
description: 'Execute a command in a sandbox',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
command: getCommand('cloud sandbox exec abc123 --timeout 5m -- bun run build'),
|
|
34
|
+
description: 'Execute with timeout',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
schema: {
|
|
38
|
+
args: z.object({
|
|
39
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
40
|
+
command: z.array(z.string()).describe('Command and arguments to execute'),
|
|
41
|
+
}),
|
|
42
|
+
options: z.object({
|
|
43
|
+
timeout: z.string().optional().describe('Execution timeout (e.g., "5m", "1h")'),
|
|
44
|
+
timestamps: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.default(false)
|
|
47
|
+
.optional()
|
|
48
|
+
.describe('Include timestamps in output (default: false)'),
|
|
49
|
+
}),
|
|
50
|
+
response: SandboxExecResponseSchema,
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async handler(ctx) {
|
|
54
|
+
const { args, opts, options, auth, region, logger, orgId } = ctx;
|
|
55
|
+
const client = createSandboxClient(logger, auth, region);
|
|
56
|
+
const started = Date.now();
|
|
57
|
+
|
|
58
|
+
const abortController = new AbortController();
|
|
59
|
+
const handleSignal = () => {
|
|
60
|
+
abortController.abort();
|
|
61
|
+
};
|
|
62
|
+
process.on('SIGINT', handleSignal);
|
|
63
|
+
process.on('SIGTERM', handleSignal);
|
|
64
|
+
|
|
65
|
+
const outputChunks: string[] = [];
|
|
66
|
+
|
|
67
|
+
// For JSON output, capture to buffer; otherwise stream to process
|
|
68
|
+
const stdout = options.json
|
|
69
|
+
? createCaptureStream((chunk) => outputChunks.push(chunk))
|
|
70
|
+
: process.stdout;
|
|
71
|
+
const stderr = options.json
|
|
72
|
+
? createCaptureStream((chunk) => outputChunks.push(chunk))
|
|
73
|
+
: process.stderr;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const execution = await sandboxExecute(client, {
|
|
77
|
+
sandboxId: args.sandboxId,
|
|
78
|
+
options: {
|
|
79
|
+
command: args.command,
|
|
80
|
+
timeout: opts.timeout,
|
|
81
|
+
stream: opts.timestamps !== undefined ? { timestamps: opts.timestamps } : undefined,
|
|
82
|
+
},
|
|
83
|
+
orgId,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const stdoutStreamUrl = execution.stdoutStreamUrl;
|
|
87
|
+
const stderrStreamUrl = execution.stderrStreamUrl;
|
|
88
|
+
const streamAbortController = new AbortController();
|
|
89
|
+
const streamPromises: Promise<void>[] = [];
|
|
90
|
+
|
|
91
|
+
// Check if stdout and stderr are the same stream (combined output)
|
|
92
|
+
const isCombinedOutput =
|
|
93
|
+
stdoutStreamUrl && stderrStreamUrl && stdoutStreamUrl === stderrStreamUrl;
|
|
94
|
+
|
|
95
|
+
if (isCombinedOutput) {
|
|
96
|
+
// Stream combined output to stdout only to avoid duplicates
|
|
97
|
+
logger.debug('using combined output stream (stdout === stderr): %s', stdoutStreamUrl);
|
|
98
|
+
streamPromises.push(
|
|
99
|
+
streamUrlToWritable(stdoutStreamUrl, stdout, streamAbortController.signal, logger)
|
|
100
|
+
);
|
|
101
|
+
} else {
|
|
102
|
+
if (stdoutStreamUrl) {
|
|
103
|
+
logger.debug('starting stdout stream from: %s', stdoutStreamUrl);
|
|
104
|
+
streamPromises.push(
|
|
105
|
+
streamUrlToWritable(stdoutStreamUrl, stdout, streamAbortController.signal, logger)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (stderrStreamUrl) {
|
|
110
|
+
logger.debug('starting stderr stream from: %s', stderrStreamUrl);
|
|
111
|
+
streamPromises.push(
|
|
112
|
+
streamUrlToWritable(stderrStreamUrl, stderr, streamAbortController.signal, logger)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let attempts = 0;
|
|
118
|
+
let finalExecution = execution;
|
|
119
|
+
|
|
120
|
+
while (attempts < MAX_POLL_ATTEMPTS) {
|
|
121
|
+
if (abortController.signal.aborted) {
|
|
122
|
+
throw new Error('Execution cancelled');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await sleep(POLL_INTERVAL_MS);
|
|
126
|
+
attempts++;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const execInfo = await executionGet(client, {
|
|
130
|
+
executionId: execution.executionId,
|
|
131
|
+
orgId,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
execInfo.status === 'completed' ||
|
|
136
|
+
execInfo.status === 'failed' ||
|
|
137
|
+
execInfo.status === 'timeout' ||
|
|
138
|
+
execInfo.status === 'cancelled'
|
|
139
|
+
) {
|
|
140
|
+
finalExecution = {
|
|
141
|
+
executionId: execInfo.executionId,
|
|
142
|
+
status: execInfo.status,
|
|
143
|
+
exitCode: execInfo.exitCode,
|
|
144
|
+
durationMs: execInfo.durationMs,
|
|
145
|
+
};
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Wait for all streams to reach EOF (Pulse blocks until true EOF)
|
|
154
|
+
await Promise.all(streamPromises);
|
|
155
|
+
|
|
156
|
+
// Ensure stdout is fully flushed before continuing
|
|
157
|
+
if (!options.json && process.stdout.writable) {
|
|
158
|
+
await new Promise<void>((resolve) => {
|
|
159
|
+
if (process.stdout.writableNeedDrain) {
|
|
160
|
+
process.stdout.once('drain', () => resolve());
|
|
161
|
+
} else {
|
|
162
|
+
resolve();
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const duration = Date.now() - started;
|
|
168
|
+
const output = outputChunks.join('');
|
|
169
|
+
|
|
170
|
+
if (!options.json) {
|
|
171
|
+
if (finalExecution.exitCode === 0) {
|
|
172
|
+
// no op
|
|
173
|
+
} else if (finalExecution.exitCode !== undefined) {
|
|
174
|
+
tui.error(`failed with exit code ${finalExecution.exitCode} in ${duration}ms`);
|
|
175
|
+
} else {
|
|
176
|
+
tui.info(
|
|
177
|
+
`Execution ${tui.bold(finalExecution.executionId)} - Status: ${finalExecution.status}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
executionId: finalExecution.executionId,
|
|
184
|
+
status: finalExecution.status,
|
|
185
|
+
exitCode: finalExecution.exitCode,
|
|
186
|
+
durationMs: finalExecution.durationMs,
|
|
187
|
+
output: options.json ? output : undefined,
|
|
188
|
+
};
|
|
189
|
+
} finally {
|
|
190
|
+
process.off('SIGINT', handleSignal);
|
|
191
|
+
process.off('SIGTERM', handleSignal);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
async function streamUrlToWritable(
|
|
197
|
+
url: string,
|
|
198
|
+
writable: NodeJS.WritableStream,
|
|
199
|
+
signal: AbortSignal,
|
|
200
|
+
logger: Logger
|
|
201
|
+
): Promise<void> {
|
|
202
|
+
try {
|
|
203
|
+
logger.debug('fetching stream: %s', url);
|
|
204
|
+
const response = await fetch(url, { signal });
|
|
205
|
+
logger.debug('stream response status: %d', response.status);
|
|
206
|
+
|
|
207
|
+
if (!response.ok || !response.body) {
|
|
208
|
+
logger.debug('stream response not ok or no body');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const reader = response.body.getReader();
|
|
213
|
+
|
|
214
|
+
// Read until EOF - Pulse will block until data is available
|
|
215
|
+
while (true) {
|
|
216
|
+
const { done, value } = await reader.read();
|
|
217
|
+
if (done) {
|
|
218
|
+
logger.debug('stream EOF');
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (value) {
|
|
223
|
+
logger.debug('stream chunk: %d bytes', value.length);
|
|
224
|
+
await writeAndDrain(writable, value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} catch (err) {
|
|
228
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
229
|
+
logger.debug('stream aborted');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
logger.debug('stream error: %s', err);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function createCaptureStream(onChunk: (chunk: string) => void): NodeJS.WritableStream {
|
|
237
|
+
return new Writable({
|
|
238
|
+
write(
|
|
239
|
+
chunk: Buffer | string,
|
|
240
|
+
_encoding: string,
|
|
241
|
+
callback: (error?: Error | null) => void
|
|
242
|
+
): void {
|
|
243
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString('utf-8');
|
|
244
|
+
onChunk(text);
|
|
245
|
+
callback();
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function sleep(ms: number): Promise<void> {
|
|
251
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export default execSubcommand;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../../types';
|
|
3
|
+
import * as tui from '../../../../tui';
|
|
4
|
+
import { createSandboxClient } from '../util';
|
|
5
|
+
import { getCommand } from '../../../../command-prefix';
|
|
6
|
+
import { executionGet } from '@agentuity/server';
|
|
7
|
+
|
|
8
|
+
const ExecutionGetResponseSchema = z.object({
|
|
9
|
+
executionId: z.string().describe('Execution ID'),
|
|
10
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
11
|
+
status: z.string().describe('Current status'),
|
|
12
|
+
command: z.array(z.string()).optional().describe('Command that was executed'),
|
|
13
|
+
exitCode: z.number().optional().describe('Exit code'),
|
|
14
|
+
durationMs: z.number().optional().describe('Duration in milliseconds'),
|
|
15
|
+
startedAt: z.string().optional().describe('Start timestamp'),
|
|
16
|
+
completedAt: z.string().optional().describe('Completion timestamp'),
|
|
17
|
+
error: z.string().optional().describe('Error message if failed'),
|
|
18
|
+
stdoutStreamUrl: z.string().optional().describe('URL to stream stdout'),
|
|
19
|
+
stderrStreamUrl: z.string().optional().describe('URL to stream stderr'),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const getSubcommand = createCommand({
|
|
23
|
+
name: 'get',
|
|
24
|
+
aliases: ['info', 'show'],
|
|
25
|
+
description: 'Get information about a specific execution',
|
|
26
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
27
|
+
requires: { auth: true, region: true, org: true },
|
|
28
|
+
idempotent: true,
|
|
29
|
+
examples: [
|
|
30
|
+
{
|
|
31
|
+
command: getCommand('cloud sandbox execution get exec_abc123'),
|
|
32
|
+
description: 'Get execution information',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
schema: {
|
|
36
|
+
args: z.object({
|
|
37
|
+
executionId: z.string().describe('Execution ID'),
|
|
38
|
+
}),
|
|
39
|
+
response: ExecutionGetResponseSchema,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async handler(ctx) {
|
|
43
|
+
const { args, options, auth, region, logger, orgId } = ctx;
|
|
44
|
+
const client = createSandboxClient(logger, auth, region);
|
|
45
|
+
|
|
46
|
+
const result = await executionGet(client, { executionId: args.executionId, orgId });
|
|
47
|
+
|
|
48
|
+
if (!options.json) {
|
|
49
|
+
const statusColor =
|
|
50
|
+
result.status === 'completed'
|
|
51
|
+
? tui.colorSuccess
|
|
52
|
+
: result.status === 'running'
|
|
53
|
+
? tui.colorWarning
|
|
54
|
+
: result.status === 'failed' || result.status === 'timeout'
|
|
55
|
+
? tui.colorError
|
|
56
|
+
: tui.colorMuted;
|
|
57
|
+
|
|
58
|
+
console.log(`${tui.muted('Execution:')} ${tui.bold(result.executionId)}`);
|
|
59
|
+
console.log(`${tui.muted('Sandbox:')} ${result.sandboxId}`);
|
|
60
|
+
console.log(`${tui.muted('Status:')} ${statusColor(result.status)}`);
|
|
61
|
+
if (result.exitCode !== undefined) {
|
|
62
|
+
const exitCodeColor = result.exitCode === 0 ? tui.colorSuccess : tui.colorError;
|
|
63
|
+
console.log(
|
|
64
|
+
`${tui.muted('Exit Code:')} ${exitCodeColor(String(result.exitCode))}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (result.durationMs !== undefined) {
|
|
68
|
+
console.log(`${tui.muted('Duration:')} ${result.durationMs}ms`);
|
|
69
|
+
}
|
|
70
|
+
if (result.startedAt) {
|
|
71
|
+
console.log(`${tui.muted('Started:')} ${result.startedAt}`);
|
|
72
|
+
}
|
|
73
|
+
if (result.completedAt) {
|
|
74
|
+
console.log(`${tui.muted('Completed:')} ${result.completedAt}`);
|
|
75
|
+
}
|
|
76
|
+
if (result.error) {
|
|
77
|
+
console.log(`${tui.muted('Error:')} ${tui.colorError(result.error)}`);
|
|
78
|
+
}
|
|
79
|
+
if (result.stdoutStreamUrl) {
|
|
80
|
+
console.log(`${tui.muted('Stdout:')} ${result.stdoutStreamUrl}`);
|
|
81
|
+
}
|
|
82
|
+
if (result.stderrStreamUrl) {
|
|
83
|
+
console.log(`${tui.muted('Stderr:')} ${result.stderrStreamUrl}`);
|
|
84
|
+
}
|
|
85
|
+
if (result.command && result.command.length > 0) {
|
|
86
|
+
console.log(`${tui.muted('Command:')} ${result.command.join(' ')}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
executionId: result.executionId,
|
|
92
|
+
sandboxId: result.sandboxId,
|
|
93
|
+
status: result.status,
|
|
94
|
+
command: result.command,
|
|
95
|
+
exitCode: result.exitCode,
|
|
96
|
+
durationMs: result.durationMs,
|
|
97
|
+
startedAt: result.startedAt,
|
|
98
|
+
completedAt: result.completedAt,
|
|
99
|
+
error: result.error,
|
|
100
|
+
stdoutStreamUrl: result.stdoutStreamUrl,
|
|
101
|
+
stderrStreamUrl: result.stderrStreamUrl,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export default getSubcommand;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createCommand } from '../../../../types';
|
|
2
|
+
import { getSubcommand } from './get';
|
|
3
|
+
import { listSubcommand } from './list';
|
|
4
|
+
import { getCommand } from '../../../../command-prefix';
|
|
5
|
+
|
|
6
|
+
export const command = createCommand({
|
|
7
|
+
name: 'execution',
|
|
8
|
+
aliases: ['executions'],
|
|
9
|
+
description: 'Manage sandbox executions',
|
|
10
|
+
tags: ['read-only', 'requires-auth'],
|
|
11
|
+
examples: [
|
|
12
|
+
{
|
|
13
|
+
command: getCommand('cloud sandbox execution list snbx_abc123'),
|
|
14
|
+
description: 'List executions for a sandbox',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
command: getCommand('cloud sandbox execution get exec_abc123'),
|
|
18
|
+
description: 'Get details of a specific execution',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
subcommands: [getSubcommand, listSubcommand],
|
|
22
|
+
requires: { auth: true, region: true, org: true },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export default command;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../../types';
|
|
3
|
+
import * as tui from '../../../../tui';
|
|
4
|
+
import { createSandboxClient } from '../util';
|
|
5
|
+
import { getCommand } from '../../../../command-prefix';
|
|
6
|
+
import { executionList } from '@agentuity/server';
|
|
7
|
+
|
|
8
|
+
const ExecutionInfoSchema = z.object({
|
|
9
|
+
executionId: z.string().describe('Execution ID'),
|
|
10
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
11
|
+
status: z.string().describe('Current status'),
|
|
12
|
+
exitCode: z.number().optional().describe('Exit code'),
|
|
13
|
+
durationMs: z.number().optional().describe('Duration in milliseconds'),
|
|
14
|
+
startedAt: z.string().optional().describe('Start timestamp'),
|
|
15
|
+
completedAt: z.string().optional().describe('Completion timestamp'),
|
|
16
|
+
error: z.string().optional().describe('Error message if failed'),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const ExecutionListResponseSchema = z.object({
|
|
20
|
+
executions: z.array(ExecutionInfoSchema).describe('List of executions'),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const listSubcommand = createCommand({
|
|
24
|
+
name: 'list',
|
|
25
|
+
aliases: ['ls'],
|
|
26
|
+
description: 'List executions for a sandbox',
|
|
27
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
28
|
+
requires: { auth: true, region: true, org: true },
|
|
29
|
+
idempotent: true,
|
|
30
|
+
examples: [
|
|
31
|
+
{
|
|
32
|
+
command: getCommand('cloud sandbox execution list snbx_abc123'),
|
|
33
|
+
description: 'List executions for a sandbox',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
command: getCommand('cloud sandbox execution list snbx_abc123 --limit 10'),
|
|
37
|
+
description: 'List with a limit',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
schema: {
|
|
41
|
+
args: z.object({
|
|
42
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
43
|
+
}),
|
|
44
|
+
options: z.object({
|
|
45
|
+
limit: z.number().optional().describe('Maximum number of results (default: 50, max: 100)'),
|
|
46
|
+
}),
|
|
47
|
+
response: ExecutionListResponseSchema,
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
async handler(ctx) {
|
|
51
|
+
const { args, opts, options, auth, region, logger, orgId } = ctx;
|
|
52
|
+
const client = createSandboxClient(logger, auth, region);
|
|
53
|
+
|
|
54
|
+
const result = await executionList(client, {
|
|
55
|
+
sandboxId: args.sandboxId,
|
|
56
|
+
orgId,
|
|
57
|
+
limit: opts.limit,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (!options.json) {
|
|
61
|
+
if (result.executions.length === 0) {
|
|
62
|
+
tui.info('No executions found');
|
|
63
|
+
} else {
|
|
64
|
+
const tableData = result.executions.map((exec) => {
|
|
65
|
+
const statusColor =
|
|
66
|
+
exec.status === 'completed'
|
|
67
|
+
? tui.colorSuccess
|
|
68
|
+
: exec.status === 'running'
|
|
69
|
+
? tui.colorWarning
|
|
70
|
+
: exec.status === 'failed' || exec.status === 'timeout'
|
|
71
|
+
? tui.colorError
|
|
72
|
+
: tui.colorMuted;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
ID: exec.executionId,
|
|
76
|
+
Status: statusColor(exec.status),
|
|
77
|
+
'Exit Code': exec.exitCode !== undefined ? String(exec.exitCode) : '-',
|
|
78
|
+
Duration: exec.durationMs !== undefined ? `${exec.durationMs}ms` : '-',
|
|
79
|
+
Started: exec.startedAt || '-',
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
tui.table(tableData, [
|
|
83
|
+
{ name: 'ID', alignment: 'left' },
|
|
84
|
+
{ name: 'Status', alignment: 'left' },
|
|
85
|
+
{ name: 'Exit Code', alignment: 'right' },
|
|
86
|
+
{ name: 'Duration', alignment: 'right' },
|
|
87
|
+
{ name: 'Started', alignment: 'left' },
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
tui.info(
|
|
91
|
+
`Total: ${result.executions.length} ${tui.plural(result.executions.length, 'execution', 'executions')}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
executions: result.executions.map((e) => ({
|
|
98
|
+
executionId: e.executionId,
|
|
99
|
+
sandboxId: e.sandboxId,
|
|
100
|
+
status: e.status,
|
|
101
|
+
exitCode: e.exitCode,
|
|
102
|
+
durationMs: e.durationMs,
|
|
103
|
+
startedAt: e.startedAt,
|
|
104
|
+
completedAt: e.completedAt,
|
|
105
|
+
error: e.error,
|
|
106
|
+
})),
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export default listSubcommand;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { createSandboxClient } from './util';
|
|
5
|
+
import { getCommand } from '../../../command-prefix';
|
|
6
|
+
import { sandboxGet } from '@agentuity/server';
|
|
7
|
+
|
|
8
|
+
const SandboxGetResponseSchema = z.object({
|
|
9
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
10
|
+
status: z.string().describe('Current status'),
|
|
11
|
+
createdAt: z.string().describe('Creation timestamp'),
|
|
12
|
+
region: z.string().optional().describe('Region where sandbox is running'),
|
|
13
|
+
snapshotId: z.string().optional().describe('Snapshot ID sandbox was created from'),
|
|
14
|
+
snapshotTag: z.string().optional().describe('Snapshot tag sandbox was created from'),
|
|
15
|
+
executions: z.number().describe('Number of executions'),
|
|
16
|
+
stdoutStreamUrl: z.string().optional().describe('URL to stdout output stream'),
|
|
17
|
+
stderrStreamUrl: z.string().optional().describe('URL to stderr output stream'),
|
|
18
|
+
dependencies: z.array(z.string()).optional().describe('Apt packages installed'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const getSubcommand = createCommand({
|
|
22
|
+
name: 'get',
|
|
23
|
+
aliases: ['info', 'show'],
|
|
24
|
+
description: 'Get information about a sandbox',
|
|
25
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
26
|
+
requires: { auth: true, region: true, org: true },
|
|
27
|
+
idempotent: true,
|
|
28
|
+
examples: [
|
|
29
|
+
{
|
|
30
|
+
command: getCommand('cloud sandbox get abc123'),
|
|
31
|
+
description: 'Get sandbox information',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
schema: {
|
|
35
|
+
args: z.object({
|
|
36
|
+
sandboxId: z.string().describe('Sandbox ID'),
|
|
37
|
+
}),
|
|
38
|
+
response: SandboxGetResponseSchema,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async handler(ctx) {
|
|
42
|
+
const { args, options, auth, region, logger, orgId } = ctx;
|
|
43
|
+
const client = createSandboxClient(logger, auth, region);
|
|
44
|
+
|
|
45
|
+
const result = await sandboxGet(client, { sandboxId: args.sandboxId, orgId });
|
|
46
|
+
|
|
47
|
+
if (!options.json) {
|
|
48
|
+
const statusColor =
|
|
49
|
+
result.status === 'running'
|
|
50
|
+
? tui.colorSuccess
|
|
51
|
+
: result.status === 'idle'
|
|
52
|
+
? tui.colorWarning
|
|
53
|
+
: result.status === 'failed'
|
|
54
|
+
? tui.colorError
|
|
55
|
+
: tui.colorMuted;
|
|
56
|
+
|
|
57
|
+
console.log(`${tui.muted('Sandbox:')} ${tui.bold(result.sandboxId)}`);
|
|
58
|
+
console.log(`${tui.muted('Status:')} ${statusColor(result.status)}`);
|
|
59
|
+
console.log(`${tui.muted('Created:')} ${result.createdAt}`);
|
|
60
|
+
if (result.region) {
|
|
61
|
+
console.log(`${tui.muted('Region:')} ${result.region}`);
|
|
62
|
+
}
|
|
63
|
+
if (result.snapshotId || result.snapshotTag) {
|
|
64
|
+
const snapshotDisplay = result.snapshotTag
|
|
65
|
+
? `${result.snapshotTag} ${tui.muted('(' + result.snapshotId + ')')}`
|
|
66
|
+
: result.snapshotId;
|
|
67
|
+
console.log(`${tui.muted('Snapshot:')} ${snapshotDisplay}`);
|
|
68
|
+
}
|
|
69
|
+
console.log(`${tui.muted('Executions:')} ${result.executions}`);
|
|
70
|
+
if (
|
|
71
|
+
result.stdoutStreamUrl &&
|
|
72
|
+
result.stderrStreamUrl &&
|
|
73
|
+
result.stdoutStreamUrl === result.stderrStreamUrl
|
|
74
|
+
) {
|
|
75
|
+
console.log(`${tui.muted('Stream:')} ${tui.link(result.stdoutStreamUrl)}`);
|
|
76
|
+
} else {
|
|
77
|
+
if (result.stdoutStreamUrl) {
|
|
78
|
+
console.log(`${tui.muted('Stream (stdout):')} ${tui.link(result.stdoutStreamUrl)}`);
|
|
79
|
+
}
|
|
80
|
+
if (result.stderrStreamUrl) {
|
|
81
|
+
console.log(`${tui.muted('Stream (stderr):')} ${tui.link(result.stderrStreamUrl)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (result.dependencies && result.dependencies.length > 0) {
|
|
85
|
+
console.log(`${tui.muted('Dependencies:')} ${result.dependencies.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
sandboxId: result.sandboxId,
|
|
91
|
+
status: result.status,
|
|
92
|
+
createdAt: result.createdAt,
|
|
93
|
+
region: result.region,
|
|
94
|
+
snapshotId: result.snapshotId,
|
|
95
|
+
snapshotTag: result.snapshotTag,
|
|
96
|
+
executions: result.executions,
|
|
97
|
+
stdoutStreamUrl: result.stdoutStreamUrl,
|
|
98
|
+
stderrStreamUrl: result.stderrStreamUrl,
|
|
99
|
+
dependencies: result.dependencies,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export default getSubcommand;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createCommand } from '../../../types';
|
|
2
|
+
import { runSubcommand } from './run';
|
|
3
|
+
import { createSubcommand } from './create';
|
|
4
|
+
import { execSubcommand } from './exec';
|
|
5
|
+
import { listSubcommand } from './list';
|
|
6
|
+
import { getSubcommand } from './get';
|
|
7
|
+
import { deleteSubcommand } from './delete';
|
|
8
|
+
import { snapshotCommand } from './snapshot';
|
|
9
|
+
import { cpSubcommand } from './cp';
|
|
10
|
+
import { command as executionCommand } from './execution';
|
|
11
|
+
import { getCommand } from '../../../command-prefix';
|
|
12
|
+
|
|
13
|
+
export const command = createCommand({
|
|
14
|
+
name: 'sandbox',
|
|
15
|
+
aliases: ['sb'],
|
|
16
|
+
description: 'Manage sandboxes for isolated code execution',
|
|
17
|
+
tags: ['slow', 'requires-auth'],
|
|
18
|
+
examples: [
|
|
19
|
+
{
|
|
20
|
+
command: getCommand('cloud sandbox run -- echo "hello"'),
|
|
21
|
+
description: 'Run a one-shot command in a sandbox',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
command: getCommand('cloud sandbox create'),
|
|
25
|
+
description: 'Create an interactive sandbox',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: getCommand('cloud sandbox list'),
|
|
29
|
+
description: 'List all sandboxes',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
subcommands: [
|
|
33
|
+
runSubcommand,
|
|
34
|
+
createSubcommand,
|
|
35
|
+
execSubcommand,
|
|
36
|
+
listSubcommand,
|
|
37
|
+
getSubcommand,
|
|
38
|
+
deleteSubcommand,
|
|
39
|
+
snapshotCommand,
|
|
40
|
+
cpSubcommand,
|
|
41
|
+
executionCommand,
|
|
42
|
+
],
|
|
43
|
+
requires: { auth: true, region: true, org: true },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default command;
|