bdy 1.23.14-dev-target-commands → 1.23.14-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/README.md +181 -0
- package/distTs/package.json +3 -3
- package/distTs/src/agent/manager.js +9 -2
- package/distTs/src/api/client.js +22 -11
- package/distTs/src/command/api/request.js +5 -4
- package/distTs/src/command/environment/delete.js +2 -1
- package/distTs/src/command/environment/resolve.js +21 -4
- package/distTs/src/command/environment/update.js +2 -1
- package/distTs/src/command/pre.js +9 -8
- package/distTs/src/command/project/get.js +18 -0
- package/distTs/src/command/project/git/credential.js +6 -4
- package/distTs/src/command/project/set.js +31 -0
- package/distTs/src/command/sandbox/exec/command.js +1 -1
- package/distTs/src/command/sandbox/exec/logs.js +1 -1
- package/distTs/src/command/sandbox/get/yaml.js +30 -0
- package/distTs/src/command/target/delete.js +2 -1
- package/distTs/src/command/target/exec/command.js +3 -5
- package/distTs/src/command/target/scope.js +37 -7
- package/distTs/src/command/target/update.js +2 -1
- package/distTs/src/command/version.js +1 -1
- package/distTs/src/command/vt/scrape.js +193 -0
- package/distTs/src/index.js +14 -0
- package/distTs/src/logger.js +7 -1
- package/distTs/src/output/pipeline.js +5 -3
- package/distTs/src/output.js +12 -3
- package/distTs/src/texts.js +8 -9
- package/distTs/src/tunnel/server/sftp.js +24 -5
- package/distTs/src/tunnel/ssh/client.js +35 -4
- package/distTs/src/utils.js +49 -14
- package/package.json +3 -3
- package/distTs/detect-rules.json +0 -351
- package/distTs/src/command/pipeline/run/apply.js +0 -62
- package/distTs/src/command/yaml/actions/detect.js +0 -268
- package/distTs/src/command/yaml/actions/info.js +0 -56
- package/distTs/src/command/yaml/actions/list.js +0 -70
- package/distTs/src/command/yaml/actions/schema.js +0 -104
- package/distTs/src/command/yaml/actions.js +0 -13
- package/distTs/src/command/yaml/agents.js +0 -88
- package/distTs/src/command/yaml/cache.js +0 -98
- package/distTs/src/command/yaml/init.js +0 -110
- package/distTs/src/command/yaml/pipeline.js +0 -42
- package/distTs/src/command/yaml/render.js +0 -83
- package/distTs/src/command/yaml/schemaUtils.js +0 -139
- package/distTs/src/command/yaml/validate.js +0 -259
- package/distTs/src/command/yaml.js +0 -18
- package/distTs/src/diskCache.js +0 -82
package/distTs/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Buddy CLI (`bdy`)
|
|
2
|
+
|
|
3
|
+
Work seamlessly with [Buddy](https://buddy.works) from the command line.
|
|
4
|
+
|
|
5
|
+
`bdy` is Buddy's official command-line interface for managing CI/CD pipelines, agents, tests, artifacts, sandboxes, distributions, and more — straight from your terminal.
|
|
6
|
+
|
|
7
|
+
📚 Full documentation: https://buddy.works/docs/cli/getting-started
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### npm (recommended)
|
|
12
|
+
|
|
13
|
+
Requires Node.js 14+.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# macOS / Linux
|
|
17
|
+
sudo npm i -g bdy
|
|
18
|
+
|
|
19
|
+
# Windows
|
|
20
|
+
npm i -g bdy
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Homebrew (macOS)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
brew tap buddy/bdy
|
|
27
|
+
brew install bdy
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### APT (Linux x64 / ARM64)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
sudo apt-get update && sudo apt-get install -y software-properties-common
|
|
34
|
+
sudo gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/buddy.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys eb39332e766364ca6220e8dc631c5a16310cc0ad
|
|
35
|
+
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/buddy.gpg] https://es.buddy.works/bdy/apt-repo prod main" | sudo tee /etc/apt/sources.list.d/buddy.list > /dev/null
|
|
36
|
+
sudo apt-get update && sudo apt-get install -y bdy
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> On ARM64, replace `arch=amd64` with `arch=arm64` in the `echo` command above.
|
|
40
|
+
|
|
41
|
+
### Chocolatey (Windows)
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
choco install bdy --pre
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Direct download
|
|
48
|
+
|
|
49
|
+
Prebuilt binaries are available for macOS (Apple Silicon), Linux (x64/ARM64), and Windows (x64) on the [installation page](https://buddy.works/docs/cli/getting-started).
|
|
50
|
+
|
|
51
|
+
### Verify & update
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
bdy version # show current and latest version
|
|
55
|
+
sudo npm i -g bdy # update (or use your package manager's upgrade command)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Authentication
|
|
59
|
+
|
|
60
|
+
A [Buddy account](https://buddy.works) is required to use the CLI.
|
|
61
|
+
|
|
62
|
+
Log in interactively:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bdy login
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
New to Buddy? Create an account from the CLI:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bdy register
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Linking a project
|
|
75
|
+
|
|
76
|
+
Once logged in, link a directory to a Buddy project:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
mkdir my-proj
|
|
80
|
+
cd my-proj
|
|
81
|
+
bdy proj link
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
After linking, every `bdy` command run from this directory is executed against the linked project — no need to pass the project name each time.
|
|
85
|
+
|
|
86
|
+
Check who you're logged in as, or log out:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
bdy whoami
|
|
90
|
+
bdy logout
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Tokens & non-interactive login
|
|
94
|
+
|
|
95
|
+
For CI/CD pipelines and non-browser environments, authenticate with a [personal access token](https://buddy.works/docs/api/getting-started/oauth2/personal-access-token):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bdy login --token <token>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The login command accepts the following options:
|
|
102
|
+
|
|
103
|
+
| Option | Description | Environment variable |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `--token <token>` | Personal access token | `BUDDY_TOKEN` |
|
|
106
|
+
| `--api <url>` | API URL for On-Premises installations | `BUDDY_API_ENDPOINT` |
|
|
107
|
+
| `--region <region>` | Region: `us`, `eu`, or `as` | `BUDDY_REGION` |
|
|
108
|
+
| `-w, --workspace <domain>` | Workspace URL handle | `BUDDY_WORKSPACE` |
|
|
109
|
+
|
|
110
|
+
Each option can be supplied as a flag or via its environment variable — handy for pipelines and headless setups:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
export BUDDY_TOKEN=<token>
|
|
114
|
+
export BUDDY_WORKSPACE=<your-workspace>
|
|
115
|
+
bdy login
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Agent integration (plugin & skills)
|
|
119
|
+
|
|
120
|
+
The [Buddy Plugin](https://github.com/buddy/buddy-plugin) lets coding agents deploy applications, expose local services, and manage infrastructure on Buddy. It bundles a consolidated skill covering sandboxes, artifacts, tunnels, domains, distributions, and pipelines, plus two commands:
|
|
121
|
+
|
|
122
|
+
- `/deploy [name] [path]` — deploy a static site or server application
|
|
123
|
+
- `/expose [port]` — open a tunnel to a locally running service
|
|
124
|
+
|
|
125
|
+
Make sure the CLI is installed first (`sudo npm install -g bdy`), then:
|
|
126
|
+
|
|
127
|
+
**Claude Code** — install the plugin (commands + skills):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
claude plugin marketplace add buddy/buddy-plugin
|
|
131
|
+
claude plugin install buddy@buddy-plugin
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Other agents** — install the skills only:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx skills add buddy/buddy-plugin
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Then authenticate and link your project:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bdy login
|
|
144
|
+
cd your-project
|
|
145
|
+
bdy proj link
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The plugin auto-detects your project type: static sites generate versioned artifacts with public URLs, while server applications deploy to sandboxes with HTTPS endpoints. Monorepos can deploy multiple applications, each getting its own URL and endpoint.
|
|
149
|
+
|
|
150
|
+
## Commands
|
|
151
|
+
|
|
152
|
+
| Command | Description |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| `bdy login` / `sign-in` | Log in to Buddy |
|
|
155
|
+
| `bdy register` / `sign-up` | Register a new Buddy account |
|
|
156
|
+
| `bdy whoami` | Check login information |
|
|
157
|
+
| `bdy logout` | Log out from Buddy |
|
|
158
|
+
| `bdy workspace` / `ws` | Manage workspaces |
|
|
159
|
+
| `bdy project` / `proj` | Manage projects |
|
|
160
|
+
| `bdy pipeline` / `pip` | Interact with the pipeline service |
|
|
161
|
+
| `bdy artifact` / `art` | Interact with the artifact service |
|
|
162
|
+
| `bdy sandbox` / `sb` | Interact with sandboxes |
|
|
163
|
+
| `bdy agent` | Install and run `bdy` as an OS service (Windows, macOS, Linux) |
|
|
164
|
+
| `bdy tunnel` | Manage tunnel configuration |
|
|
165
|
+
| `bdy crawl` | Manage web crawls (Markdown, HTML, PNG/JPEG screenshots; suite linking) |
|
|
166
|
+
| `bdy tests` | Manage unit tests and visual regression (Storybook, URL captures, runners) |
|
|
167
|
+
| `bdy domain` | Manage zones used in distribution routes |
|
|
168
|
+
| `bdy distro` | Manage distributions |
|
|
169
|
+
| `bdy api` | Contact the Buddy API directly |
|
|
170
|
+
| `bdy update` / `version` | Show version and update the CLI |
|
|
171
|
+
|
|
172
|
+
Run any command with `-h` / `--help` for detailed usage:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
bdy --help
|
|
176
|
+
bdy pipeline --help
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Feedback
|
|
180
|
+
|
|
181
|
+
Found a bug or have a feature request? Let us know through [Buddy support](https://buddy.works/support) or the [documentation](https://buddy.works/docs/cli/getting-started).
|
package/distTs/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.23.14-dev
|
|
4
|
+
"version": "1.23.14-dev",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@fastify/cors": "11.2.0",
|
|
54
54
|
"@inquirer/prompts": "8.5.2",
|
|
55
55
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
56
|
-
"@puppeteer/browsers": "2.
|
|
56
|
+
"@puppeteer/browsers": "3.2.0",
|
|
57
57
|
"@scalar/json-magic": "0.12.16",
|
|
58
58
|
"@scalar/openapi-parser": "0.28.7",
|
|
59
59
|
"@scalar/openapi-types": "0.9.1",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"picomatch": "4.0.4",
|
|
82
82
|
"pino": "10.3.1",
|
|
83
83
|
"punycode": "2.3.1",
|
|
84
|
-
"puppeteer-core": "
|
|
84
|
+
"puppeteer-core": "25.6.0",
|
|
85
85
|
"range-parser": "1.2.1",
|
|
86
86
|
"socket.io-client": "4.8.3",
|
|
87
87
|
"ssh2": "1.17.0",
|
|
@@ -161,6 +161,10 @@ class AgentManagerClass {
|
|
|
161
161
|
}
|
|
162
162
|
const current = (0, utils_1.getVersion)();
|
|
163
163
|
const latest = await (0, utils_1.getLatestVersion)();
|
|
164
|
+
// Equality only, deliberately not "is latest newer": this runs on an explicit remote
|
|
165
|
+
// update action, whose meaning is "match what the channel publishes". Refusing an
|
|
166
|
+
// older latest would take away the only rollback the fleet has - moving the channel
|
|
167
|
+
// pointer back after a bad release.
|
|
164
168
|
if (!latest || latest === '0.0.0' || latest === current) {
|
|
165
169
|
logger_1.default.info((0, texts_1.LOG_AGENT_UPDATE_NOT_NEEDED)(current));
|
|
166
170
|
// Nothing to do is not a failure, and showing it as one would make an update of
|
|
@@ -174,12 +178,15 @@ class AgentManagerClass {
|
|
|
174
178
|
// - this is the value that stays behind if the restart never lands.
|
|
175
179
|
this.agent?.reportUpdateStatus(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.STARTED);
|
|
176
180
|
this.agent?.flushUpdateStatus();
|
|
177
|
-
|
|
181
|
+
// getLatestVersion already refuses a version it cannot parse, so this is belt and
|
|
182
|
+
// braces - kept because the download url is built from the triple, and shipping a
|
|
183
|
+
// malformed one would fail much further away from the cause.
|
|
184
|
+
const target = (0, utils_1.parseVersion)(latest);
|
|
178
185
|
if (!target) {
|
|
179
186
|
logger_1.default.error(texts_1.LOG_AGENT_UPDATE_FAILED);
|
|
180
187
|
return report(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.FAILED);
|
|
181
188
|
}
|
|
182
|
-
const swapped = await this.system.swapBinaryToVersion(target
|
|
189
|
+
const swapped = await this.system.swapBinaryToVersion(target.version);
|
|
183
190
|
if (!swapped) {
|
|
184
191
|
logger_1.default.error((0, texts_1.LOG_AGENT_UPDATE_BINARY_INVALID)(latest));
|
|
185
192
|
return report(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.FAILED);
|
package/distTs/src/api/client.js
CHANGED
|
@@ -86,9 +86,8 @@ class ApiClient {
|
|
|
86
86
|
}
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
89
|
-
async request(
|
|
90
|
-
|
|
91
|
-
headers = {};
|
|
89
|
+
async request(args) {
|
|
90
|
+
const { method = 'GET', path, query, body = null, headers = {}, parseResponseBody = false, rawResponseBody = false, rawResponse = false, httpUrlEncoded = false, tryRefreshingToken = true, } = args;
|
|
92
91
|
if (this.token && !headers.authorization) {
|
|
93
92
|
headers.authorization = `Bearer ${this.token}`;
|
|
94
93
|
}
|
|
@@ -176,15 +175,12 @@ class ApiClient {
|
|
|
176
175
|
const success = await this.tryRefreshToken();
|
|
177
176
|
if (success) {
|
|
178
177
|
delete headers.authorization;
|
|
178
|
+
// Retry with the complete original args (an explicit field list
|
|
179
|
+
// once silently dropped `query` here); only the mutated headers
|
|
180
|
+
// and the refresh guard are overridden.
|
|
179
181
|
return await this.request({
|
|
180
|
-
|
|
181
|
-
path,
|
|
182
|
-
body,
|
|
182
|
+
...args,
|
|
183
183
|
headers,
|
|
184
|
-
parseResponseBody,
|
|
185
|
-
rawResponseBody,
|
|
186
|
-
rawResponse,
|
|
187
|
-
httpUrlEncoded,
|
|
188
184
|
tryRefreshingToken: false,
|
|
189
185
|
});
|
|
190
186
|
}
|
|
@@ -253,6 +249,12 @@ class ApiClient {
|
|
|
253
249
|
}
|
|
254
250
|
}
|
|
255
251
|
else if (status === 204) {
|
|
252
|
+
// A no-content success still honors the raw-output contracts: callers
|
|
253
|
+
// stream the (empty) body instead of tripping over a null.
|
|
254
|
+
if (rawResponse)
|
|
255
|
+
return response;
|
|
256
|
+
if (rawResponseBody)
|
|
257
|
+
return responseBody;
|
|
256
258
|
await responseBody.dump();
|
|
257
259
|
return null;
|
|
258
260
|
}
|
|
@@ -1251,11 +1253,20 @@ class ApiClient {
|
|
|
1251
1253
|
query += '&';
|
|
1252
1254
|
query += encodeURIComponent(key) + '=' + encodeURIComponent(value);
|
|
1253
1255
|
});
|
|
1254
|
-
|
|
1256
|
+
const resolved = await this.request({
|
|
1255
1257
|
method: 'GET',
|
|
1256
1258
|
path: `/workspaces/${encodeURIComponent(workspace)}/identifiers${query}`,
|
|
1257
1259
|
parseResponseBody: true,
|
|
1258
1260
|
});
|
|
1261
|
+
// A queried project must itself resolve: for some resources
|
|
1262
|
+
// (environments, artifacts, distributions) the endpoint otherwise falls
|
|
1263
|
+
// back to the workspace resource of the same identifier, silently
|
|
1264
|
+
// answering for a different scope than asked (TARGET_API_REQUIREMENTS.md
|
|
1265
|
+
// p6). project_identifier is present whenever the project exists.
|
|
1266
|
+
if (params.project && !resolved.project_identifier) {
|
|
1267
|
+
throw new Error(texts_1.ERR_PROJECT_NOT_FOUND);
|
|
1268
|
+
}
|
|
1269
|
+
return resolved;
|
|
1259
1270
|
}
|
|
1260
1271
|
async resolveIdentifiers(workspace, params) {
|
|
1261
1272
|
let query = '';
|
|
@@ -159,14 +159,15 @@ const request = async (method, url, options) => {
|
|
|
159
159
|
output_1.default.exitNormal('');
|
|
160
160
|
}
|
|
161
161
|
else {
|
|
162
|
-
// setEncoding keeps multibyte characters intact across chunk boundaries
|
|
163
|
-
//
|
|
162
|
+
// setEncoding keeps multibyte characters intact across chunk boundaries;
|
|
163
|
+
// the response is a 1:1 payload, so it bypasses terminal-kit (whose
|
|
164
|
+
// formatting corrupts % and ^ sequences, common in URL-encoded data)
|
|
164
165
|
body.setEncoding('utf8');
|
|
165
166
|
body.on('data', (data) => {
|
|
166
|
-
output_1.default.
|
|
167
|
+
output_1.default.data(data, false);
|
|
167
168
|
});
|
|
168
169
|
body.on('close', () => {
|
|
169
|
-
output_1.default.
|
|
170
|
+
output_1.default.data('');
|
|
170
171
|
output_1.default.exitNormal('');
|
|
171
172
|
});
|
|
172
173
|
}
|
|
@@ -18,7 +18,8 @@ commandEnvironmentDelete.addHelpText('after', `\nEXAMPLES:${texts_1.EXAMPLE_ENVI
|
|
|
18
18
|
commandEnvironmentDelete.action(async (identifier, options) => {
|
|
19
19
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
20
20
|
const client = input_1.default.restApiTokenClient();
|
|
21
|
-
const environment_id = await (0, resolve_1.resolveEnvironmentId)(client, workspace, identifier, options
|
|
21
|
+
const environment_id = await (0, resolve_1.resolveEnvironmentId)(client, workspace, identifier, options,
|
|
22
|
+
/* strictProject */ true);
|
|
22
23
|
const confirmed = options.force ||
|
|
23
24
|
(await output_1.default.confirm((0, texts_1.TXT_ENVIRONMENT_DELETE_CONFIRM)(identifier)));
|
|
24
25
|
if (!confirmed)
|
|
@@ -11,12 +11,29 @@ const input_1 = __importDefault(require("../../input"));
|
|
|
11
11
|
// project context from flags/env -> /identifiers -> hash id, exiting on a
|
|
12
12
|
// miss. Without a project the identifier resolves as a workspace
|
|
13
13
|
// environment; with one, along the project line.
|
|
14
|
-
|
|
14
|
+
//
|
|
15
|
+
// strictProject is for mutating commands (update/delete): an explicit -p is
|
|
16
|
+
// a statement "the environment of this project", so the resolved environment
|
|
17
|
+
// must actually live there. Without it /identifiers silently falls back to
|
|
18
|
+
// the workspace environment of the same identifier and the write lands on a
|
|
19
|
+
// workspace-wide resource. Reads keep the fallback - that is how workspace
|
|
20
|
+
// environments stay addressable from inside a project context.
|
|
21
|
+
const resolveEnvironmentId = async (client, workspace, identifier, options, strictProject = false) => {
|
|
15
22
|
const project = input_1.default.restApiProject(options.project, true);
|
|
16
|
-
const
|
|
17
|
-
|
|
23
|
+
const resolved = await client.getEnvironmentByIdentifier(workspace, identifier, project);
|
|
24
|
+
// A nonexistent project is rejected by the API client
|
|
25
|
+
// (getResourceByIdentifier); here only the environment itself can miss.
|
|
26
|
+
if (!resolved.environment_id) {
|
|
18
27
|
output_1.default.exitError(texts_1.ERR_ENVIRONMENT_NOT_FOUND);
|
|
19
28
|
}
|
|
20
|
-
|
|
29
|
+
if (strictProject && options.project) {
|
|
30
|
+
const environment = await client.getEnvironment(workspace, resolved.environment_id);
|
|
31
|
+
// a workspace environment carries no project, so this also fails the
|
|
32
|
+
// workspace-fallback case
|
|
33
|
+
if (environment.project?.name !== resolved.project_identifier) {
|
|
34
|
+
output_1.default.exitError(texts_1.ERR_ENVIRONMENT_NOT_FOUND);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return resolved.environment_id;
|
|
21
38
|
};
|
|
22
39
|
exports.resolveEnvironmentId = resolveEnvironmentId;
|
|
@@ -52,7 +52,8 @@ commandEnvironmentUpdate.action(async (identifier, options) => {
|
|
|
52
52
|
output_1.default.exitError(texts_1.ERR_COMMAND_NO_UPDATE);
|
|
53
53
|
}
|
|
54
54
|
const client = input_1.default.restApiTokenClient();
|
|
55
|
-
const environment_id = await (0, resolve_1.resolveEnvironmentId)(client, workspace, identifier, options
|
|
55
|
+
const environment_id = await (0, resolve_1.resolveEnvironmentId)(client, workspace, identifier, options,
|
|
56
|
+
/* strictProject */ true);
|
|
56
57
|
const result = await client.updateEnvironment(workspace, environment_id, body);
|
|
57
58
|
output_1.default.exitSuccess((0, texts_1.TXT_ENVIRONMENT_UPDATED)(result.identifier, result.html_url));
|
|
58
59
|
});
|
|
@@ -46,14 +46,15 @@ const commandPre = async (_, command) => {
|
|
|
46
46
|
if (!output_1.default.isTTY() || command.hideVersionUpdate || ['json', 'jsonl'].includes(command.opts()?.format)) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// No truthiness guards on the versions: an empty or unparsable one is never newer
|
|
50
|
+
const latest = command.latestVersion || '';
|
|
51
|
+
const newCli = (0, utils_1.isVersionNewer)(latest, command.currentVersion || '');
|
|
52
|
+
const newAgent = !!command.agentStatus && (0, utils_1.isVersionNewer)(latest, command.agentStatus.version);
|
|
53
|
+
if ((0, utils_1.isDocker)() && newCli) {
|
|
54
|
+
output_1.default.newCliDockerVersion(latest);
|
|
55
|
+
}
|
|
56
|
+
else if (newCli) {
|
|
57
|
+
output_1.default.newCliVersion(latest);
|
|
57
58
|
}
|
|
58
59
|
else if (newAgent) {
|
|
59
60
|
output_1.default.newAgentVersion();
|
|
@@ -0,0 +1,18 @@
|
|
|
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 commandProjectGet = (0, utils_1.newCommand)('get', texts_1.DESC_COMMAND_PROJECT_GET);
|
|
11
|
+
commandProjectGet.action(async () => {
|
|
12
|
+
const project = cfg_1.default.getProject();
|
|
13
|
+
if (!project) {
|
|
14
|
+
output_1.default.exitError(texts_1.TXT_PROJECT_NONE);
|
|
15
|
+
}
|
|
16
|
+
output_1.default.exitNormal(project);
|
|
17
|
+
});
|
|
18
|
+
exports.default = commandProjectGet;
|
|
@@ -60,10 +60,12 @@ commandProjectGitCredential.action(async (action) => {
|
|
|
60
60
|
projects.projects[0].html_url.includes(input.host);
|
|
61
61
|
}
|
|
62
62
|
if (myHost) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
output_1.default.
|
|
66
|
-
output_1.default.
|
|
63
|
+
// git credential protocol output - a machine-read payload, so it
|
|
64
|
+
// bypasses terminal-kit formatting
|
|
65
|
+
output_1.default.data(`protocol=${input.protocol || 'https'}`);
|
|
66
|
+
output_1.default.data(`host=${input.host}`);
|
|
67
|
+
output_1.default.data(`username=${client.token}`);
|
|
68
|
+
output_1.default.data(`password=`);
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
catch {
|
|
@@ -0,0 +1,31 @@
|
|
|
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 input_1 = __importDefault(require("../../input"));
|
|
11
|
+
const commandProjectSet = (0, utils_1.newCommand)('set', texts_1.DESC_COMMAND_PROJECT_SET);
|
|
12
|
+
commandProjectSet.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
13
|
+
commandProjectSet.argument('[project]', texts_1.ARG_COMMAND_PROJECT_NAME);
|
|
14
|
+
commandProjectSet.action(async (project, options) => {
|
|
15
|
+
output_1.default.handleSignals();
|
|
16
|
+
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
17
|
+
const client = input_1.default.restApiTokenClient();
|
|
18
|
+
if (project) {
|
|
19
|
+
await client.getProject(workspace, project);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const response = await client.getProjects(workspace);
|
|
23
|
+
project = await output_1.default.selectProject(response.projects);
|
|
24
|
+
}
|
|
25
|
+
cfg_1.default.setProject(project);
|
|
26
|
+
if (!project)
|
|
27
|
+
output_1.default.exitSuccess(texts_1.TXT_PROJECT_SET_CLEARED);
|
|
28
|
+
else
|
|
29
|
+
output_1.default.exitSuccess((0, texts_1.TXT_PROJECT_SET_SUCCESS)(project));
|
|
30
|
+
});
|
|
31
|
+
exports.default = commandProjectSet;
|
|
@@ -0,0 +1,30 @@
|
|
|
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 output_1 = __importDefault(require("../../../output"));
|
|
10
|
+
const commandSandboxGetYaml = (0, utils_1.newCommand)('yaml', texts_1.DESC_COMMAND_SANDBOX_GET_YAML);
|
|
11
|
+
commandSandboxGetYaml.hideVersionUpdate = true;
|
|
12
|
+
commandSandboxGetYaml.alias('yml');
|
|
13
|
+
commandSandboxGetYaml.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
14
|
+
commandSandboxGetYaml.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
|
|
15
|
+
commandSandboxGetYaml.argument('<identifier>', texts_1.OPTION_SANDBOX_IDENTIFIER);
|
|
16
|
+
commandSandboxGetYaml.action(async (identifier, options) => {
|
|
17
|
+
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
18
|
+
const project = input_1.default.restApiProject(options.project);
|
|
19
|
+
const client = input_1.default.restApiTokenClient();
|
|
20
|
+
let result = await client.listSandboxes(workspace, project);
|
|
21
|
+
const sandboxes = result.sandboxes || [];
|
|
22
|
+
const found = sandboxes.find((s) => s.identifier === identifier);
|
|
23
|
+
if (!found) {
|
|
24
|
+
output_1.default.exitError(texts_1.ERR_SANDBOX_NOT_FOUND);
|
|
25
|
+
}
|
|
26
|
+
const sandboxId = found.id;
|
|
27
|
+
result = await client.getSandboxYaml(workspace, sandboxId);
|
|
28
|
+
output_1.default.exitNormal(Buffer.from(result.yaml, 'base64').toString('utf8'));
|
|
29
|
+
});
|
|
30
|
+
exports.default = commandSandboxGetYaml;
|
|
@@ -20,7 +20,8 @@ commandTargetDelete.addHelpText('after', `\nEXAMPLES:${texts_1.EXAMPLE_TARGET_DE
|
|
|
20
20
|
commandTargetDelete.action(async (identifier, options) => {
|
|
21
21
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
22
22
|
const client = input_1.default.restApiTokenClient();
|
|
23
|
-
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options
|
|
23
|
+
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options,
|
|
24
|
+
/* strictLine */ true);
|
|
24
25
|
const confirmed = options.force || (await output_1.default.confirm((0, texts_1.TXT_TARGET_DELETE_CONFIRM)(identifier)));
|
|
25
26
|
if (!confirmed)
|
|
26
27
|
output_1.default.exitNormal();
|
|
@@ -17,7 +17,6 @@ commandTargetExecCommand.option('-p, --project <name>', texts_1.OPTION_REST_API_
|
|
|
17
17
|
commandTargetExecCommand.option('--pipeline <identifier>', texts_1.OPTION_TARGET_PIPELINE);
|
|
18
18
|
commandTargetExecCommand.option('--environment <identifier>', texts_1.OPTION_TARGET_ENVIRONMENT);
|
|
19
19
|
commandTargetExecCommand.option('--async', texts_1.OPTION_TARGET_EXEC_ASYNC);
|
|
20
|
-
commandTargetExecCommand.option('--wait', texts_1.OPTION_TARGET_EXEC_WAIT);
|
|
21
20
|
commandTargetExecCommand.option('--file <path>', texts_1.OPTION_TARGET_EXEC_FILE);
|
|
22
21
|
commandTargetExecCommand.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
23
22
|
commandTargetExecCommand.argument('<identifier>', texts_1.OPTION_TARGET_IDENTIFIER);
|
|
@@ -42,15 +41,14 @@ commandTargetExecCommand.action(async (identifier, command, options) => {
|
|
|
42
41
|
}
|
|
43
42
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
44
43
|
const client = input_1.default.restApiTokenClient();
|
|
45
|
-
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options
|
|
44
|
+
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options,
|
|
45
|
+
/* strictLine */ true);
|
|
46
46
|
let exec = await client.executeTargetCommand(workspace, target_id, {
|
|
47
47
|
command: cmd,
|
|
48
48
|
});
|
|
49
49
|
const execId = exec.execution_id;
|
|
50
50
|
const json = options.format === 'json';
|
|
51
|
-
|
|
52
|
-
// when combined with --async.
|
|
53
|
-
if (options.async && !options.wait) {
|
|
51
|
+
if (options.async) {
|
|
54
52
|
if (json) {
|
|
55
53
|
output_1.default.json(exec);
|
|
56
54
|
}
|
|
@@ -10,14 +10,42 @@ const output_1 = __importDefault(require("../../output"));
|
|
|
10
10
|
const input_1 = __importDefault(require("../../input"));
|
|
11
11
|
// Shared resolve preamble for commands taking a target identifier: line
|
|
12
12
|
// context from flags/env -> /identifiers -> hash id, exiting on a miss.
|
|
13
|
-
|
|
13
|
+
//
|
|
14
|
+
// strictLine is for mutating commands (update/delete/exec command): an
|
|
15
|
+
// explicit line flag is a statement "the target of this project/pipeline/
|
|
16
|
+
// environment", yet /identifiers falls back to the workspace target of the
|
|
17
|
+
// same identifier when the requested line exists but does not hold it - the
|
|
18
|
+
// operation would silently land on a workspace-wide resource. Reads keep
|
|
19
|
+
// the fallback (workspace targets stay addressable from a line context).
|
|
20
|
+
const resolveTargetId = async (client, workspace, identifier, options, strictLine = false) => {
|
|
14
21
|
const project = input_1.default.restApiProject(options.project, true);
|
|
15
22
|
const line = input_1.default.targetLine(project, options.pipeline, options.environment);
|
|
16
|
-
const
|
|
17
|
-
if (!target_id) {
|
|
23
|
+
const resolved = await client.getTargetByIdentifier(workspace, identifier, line);
|
|
24
|
+
if (!resolved.target_id) {
|
|
18
25
|
output_1.default.exitError(texts_1.ERR_TARGET_NOT_FOUND);
|
|
19
26
|
}
|
|
20
|
-
|
|
27
|
+
if (strictLine) {
|
|
28
|
+
const requested = options.pipeline
|
|
29
|
+
? utils_1.TARGET_SCOPE.PIPELINE
|
|
30
|
+
: options.environment
|
|
31
|
+
? utils_1.TARGET_SCOPE.ENVIRONMENT
|
|
32
|
+
: options.project
|
|
33
|
+
? utils_1.TARGET_SCOPE.PROJECT
|
|
34
|
+
: null;
|
|
35
|
+
if (requested) {
|
|
36
|
+
const target = await client.getTarget(workspace, resolved.target_id);
|
|
37
|
+
const matches = requested === utils_1.TARGET_SCOPE.PIPELINE
|
|
38
|
+
? String(target.pipeline?.id) === String(resolved.pipeline_id)
|
|
39
|
+
: requested === utils_1.TARGET_SCOPE.ENVIRONMENT
|
|
40
|
+
? target.environment?.id === resolved.environment_id
|
|
41
|
+
: (0, exports.targetScopeOf)(target) === utils_1.TARGET_SCOPE.PROJECT &&
|
|
42
|
+
target.project?.name === resolved.project_identifier;
|
|
43
|
+
if (!matches) {
|
|
44
|
+
output_1.default.exitError(texts_1.ERR_TARGET_NOT_FOUND);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return resolved.target_id;
|
|
21
49
|
};
|
|
22
50
|
exports.resolveTargetId = resolveTargetId;
|
|
23
51
|
// Lines the CLI requests per log fetch.
|
|
@@ -95,10 +123,12 @@ const resolveTargetScope = async (client, workspace, project, scope, options) =>
|
|
|
95
123
|
};
|
|
96
124
|
if (project)
|
|
97
125
|
query.project = project;
|
|
98
|
-
const
|
|
99
|
-
|
|
126
|
+
const resolved = await client.getResourceByIdentifier(workspace, query);
|
|
127
|
+
// a nonexistent project is rejected by getResourceByIdentifier itself
|
|
128
|
+
if (!resolved.environment_id) {
|
|
100
129
|
output_1.default.exitError(texts_1.ERR_TARGET_ENVIRONMENT_NOT_FOUND);
|
|
101
|
-
|
|
130
|
+
}
|
|
131
|
+
params.environmentId = resolved.environment_id;
|
|
102
132
|
}
|
|
103
133
|
else if (scope === utils_1.TARGET_SCOPE.PROJECT) {
|
|
104
134
|
params.project = project;
|
|
@@ -21,7 +21,8 @@ commandTargetUpdate.action(async (identifier, options) => {
|
|
|
21
21
|
const yaml = input_1.default.restApiYaml(options.yaml);
|
|
22
22
|
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
23
23
|
const client = input_1.default.restApiTokenClient();
|
|
24
|
-
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options
|
|
24
|
+
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options,
|
|
25
|
+
/* strictLine */ true);
|
|
25
26
|
const body = {
|
|
26
27
|
yaml: Buffer.from(yaml, 'utf8').toString('base64'),
|
|
27
28
|
};
|
|
@@ -78,7 +78,7 @@ commandVersion.action(async () => {
|
|
|
78
78
|
const currVersion = commandVersion.currentVersion || '';
|
|
79
79
|
const lastVersion = commandVersion.latestVersion || '';
|
|
80
80
|
const env = commandVersion.env || '';
|
|
81
|
-
if (
|
|
81
|
+
if ((0, utils_1.isVersionNewer)(lastVersion, currVersion)) {
|
|
82
82
|
const platform = (0, utils_1.getPlatform)();
|
|
83
83
|
const isMac = platform === 'darwin';
|
|
84
84
|
const isWin = platform === 'win32';
|