bdy 1.25.3-dev → 1.25.5-dev
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/distTs/package.json +2 -1
- package/distTs/src/agent/socket/tunnel.js +2 -0
- package/distTs/src/api/client.js +22 -55
- package/distTs/src/cliCommand.js +52 -0
- package/distTs/src/cliError.js +34 -0
- package/distTs/src/cliIndex.js +3 -1
- package/distTs/src/command/agent/proxy/status.js +9 -2
- package/distTs/src/command/agent/status.js +22 -6
- package/distTs/src/command/agent/tags/get.js +9 -2
- package/distTs/src/command/agent/target/status.js +9 -2
- package/distTs/src/command/agent/tunnel/list.js +6 -1
- package/distTs/src/command/agent/tunnel/status.js +13 -1
- package/distTs/src/command/agent/version.js +7 -2
- package/distTs/src/command/api/info.js +50 -2
- package/distTs/src/command/api/list.js +18 -2
- package/distTs/src/command/artifact/create.js +5 -0
- package/distTs/src/command/artifact/publish.js +20 -22
- package/distTs/src/command/artifact/version/create.js +5 -0
- package/distTs/src/command/config/get/tunnel.js +8 -3
- package/distTs/src/command/config/get/tunnels.js +8 -3
- package/distTs/src/command/discover.js +9 -11
- package/distTs/src/command/distro/create.js +5 -0
- package/distTs/src/command/distro/route/create.js +5 -0
- package/distTs/src/command/distro/route/update.js +5 -0
- package/distTs/src/command/distro/update.js +5 -0
- package/distTs/src/command/domain/search.js +31 -10
- package/distTs/src/command/environment/create.js +5 -0
- package/distTs/src/command/environment/update.js +5 -0
- package/distTs/src/command/sandbox/app/add.js +7 -1
- package/distTs/src/command/sandbox/app/logs.js +9 -4
- package/distTs/src/command/sandbox/create.js +13 -5
- package/distTs/src/command/sandbox/endpoint/add.js +5 -0
- package/distTs/src/command/sandbox/exec/command.js +15 -30
- package/distTs/src/command/sandbox/exec/logs.js +4 -29
- package/distTs/src/command/sandbox/finishExec.js +34 -0
- package/distTs/src/command/sandbox/logs.js +14 -1
- package/distTs/src/command/sandbox/snapshot/create.js +9 -1
- package/distTs/src/command/target/create.js +5 -0
- package/distTs/src/command/target/exec/logs.js +15 -2
- package/distTs/src/command/target/update.js +5 -0
- package/distTs/src/command/yaml/validate.js +111 -52
- package/distTs/src/index.js +6 -1
- package/distTs/src/input.js +12 -11
- package/distTs/src/openapi.js +8 -1
- package/distTs/src/output.js +55 -13
- package/distTs/src/texts.js +272 -59
- package/package.json +2 -1
package/distTs/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.25.
|
|
4
|
+
"version": "1.25.5-dev",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"e2e": "npm run buildTs && vitest run --config e2e/vitest.config.mts",
|
|
17
17
|
"e2e:version": "vitest run --config e2e/vitest.config.mts e2e/modules/version/",
|
|
18
18
|
"e2e:discover": "vitest run --config e2e/vitest.discover.config.mts",
|
|
19
|
+
"e2e:errors": "vitest run --config e2e/vitest.errors.config.mts",
|
|
19
20
|
"e2e:tunnel-config": "vitest run --config e2e/vitest.config.mts e2e/modules/tunnel-config/",
|
|
20
21
|
"e2e:auth": "vitest run --config e2e/vitest.config.mts e2e/modules/auth/",
|
|
21
22
|
"e2e:workspace": "vitest run --config e2e/vitest.config.mts e2e/modules/workspace/",
|
|
@@ -30,6 +30,7 @@ class AgentSocketTunnel extends events_1.default {
|
|
|
30
30
|
totalConnections;
|
|
31
31
|
connections;
|
|
32
32
|
dns;
|
|
33
|
+
rawStatus;
|
|
33
34
|
constructor(socket, id) {
|
|
34
35
|
super();
|
|
35
36
|
this.id = id;
|
|
@@ -65,6 +66,7 @@ class AgentSocketTunnel extends events_1.default {
|
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
onTunnelData(data) {
|
|
69
|
+
this.rawStatus = data;
|
|
68
70
|
if (data.regionLatency)
|
|
69
71
|
this.regionLatency = data.regionLatency;
|
|
70
72
|
if (data.targetLatency)
|
package/distTs/src/api/client.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.formDataFileName = void 0;
|
|
7
7
|
const texts_1 = require("../texts");
|
|
8
|
+
const cliError_1 = __importDefault(require("../cliError"));
|
|
8
9
|
const logger_1 = __importDefault(require("../logger"));
|
|
9
10
|
const utils_1 = require("../utils");
|
|
10
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
@@ -155,7 +156,9 @@ class ApiClient {
|
|
|
155
156
|
await (0, utils_1.sleep)(attempt * 1000);
|
|
156
157
|
continue;
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
+
const networkCode = err.code ?? err.cause?.code;
|
|
160
|
+
const timeout = ['ETIMEDOUT', 'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT'].includes(networkCode);
|
|
161
|
+
throw new cliError_1.default(timeout ? 'timeout' : 'connection_error', timeout ? texts_1.ERR_REST_API_TIMEOUT : texts_1.ERR_REST_API_CONNECT_ERROR, timeout ? texts_1.TXT_CLI_RETRY_HINT : texts_1.TXT_CLI_ENDPOINT_HINT, { cause: err });
|
|
159
162
|
}
|
|
160
163
|
rateLimitReset = headerValue(response.headers['x-rate-limit-reset']);
|
|
161
164
|
status = response.statusCode;
|
|
@@ -173,7 +176,8 @@ class ApiClient {
|
|
|
173
176
|
this.warnRateLimitLow(headerValue(response.headers['x-rate-limit-remaining']), rateLimitReset);
|
|
174
177
|
if (status === 429) {
|
|
175
178
|
await responseBody.dump();
|
|
176
|
-
|
|
179
|
+
const error = (0, texts_1.ERR_API_DETAILS)(status, undefined, path, this.baseUrl, rateLimitReset);
|
|
180
|
+
throw new cliError_1.default(error.code, error.message, error.hint);
|
|
177
181
|
}
|
|
178
182
|
if (status === 401) {
|
|
179
183
|
await responseBody.dump();
|
|
@@ -194,43 +198,7 @@ class ApiClient {
|
|
|
194
198
|
});
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
|
-
throw new
|
|
198
|
-
}
|
|
199
|
-
if (status === 403) {
|
|
200
|
-
let json;
|
|
201
|
-
try {
|
|
202
|
-
json = await responseBody.json();
|
|
203
|
-
logger_1.default.debug('API CLIENT PARSED RESPONSE:');
|
|
204
|
-
logger_1.default.debug(json);
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
await responseBody.dump();
|
|
208
|
-
throw new Error((0, texts_1.ERR_REST_API_RATE_LIMIT)(rateLimitReset));
|
|
209
|
-
}
|
|
210
|
-
if (json.errors && json.errors[0] && json.errors[0].message) {
|
|
211
|
-
throw new Error((0, texts_1.ERR_API_MESSAGE_REPLACER)(json.errors[0].message, path, this.baseUrl, rateLimitReset));
|
|
212
|
-
}
|
|
213
|
-
throw new Error((0, texts_1.ERR_REST_API_RATE_LIMIT)(rateLimitReset));
|
|
214
|
-
}
|
|
215
|
-
if ([400, 404].includes(status)) {
|
|
216
|
-
let json;
|
|
217
|
-
try {
|
|
218
|
-
json = await responseBody.json();
|
|
219
|
-
}
|
|
220
|
-
catch (err) {
|
|
221
|
-
logger_1.default.debug('API CLIENT PARSED RESPONSE ERROR:');
|
|
222
|
-
logger_1.default.debug(err);
|
|
223
|
-
throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR, { cause: err });
|
|
224
|
-
}
|
|
225
|
-
logger_1.default.debug('API CLIENT PARSED RESPONSE:');
|
|
226
|
-
logger_1.default.debug(json);
|
|
227
|
-
if (json.errors && json.errors[0] && json.errors[0].message) {
|
|
228
|
-
throw new Error((0, texts_1.ERR_API_MESSAGE_REPLACER)(json.errors[0].message, path, this.baseUrl, rateLimitReset));
|
|
229
|
-
}
|
|
230
|
-
if (status === 404)
|
|
231
|
-
throw new Error(texts_1.ERR_REST_API_RESOURCE_NOT_FOUND);
|
|
232
|
-
else
|
|
233
|
-
throw new Error((0, texts_1.ERR_REST_API_HTTP_ERROR)(status));
|
|
201
|
+
throw new cliError_1.default('unauthorized', texts_1.ERR_REST_API_UNAUTHORIZED, texts_1.TXT_CLI_LOGIN_HINT);
|
|
234
202
|
}
|
|
235
203
|
if ([200, 201].includes(status)) {
|
|
236
204
|
if (parseResponseBody) {
|
|
@@ -267,26 +235,25 @@ class ApiClient {
|
|
|
267
235
|
await responseBody.dump();
|
|
268
236
|
return null;
|
|
269
237
|
}
|
|
270
|
-
else if (status === 503) {
|
|
271
|
-
throw new Error(texts_1.ERR_REST_API_NOT_RESPONDING);
|
|
272
|
-
}
|
|
273
238
|
else {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// instead of reducing it to a bare HTTP code.
|
|
277
|
-
let json;
|
|
239
|
+
let message;
|
|
240
|
+
let nonJsonBody = false;
|
|
278
241
|
try {
|
|
279
|
-
json = await responseBody.json();
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
242
|
+
const json = await responseBody.json();
|
|
243
|
+
logger_1.default.debug('API CLIENT PARSED RESPONSE:');
|
|
244
|
+
logger_1.default.debug(json);
|
|
245
|
+
const value = json?.errors?.[0]?.message;
|
|
246
|
+
if (typeof value === 'string')
|
|
247
|
+
message = value;
|
|
283
248
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
throw new Error((0, texts_1.ERR_API_MESSAGE_REPLACER)(json.errors[0].message, path, this.baseUrl, rateLimitReset));
|
|
249
|
+
catch (err) {
|
|
250
|
+
nonJsonBody = true;
|
|
251
|
+
logger_1.default.debug(err);
|
|
288
252
|
}
|
|
289
|
-
|
|
253
|
+
// Edge/WAF throttling uses 403 with an HTML body. Preserve the legacy
|
|
254
|
+
// rate-limit classification; JSON 403 responses still report API errors.
|
|
255
|
+
const error = (0, texts_1.ERR_API_DETAILS)(status === 403 && nonJsonBody ? 429 : status, message, path, this.baseUrl, rateLimitReset);
|
|
256
|
+
throw new cliError_1.default(error.code, error.message, error.hint);
|
|
290
257
|
}
|
|
291
258
|
}
|
|
292
259
|
// Set by the CLI bootstrap (Input.restApiTokenClient); the transport layer
|
package/distTs/src/cliCommand.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
const commander_1 = require("commander");
|
|
7
|
+
const output_1 = __importDefault(require("./output"));
|
|
8
|
+
const cliError_1 = __importDefault(require("./cliError"));
|
|
9
|
+
const texts_1 = require("./texts");
|
|
4
10
|
/** Command documentation shared by terminal help and machine discovery. */
|
|
5
11
|
class CliCommand extends commander_1.Command {
|
|
6
12
|
agentStatus;
|
|
@@ -13,11 +19,57 @@ class CliCommand extends commander_1.Command {
|
|
|
13
19
|
hideVersionUpdate;
|
|
14
20
|
agentPort;
|
|
15
21
|
skipPre = false;
|
|
22
|
+
defaultErrorFormat = 'text';
|
|
16
23
|
metadata;
|
|
17
24
|
constructor(name, metadata = {}) {
|
|
18
25
|
super(name);
|
|
19
26
|
this.metadata = metadata;
|
|
20
27
|
}
|
|
28
|
+
addOption(option) {
|
|
29
|
+
if (option.long === '--format' && !option.argChoices) {
|
|
30
|
+
const choices = option.flags.match(/<([^>]+)>/)?.[1].split('|');
|
|
31
|
+
if (choices?.every((value) => ['text', 'json', 'jsonl'].includes(value)))
|
|
32
|
+
option.choices(choices);
|
|
33
|
+
}
|
|
34
|
+
return super.addOption(option);
|
|
35
|
+
}
|
|
36
|
+
parseOptions(args) {
|
|
37
|
+
output_1.default.errorFormat = this.defaultErrorFormat;
|
|
38
|
+
const formatOption = this.options.find((option) => option.long === '--format');
|
|
39
|
+
if (formatOption && this.defaultErrorFormat === 'text') {
|
|
40
|
+
// Let Commander handle option syntax without running our parsers, hooks
|
|
41
|
+
// or actions. Even if a required value is missing, earlier format values
|
|
42
|
+
// are available before the real parser reports the error.
|
|
43
|
+
const probe = new commander_1.Command()
|
|
44
|
+
.exitOverride()
|
|
45
|
+
.configureOutput({ writeErr: () => { } });
|
|
46
|
+
for (const option of this.options)
|
|
47
|
+
probe.option(option.flags);
|
|
48
|
+
try {
|
|
49
|
+
probe.parseOptions(args);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (!(err instanceof commander_1.CommanderError))
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
const format = probe.opts().format;
|
|
56
|
+
if (formatOption.argChoices?.includes(format) &&
|
|
57
|
+
(format === 'json' || format === 'jsonl'))
|
|
58
|
+
output_1.default.errorFormat = format;
|
|
59
|
+
}
|
|
60
|
+
this.showSuggestionAfterError(output_1.default.errorFormat === 'text');
|
|
61
|
+
return super.parseOptions(args);
|
|
62
|
+
}
|
|
63
|
+
error(message, options) {
|
|
64
|
+
if (output_1.default.errorFormat === 'text')
|
|
65
|
+
return super.error(message, options);
|
|
66
|
+
const path = [this.name()];
|
|
67
|
+
for (let command = this.parent; command; command = command.parent)
|
|
68
|
+
path.unshift(command.name());
|
|
69
|
+
output_1.default.exitError(new cliError_1.default(options?.code === 'commander.unknownCommand'
|
|
70
|
+
? 'unknown_command'
|
|
71
|
+
: 'usage_error', message.replace(/^error: /, '').trim(), (0, texts_1.TXT_CLI_USAGE_HINT)(path.join(' '))));
|
|
72
|
+
}
|
|
21
73
|
get requiredScopes() {
|
|
22
74
|
return this.metadata.requiredScopes ?? null;
|
|
23
75
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class CliError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
hint;
|
|
6
|
+
constructor(code, message, hint = null, options) {
|
|
7
|
+
super(message, options);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.hint = hint;
|
|
10
|
+
this.name = 'CliError';
|
|
11
|
+
}
|
|
12
|
+
static details(error) {
|
|
13
|
+
if (error instanceof CliError) {
|
|
14
|
+
return { code: error.code, message: error.message, hint: error.hint };
|
|
15
|
+
}
|
|
16
|
+
const { code, syscall } = error ?? {};
|
|
17
|
+
const missingFile = code === 'ENOENT' &&
|
|
18
|
+
[
|
|
19
|
+
'open',
|
|
20
|
+
'stat',
|
|
21
|
+
'lstat',
|
|
22
|
+
'access',
|
|
23
|
+
'scandir',
|
|
24
|
+
'readlink',
|
|
25
|
+
'realpath',
|
|
26
|
+
].includes(syscall ?? '');
|
|
27
|
+
return {
|
|
28
|
+
code: missingFile ? 'file_not_found' : 'command_failed',
|
|
29
|
+
message: error instanceof Error ? error.message : String(error),
|
|
30
|
+
hint: null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.default = CliError;
|
package/distTs/src/cliIndex.js
CHANGED
|
@@ -30,6 +30,8 @@ const environment_1 = __importDefault(require("./command/environment"));
|
|
|
30
30
|
const register_1 = __importDefault(require("./command/register"));
|
|
31
31
|
const mcp_1 = __importDefault(require("./command/mcp"));
|
|
32
32
|
const discover_1 = __importDefault(require("./command/discover"));
|
|
33
|
+
const cliError_1 = __importDefault(require("./cliError"));
|
|
34
|
+
const texts_2 = require("./texts");
|
|
33
35
|
function cliIndex() {
|
|
34
36
|
const program = (0, utils_1.newCommand)('bdy', texts_1.DESC_PROGRAM);
|
|
35
37
|
program.hook('preAction', pre_1.default);
|
|
@@ -65,7 +67,7 @@ function cliIndex() {
|
|
|
65
67
|
program.addCommand(discover_1.default);
|
|
66
68
|
program.action((_, cmd) => {
|
|
67
69
|
if (cmd.args.length > 0) {
|
|
68
|
-
output_1.default.exitError((0, texts_1.ERR_COMMAND_NOT_FOUND)(cmd.args[0]));
|
|
70
|
+
output_1.default.exitError(new cliError_1.default('unknown_command', (0, texts_1.ERR_COMMAND_NOT_FOUND)(cmd.args[0]), (0, texts_2.TXT_CLI_USAGE_HINT)('bdy')));
|
|
69
71
|
}
|
|
70
72
|
program.help();
|
|
71
73
|
});
|
|
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const utils_1 = require("../../../utils");
|
|
7
7
|
const texts_1 = require("../../../texts");
|
|
8
8
|
const output_1 = __importDefault(require("../../../output"));
|
|
9
|
-
const commandAgentProxyStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_PROXY_STATUS);
|
|
10
|
-
commandAgentProxyStatus.
|
|
9
|
+
const commandAgentProxyStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_PROXY_STATUS, { examples: texts_1.EXAMPLE_AGENT_PROXY_STATUS });
|
|
10
|
+
commandAgentProxyStatus.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
11
|
+
commandAgentProxyStatus.action(async (options) => {
|
|
11
12
|
const ApiAgent = require('../../../tunnel/api/agent').default;
|
|
12
13
|
if (!commandAgentProxyStatus.agentInstalled) {
|
|
13
14
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
@@ -18,6 +19,12 @@ commandAgentProxyStatus.action(async () => {
|
|
|
18
19
|
try {
|
|
19
20
|
const api = new ApiAgent(commandAgentProxyStatus.agentPort || 0);
|
|
20
21
|
const data = await api.fetchAgentProxy();
|
|
22
|
+
if (options.format === 'json') {
|
|
23
|
+
output_1.default.json(data);
|
|
24
|
+
if (!data.proxy)
|
|
25
|
+
output_1.default.exitError(texts_1.TXT_AGENT_PROXY_DISABLED);
|
|
26
|
+
output_1.default.exitNormal();
|
|
27
|
+
}
|
|
21
28
|
if (data.proxy) {
|
|
22
29
|
output_1.default.exitSuccess(texts_1.TXT_AGENT_PROXY_ENABLED);
|
|
23
30
|
}
|
|
@@ -8,8 +8,8 @@ const node_fs_1 = require("node:fs");
|
|
|
8
8
|
const texts_1 = require("../../texts");
|
|
9
9
|
const utils_1 = require("../../utils");
|
|
10
10
|
const tunnel_1 = require("../../types/tunnel");
|
|
11
|
-
const commandAgentStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_STATUS);
|
|
12
|
-
const outputStatus = (installed, standalone, version, id, running, enabled, target, tunneling, proxy, status) => {
|
|
11
|
+
const commandAgentStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_STATUS, { examples: texts_1.EXAMPLE_AGENT_STATUS, helpText: texts_1.HELP_AGENT_STATUS_JSON });
|
|
12
|
+
const outputStatus = (installed, standalone, version, id, running, enabled, target, tunneling, proxy, status, json) => {
|
|
13
13
|
const AgentManager = require('../../agent/manager').default;
|
|
14
14
|
if (!installed) {
|
|
15
15
|
// Nothing loaded, yet there is a file where the install would be - it is unreadable or not
|
|
@@ -22,6 +22,22 @@ const outputStatus = (installed, standalone, version, id, running, enabled, targ
|
|
|
22
22
|
}
|
|
23
23
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
24
24
|
}
|
|
25
|
+
const orphan = AgentManager.system.getStandaloneConfigPath();
|
|
26
|
+
const orphanedConfig = !AgentManager.system.isUserOwnedInstall() && (0, node_fs_1.existsSync)(orphan)
|
|
27
|
+
? orphan
|
|
28
|
+
: null;
|
|
29
|
+
if (json) {
|
|
30
|
+
output_1.default.json({
|
|
31
|
+
...commandAgentStatus.agentStatus,
|
|
32
|
+
installed,
|
|
33
|
+
standalone,
|
|
34
|
+
id: id ?? null,
|
|
35
|
+
version: version || null,
|
|
36
|
+
running,
|
|
37
|
+
orphaned_config: orphanedConfig,
|
|
38
|
+
});
|
|
39
|
+
output_1.default.exitNormal();
|
|
40
|
+
}
|
|
25
41
|
let txt = 'Installation: ';
|
|
26
42
|
if (standalone)
|
|
27
43
|
txt += 'App @ ';
|
|
@@ -66,13 +82,13 @@ const outputStatus = (installed, standalone, version, id, running, enabled, targ
|
|
|
66
82
|
// above: an install made before those files moved keeps its config in the system directory
|
|
67
83
|
// and reports itself as an app, and a leftover in the home is just as inert next to it.
|
|
68
84
|
// Inert, but only findable if it is said out loud.
|
|
69
|
-
|
|
70
|
-
if (!AgentManager.system.isUserOwnedInstall() && (0, node_fs_1.existsSync)(orphan)) {
|
|
85
|
+
if (orphanedConfig) {
|
|
71
86
|
output_1.default.error((0, texts_1.TXT_AGENT_APP_INSTALL_ORPHANED)(orphan));
|
|
72
87
|
}
|
|
73
88
|
output_1.default.exitNormal();
|
|
74
89
|
};
|
|
75
|
-
commandAgentStatus.
|
|
90
|
+
commandAgentStatus.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
91
|
+
commandAgentStatus.action((options) => {
|
|
76
92
|
let version = '';
|
|
77
93
|
const id = commandAgentStatus.agentId;
|
|
78
94
|
const running = !!commandAgentStatus.agentStatus;
|
|
@@ -89,6 +105,6 @@ commandAgentStatus.action(async () => {
|
|
|
89
105
|
tunneling = commandAgentStatus.agentStatus.tunneling;
|
|
90
106
|
proxy = commandAgentStatus.agentStatus.proxy;
|
|
91
107
|
}
|
|
92
|
-
outputStatus(!!commandAgentStatus.agentInstalled, !!commandAgentStatus.agentStandalone, version, id, running, enabled, target, tunneling, proxy, status);
|
|
108
|
+
outputStatus(!!commandAgentStatus.agentInstalled, !!commandAgentStatus.agentStandalone, version, id, running, enabled, target, tunneling, proxy, status, options.format === 'json');
|
|
93
109
|
});
|
|
94
110
|
exports.default = commandAgentStatus;
|
|
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const utils_1 = require("../../../utils");
|
|
7
7
|
const texts_1 = require("../../../texts");
|
|
8
8
|
const output_1 = __importDefault(require("../../../output"));
|
|
9
|
-
const commandAgentTagsGet = (0, utils_1.newCommand)('get', texts_1.DESC_COMMAND_AGENT_TAGS_GET);
|
|
10
|
-
commandAgentTagsGet.
|
|
9
|
+
const commandAgentTagsGet = (0, utils_1.newCommand)('get', texts_1.DESC_COMMAND_AGENT_TAGS_GET, { examples: texts_1.EXAMPLE_AGENT_TAGS_GET });
|
|
10
|
+
commandAgentTagsGet.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
11
|
+
commandAgentTagsGet.action(async (options) => {
|
|
11
12
|
const ApiAgent = require('../../../tunnel/api/agent').default;
|
|
12
13
|
if (!commandAgentTagsGet.agentInstalled) {
|
|
13
14
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
@@ -18,6 +19,12 @@ commandAgentTagsGet.action(async () => {
|
|
|
18
19
|
try {
|
|
19
20
|
const api = new ApiAgent(commandAgentTagsGet.agentPort || 0);
|
|
20
21
|
const data = await api.fetchAgentTags();
|
|
22
|
+
if (options.format === 'json') {
|
|
23
|
+
output_1.default.json(data);
|
|
24
|
+
if (!data.tags?.length)
|
|
25
|
+
output_1.default.exitError(texts_1.TXT_AGENT_NO_TAGS);
|
|
26
|
+
output_1.default.exitNormal();
|
|
27
|
+
}
|
|
21
28
|
if (data.tags && data.tags.length > 0) {
|
|
22
29
|
output_1.default.exitNormal(data.tags.join(' '));
|
|
23
30
|
}
|
|
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const utils_1 = require("../../../utils");
|
|
7
7
|
const texts_1 = require("../../../texts");
|
|
8
8
|
const output_1 = __importDefault(require("../../../output"));
|
|
9
|
-
const commandAgentTargetStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_TARGET_STATUS);
|
|
10
|
-
commandAgentTargetStatus.
|
|
9
|
+
const commandAgentTargetStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_TARGET_STATUS, { examples: texts_1.EXAMPLE_AGENT_TARGET_STATUS });
|
|
10
|
+
commandAgentTargetStatus.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
11
|
+
commandAgentTargetStatus.action(async (options) => {
|
|
11
12
|
const ApiAgent = require('../../../tunnel/api/agent').default;
|
|
12
13
|
if (!commandAgentTargetStatus.agentInstalled) {
|
|
13
14
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
@@ -18,6 +19,12 @@ commandAgentTargetStatus.action(async () => {
|
|
|
18
19
|
try {
|
|
19
20
|
const api = new ApiAgent(commandAgentTargetStatus.agentPort || 0);
|
|
20
21
|
const data = await api.fetchAgentTarget();
|
|
22
|
+
if (options.format === 'json') {
|
|
23
|
+
output_1.default.json(data);
|
|
24
|
+
if (!data.target)
|
|
25
|
+
output_1.default.exitError(texts_1.TXT_AGENT_TARGET_DISABLED);
|
|
26
|
+
output_1.default.exitNormal();
|
|
27
|
+
}
|
|
21
28
|
if (data.target) {
|
|
22
29
|
output_1.default.exitSuccess(texts_1.TXT_AGENT_TARGET_ENABLED);
|
|
23
30
|
}
|
|
@@ -8,7 +8,8 @@ const texts_1 = require("../../../texts");
|
|
|
8
8
|
const utils_1 = require("../../../utils");
|
|
9
9
|
const commandAgentTunnelList = (0, utils_1.newCommand)('list', texts_1.DESC_COMMAND_AGENT_TUNNEL_LIST, { examples: texts_1.EXAMPLE_AGENT_TUNNEL_LIST });
|
|
10
10
|
commandAgentTunnelList.alias('ls');
|
|
11
|
-
commandAgentTunnelList.
|
|
11
|
+
commandAgentTunnelList.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
12
|
+
commandAgentTunnelList.action(async (options) => {
|
|
12
13
|
const ApiAgent = require('../../../tunnel/api/agent').default;
|
|
13
14
|
if (!commandAgentTunnelList.agentInstalled) {
|
|
14
15
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
@@ -19,6 +20,10 @@ commandAgentTunnelList.action(async () => {
|
|
|
19
20
|
try {
|
|
20
21
|
const api = new ApiAgent(commandAgentTunnelList.agentPort || 0);
|
|
21
22
|
const data = await api.fetchTunnels();
|
|
23
|
+
if (options.format === 'json') {
|
|
24
|
+
output_1.default.json(data);
|
|
25
|
+
output_1.default.exitNormal();
|
|
26
|
+
}
|
|
22
27
|
output_1.default.agentTunnels(data.tunnels);
|
|
23
28
|
}
|
|
24
29
|
catch (err) {
|
|
@@ -8,7 +8,8 @@ const output_1 = __importDefault(require("../../../output"));
|
|
|
8
8
|
const utils_1 = require("../../../utils");
|
|
9
9
|
const commandAgentTunnelStatus = (0, utils_1.newCommand)('status', texts_1.DESC_COMMAND_AGENT_TUNNEL_STATUS, { examples: texts_1.EXAMPLE_AGENT_TUNNEL_STATUS });
|
|
10
10
|
commandAgentTunnelStatus.argument('[id]', texts_1.OPTION_ID);
|
|
11
|
-
commandAgentTunnelStatus.
|
|
11
|
+
commandAgentTunnelStatus.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
12
|
+
commandAgentTunnelStatus.action(async (id, options) => {
|
|
12
13
|
const ApiAgent = require('../../../tunnel/api/agent').default;
|
|
13
14
|
const AgentSocketTunnel = require('../../../agent/socket/tunnel').default;
|
|
14
15
|
if (!commandAgentTunnelStatus.agentInstalled) {
|
|
@@ -21,6 +22,12 @@ commandAgentTunnelStatus.action(async (id) => {
|
|
|
21
22
|
const api = new ApiAgent(commandAgentTunnelStatus.agentPort || 0);
|
|
22
23
|
if (!id) {
|
|
23
24
|
const data = await api.fetchAgentTunneling();
|
|
25
|
+
if (options.format === 'json') {
|
|
26
|
+
output_1.default.json(data);
|
|
27
|
+
if (!data.tunneling)
|
|
28
|
+
output_1.default.exitError(texts_1.TXT_AGENT_TUNNELING_DISABLED);
|
|
29
|
+
output_1.default.exitNormal();
|
|
30
|
+
}
|
|
24
31
|
if (data.tunneling) {
|
|
25
32
|
output_1.default.exitSuccess(texts_1.TXT_AGENT_TUNNELING_ENABLED);
|
|
26
33
|
}
|
|
@@ -32,6 +39,11 @@ commandAgentTunnelStatus.action(async (id) => {
|
|
|
32
39
|
const ws = await api.socketTunnel(id);
|
|
33
40
|
const tunnel = new AgentSocketTunnel(ws, id);
|
|
34
41
|
await tunnel.waitForReady();
|
|
42
|
+
if (options.format === 'json') {
|
|
43
|
+
output_1.default.json(tunnel.rawStatus);
|
|
44
|
+
tunnel.close();
|
|
45
|
+
output_1.default.exitNormal();
|
|
46
|
+
}
|
|
35
47
|
output_1.default.tunnel(tunnel);
|
|
36
48
|
}
|
|
37
49
|
catch (err) {
|
|
@@ -6,14 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const output_1 = __importDefault(require("../../output"));
|
|
7
7
|
const texts_1 = require("../../texts");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
|
-
const commandAgentVersion = (0, utils_1.newCommand)('version', texts_1.DESC_COMMAND_AGENT_VERSION);
|
|
10
|
-
commandAgentVersion.
|
|
9
|
+
const commandAgentVersion = (0, utils_1.newCommand)('version', texts_1.DESC_COMMAND_AGENT_VERSION, { examples: texts_1.EXAMPLE_AGENT_VERSION });
|
|
10
|
+
commandAgentVersion.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
11
|
+
commandAgentVersion.action(async (options) => {
|
|
11
12
|
if (!commandAgentVersion.agentInstalled) {
|
|
12
13
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_INSTALLED);
|
|
13
14
|
}
|
|
14
15
|
if (!commandAgentVersion.agentStatus) {
|
|
15
16
|
output_1.default.exitError(texts_1.ERR_AGENT_NOT_RUNNING);
|
|
16
17
|
}
|
|
18
|
+
if (options.format === 'json') {
|
|
19
|
+
output_1.default.json(commandAgentVersion.agentStatus);
|
|
20
|
+
output_1.default.exitNormal();
|
|
21
|
+
}
|
|
17
22
|
output_1.default.exitNormal(commandAgentVersion.agentStatus.version);
|
|
18
23
|
});
|
|
19
24
|
exports.default = commandAgentVersion;
|
|
@@ -9,6 +9,8 @@ const input_1 = __importDefault(require("../../input"));
|
|
|
9
9
|
const output_1 = __importDefault(require("../../output"));
|
|
10
10
|
/** `members` keeps declaration order, which numbers the listing; `values` does not. */
|
|
11
11
|
const orderedMembers = (body) => {
|
|
12
|
+
if (body.kind === 'array')
|
|
13
|
+
return orderedMembers(body.items);
|
|
12
14
|
if (body.kind !== 'union')
|
|
13
15
|
return [];
|
|
14
16
|
const members = body.discriminator?.members;
|
|
@@ -217,15 +219,19 @@ const chosen = (asked, names) => {
|
|
|
217
219
|
const position = Number(asked);
|
|
218
220
|
return Number.isInteger(position) && position >= 1 ? names[position - 1] : undefined;
|
|
219
221
|
};
|
|
220
|
-
const commandApiInfo = (0, utils_1.newCommand)('info', texts_1.DESC_COMMAND_API_INFO
|
|
222
|
+
const commandApiInfo = (0, utils_1.newCommand)('info', texts_1.DESC_COMMAND_API_INFO, {
|
|
223
|
+
helpText: texts_1.HELP_API_INFO_JSON,
|
|
224
|
+
examples: texts_1.EXAMPLE_API_INFO,
|
|
225
|
+
});
|
|
221
226
|
commandApiInfo.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
222
227
|
commandApiInfo.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
|
|
223
228
|
commandApiInfo.option('--request-schema <schema>', texts_1.OPT_COMMAND_API_INFO_SCHEMA);
|
|
224
229
|
commandApiInfo.option('--response-schema <schema>', texts_1.OPT_COMMAND_API_INFO_SCHEMA);
|
|
225
230
|
commandApiInfo.argument('<method>', texts_1.OPT_COMMAND_API_INFO_METHOD);
|
|
226
231
|
commandApiInfo.argument('<url>', texts_1.OPT_COMMAND_API_INFO_URL);
|
|
232
|
+
commandApiInfo.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
227
233
|
commandApiInfo.action(async (fm, filterUrl, options) => {
|
|
228
|
-
const { apiEndpoints } = require('../../openapi');
|
|
234
|
+
const { apiEndpoints, apiSpecification } = require('../../openapi');
|
|
229
235
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
230
236
|
const project = input_1.default.restApiProject(options.project, true);
|
|
231
237
|
const method = (input_1.default.restApiMethod(fm, false) ?? '').toLowerCase();
|
|
@@ -246,6 +252,48 @@ commandApiInfo.action(async (fm, filterUrl, options) => {
|
|
|
246
252
|
const namesOf = (bodies) => bodies.flatMap((body) => [...orderedMembers(body)]);
|
|
247
253
|
const request = chosen(options.requestSchema, namesOf((listed.request?.contents ?? []).map((content) => content.body)));
|
|
248
254
|
const response = chosen(options.responseSchema, namesOf(listed.responses.flatMap((one) => one.contents.map((content) => content.body))));
|
|
255
|
+
if (options.format === 'json') {
|
|
256
|
+
const raw = await apiSpecification(client.baseUrl);
|
|
257
|
+
const schema = (asked, selected, bodies) => {
|
|
258
|
+
const refs = [];
|
|
259
|
+
for (let body of bodies) {
|
|
260
|
+
while (body.kind === 'array')
|
|
261
|
+
body = body.items;
|
|
262
|
+
if (body.kind === 'union' && selected) {
|
|
263
|
+
const name = memberDef(body, selected);
|
|
264
|
+
if (name && raw.components?.schemas?.[name])
|
|
265
|
+
return raw.components.schemas[name];
|
|
266
|
+
}
|
|
267
|
+
if ('ref' in body && body.ref)
|
|
268
|
+
refs.push(body.ref.name);
|
|
269
|
+
}
|
|
270
|
+
// Union positions use the same member list as text output. Only bodies
|
|
271
|
+
// without unions use reference positions across response/content order.
|
|
272
|
+
const name = refs.includes(asked)
|
|
273
|
+
? asked
|
|
274
|
+
: namesOf(bodies).length === 0
|
|
275
|
+
? chosen(asked, refs)
|
|
276
|
+
: undefined;
|
|
277
|
+
if (name && raw.components?.schemas?.[name])
|
|
278
|
+
return raw.components.schemas[name];
|
|
279
|
+
output_1.default.exitError((0, texts_1.ERR_API_SCHEMA_NOT_FOUND)(asked));
|
|
280
|
+
};
|
|
281
|
+
const requestSchema = options.requestSchema
|
|
282
|
+
? schema(options.requestSchema, request, (listed.request?.contents ?? []).map((content) => content.body))
|
|
283
|
+
: undefined;
|
|
284
|
+
const responseSchema = options.responseSchema
|
|
285
|
+
? schema(options.responseSchema, response, listed.responses.flatMap((one) => one.contents.map((content) => content.body)))
|
|
286
|
+
: undefined;
|
|
287
|
+
output_1.default.json(requestSchema && responseSchema
|
|
288
|
+
? { request: requestSchema, response: responseSchema }
|
|
289
|
+
: (requestSchema ??
|
|
290
|
+
responseSchema ?? {
|
|
291
|
+
method: wanted.method.toUpperCase(),
|
|
292
|
+
path: (0, utils_1.openApiPrepareUrl)(wanted.path, workspace, project),
|
|
293
|
+
operation: raw.paths[wanted.path][method],
|
|
294
|
+
}));
|
|
295
|
+
output_1.default.exitNormal();
|
|
296
|
+
}
|
|
249
297
|
const member = request || response
|
|
250
298
|
? { ...(request ? { request } : {}), ...(response ? { response } : {}) }
|
|
251
299
|
: undefined;
|
|
@@ -25,12 +25,15 @@ const outputUrl = (operation, workspace, project) => {
|
|
|
25
25
|
if (operation.summary)
|
|
26
26
|
output_1.default.gray(operation.summary);
|
|
27
27
|
};
|
|
28
|
-
const commandApiList = (0, utils_1.newCommand)('list', texts_1.DESC_COMMAND_API_LIST
|
|
28
|
+
const commandApiList = (0, utils_1.newCommand)('list', texts_1.DESC_COMMAND_API_LIST, {
|
|
29
|
+
examples: texts_1.EXAMPLE_API_LIST,
|
|
30
|
+
});
|
|
29
31
|
commandApiList.alias('ls');
|
|
30
32
|
commandApiList.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
31
33
|
commandApiList.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
|
|
32
34
|
commandApiList.option('-m, --method <method>', texts_1.OPT_COMMAND_API_LIST_METHOD);
|
|
33
35
|
commandApiList.option('-s, --search <phrase>', texts_1.OPT_COMMAND_API_LIST_SEARCH);
|
|
36
|
+
commandApiList.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
34
37
|
commandApiList.action(async (options) => {
|
|
35
38
|
const { apiEndpoints } = require('../../openapi');
|
|
36
39
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
@@ -39,12 +42,25 @@ commandApiList.action(async (options) => {
|
|
|
39
42
|
const filter = options.search;
|
|
40
43
|
const client = input_1.default.restApiTokenClient(true);
|
|
41
44
|
const api = await apiEndpoints(client.baseUrl);
|
|
45
|
+
const operations = [];
|
|
42
46
|
for (const operation of await api.operations()) {
|
|
43
47
|
if (filter && !operation.path.includes(filter))
|
|
44
48
|
continue;
|
|
45
49
|
if (filterMethod && filterMethod !== operation.method.toLowerCase())
|
|
46
50
|
continue;
|
|
47
|
-
|
|
51
|
+
if (options.format === 'json') {
|
|
52
|
+
operations.push({
|
|
53
|
+
method: operation.method.toUpperCase(),
|
|
54
|
+
path: (0, utils_1.openApiPrepareUrl)(operation.path, workspace, project),
|
|
55
|
+
summary: operation.summary,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
outputUrl(operation, workspace, project);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (options.format === 'json') {
|
|
63
|
+
output_1.default.json(operations);
|
|
48
64
|
}
|
|
49
65
|
output_1.default.exitNormal();
|
|
50
66
|
});
|
|
@@ -23,6 +23,7 @@ commandArtifactCreate.option('-a, --auth <user:pass>', texts_1.OPTION_HTTP_AUTH)
|
|
|
23
23
|
commandArtifactCreate.option('--note <note>', texts_1.OPT_COMMAND_NOTE);
|
|
24
24
|
commandArtifactCreate.option('--agent-note <note>', texts_1.OPT_COMMAND_AGENT_NOTE);
|
|
25
25
|
commandArtifactCreate.argument('[name]', texts_1.OPT_COMMAND_ARTIFACT_NAME);
|
|
26
|
+
commandArtifactCreate.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
26
27
|
commandArtifactCreate.action(async (name, options) => {
|
|
27
28
|
const humanId = require('human-id').default;
|
|
28
29
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
@@ -71,6 +72,10 @@ commandArtifactCreate.action(async (name, options) => {
|
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
const result = await client.createArtifact(workspace, data);
|
|
75
|
+
if (options.format === 'json') {
|
|
76
|
+
output_1.default.json(result);
|
|
77
|
+
output_1.default.exitNormal();
|
|
78
|
+
}
|
|
74
79
|
output_1.default.exitSuccess((0, texts_1.TXT_ARTIFACT_CREATED)(client.baseUrl, workspace, result));
|
|
75
80
|
});
|
|
76
81
|
exports.default = commandArtifactCreate;
|