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.
- package/distTs/package.json +4 -3
- package/distTs/src/api/client.js +78 -2
- package/distTs/src/command/login.js +280 -0
- package/distTs/src/command/logout.js +15 -0
- package/distTs/src/command/package/download.js +259 -0
- package/distTs/src/command/package/publish.js +231 -0
- package/distTs/src/command/package.js +14 -0
- package/distTs/src/command/project/list.js +61 -0
- package/distTs/src/command/project/set.js +85 -0
- package/distTs/src/command/project.js +14 -0
- package/distTs/src/command/sandbox/command/kill.js +35 -0
- package/distTs/src/command/sandbox/command/list.js +54 -0
- package/distTs/src/command/sandbox/command/logs.js +133 -0
- package/distTs/src/command/sandbox/command/status.js +44 -0
- package/distTs/src/command/sandbox/command.js +18 -0
- package/distTs/src/command/sandbox/cp.js +123 -0
- package/distTs/src/command/sandbox/create.js +99 -0
- package/distTs/src/command/sandbox/destroy.js +35 -0
- package/distTs/src/command/sandbox/endpoint/add.js +91 -0
- package/distTs/src/command/sandbox/endpoint/delete.js +46 -0
- package/distTs/src/command/sandbox/endpoint/get.js +58 -0
- package/distTs/src/command/sandbox/endpoint/list.js +51 -0
- package/distTs/src/command/sandbox/endpoint/update.js +85 -0
- package/distTs/src/command/sandbox/endpoint.js +20 -0
- package/distTs/src/command/sandbox/exec.js +127 -0
- package/distTs/src/command/sandbox/get.js +51 -0
- package/distTs/src/command/sandbox/list.js +41 -0
- package/distTs/src/command/sandbox/restart.js +49 -0
- package/distTs/src/command/sandbox/snapshot/create.js +68 -0
- package/distTs/src/command/sandbox/snapshot/delete.js +42 -0
- package/distTs/src/command/sandbox/snapshot/get.js +54 -0
- package/distTs/src/command/sandbox/snapshot/list.js +48 -0
- package/distTs/src/command/sandbox/snapshot.js +18 -0
- package/distTs/src/command/sandbox/start.js +49 -0
- package/distTs/src/command/sandbox/status.js +35 -0
- package/distTs/src/command/sandbox/stop.js +49 -0
- package/distTs/src/command/sandbox.js +36 -0
- package/distTs/src/command/workspace/list.js +57 -0
- package/distTs/src/command/workspace/set.js +81 -0
- package/distTs/src/command/workspace.js +14 -0
- package/distTs/src/index.js +10 -0
- package/distTs/src/input.js +15 -4
- package/distTs/src/texts.js +155 -1
- package/distTs/src/tunnel/cfg.js +38 -0
- package/package.json +4 -3
- package/distTs/src/command/agent/standalone/kill.js +0 -22
- package/distTs/src/command/agent/standalone.js +0 -136
- package/distTs/src/command/vt/scrap.js +0 -193
package/distTs/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.16.11-
|
|
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",
|
package/distTs/src/api/client.js
CHANGED
|
@@ -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
|
-
|
|
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;
|