bdy 1.16.11-master → 1.16.11-sbs-1

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 (48) hide show
  1. package/distTs/package.json +4 -3
  2. package/distTs/src/api/client.js +78 -2
  3. package/distTs/src/command/login.js +280 -0
  4. package/distTs/src/command/logout.js +15 -0
  5. package/distTs/src/command/package/download.js +259 -0
  6. package/distTs/src/command/package/publish.js +231 -0
  7. package/distTs/src/command/package.js +14 -0
  8. package/distTs/src/command/project/list.js +61 -0
  9. package/distTs/src/command/project/set.js +85 -0
  10. package/distTs/src/command/project.js +14 -0
  11. package/distTs/src/command/sandbox/command/kill.js +35 -0
  12. package/distTs/src/command/sandbox/command/list.js +54 -0
  13. package/distTs/src/command/sandbox/command/logs.js +133 -0
  14. package/distTs/src/command/sandbox/command/status.js +44 -0
  15. package/distTs/src/command/sandbox/command.js +18 -0
  16. package/distTs/src/command/sandbox/cp.js +123 -0
  17. package/distTs/src/command/sandbox/create.js +99 -0
  18. package/distTs/src/command/sandbox/destroy.js +35 -0
  19. package/distTs/src/command/sandbox/endpoint/add.js +91 -0
  20. package/distTs/src/command/sandbox/endpoint/delete.js +46 -0
  21. package/distTs/src/command/sandbox/endpoint/get.js +58 -0
  22. package/distTs/src/command/sandbox/endpoint/list.js +51 -0
  23. package/distTs/src/command/sandbox/endpoint/update.js +85 -0
  24. package/distTs/src/command/sandbox/endpoint.js +20 -0
  25. package/distTs/src/command/sandbox/exec.js +127 -0
  26. package/distTs/src/command/sandbox/get.js +51 -0
  27. package/distTs/src/command/sandbox/list.js +41 -0
  28. package/distTs/src/command/sandbox/restart.js +49 -0
  29. package/distTs/src/command/sandbox/snapshot/create.js +68 -0
  30. package/distTs/src/command/sandbox/snapshot/delete.js +42 -0
  31. package/distTs/src/command/sandbox/snapshot/get.js +54 -0
  32. package/distTs/src/command/sandbox/snapshot/list.js +48 -0
  33. package/distTs/src/command/sandbox/snapshot.js +18 -0
  34. package/distTs/src/command/sandbox/start.js +49 -0
  35. package/distTs/src/command/sandbox/status.js +35 -0
  36. package/distTs/src/command/sandbox/stop.js +49 -0
  37. package/distTs/src/command/sandbox.js +36 -0
  38. package/distTs/src/command/workspace/list.js +57 -0
  39. package/distTs/src/command/workspace/set.js +81 -0
  40. package/distTs/src/command/workspace.js +14 -0
  41. package/distTs/src/index.js +10 -0
  42. package/distTs/src/input.js +15 -4
  43. package/distTs/src/texts.js +155 -1
  44. package/distTs/src/tunnel/cfg.js +38 -0
  45. package/package.json +4 -3
  46. package/distTs/src/command/agent/standalone/kill.js +0 -22
  47. package/distTs/src/command/agent/standalone.js +0 -136
  48. package/distTs/src/command/vt/scrap.js +0 -193
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.16.11-master",
4
+ "version": "1.16.11-sbs-1",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -31,6 +31,8 @@
31
31
  "eventsource": "4.0.0",
32
32
  "fastify": "4.28.1",
33
33
  "fdir": "6.5.0",
34
+ "fflate": "0.8.2",
35
+ "human-id": "^4.1.3",
34
36
  "isbinaryfile": "5.0.2",
35
37
  "jsonwebtoken": "9.0.2",
36
38
  "mime-db": "1.52.0",
@@ -52,8 +54,7 @@
52
54
  "uuid": "10.0.0",
53
55
  "which": "4.0.0",
54
56
  "ws": "8.18.0",
55
- "zod": "3.24.2",
56
- "fflate": "0.8.2"
57
+ "zod": "3.24.2"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@eslint/js": "9.13.0",
@@ -55,7 +55,19 @@ class ApiClient {
55
55
  throw new Error(texts_1.ERR_REST_API_WRONG_TOKEN);
56
56
  }
57
57
  if (status === 403) {
58
- await responseBody.dump();
58
+ let json;
59
+ try {
60
+ json = await responseBody.json();
61
+ logger_1.default.debug('API CLIENT PARSED RESPONSE:');
62
+ logger_1.default.debug(json);
63
+ }
64
+ catch (err) {
65
+ await responseBody.dump();
66
+ throw new Error(texts_1.ERR_REST_API_RATE_LIMIT);
67
+ }
68
+ if (json.errors && json.errors[0] && json.errors[0].message) {
69
+ throw new Error(json.errors[0].message);
70
+ }
59
71
  throw new Error(texts_1.ERR_REST_API_RATE_LIMIT);
60
72
  }
61
73
  if ([400, 404].includes(status)) {
@@ -79,7 +91,7 @@ class ApiClient {
79
91
  else
80
92
  throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR);
81
93
  }
82
- if (status === 200) {
94
+ if (status === 200 || status === 201) {
83
95
  if (parseResponseBody) {
84
96
  try {
85
97
  const b = await responseBody.json();
@@ -98,6 +110,10 @@ class ApiClient {
98
110
  return null;
99
111
  }
100
112
  }
113
+ else if (status === 204) {
114
+ await responseBody.dump();
115
+ return null;
116
+ }
101
117
  else {
102
118
  await responseBody.dump();
103
119
  throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR);
@@ -112,5 +128,65 @@ class ApiClient {
112
128
  async postPipelineRun(workspace, project, pipelineId, body) {
113
129
  return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/projects/${encodeURIComponent(project)}/pipelines/${encodeURIComponent(pipelineId)}/executions`, body, true);
114
130
  }
131
+ // Sandbox methods
132
+ async createSandbox(workspace, project, body) {
133
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes?project_name=${encodeURIComponent(project)}`, body, true);
134
+ }
135
+ async listSandboxes(workspace, project) {
136
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes?project_name=${encodeURIComponent(project)}`, null, true);
137
+ }
138
+ async getSandbox(workspace, sandboxId) {
139
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}`, null, true);
140
+ }
141
+ async deleteSandbox(workspace, sandboxId) {
142
+ return await this.request('DELETE', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}`, null, false);
143
+ }
144
+ async updateSandbox(workspace, sandboxId, body) {
145
+ return await this.request('PATCH', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}`, body, true);
146
+ }
147
+ async startSandbox(workspace, sandboxId) {
148
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/start`, {}, true);
149
+ }
150
+ async stopSandbox(workspace, sandboxId) {
151
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/stop`, {}, true);
152
+ }
153
+ async restartSandbox(workspace, sandboxId) {
154
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/restart`, {}, true);
155
+ }
156
+ async executeSandboxCommand(workspace, sandboxId, body) {
157
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/commands`, body, true);
158
+ }
159
+ async getSandboxCommand(workspace, sandboxId, commandId) {
160
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/commands/${encodeURIComponent(commandId)}`, null, true);
161
+ }
162
+ async listSandboxCommands(workspace, sandboxId) {
163
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/commands`, null, true);
164
+ }
165
+ async getSandboxCommandLogsUrl(workspace, sandboxId, commandId) {
166
+ return `${this.baseUrl.origin}/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/commands/${encodeURIComponent(commandId)}/logs`;
167
+ }
168
+ async terminateSandboxCommand(workspace, sandboxId, commandId) {
169
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/commands/${encodeURIComponent(commandId)}/terminate`, {}, true);
170
+ }
171
+ // Snapshot methods
172
+ async listSandboxSnapshots(workspace, sandboxId) {
173
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/snapshots`, null, true);
174
+ }
175
+ async createSandboxSnapshot(workspace, sandboxId, body) {
176
+ return await this.request('POST', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/snapshots`, body, true);
177
+ }
178
+ async getSandboxSnapshot(workspace, sandboxId, snapshotId) {
179
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/snapshots/${encodeURIComponent(snapshotId)}`, null, true);
180
+ }
181
+ async deleteSandboxSnapshot(workspace, sandboxId, snapshotId) {
182
+ return await this.request('DELETE', `/workspaces/${encodeURIComponent(workspace)}/sandboxes/${encodeURIComponent(sandboxId)}/snapshots/${encodeURIComponent(snapshotId)}`, null, false);
183
+ }
184
+ // Workspace and Project methods
185
+ async getWorkspaces() {
186
+ return await this.request('GET', '/workspaces', null, true);
187
+ }
188
+ async getProjects(workspace) {
189
+ return await this.request('GET', `/workspaces/${encodeURIComponent(workspace)}/projects`, null, true);
190
+ }
115
191
  }
116
192
  exports.default = ApiClient;
@@ -0,0 +1,280 @@
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
+ // @ts-ignore
7
+ const termkit_no_lazy_require_1 = __importDefault(require("terminal-kit/lib/termkit-no-lazy-require"));
8
+ const cfg_1 = __importDefault(require("../tunnel/cfg"));
9
+ const output_1 = __importDefault(require("../output"));
10
+ const client_1 = __importDefault(require("../api/client"));
11
+ const texts_1 = require("../texts");
12
+ const utils_1 = require("../utils");
13
+ const terminal = termkit_no_lazy_require_1.default.terminal;
14
+ // Handle Ctrl+C and ESC
15
+ terminal.on('key', (key) => {
16
+ if (key === 'CTRL_C' || key === 'ESCAPE') {
17
+ terminal.grabInput(false);
18
+ terminal('\nCanceled\n');
19
+ process.exit(0);
20
+ }
21
+ });
22
+ function getSecurityUrl(region, baseUrl) {
23
+ switch (region) {
24
+ case 'us':
25
+ return 'https://app.buddy.works/security';
26
+ case 'eu':
27
+ return 'https://eu.buddy.works/security';
28
+ case 'as':
29
+ return 'https://asia.buddy.works/security';
30
+ case 'onprem':
31
+ return `https://${baseUrl}/security`;
32
+ }
33
+ }
34
+ function getApiUrl(region, baseUrl) {
35
+ switch (region) {
36
+ case 'us':
37
+ return 'api.buddy.works';
38
+ case 'eu':
39
+ return 'api.eu.buddy.works';
40
+ case 'as':
41
+ return 'api.asia.buddy.works';
42
+ case 'onprem':
43
+ return `${baseUrl}/api`;
44
+ }
45
+ }
46
+ function normalizeBaseUrl(url) {
47
+ let normalized = url.trim();
48
+ normalized = normalized.replace(/^https?:\/\//i, '');
49
+ normalized = normalized.replace(/\/+$/, '');
50
+ return normalized;
51
+ }
52
+ async function selectRegion() {
53
+ terminal(`${texts_1.TXT_LOGIN_SELECT_REGION}\n`);
54
+ const items = ['US (default)', 'EU', 'Asia', 'On-premises'];
55
+ return new Promise((resolve) => {
56
+ terminal.singleColumnMenu(items, (error, response) => {
57
+ terminal('\n');
58
+ if (response.selectedIndex === 0)
59
+ resolve('us');
60
+ else if (response.selectedIndex === 1)
61
+ resolve('eu');
62
+ else if (response.selectedIndex === 2)
63
+ resolve('as');
64
+ else
65
+ resolve('onprem');
66
+ });
67
+ });
68
+ }
69
+ async function inputBaseUrl() {
70
+ terminal(`${texts_1.TXT_LOGIN_ENTER_BASE_URL} `);
71
+ return new Promise((resolve) => {
72
+ terminal.inputField((error, input) => {
73
+ terminal('\n');
74
+ const normalized = normalizeBaseUrl(input || '');
75
+ if (!normalized) {
76
+ output_1.default.exitError(texts_1.ERR_LOGIN_INVALID_BASE_URL);
77
+ }
78
+ resolve(normalized);
79
+ });
80
+ });
81
+ }
82
+ async function inputToken() {
83
+ terminal(`${texts_1.TXT_LOGIN_ENTER_TOKEN} `);
84
+ return new Promise((resolve) => {
85
+ terminal.inputField((error, input) => {
86
+ terminal('\n');
87
+ resolve(input?.trim() || '');
88
+ });
89
+ });
90
+ }
91
+ async function selectWorkspace(workspaces) {
92
+ terminal(`${texts_1.TXT_LOGIN_SELECT_WORKSPACE}\n`);
93
+ const items = workspaces.map((w) => `${w.name} (${w.domain})`);
94
+ return new Promise((resolve) => {
95
+ terminal.singleColumnMenu(items, (error, response) => {
96
+ terminal('\n');
97
+ resolve(workspaces[response.selectedIndex]);
98
+ });
99
+ });
100
+ }
101
+ async function selectProject(projects) {
102
+ return new Promise((resolve) => {
103
+ terminal(`${texts_1.TXT_LOGIN_SELECT_PROJECT}\n`);
104
+ const items = ["Don't set (default)", ...projects.map((p) => `${p.display_name} (${p.name})`)];
105
+ // Use nextTick to ensure text is rendered before menu
106
+ process.nextTick(() => {
107
+ terminal.singleColumnMenu(items, (error, response) => {
108
+ terminal('\n');
109
+ if (response.selectedIndex === 0) {
110
+ resolve(null);
111
+ }
112
+ else {
113
+ resolve(projects[response.selectedIndex - 1]);
114
+ }
115
+ });
116
+ });
117
+ });
118
+ }
119
+ async function fetchProjects(apiUrl, token, workspace) {
120
+ try {
121
+ const client = new client_1.default(new URL(`https://${apiUrl}`), token);
122
+ const response = await client.getProjects(workspace);
123
+ return response.projects || [];
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ }
129
+ async function verifyTokenAndGetWorkspaces(apiUrl, token) {
130
+ const client = new client_1.default(new URL(`https://${apiUrl}`), token);
131
+ const response = await client.getWorkspaces();
132
+ return response.workspaces;
133
+ }
134
+ const commandLogin = (0, utils_1.newCommand)('login', texts_1.DESC_COMMAND_LOGIN);
135
+ commandLogin.option('--token <token>', texts_1.OPTION_REST_API_TOKEN);
136
+ commandLogin.option('--api <url>', texts_1.OPTION_REST_API_ENDPOINT);
137
+ commandLogin.option('--region <region>', texts_1.OPTION_REST_API_REGION);
138
+ commandLogin.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
139
+ commandLogin.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
140
+ commandLogin.action(async (options) => {
141
+ try {
142
+ const hasToken = !!options.token;
143
+ const hasRegionOrApi = !!options.region || !!options.api;
144
+ // Non-interactive mode: token is required, region/api optional (defaults to us)
145
+ if (hasToken) {
146
+ const token = options.token;
147
+ let region;
148
+ let baseUrl;
149
+ let apiUrl;
150
+ if (options.api) {
151
+ region = 'onprem';
152
+ baseUrl = normalizeBaseUrl(options.api);
153
+ apiUrl = baseUrl;
154
+ }
155
+ else if (options.region?.toLowerCase() === 'eu') {
156
+ region = 'eu';
157
+ apiUrl = 'api.eu.buddy.works';
158
+ }
159
+ else if (options.region?.toLowerCase() === 'as') {
160
+ region = 'as';
161
+ apiUrl = 'api.asia.buddy.works';
162
+ }
163
+ else {
164
+ region = 'us';
165
+ apiUrl = 'api.buddy.works';
166
+ }
167
+ // Verify token
168
+ output_1.default.normal(texts_1.TXT_LOGIN_VERIFYING);
169
+ let workspaces;
170
+ try {
171
+ workspaces = await verifyTokenAndGetWorkspaces(apiUrl, token);
172
+ }
173
+ catch {
174
+ output_1.default.exitError(texts_1.ERR_LOGIN_INVALID_TOKEN);
175
+ }
176
+ if (!workspaces || workspaces.length === 0) {
177
+ output_1.default.exitError(texts_1.ERR_LOGIN_NO_WORKSPACES);
178
+ }
179
+ // Select workspace
180
+ let selectedWorkspace;
181
+ if (options.workspace) {
182
+ const found = workspaces.find((w) => w.domain === options.workspace);
183
+ if (!found) {
184
+ output_1.default.exitError(`Workspace "${options.workspace}" not found`);
185
+ }
186
+ selectedWorkspace = found;
187
+ }
188
+ else if (workspaces.length === 1) {
189
+ selectedWorkspace = workspaces[0];
190
+ }
191
+ else {
192
+ selectedWorkspace = await selectWorkspace(workspaces);
193
+ }
194
+ // Select project (optional)
195
+ let selectedProject = null;
196
+ if (options.project) {
197
+ const projects = await fetchProjects(apiUrl, token, selectedWorkspace.domain);
198
+ const found = projects.find((p) => p.name === options.project);
199
+ if (found) {
200
+ selectedProject = found;
201
+ }
202
+ }
203
+ // Save to config
204
+ cfg_1.default.clearLogin();
205
+ cfg_1.default.setToken(token);
206
+ cfg_1.default.setWorkspace(selectedWorkspace.domain);
207
+ if (selectedProject) {
208
+ cfg_1.default.setProject(selectedProject.name);
209
+ }
210
+ if (region === 'onprem' && baseUrl) {
211
+ cfg_1.default.setBaseUrl(apiUrl);
212
+ }
213
+ else {
214
+ cfg_1.default.setRegion(region.toUpperCase());
215
+ }
216
+ output_1.default.exitSuccess(texts_1.TXT_LOGIN_SUCCESS);
217
+ return;
218
+ }
219
+ // Interactive mode
220
+ // Step 1: Select region
221
+ const region = await selectRegion();
222
+ // Step 2: Get base URL if on-premises
223
+ let baseUrl;
224
+ if (region === 'onprem') {
225
+ baseUrl = await inputBaseUrl();
226
+ }
227
+ // Step 3: Show token URL and ask for token
228
+ const securityUrl = getSecurityUrl(region, baseUrl);
229
+ terminal.cyan(`${(0, texts_1.TXT_LOGIN_TOKEN_INFO)(securityUrl)}\n`);
230
+ const token = await inputToken();
231
+ if (!token) {
232
+ output_1.default.exitError(texts_1.ERR_LOGIN_INVALID_TOKEN);
233
+ }
234
+ // Step 4: Verify token by fetching workspaces
235
+ terminal(`${texts_1.TXT_LOGIN_VERIFYING}\n`);
236
+ const apiUrl = getApiUrl(region, baseUrl);
237
+ let workspaces;
238
+ try {
239
+ workspaces = await verifyTokenAndGetWorkspaces(apiUrl, token);
240
+ }
241
+ catch {
242
+ output_1.default.exitError(texts_1.ERR_LOGIN_INVALID_TOKEN);
243
+ }
244
+ if (!workspaces || workspaces.length === 0) {
245
+ output_1.default.exitError(texts_1.ERR_LOGIN_NO_WORKSPACES);
246
+ }
247
+ // Step 5: Select workspace (or auto-select if only one)
248
+ let selectedWorkspace;
249
+ if (workspaces.length === 1) {
250
+ selectedWorkspace = workspaces[0];
251
+ }
252
+ else {
253
+ selectedWorkspace = await selectWorkspace(workspaces);
254
+ }
255
+ // Step 6: Optionally select project
256
+ const projects = await fetchProjects(apiUrl, token, selectedWorkspace.domain);
257
+ let selectedProject = null;
258
+ if (projects.length > 0) {
259
+ selectedProject = await selectProject(projects);
260
+ }
261
+ // Step 7: Save to config
262
+ cfg_1.default.clearLogin();
263
+ cfg_1.default.setToken(token);
264
+ cfg_1.default.setWorkspace(selectedWorkspace.domain);
265
+ if (selectedProject) {
266
+ cfg_1.default.setProject(selectedProject.name);
267
+ }
268
+ if (region === 'onprem' && baseUrl) {
269
+ cfg_1.default.setBaseUrl(apiUrl);
270
+ }
271
+ else {
272
+ cfg_1.default.setRegion(region.toUpperCase());
273
+ }
274
+ output_1.default.exitSuccess(texts_1.TXT_LOGIN_SUCCESS);
275
+ }
276
+ catch (err) {
277
+ output_1.default.exitError(err);
278
+ }
279
+ });
280
+ exports.default = commandLogin;
@@ -0,0 +1,15 @@
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 texts_1 = require("../texts");
9
+ const utils_1 = require("../utils");
10
+ const commandLogout = (0, utils_1.newCommand)('logout', texts_1.DESC_COMMAND_LOGOUT);
11
+ commandLogout.action(async () => {
12
+ cfg_1.default.clearLogin();
13
+ output_1.default.exitSuccess(texts_1.TXT_LOGOUT_SUCCESS);
14
+ });
15
+ exports.default = commandLogout;
@@ -0,0 +1,259 @@
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 utils_1 = require("../../utils");
7
+ const texts_1 = require("../../texts");
8
+ const input_1 = __importDefault(require("../../input"));
9
+ const client_1 = __importDefault(require("../../api/client"));
10
+ const output_1 = __importDefault(require("../../output"));
11
+ const logger_1 = __importDefault(require("../../logger"));
12
+ const path_1 = require("path");
13
+ const uuid_1 = require("uuid");
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const promises_1 = __importDefault(require("stream/promises"));
16
+ const fflate_1 = require("fflate");
17
+ const commandPackageDownload = (0, utils_1.newCommand)('download', texts_1.DESC_COMMAND_PACKAGE_DOWNLOAD);
18
+ commandPackageDownload.alias('dd');
19
+ commandPackageDownload.option('-t, --token <token>', texts_1.OPTION_REST_API_TOKEN);
20
+ commandPackageDownload.option('--api <url>', texts_1.OPTION_REST_API_ENDPOINT);
21
+ commandPackageDownload.option('--region <region>', texts_1.OPTION_REST_API_REGION);
22
+ commandPackageDownload.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
23
+ commandPackageDownload.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
24
+ commandPackageDownload.option('-m, --merge', texts_1.OPTION_PACKAGE_DOWNLOAD_MERGE);
25
+ commandPackageDownload.option('-r, --replace', texts_1.OPTION_PACKAGE_DOWNLOAD_REPLACE);
26
+ commandPackageDownload.argument('<identifier>', texts_1.OPTION_PACKAGE_ID);
27
+ commandPackageDownload.argument('<directory>', texts_1.OPTION_PACKAGE_DOWNLOAD_PATH);
28
+ commandPackageDownload.action(async (id, path, options) => {
29
+ const token = input_1.default.restApiToken(options.token);
30
+ const baseUrl = input_1.default.restApiBaseUrl(options.api, options.region);
31
+ const workspace = input_1.default.restApiWorkspace(options.workspace);
32
+ const project = input_1.default.restApiProject(options.project, true);
33
+ const client = new client_1.default(baseUrl, token);
34
+ // eslint-disable-next-line prefer-const
35
+ let { identifier, version } = input_1.default.packageSplitIdentifier(id);
36
+ const data = await client.getPackageVersionByIdentifier(workspace, project, identifier, version);
37
+ if (!data || !data.domain) {
38
+ output_1.default.exitError(texts_1.ERR_WORKSPACE_NOT_FOUND);
39
+ }
40
+ if (project && !data.project_identifier) {
41
+ output_1.default.exitError(texts_1.ERR_PROJECT_NOT_FOUND);
42
+ }
43
+ const packageId = data.pkg_id;
44
+ if (!packageId) {
45
+ output_1.default.exitError(texts_1.ERR_PACKAGE_DOWNLOAD_NOT_FOUND);
46
+ }
47
+ let versionId = data.pkg_version_id;
48
+ if (version && !versionId) {
49
+ output_1.default.exitError(texts_1.ERR_PACKAGE_VERSION_NOT_FOUND);
50
+ }
51
+ if (!version || !versionId) {
52
+ const v = await client.getPackageLatest(workspace, packageId);
53
+ if (!v) {
54
+ output_1.default.exitError(texts_1.ERR_PACKAGE_VERSION_NOT_FOUND);
55
+ }
56
+ version = v.version;
57
+ versionId = v.id;
58
+ }
59
+ const dirPath = (0, path_1.resolve)(path);
60
+ const exists = fs_1.default.existsSync(dirPath);
61
+ if (!exists) {
62
+ try {
63
+ fs_1.default.mkdirSync(dirPath, {
64
+ recursive: true,
65
+ });
66
+ }
67
+ catch (err) {
68
+ logger_1.default.error(err);
69
+ output_1.default.exitError((0, texts_1.ERR_PACKAGE_DOWNLOAD_MKDIR)(dirPath));
70
+ }
71
+ }
72
+ else if (options.replace) {
73
+ try {
74
+ fs_1.default.rmSync(dirPath, {
75
+ recursive: true,
76
+ force: true,
77
+ });
78
+ fs_1.default.mkdirSync(dirPath, {
79
+ recursive: true,
80
+ });
81
+ }
82
+ catch (err) {
83
+ logger_1.default.error(err);
84
+ output_1.default.exitError((0, texts_1.ERR_PACKAGE_DOWNLOAD_REPLACE)(dirPath));
85
+ }
86
+ }
87
+ try {
88
+ const stat = fs_1.default.statSync(dirPath);
89
+ if (!stat.isDirectory()) {
90
+ output_1.default.exitError((0, texts_1.ERR_PACKAGE_DOWNLOAD_IS_FILE)(dirPath));
91
+ }
92
+ }
93
+ catch (err) {
94
+ logger_1.default.error(err);
95
+ output_1.default.exitError(texts_1.ERR_SWW);
96
+ }
97
+ let empty = true;
98
+ try {
99
+ const entries = fs_1.default.readdirSync(dirPath);
100
+ empty = !entries.length;
101
+ }
102
+ catch (err) {
103
+ logger_1.default.error(err);
104
+ output_1.default.exitError((0, texts_1.ERR_PACKAGE_DOWNLOAD_READDIR)(dirPath));
105
+ }
106
+ if (!empty && !options.merge) {
107
+ output_1.default.exitError((0, texts_1.ERR_PACKAGE_DOWNLOAD_NOT_EMPTY_DIR)(dirPath));
108
+ }
109
+ const zipPath = (0, path_1.join)((0, utils_1.getHomeDirectory)(), `${(0, uuid_1.v4)()}.zip`);
110
+ const clearZip = () => {
111
+ try {
112
+ fs_1.default.rmSync(zipPath);
113
+ }
114
+ catch {
115
+ // do nothing
116
+ }
117
+ };
118
+ output_1.default.normal(texts_1.TXT_PACKAGE_DOWNLOADING_ZIP);
119
+ const body = await client.downloadPackageVersion(workspace, packageId, versionId);
120
+ try {
121
+ await promises_1.default.pipeline(body, fs_1.default.createWriteStream(zipPath));
122
+ output_1.default.clearPreviousLine();
123
+ output_1.default.normal(texts_1.TXT_PACKAGE_DOWNLOADED_ZIP);
124
+ output_1.default.normal(texts_1.TXT_PACKAGE_UNZIPPING);
125
+ let count = 0;
126
+ await unzip(dirPath, zipPath, () => {
127
+ count += 1;
128
+ output_1.default.clearPreviousLine();
129
+ output_1.default.normal((0, texts_1.TXT_PACKAGE_UNZIPPING_COUNT)(count));
130
+ });
131
+ clearZip();
132
+ output_1.default.clearPreviousLine();
133
+ output_1.default.normal(texts_1.TXT_PACKAGE_UNZIPPED);
134
+ }
135
+ catch (err) {
136
+ logger_1.default.error(err);
137
+ clearZip();
138
+ output_1.default.exitError(texts_1.ERR_SWW);
139
+ }
140
+ output_1.default.exitSuccess((0, texts_1.TXT_PACKAGE_DOWNLOADED)(version, dirPath));
141
+ });
142
+ const unzip = (dirPath, zipPath, onFile) => {
143
+ return new Promise((resolve, reject) => {
144
+ let _startedFiles = 0;
145
+ let _finishedFiles = 0;
146
+ let _finishedStream = false;
147
+ let _finishedError = null;
148
+ let _calledResolve = false;
149
+ const rs = fs_1.default.createReadStream(zipPath);
150
+ const _finish = () => {
151
+ if (_finishedError || _finishedStream) {
152
+ try {
153
+ rs.removeAllListeners();
154
+ rs.close();
155
+ }
156
+ catch {
157
+ // do nothing
158
+ }
159
+ }
160
+ if (_calledResolve)
161
+ return;
162
+ if (_finishedError) {
163
+ _calledResolve = true;
164
+ reject(_finishedError);
165
+ return;
166
+ }
167
+ if (_finishedStream && _startedFiles === _finishedFiles) {
168
+ _calledResolve = true;
169
+ resolve();
170
+ }
171
+ };
172
+ const finishFile = (err, fws) => {
173
+ if (!_finishedError && err)
174
+ _finishedError = err;
175
+ _finishedFiles += 1;
176
+ if (fws) {
177
+ try {
178
+ fws.removeAllListeners();
179
+ fws.close();
180
+ }
181
+ catch {
182
+ // do nothing
183
+ }
184
+ }
185
+ onFile();
186
+ _finish();
187
+ };
188
+ const finishStream = (err) => {
189
+ if (!_finishedError && err)
190
+ _finishedError = err;
191
+ _finishedStream = true;
192
+ _finish();
193
+ };
194
+ const unzip = new fflate_1.Unzip(async (file) => {
195
+ if (_finishedError)
196
+ return;
197
+ _startedFiles += 1;
198
+ const fullPath = (0, path_1.join)(dirPath, file.name);
199
+ const parentPath = (0, path_1.dirname)(fullPath);
200
+ let fws;
201
+ try {
202
+ await fs_1.default.promises.rm(fullPath, {
203
+ recursive: true,
204
+ force: true,
205
+ });
206
+ }
207
+ catch {
208
+ // do nothing
209
+ }
210
+ try {
211
+ if (fullPath.endsWith('/')) {
212
+ await fs_1.default.promises.mkdir(fullPath, {
213
+ recursive: true,
214
+ });
215
+ finishFile();
216
+ return;
217
+ }
218
+ await fs_1.default.promises.mkdir(parentPath, {
219
+ recursive: true,
220
+ });
221
+ fws = fs_1.default.createWriteStream(fullPath, {
222
+ flags: 'w',
223
+ });
224
+ fws.on('error', (err) => {
225
+ finishFile(err, fws);
226
+ });
227
+ }
228
+ catch (err) {
229
+ finishFile(err, fws);
230
+ return;
231
+ }
232
+ file.ondata = (err, data, final) => {
233
+ if (_finishedError)
234
+ return;
235
+ if (err)
236
+ finishFile(err, fws);
237
+ else {
238
+ if (fws)
239
+ fws.write(data);
240
+ if (final)
241
+ finishFile(null, fws);
242
+ }
243
+ };
244
+ file.start();
245
+ });
246
+ unzip.register(fflate_1.AsyncUnzipInflate);
247
+ rs.on('data', (chunk) => {
248
+ unzip.push(chunk, false);
249
+ });
250
+ rs.on('error', (err) => {
251
+ finishStream(err);
252
+ });
253
+ rs.on('end', () => {
254
+ unzip.push(new Uint8Array(0), true);
255
+ finishStream();
256
+ });
257
+ });
258
+ };
259
+ exports.default = commandPackageDownload;