bdy 1.16.12 → 1.16.15-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.
Files changed (46) hide show
  1. package/distTs/package.json +4 -3
  2. package/distTs/src/api/client.js +176 -5
  3. package/distTs/src/command/login.js +130 -0
  4. package/distTs/src/command/logout.js +15 -0
  5. package/distTs/src/command/project/get.js +18 -0
  6. package/distTs/src/command/project/list.js +33 -0
  7. package/distTs/src/command/project/set.js +45 -0
  8. package/distTs/src/command/project.js +16 -0
  9. package/distTs/src/command/sandbox/cp.js +71 -0
  10. package/distTs/src/command/sandbox/create.js +147 -0
  11. package/distTs/src/command/sandbox/destroy.js +34 -0
  12. package/distTs/src/command/sandbox/endpoint/add.js +40 -0
  13. package/distTs/src/command/sandbox/endpoint/delete.js +41 -0
  14. package/distTs/src/command/sandbox/endpoint/get.js +101 -0
  15. package/distTs/src/command/sandbox/endpoint/list.js +48 -0
  16. package/distTs/src/command/sandbox/endpoint.js +18 -0
  17. package/distTs/src/command/sandbox/exec/command.js +72 -0
  18. package/distTs/src/command/sandbox/exec/kill.js +34 -0
  19. package/distTs/src/command/sandbox/exec/list.js +53 -0
  20. package/distTs/src/command/sandbox/exec/logs.js +59 -0
  21. package/distTs/src/command/sandbox/exec/status.js +43 -0
  22. package/distTs/src/command/sandbox/exec.js +19 -0
  23. package/distTs/src/command/sandbox/get.js +50 -0
  24. package/distTs/src/command/sandbox/list.js +41 -0
  25. package/distTs/src/command/sandbox/restart.js +48 -0
  26. package/distTs/src/command/sandbox/snapshot/create.js +56 -0
  27. package/distTs/src/command/sandbox/snapshot/delete.js +40 -0
  28. package/distTs/src/command/sandbox/snapshot/get.js +50 -0
  29. package/distTs/src/command/sandbox/snapshot/list.js +46 -0
  30. package/distTs/src/command/sandbox/snapshot.js +18 -0
  31. package/distTs/src/command/sandbox/start.js +48 -0
  32. package/distTs/src/command/sandbox/status.js +38 -0
  33. package/distTs/src/command/sandbox/stop.js +48 -0
  34. package/distTs/src/command/sandbox.js +34 -0
  35. package/distTs/src/command/whoami.js +31 -0
  36. package/distTs/src/command/workspace/get.js +18 -0
  37. package/distTs/src/command/workspace/list.js +32 -0
  38. package/distTs/src/command/workspace/set.js +40 -0
  39. package/distTs/src/command/workspace.js +16 -0
  40. package/distTs/src/index.js +12 -0
  41. package/distTs/src/input.js +142 -14
  42. package/distTs/src/output.js +44 -0
  43. package/distTs/src/texts.js +186 -8
  44. package/distTs/src/tunnel/cfg.js +47 -0
  45. package/distTs/src/utils.js +91 -3
  46. package/package.json +4 -3
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const output_1 = __importDefault(require("../../output"));
7
+ const client_1 = __importDefault(require("../../api/client"));
8
+ const texts_1 = require("../../texts");
9
+ const utils_1 = require("../../utils");
10
+ const input_1 = __importDefault(require("../../input"));
11
+ const commandWorkspaceList = (0, utils_1.newCommand)('list', texts_1.DESC_COMMAND_WORKSPACE_LIST);
12
+ commandWorkspaceList.option('--token <token>', texts_1.OPTION_REST_API_TOKEN);
13
+ commandWorkspaceList.option('--api <url>', texts_1.OPTION_REST_API_ENDPOINT);
14
+ commandWorkspaceList.option('--region <region>', texts_1.OPTION_REST_API_REGION);
15
+ commandWorkspaceList.alias('ls');
16
+ commandWorkspaceList.action(async (options) => {
17
+ const token = input_1.default.restApiToken(options.token);
18
+ const baseUrl = input_1.default.restApiBaseUrl(options.api, options.region);
19
+ const client = new client_1.default(baseUrl, token);
20
+ const response = await client.getWorkspaces();
21
+ if (!response.workspaces || response.workspaces.length === 0) {
22
+ output_1.default.normal('No workspaces found.');
23
+ output_1.default.exitNormal();
24
+ }
25
+ const data = [['NAME', 'DOMAIN']];
26
+ for (const ws of response.workspaces) {
27
+ data.push([ws.name, ws.domain]);
28
+ }
29
+ output_1.default.table(data);
30
+ output_1.default.exitNormal();
31
+ });
32
+ exports.default = commandWorkspaceList;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const cfg_1 = __importDefault(require("../../tunnel/cfg"));
7
+ const output_1 = __importDefault(require("../../output"));
8
+ const client_1 = __importDefault(require("../../api/client"));
9
+ const texts_1 = require("../../texts");
10
+ const utils_1 = require("../../utils");
11
+ const input_1 = __importDefault(require("../../input"));
12
+ const commandWorkspaceSet = (0, utils_1.newCommand)('set', texts_1.DESC_COMMAND_WORKSPACE_SET);
13
+ commandWorkspaceSet.option('--token <token>', texts_1.OPTION_REST_API_TOKEN);
14
+ commandWorkspaceSet.option('--api <url>', texts_1.OPTION_REST_API_ENDPOINT);
15
+ commandWorkspaceSet.option('--region <region>', texts_1.OPTION_REST_API_REGION);
16
+ commandWorkspaceSet.argument('[workspace]', texts_1.ARG_COMMAND_WORKSPACE);
17
+ commandWorkspaceSet.action(async (workspaceDomain, options) => {
18
+ output_1.default.handleSignals();
19
+ const token = input_1.default.restApiToken(options.token);
20
+ const baseUrl = input_1.default.restApiBaseUrl(options.api, options.region);
21
+ const client = new client_1.default(baseUrl, token);
22
+ const response = await client.getWorkspaces();
23
+ if (!response.workspaces || response.workspaces.length === 0) {
24
+ output_1.default.exitError(texts_1.ERR_LOGIN_NO_WORKSPACES);
25
+ }
26
+ let workspace;
27
+ if (workspaceDomain) {
28
+ const found = response.workspaces.find((w) => w.domain === workspaceDomain);
29
+ if (!found) {
30
+ output_1.default.exitError(texts_1.ERR_LOGIN_NO_WORKSPACE_FOUND);
31
+ }
32
+ workspace = found.domain;
33
+ }
34
+ else {
35
+ workspace = await output_1.default.selectWorkspace(response.workspaces);
36
+ }
37
+ cfg_1.default.setWorkspace(workspace);
38
+ output_1.default.exitSuccess((0, texts_1.TXT_WORKSPACE_SET_SUCCESS)(workspace));
39
+ });
40
+ exports.default = commandWorkspaceSet;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const texts_1 = require("../texts");
7
+ const utils_1 = require("../utils");
8
+ const list_1 = __importDefault(require("./workspace/list"));
9
+ const set_1 = __importDefault(require("./workspace/set"));
10
+ const get_1 = __importDefault(require("./workspace/get"));
11
+ const commandWorkspace = (0, utils_1.newCommand)('workspace', texts_1.DESC_COMMAND_WORKSPACE);
12
+ commandWorkspace.alias('ws');
13
+ commandWorkspace.addCommand(list_1.default);
14
+ commandWorkspace.addCommand(set_1.default);
15
+ commandWorkspace.addCommand(get_1.default);
16
+ exports.default = commandWorkspace;
@@ -15,6 +15,12 @@ const vt_1 = __importDefault(require("./command/vt"));
15
15
  const ut_1 = __importDefault(require("./command/ut"));
16
16
  const tunnel_1 = __importDefault(require("./command/tunnel"));
17
17
  const pipeline_1 = __importDefault(require("./command/pipeline"));
18
+ const sandbox_1 = __importDefault(require("./command/sandbox"));
19
+ const login_1 = __importDefault(require("./command/login"));
20
+ const logout_1 = __importDefault(require("./command/logout"));
21
+ const workspace_1 = __importDefault(require("./command/workspace"));
22
+ const project_1 = __importDefault(require("./command/project"));
23
+ const whoami_1 = __importDefault(require("./command/whoami"));
18
24
  stream_1.default.setDefaultHighWaterMark(false, 67108864);
19
25
  process.title = 'bdy';
20
26
  process.on('uncaughtException', (err) => {
@@ -33,4 +39,10 @@ program.addCommand(version_1.default);
33
39
  program.addCommand(vt_1.default);
34
40
  program.addCommand(ut_1.default);
35
41
  program.addCommand(pipeline_1.default);
42
+ program.addCommand(sandbox_1.default);
43
+ program.addCommand(login_1.default);
44
+ program.addCommand(whoami_1.default);
45
+ program.addCommand(logout_1.default);
46
+ program.addCommand(workspace_1.default);
47
+ program.addCommand(project_1.default);
36
48
  program.parse();
@@ -39,12 +39,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const output_1 = __importDefault(require("./output"));
40
40
  const netmask_1 = __importDefault(require("netmask"));
41
41
  const punycode_1 = __importDefault(require("punycode/"));
42
- const fs_1 = __importStar(require("fs"));
42
+ const node_fs_1 = __importStar(require("node:fs"));
43
43
  const tls_1 = __importDefault(require("tls"));
44
- const crypto_1 = __importDefault(require("crypto"));
44
+ const node_crypto_1 = __importDefault(require("node:crypto"));
45
+ const node_path_1 = __importDefault(require("node:path"));
45
46
  const utils_1 = require("./utils");
46
47
  const texts_1 = require("./texts");
47
48
  const tunnel_1 = require("./types/tunnel");
49
+ const cfg_1 = __importDefault(require("./tunnel/cfg"));
48
50
  class Input {
49
51
  static timeout(timeout) {
50
52
  const t = parseInt(timeout, 10);
@@ -57,7 +59,7 @@ class Input {
57
59
  if (!serve)
58
60
  return serve;
59
61
  try {
60
- const info = (0, fs_1.statSync)(serve);
62
+ const info = (0, node_fs_1.statSync)(serve);
61
63
  if (!info.isDirectory()) {
62
64
  output_1.default.exitError((0, texts_1.ERR_PATH_IS_NOT_DIRECTORY)(serve));
63
65
  }
@@ -76,6 +78,18 @@ class Input {
76
78
  }
77
79
  return n;
78
80
  }
81
+ static sandboxHeaders(headers) {
82
+ const r = {};
83
+ (headers || []).forEach((h) => {
84
+ if (!h)
85
+ return;
86
+ const s = h.split(':');
87
+ if (s.length !== 2)
88
+ return;
89
+ r[s[0]] = s[1];
90
+ });
91
+ return r;
92
+ }
79
93
  static headers(headers) {
80
94
  const r = [];
81
95
  (headers || []).forEach((h) => {
@@ -91,6 +105,65 @@ class Input {
91
105
  });
92
106
  return r;
93
107
  }
108
+ static prepareSandboxEndpoint(options) {
109
+ const type = this.type(options.type);
110
+ const ep = {
111
+ type,
112
+ name: this.name(options.name),
113
+ endpoint: this.target(options.endpoint, type),
114
+ };
115
+ if (options.epRegion !== undefined)
116
+ ep.region = this.region(options.epRegion);
117
+ if (options.whitelist !== undefined)
118
+ ep.whitelist = this.whitelist(options.whitelist);
119
+ if (options.timeout !== undefined)
120
+ ep.timeout = this.timeout(options.timeout);
121
+ if (type === tunnel_1.TUNNEL_TYPE.HTTP) {
122
+ ep.http = {};
123
+ ep.http.auth_type = tunnel_1.TUNNEL_HTTP_AUTH_TYPE.NONE;
124
+ if (options.host !== undefined) {
125
+ ep.http.rewrite_host_header = options.host;
126
+ }
127
+ if (options.auth !== undefined) {
128
+ const { password, login } = this.auth(options.auth);
129
+ ep.http.login = login;
130
+ ep.http.password = password;
131
+ ep.http.auth_type = tunnel_1.TUNNEL_HTTP_AUTH_TYPE.BASIC;
132
+ }
133
+ else if (options.buddy) {
134
+ ep.http.auth_type = tunnel_1.TUNNEL_HTTP_AUTH_TYPE.BUDDY;
135
+ }
136
+ if (options.ca !== undefined)
137
+ ep.http.tls_ca = this.ca(options.ca);
138
+ if (options.serve !== undefined)
139
+ ep.http.serve_path = this.serve(options.serve);
140
+ if (options.useragent !== undefined)
141
+ ep.http.whitelist_user_agents = this.useragents(options.useragent);
142
+ if (options.header !== undefined)
143
+ ep.http.request_headers = this.sandboxHeaders(options.header);
144
+ if (options.responseHeader !== undefined)
145
+ ep.http.response_headers = this.sandboxHeaders(options.responseHeader);
146
+ if (options.circuitBreaker !== undefined) {
147
+ ep.http.circuit_breaker = this.circuitBreaker(options.circuitBreaker) * 100;
148
+ }
149
+ ep.http.log_requests = !!options.log;
150
+ ep.http.verify_certificate = !!options.verify;
151
+ ep.http.http2 = !!options.http2;
152
+ ep.http.compression = !!options.compression;
153
+ }
154
+ else if (type === tunnel_1.TUNNEL_TYPE.TLS) {
155
+ ep.tls = {};
156
+ ep.tls.terminate_at = this.terminate(options.terminate);
157
+ if (options.key !== undefined || options.cert !== undefined) {
158
+ const { key, cert } = this.keyCert(options.key, options.cert);
159
+ ep.tls.private_key = key;
160
+ ep.tls.certificate = cert;
161
+ }
162
+ if (options.ca !== undefined)
163
+ ep.tls.ca_certificate = this.ca(options.ca);
164
+ }
165
+ return ep;
166
+ }
94
167
  static auth(auth) {
95
168
  const s = auth.split(':');
96
169
  if (s.length !== 2) {
@@ -229,6 +302,8 @@ class Input {
229
302
  let t = process.env.BUDDY_TOKEN;
230
303
  if (token)
231
304
  t = token;
305
+ if (!t)
306
+ t = cfg_1.default.getApiToken();
232
307
  if (!t) {
233
308
  output_1.default.exitError(texts_1.ERR_REST_API_TOKEN);
234
309
  }
@@ -306,23 +381,34 @@ class Input {
306
381
  });
307
382
  return list;
308
383
  }
384
+ static restApiRegion(region) {
385
+ const r = region.toLowerCase();
386
+ if (Object.values(utils_1.REST_API_REGION).includes(r)) {
387
+ return r;
388
+ }
389
+ else {
390
+ output_1.default.exitError(texts_1.ERR_REST_API_REGION);
391
+ }
392
+ }
309
393
  static restApiBaseUrl(api, region) {
310
394
  let u = process.env.BUDDY_API_ENDPOINT;
311
395
  if (api)
312
396
  u = api;
397
+ if (!u)
398
+ u = cfg_1.default.getBaseUrl();
313
399
  if (!u) {
314
400
  let r = process.env.BUDDY_REGION;
315
401
  if (region)
316
402
  r = region;
317
- if (r === 'eu')
318
- u = 'api.eu.buddy.works';
319
- else if (r === 'as')
320
- u = 'api.as.buddy.works';
321
- else if (r === 'us')
322
- u = 'api.buddy.works';
403
+ if (r?.toLowerCase() === utils_1.REST_API_REGION.EU)
404
+ u = utils_1.REST_API_ENDPOINT.EU;
405
+ else if (r?.toLowerCase() === utils_1.REST_API_REGION.AS)
406
+ u = utils_1.REST_API_ENDPOINT.AS;
407
+ else if (r?.toLowerCase() === utils_1.REST_API_REGION.US)
408
+ u = utils_1.REST_API_ENDPOINT.US;
323
409
  }
324
410
  if (!u)
325
- u = 'api.buddy.works';
411
+ u = utils_1.REST_API_ENDPOINT.US;
326
412
  u = u.replace(/^https?:\/\//g, '');
327
413
  try {
328
414
  return new URL(`https://${u}`);
@@ -345,20 +431,62 @@ class Input {
345
431
  let w = process.env.BUDDY_WORKSPACE;
346
432
  if (workspace)
347
433
  w = workspace;
434
+ if (!w)
435
+ w = cfg_1.default.getWorkspace();
348
436
  if (!w) {
349
437
  output_1.default.exitError(texts_1.ERR_REST_API_WORKSPACE);
350
438
  }
351
439
  return w;
352
440
  }
441
+ static restApiSandboxDestinationPath(destPath) {
442
+ const s = (destPath || '').split(':');
443
+ if (s.length !== 2) {
444
+ output_1.default.exitError(texts_1.ERR_SANDBOX_CP_INVALID_DEST);
445
+ }
446
+ const identifier = s[0];
447
+ let remotePath = s[1];
448
+ if (!identifier) {
449
+ output_1.default.exitError(texts_1.ERR_SANDBOX_CP_INVALID_DEST);
450
+ }
451
+ if (!remotePath.startsWith('/')) {
452
+ remotePath = `/${remotePath}`;
453
+ }
454
+ return {
455
+ identifier,
456
+ remotePath
457
+ };
458
+ }
459
+ static restApiSandboxSourcePath(sourcePath) {
460
+ const s = node_path_1.default.resolve(sourcePath);
461
+ let sourceStats;
462
+ try {
463
+ sourceStats = node_fs_1.default.statSync(s);
464
+ }
465
+ catch {
466
+ output_1.default.exitError((0, texts_1.ERR_SANDBOX_CP_SOURCE_NOT_FOUND)(sourcePath));
467
+ }
468
+ return { sourceStats, sourcePath: s };
469
+ }
353
470
  static restApiProject(project) {
354
471
  let p = process.env.BUDDY_PROJECT;
355
472
  if (project)
356
473
  p = project;
474
+ if (!p)
475
+ p = cfg_1.default.getProject();
357
476
  if (!p) {
358
477
  output_1.default.exitError(texts_1.ERR_REST_API_PROJECT);
359
478
  }
360
479
  return p;
361
480
  }
481
+ static restApiSandboxExecRuntime(runtime) {
482
+ if (!runtime)
483
+ runtime = utils_1.SANDBOX_EXEC_RUNTIME.BASH;
484
+ const vals = Object.values(utils_1.SANDBOX_EXEC_RUNTIME);
485
+ if (!vals.includes(runtime)) {
486
+ output_1.default.exitError(texts_1.ERR_SANDBOX_EXEC_RUNTIME_NOT_FOUND);
487
+ }
488
+ return runtime;
489
+ }
362
490
  static name(name) {
363
491
  if (name.includes('*')) {
364
492
  output_1.default.exitError(texts_1.ERR_NAME_WITHOUT_ASTERISK);
@@ -368,13 +496,13 @@ class Input {
368
496
  static ca(caPath) {
369
497
  let cert;
370
498
  try {
371
- cert = fs_1.default.readFileSync(caPath, 'utf8');
499
+ cert = node_fs_1.default.readFileSync(caPath, 'utf8');
372
500
  }
373
501
  catch {
374
502
  output_1.default.exitError((0, texts_1.ERR_CA_PATH_IS_NOT_VALID)(caPath));
375
503
  }
376
504
  try {
377
- new crypto_1.default.X509Certificate(cert);
505
+ new node_crypto_1.default.X509Certificate(cert);
378
506
  }
379
507
  catch {
380
508
  output_1.default.exitError((0, texts_1.ERR_WRONG_CA)(caPath));
@@ -385,13 +513,13 @@ class Input {
385
513
  let key;
386
514
  let cert;
387
515
  try {
388
- key = fs_1.default.readFileSync(keyPath, 'utf8');
516
+ key = node_fs_1.default.readFileSync(keyPath, 'utf8');
389
517
  }
390
518
  catch {
391
519
  output_1.default.exitError((0, texts_1.ERR_KEY_PATH_IS_NOT_VALID)(keyPath));
392
520
  }
393
521
  try {
394
- cert = fs_1.default.readFileSync(certPath, 'utf8');
522
+ cert = node_fs_1.default.readFileSync(certPath, 'utf8');
395
523
  }
396
524
  catch {
397
525
  output_1.default.exitError((0, texts_1.ERR_CERT_PATH_IS_NOT_VALID)(certPath));
@@ -152,6 +152,50 @@ class Output {
152
152
  const on = new tunnel_3.default(terminal, tunnel);
153
153
  on.start(this);
154
154
  }
155
+ static handleSignals() {
156
+ terminal.on('key', (key) => {
157
+ if (key === 'CTRL_C' || key === 'ESCAPE') {
158
+ terminal.fullscreen(false);
159
+ terminal.hideCursor(false);
160
+ terminal.grabInput(false);
161
+ terminal('\nCanceled\n');
162
+ process.exit(0);
163
+ }
164
+ });
165
+ }
166
+ static async inputString() {
167
+ return new Promise((resolve) => {
168
+ terminal.inputField((_, input) => {
169
+ terminal('\n');
170
+ resolve((input || '').trim());
171
+ });
172
+ });
173
+ }
174
+ static async inputMenu(items) {
175
+ return new Promise((resolve) => {
176
+ terminal.singleColumnMenu(items, (_, response) => {
177
+ terminal('\n');
178
+ resolve(response.selectedIndex);
179
+ });
180
+ });
181
+ }
182
+ static async selectWorkspace(workspaces) {
183
+ Output.normal(texts_1.TXT_LOGIN_SELECT_WORKSPACE);
184
+ const items = workspaces.map((w) => `${w.name} (${w.domain})`);
185
+ const index = await Output.inputMenu(items);
186
+ return workspaces[index].domain;
187
+ }
188
+ static async selectProject(projects) {
189
+ Output.normal(texts_1.TXT_LOGIN_SELECT_PROJECT);
190
+ const items = [
191
+ "Don't set (default)",
192
+ ...projects.map((p) => `${p.display_name} (${p.name})`),
193
+ ];
194
+ const index = await Output.inputMenu(items);
195
+ if (index > 0)
196
+ return projects[index - 1].name;
197
+ return '';
198
+ }
155
199
  static exitError(err) {
156
200
  this.killSpinner();
157
201
  terminal.fullscreen(false);