bdy 1.23.13-dev-target-commands → 1.23.13-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/distTs/README.md +181 -0
  2. package/distTs/package.json +3 -5
  3. package/distTs/src/agent/manager.js +9 -2
  4. package/distTs/src/api/client.js +12 -222
  5. package/distTs/src/cliIndex.js +0 -4
  6. package/distTs/src/command/artifact/list.js +6 -2
  7. package/distTs/src/command/distro/list.js +0 -1
  8. package/distTs/src/command/distro/route/update.js +1 -1
  9. package/distTs/src/command/distro/update.js +9 -9
  10. package/distTs/src/command/pre.js +9 -8
  11. package/distTs/src/command/project/get.js +18 -0
  12. package/distTs/src/command/project/set.js +31 -0
  13. package/distTs/src/command/sandbox/get/yaml.js +30 -0
  14. package/distTs/src/command/version.js +1 -1
  15. package/distTs/src/command/vt/scrape.js +193 -0
  16. package/distTs/src/input.js +0 -65
  17. package/distTs/src/logger.js +7 -1
  18. package/distTs/src/output.js +2 -21
  19. package/distTs/src/texts.js +27 -252
  20. package/distTs/src/tunnel/server/sftp.js +24 -5
  21. package/distTs/src/tunnel/ssh/client.js +35 -4
  22. package/distTs/src/utils.js +50 -31
  23. package/package.json +3 -5
  24. package/distTs/detect-rules.json +0 -351
  25. package/distTs/src/command/environment/create.js +0 -70
  26. package/distTs/src/command/environment/delete.js +0 -29
  27. package/distTs/src/command/environment/get.js +0 -48
  28. package/distTs/src/command/environment/list.js +0 -45
  29. package/distTs/src/command/environment/resolve.js +0 -22
  30. package/distTs/src/command/environment/update.js +0 -59
  31. package/distTs/src/command/environment.js +0 -20
  32. package/distTs/src/command/pipeline/run/apply.js +0 -62
  33. package/distTs/src/command/target/create.js +0 -33
  34. package/distTs/src/command/target/delete.js +0 -30
  35. package/distTs/src/command/target/exec/command.js +0 -103
  36. package/distTs/src/command/target/exec/kill.js +0 -31
  37. package/distTs/src/command/target/exec/list.js +0 -53
  38. package/distTs/src/command/target/exec/logs.js +0 -27
  39. package/distTs/src/command/target/exec/status.js +0 -44
  40. package/distTs/src/command/target/exec.js +0 -24
  41. package/distTs/src/command/target/get.js +0 -63
  42. package/distTs/src/command/target/list.js +0 -49
  43. package/distTs/src/command/target/scope.js +0 -108
  44. package/distTs/src/command/target/update.js +0 -31
  45. package/distTs/src/command/target.js +0 -22
  46. package/distTs/src/command/yaml/actions/detect.js +0 -268
  47. package/distTs/src/command/yaml/actions/info.js +0 -56
  48. package/distTs/src/command/yaml/actions/list.js +0 -70
  49. package/distTs/src/command/yaml/actions/schema.js +0 -104
  50. package/distTs/src/command/yaml/actions.js +0 -13
  51. package/distTs/src/command/yaml/agents.js +0 -88
  52. package/distTs/src/command/yaml/cache.js +0 -98
  53. package/distTs/src/command/yaml/init.js +0 -110
  54. package/distTs/src/command/yaml/pipeline.js +0 -42
  55. package/distTs/src/command/yaml/render.js +0 -83
  56. package/distTs/src/command/yaml/schemaUtils.js +0 -139
  57. package/distTs/src/command/yaml/validate.js +0 -259
  58. package/distTs/src/command/yaml.js +0 -18
  59. package/distTs/src/diskCache.js +0 -82
@@ -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).
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.23.13-dev-target-commands",
4
+ "version": "1.23.13-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "homepage": "https://buddy.works/docs/cli",
@@ -27,8 +27,6 @@
27
27
  "e2e:tunnel": "vitest run --config e2e/vitest.config.ts e2e/modules/tunnel/",
28
28
  "e2e:agent": "vitest run --config e2e/vitest.config.ts e2e/modules/agent/",
29
29
  "e2e:distro": "vitest run --config e2e/vitest.config.ts e2e/modules/distro/",
30
- "e2e:target": "vitest run --config e2e/vitest.config.ts e2e/modules/target/",
31
- "e2e:environment": "vitest run --config e2e/vitest.config.ts e2e/modules/environment/",
32
30
  "unit": "vitest run --config vitest.config.ts"
33
31
  },
34
32
  "files": [
@@ -53,7 +51,7 @@
53
51
  "@fastify/cors": "11.2.0",
54
52
  "@inquirer/prompts": "8.5.2",
55
53
  "@modelcontextprotocol/sdk": "1.30.0",
56
- "@puppeteer/browsers": "2.13.0",
54
+ "@puppeteer/browsers": "3.2.0",
57
55
  "@scalar/json-magic": "0.12.16",
58
56
  "@scalar/openapi-parser": "0.28.7",
59
57
  "@scalar/openapi-types": "0.9.1",
@@ -81,7 +79,7 @@
81
79
  "picomatch": "4.0.4",
82
80
  "pino": "10.3.1",
83
81
  "punycode": "2.3.1",
84
- "puppeteer-core": "23.11.1",
82
+ "puppeteer-core": "25.6.0",
85
83
  "range-parser": "1.2.1",
86
84
  "socket.io-client": "4.8.3",
87
85
  "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
- const target = latest.match(/^\d+\.\d+\.\d+/);
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[0]);
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);
@@ -16,25 +16,6 @@ const cfg_1 = __importDefault(require("../tunnel/cfg"));
16
16
  * echo back the escaped form - callers comparing a response against the names
17
17
  * they passed in must apply the same mapping.
18
18
  */
19
- const RETRIABLE_NETWORK_ERROR = /ECONNREFUSED|ECONNRESET|ETIMEDOUT/;
20
- // undici headers can be string | string[]; callers want the first value.
21
- const headerValue = (h) => Array.isArray(h) ? h[0] : h;
22
- // How long to wait before retrying a transient status on an idempotent
23
- // request, or null when a retry makes no sense (non-retriable status, or a
24
- // rate limit window that resets too far in the future to wait for).
25
- const retryDelay = (status, attempt, rateLimitReset) => {
26
- if (status === 429) {
27
- if (!rateLimitReset)
28
- return null;
29
- const ms = parseInt(rateLimitReset, 10) * 1000 - Date.now();
30
- if (ms <= 0)
31
- return 1000;
32
- return ms > 15000 ? null : ms + 500;
33
- }
34
- return [502, 503, 504].includes(status) ? attempt * 1000 : null;
35
- };
36
- const RATE_LIMIT_WARN_THRESHOLD = 25;
37
- let rateLimitWarned = false;
38
19
  const formDataFileName = (name) => name.replace(/["\r\n]/g, (c) => ({ '"': '%22', '\r': '%0D', '\n': '%0A' })[c]);
39
20
  exports.formDataFileName = formDataFileName;
40
21
  class ApiClient {
@@ -131,41 +112,20 @@ class ApiClient {
131
112
  logger_1.default.debug(headers);
132
113
  logger_1.default.debug('BODY');
133
114
  logger_1.default.debug(bodyParsed);
134
- // Transient failures (rate limit with a nearby reset, 5xx, connection
135
- // resets) are retried with a backoff, but only for idempotent requests -
136
- // retrying a POST could duplicate the operation.
137
- const maxAttempts = method === 'GET' ? 3 : 1;
138
- for (let attempt = 1;; attempt += 1) {
139
- try {
140
- response = await this.client.request(opts);
141
- }
142
- catch (err) {
143
- logger_1.default.debug('API CLIENT RESPONSE ERROR');
144
- logger_1.default.debug(err);
145
- const retriable = RETRIABLE_NETWORK_ERROR.test(err.message);
146
- if (retriable && attempt < maxAttempts) {
147
- await (0, utils_1.sleep)(attempt * 1000);
148
- continue;
149
- }
150
- throw new Error(retriable ? texts_1.ERR_REST_API_CONNECT_ERROR : texts_1.ERR_REST_API_GENERAL_ERROR, { cause: err });
151
- }
152
- rateLimitReset = headerValue(response.headers['x-rate-limit-reset']);
115
+ try {
116
+ response = await this.client.request(opts);
117
+ rateLimitReset = response.headers['x-rate-limit-reset'];
153
118
  status = response.statusCode;
154
119
  responseBody = response.body;
155
120
  logger_1.default.debug(`API CLIENT RESPONSE STATUS: ${status}`);
156
- const delay = attempt < maxAttempts
157
- ? retryDelay(status, attempt, rateLimitReset)
158
- : null;
159
- if (delay === null)
160
- break;
161
- await responseBody.dump();
162
- logger_1.default.debug(`API CLIENT RETRY IN ${delay}ms`);
163
- await (0, utils_1.sleep)(delay);
164
121
  }
165
- this.warnRateLimitLow(headerValue(response.headers['x-rate-limit-remaining']), rateLimitReset);
166
- if (status === 429) {
167
- await responseBody.dump();
168
- throw new Error((0, texts_1.ERR_REST_API_RATE_LIMIT)(rateLimitReset));
122
+ catch (err) {
123
+ logger_1.default.debug('API CLIENT RESPONSE ERROR');
124
+ logger_1.default.debug(err);
125
+ if (/ECONNREFUSED|ECONNRESET|ETIMEDOUT/.test(err.message)) {
126
+ throw new Error(texts_1.ERR_REST_API_CONNECT_ERROR, { cause: err });
127
+ }
128
+ throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR, { cause: err });
169
129
  }
170
130
  if (status === 401) {
171
131
  await responseBody.dump();
@@ -225,7 +185,7 @@ class ApiClient {
225
185
  if (status === 404)
226
186
  throw new Error(texts_1.ERR_REST_API_RESOURCE_NOT_FOUND);
227
187
  else
228
- throw new Error((0, texts_1.ERR_REST_API_HTTP_ERROR)(status));
188
+ throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR);
229
189
  }
230
190
  if ([200, 201].includes(status)) {
231
191
  if (parseResponseBody) {
@@ -261,23 +221,9 @@ class ApiClient {
261
221
  }
262
222
  else {
263
223
  await responseBody.dump();
264
- throw new Error((0, texts_1.ERR_REST_API_HTTP_ERROR)(status));
224
+ throw new Error(texts_1.ERR_REST_API_GENERAL_ERROR);
265
225
  }
266
226
  }
267
- // Set by the CLI bootstrap (Input.restApiTokenClient); the transport layer
268
- // only detects the condition and leaves rendering to the caller, so
269
- // non-interactive consumers (MCP proxy) stay silent.
270
- static onRateLimitLow;
271
- // One-time notification when the rate limit window is nearly exhausted.
272
- warnRateLimitLow(remaining, reset) {
273
- if (rateLimitWarned || !remaining || !ApiClient.onRateLimitLow)
274
- return;
275
- const left = parseInt(remaining, 10);
276
- if (Number.isNaN(left) || left > RATE_LIMIT_WARN_THRESHOLD)
277
- return;
278
- rateLimitWarned = true;
279
- ApiClient.onRateLimitLow(remaining, reset);
280
- }
281
227
  async getPipelineRunActionLogs(workspace, project, pipelineId, executionId, actionExecutionId, offset, limit) {
282
228
  const query = {
283
229
  offset: String(offset),
@@ -617,162 +563,6 @@ class ApiClient {
617
563
  q.project = project;
618
564
  return await this.getResourceByIdentifier(workspace, q);
619
565
  }
620
- // Target methods
621
- targetScopeQuery(scope) {
622
- const query = {};
623
- if (scope.project)
624
- query.project_name = scope.project;
625
- if (scope.pipelineId)
626
- query.pipeline_id = scope.pipelineId.toString();
627
- if (scope.environmentId)
628
- query.environment_id = scope.environmentId;
629
- return query;
630
- }
631
- async getTargets(workspace, filters = {}) {
632
- return await this.request({
633
- method: 'GET',
634
- path: `/workspaces/${encodeURIComponent(workspace)}/targets`,
635
- query: this.targetScopeQuery(filters),
636
- parseResponseBody: true,
637
- });
638
- }
639
- async getTarget(workspace, targetId) {
640
- return await this.request({
641
- method: 'GET',
642
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/${encodeURIComponent(targetId)}`,
643
- parseResponseBody: true,
644
- });
645
- }
646
- async createTargetByYaml(workspace, scope, body) {
647
- return await this.request({
648
- method: 'POST',
649
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/yaml`,
650
- query: this.targetScopeQuery(scope),
651
- body,
652
- parseResponseBody: true,
653
- });
654
- }
655
- async updateTargetByYaml(workspace, targetId, body) {
656
- return await this.request({
657
- method: 'PATCH',
658
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/${encodeURIComponent(targetId)}/yaml`,
659
- body,
660
- parseResponseBody: true,
661
- });
662
- }
663
- async deleteTarget(workspace, targetId) {
664
- return await this.request({
665
- method: 'DELETE',
666
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/${encodeURIComponent(targetId)}`,
667
- parseResponseBody: false,
668
- });
669
- }
670
- async executeTargetCommand(workspace, targetId, body) {
671
- return await this.request({
672
- method: 'POST',
673
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/${encodeURIComponent(targetId)}/execs`,
674
- body,
675
- parseResponseBody: true,
676
- });
677
- }
678
- async getTargetExecs(workspace, targetId, limit) {
679
- const query = {};
680
- if (limit)
681
- query.limit = limit.toString();
682
- return await this.request({
683
- method: 'GET',
684
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/${encodeURIComponent(targetId)}/execs`,
685
- query,
686
- parseResponseBody: true,
687
- });
688
- }
689
- async getTargetExec(workspace, execId, wait) {
690
- const query = {};
691
- if (wait)
692
- query.wait = wait.toString();
693
- return await this.request({
694
- method: 'GET',
695
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/execs/${encodeURIComponent(execId)}`,
696
- query,
697
- parseResponseBody: true,
698
- });
699
- }
700
- async cancelTargetExec(workspace, execId) {
701
- return await this.request({
702
- method: 'DELETE',
703
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/execs/${encodeURIComponent(execId)}`,
704
- parseResponseBody: false,
705
- });
706
- }
707
- async getTargetExecLog(workspace, execId, offset, limit) {
708
- const query = {};
709
- if (offset)
710
- query.offset = offset.toString();
711
- if (limit)
712
- query.limit = limit.toString();
713
- return await this.request({
714
- method: 'GET',
715
- path: `/workspaces/${encodeURIComponent(workspace)}/targets/execs/${encodeURIComponent(execId)}/log`,
716
- query,
717
- parseResponseBody: true,
718
- });
719
- }
720
- async getTargetByIdentifier(workspace, identifier, line = {}) {
721
- return await this.getResourceByIdentifier(workspace, {
722
- target: identifier,
723
- ...line,
724
- });
725
- }
726
- // Environment methods
727
- async getEnvironments(workspace, project) {
728
- const query = {};
729
- if (project)
730
- query.project_name = project;
731
- return await this.request({
732
- method: 'GET',
733
- path: `/workspaces/${encodeURIComponent(workspace)}/environments`,
734
- query,
735
- parseResponseBody: true,
736
- });
737
- }
738
- async getEnvironment(workspace, environmentId) {
739
- return await this.request({
740
- method: 'GET',
741
- path: `/workspaces/${encodeURIComponent(workspace)}/environments/${encodeURIComponent(environmentId)}`,
742
- parseResponseBody: true,
743
- });
744
- }
745
- async createEnvironment(workspace, body) {
746
- return await this.request({
747
- method: 'POST',
748
- path: `/workspaces/${encodeURIComponent(workspace)}/environments`,
749
- body,
750
- parseResponseBody: true,
751
- });
752
- }
753
- async updateEnvironment(workspace, environmentId, body) {
754
- return await this.request({
755
- method: 'PATCH',
756
- path: `/workspaces/${encodeURIComponent(workspace)}/environments/${encodeURIComponent(environmentId)}`,
757
- body,
758
- parseResponseBody: true,
759
- });
760
- }
761
- async deleteEnvironment(workspace, environmentId) {
762
- return await this.request({
763
- method: 'DELETE',
764
- path: `/workspaces/${encodeURIComponent(workspace)}/environments/${encodeURIComponent(environmentId)}`,
765
- parseResponseBody: false,
766
- });
767
- }
768
- async getEnvironmentByIdentifier(workspace, identifier, project) {
769
- const q = {
770
- environment: identifier,
771
- };
772
- if (project)
773
- q.project = project;
774
- return await this.getResourceByIdentifier(workspace, q);
775
- }
776
566
  // Sandbox methods
777
567
  async createSandbox(workspace, project, body) {
778
568
  return await this.request({
@@ -24,8 +24,6 @@ const artifact_1 = __importDefault(require("./command/artifact"));
24
24
  const api_1 = __importDefault(require("./command/api"));
25
25
  const domain_1 = __importDefault(require("./command/domain"));
26
26
  const distro_1 = __importDefault(require("./command/distro"));
27
- const target_1 = __importDefault(require("./command/target"));
28
- const environment_1 = __importDefault(require("./command/environment"));
29
27
  const register_1 = __importDefault(require("./command/register"));
30
28
  const mcp_1 = __importDefault(require("./command/mcp"));
31
29
  function cliIndex() {
@@ -49,8 +47,6 @@ function cliIndex() {
49
47
  program.addCommand(sandbox_1.default);
50
48
  program.addCommand(domain_1.default);
51
49
  program.addCommand(distro_1.default);
52
- program.addCommand(target_1.default);
53
- program.addCommand(environment_1.default);
54
50
  program.addCommand(login_1.default);
55
51
  program.addCommand(register_1.default);
56
52
  program.addCommand(whoami_1.default);
@@ -18,12 +18,14 @@ commandArtifactList.action(async (options) => {
18
18
  const workspace = input_1.default.restApiWorkspace(options.workspace);
19
19
  const project = input_1.default.restApiProject(options.project, true);
20
20
  const scope = input_1.default.scope(project, options.scope);
21
- input_1.default.projectScopeNote(options, project, scope, 'artifacts');
22
21
  const client = input_1.default.restApiTokenClient();
23
22
  const response = await client.getArtifacts(workspace, scope === distro_1.SCOPE.PROJECT ? project : null);
24
23
  const artifacts = response.artifacts || [];
25
24
  if (options.format === 'json') {
26
- output_1.default.json(artifacts);
25
+ const filtered = scope === distro_1.SCOPE.PROJECT
26
+ ? artifacts.filter((a) => a.scope !== distro_1.SCOPE.WORKSPACE)
27
+ : artifacts;
28
+ output_1.default.json(filtered);
27
29
  }
28
30
  else {
29
31
  if (artifacts.length === 0) {
@@ -31,6 +33,8 @@ commandArtifactList.action(async (options) => {
31
33
  }
32
34
  const data = [['NAME', 'IDENTIFIER', 'TYPE', 'URL']];
33
35
  for (const artifact of artifacts) {
36
+ if (scope === distro_1.SCOPE.PROJECT && artifact.scope === distro_1.SCOPE.WORKSPACE)
37
+ continue;
34
38
  data.push([
35
39
  artifact.name,
36
40
  artifact.identifier,
@@ -19,7 +19,6 @@ commandDistroList.action(async (options) => {
19
19
  const workspace = input_1.default.restApiWorkspace(options.workspace);
20
20
  const project = input_1.default.restApiProject(options.project, true);
21
21
  const scope = input_1.default.scope(project, options.scope);
22
- input_1.default.projectScopeNote(options, project, scope, 'distributions');
23
22
  const client = input_1.default.restApiTokenClient();
24
23
  const r = await client.getDistributions(workspace, scope === distro_1.SCOPE.PROJECT ? project : null);
25
24
  const distributions = r.distributions || [];
@@ -77,7 +77,7 @@ commandDistroRouteUpdate.action(async (identifier, routeId, options) => {
77
77
  changed = true;
78
78
  }
79
79
  if (!changed) {
80
- output_1.default.exitError(texts_1.ERR_COMMAND_NO_UPDATE);
80
+ output_1.default.exitError(texts_1.ERR_COMMAND_ROUTE_NO_UPDATE);
81
81
  }
82
82
  const result = await client.updateRoute(workspace, scope === distro_1.SCOPE.PROJECT ? project : null, data.distribution_id, routeId, body);
83
83
  output_1.default.id(result.id);
@@ -27,6 +27,14 @@ commandDistroUpdate.action(async (identifier, options) => {
27
27
  const workspace = input_1.default.restApiWorkspace(options.workspace);
28
28
  const project = input_1.default.restApiProject(options.project, true);
29
29
  const scope = input_1.default.scope(project, options.scope);
30
+ const client = input_1.default.restApiTokenClient();
31
+ const data = await client.getDistributionByIdentifier(workspace, scope === distro_1.SCOPE.PROJECT ? project : null, identifier);
32
+ if (!data || !data.domain) {
33
+ output_1.default.exitError(texts_1.ERR_WORKSPACE_NOT_FOUND);
34
+ }
35
+ if (!data || !data.distribution_id) {
36
+ output_1.default.exitError(texts_1.ERR_COMMAND_DISTRO_NOT_FOUND);
37
+ }
30
38
  const body = {};
31
39
  let changed = false;
32
40
  if (options.name) {
@@ -46,15 +54,7 @@ commandDistroUpdate.action(async (identifier, options) => {
46
54
  changed = true;
47
55
  }
48
56
  if (!changed) {
49
- output_1.default.exitError(texts_1.ERR_COMMAND_NO_UPDATE);
50
- }
51
- const client = input_1.default.restApiTokenClient();
52
- const data = await client.getDistributionByIdentifier(workspace, scope === distro_1.SCOPE.PROJECT ? project : null, identifier);
53
- if (!data || !data.domain) {
54
- output_1.default.exitError(texts_1.ERR_WORKSPACE_NOT_FOUND);
55
- }
56
- if (!data || !data.distribution_id) {
57
- output_1.default.exitError(texts_1.ERR_COMMAND_DISTRO_NOT_FOUND);
57
+ output_1.default.exitError(texts_1.ERR_COMMAND_DISTRO_NO_UPDATE);
58
58
  }
59
59
  const result = await client.updateDistribution(workspace, scope === distro_1.SCOPE.PROJECT ? project : null, data.distribution_id, body);
60
60
  output_1.default.identifier(result.identifier);
@@ -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
- const newCli = !!command.latestVersion && command.currentVersion !== command.latestVersion;
50
- const newAgent = !!command.agentStatus && !!command.latestVersion &&
51
- command.agentStatus.version !== command.latestVersion;
52
- if ((0, utils_1.isDocker)() && newCli && command.latestVersion) {
53
- output_1.default.newCliDockerVersion(command.latestVersion);
54
- }
55
- else if (newCli && command.latestVersion) {
56
- output_1.default.newCliVersion(command.latestVersion);
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;