@laioutr/cli 0.3.2 → 0.4.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @laioutr/cli
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e5eb46a: Add a `deploy` command group for managing project deployments from the CLI. Every command targets a project as `--project org/project` and authenticates with an organization API key (`--key`, or the `LAIOUTR_API_KEY` env var).
8
+
9
+ - `laioutr deploy trigger` — trigger a production or named-preview deployment (`--preview <name>`). Waits for completion by default (`--no-wait` to return immediately) and can stream build logs with `--logs`.
10
+ - `laioutr deploy status` — show a deployment's status, environment, URL, and who created it.
11
+ - `laioutr deploy list` — list recent deployments for a project (`--limit`, default 20, max 100).
12
+ - `laioutr deploy logs` — fetch and follow a deployment's build logs.
13
+
3
14
  ## 0.3.1
4
15
 
5
16
  ### Patch Changes
package/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @laioutr/cli
20
20
  $ laioutr COMMAND
21
21
  running command...
22
22
  $ laioutr (--version)
23
- @laioutr/cli/0.3.2 darwin-arm64 node-v22.22.2
23
+ @laioutr/cli/0.4.0 linux-x64 node-v24.18.0
24
24
  $ laioutr --help [COMMAND]
25
25
  USAGE
26
26
  $ laioutr COMMAND
@@ -32,6 +32,10 @@ USAGE
32
32
 
33
33
  <!-- commands -->
34
34
  * [`laioutr app release`](#laioutr-app-release)
35
+ * [`laioutr deploy list`](#laioutr-deploy-list)
36
+ * [`laioutr deploy logs`](#laioutr-deploy-logs)
37
+ * [`laioutr deploy status`](#laioutr-deploy-status)
38
+ * [`laioutr deploy trigger`](#laioutr-deploy-trigger)
35
39
  * [`laioutr help [COMMAND]`](#laioutr-help-command)
36
40
  * [`laioutr plugins`](#laioutr-plugins)
37
41
  * [`laioutr plugins add PLUGIN`](#laioutr-plugins-add-plugin)
@@ -60,7 +64,8 @@ FLAGS
60
64
  <options: stable|testing>
61
65
  -k, --key=<value> (required) API key for authentication
62
66
  --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
63
- --cwd=<value> (required) [default: /Users/sl/src/laioutr/apps/cli] current working directory
67
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
68
+ directory
64
69
 
65
70
  DESCRIPTION
66
71
  Publish an app version to the Laioutr platform.
@@ -73,7 +78,132 @@ EXAMPLES
73
78
  LAIOUTR_API_KEY=orgKey_xxx laioutr app release
74
79
  ```
75
80
 
76
- _See code: [src/commands/app/release.ts](https://github.com/laioutr/laioutr/blob/v0.3.2/src/commands/app/release.ts)_
81
+ _See code: [src/commands/app/release.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/app/release.ts)_
82
+
83
+ ## `laioutr deploy list`
84
+
85
+ List recent deployments for a project.
86
+
87
+ ```
88
+ USAGE
89
+ $ laioutr deploy list --cockpitApiHost <value> --cwd <value> -p <value> -k <value> [--json] [--limit <value>]
90
+
91
+ FLAGS
92
+ -k, --key=<value> (required) Organization API key with project:deploy scope
93
+ -p, --project=<value> (required) Project identifier in org/project format
94
+ --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
95
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
96
+ directory
97
+ --limit=<value> [default: 20] Maximum number of deployments to list (1-100)
98
+
99
+ GLOBAL FLAGS
100
+ --json Format output as json.
101
+
102
+ DESCRIPTION
103
+ List recent deployments for a project.
104
+
105
+ EXAMPLES
106
+ $ laioutr deploy list --project org/project --key orgKey_xxx
107
+
108
+ $ laioutr deploy list --project org/project --key orgKey_xxx --limit 50
109
+ ```
110
+
111
+ _See code: [src/commands/deploy/list.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/deploy/list.ts)_
112
+
113
+ ## `laioutr deploy logs`
114
+
115
+ Fetch and display build logs for a deployment.
116
+
117
+ ```
118
+ USAGE
119
+ $ laioutr deploy logs --cockpitApiHost <value> --cwd <value> -p <value> -k <value> --deploymentId <value>
120
+ [--json]
121
+
122
+ FLAGS
123
+ -k, --key=<value> (required) Organization API key with project:deploy scope
124
+ -p, --project=<value> (required) Project identifier in org/project format
125
+ --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
126
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
127
+ directory
128
+ --deploymentId=<value> (required) Deployment ID
129
+
130
+ GLOBAL FLAGS
131
+ --json Format output as json.
132
+
133
+ DESCRIPTION
134
+ Fetch and display build logs for a deployment.
135
+
136
+ EXAMPLES
137
+ $ laioutr deploy logs --project org/project --key orgKey_xxx --deploymentId dep_abc123
138
+ ```
139
+
140
+ _See code: [src/commands/deploy/logs.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/deploy/logs.ts)_
141
+
142
+ ## `laioutr deploy status`
143
+
144
+ Show status of a deployment.
145
+
146
+ ```
147
+ USAGE
148
+ $ laioutr deploy status --cockpitApiHost <value> --cwd <value> -p <value> -k <value> --deploymentId <value>
149
+ [--json]
150
+
151
+ FLAGS
152
+ -k, --key=<value> (required) Organization API key with project:deploy scope
153
+ -p, --project=<value> (required) Project identifier in org/project format
154
+ --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
155
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
156
+ directory
157
+ --deploymentId=<value> (required) Deployment ID
158
+
159
+ GLOBAL FLAGS
160
+ --json Format output as json.
161
+
162
+ DESCRIPTION
163
+ Show status of a deployment.
164
+
165
+ EXAMPLES
166
+ $ laioutr deploy status --project org/project --key orgKey_xxx --deploymentId dep_abc123
167
+ ```
168
+
169
+ _See code: [src/commands/deploy/status.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/deploy/status.ts)_
170
+
171
+ ## `laioutr deploy trigger`
172
+
173
+ Trigger a project deployment.
174
+
175
+ ```
176
+ USAGE
177
+ $ laioutr deploy trigger --cockpitApiHost <value> --cwd <value> -p <value> -k <value> [--json] [--wait] [--logs]
178
+ [--preview <value>]
179
+
180
+ FLAGS
181
+ -k, --key=<value> (required) Organization API key with project:deploy scope
182
+ -p, --project=<value> (required) Project identifier in org/project format
183
+ --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
184
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
185
+ directory
186
+ --logs Stream build log output instead of spinner
187
+ --preview=<value> Create a preview deployment with optional name
188
+ --[no-]wait Wait for deployment to complete
189
+
190
+ GLOBAL FLAGS
191
+ --json Format output as json.
192
+
193
+ DESCRIPTION
194
+ Trigger a project deployment.
195
+
196
+ EXAMPLES
197
+ $ laioutr deploy trigger --project org/project --key orgKey_xxx
198
+
199
+ $ laioutr deploy trigger --project org/project --key orgKey_xxx --no-wait
200
+
201
+ $ laioutr deploy trigger --project org/project --key orgKey_xxx --logs
202
+
203
+ $ laioutr deploy trigger --project org/project --key orgKey_xxx --preview my-feature
204
+ ```
205
+
206
+ _See code: [src/commands/deploy/trigger.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/deploy/trigger.ts)_
77
207
 
78
208
  ## `laioutr help [COMMAND]`
79
209
 
@@ -398,7 +528,8 @@ FLAGS
398
528
  -p, --project=<value> (required) <organization slug>/<project slug>
399
529
  -s, --projectSecret=<value> (required) project secret
400
530
  --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
401
- --cwd=<value> (required) [default: /Users/sl/src/laioutr/apps/cli] current working directory
531
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
532
+ directory
402
533
 
403
534
  DESCRIPTION
404
535
  Fetches the laioutrrc.json of a project from the cockpit api.
@@ -423,7 +554,8 @@ FLAGS
423
554
  -p, --project=<value> (required) <organization slug>/<project slug>
424
555
  -s, --projectSecret=<value> (required) project secret
425
556
  --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
426
- --cwd=<value> (required) [default: /Users/sl/src/laioutr/apps/cli] current working directory
557
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working
558
+ directory
427
559
 
428
560
  DESCRIPTION
429
561
  Fetches the laioutrrc.json of a project from the cockpit api.
@@ -435,7 +567,7 @@ EXAMPLES
435
567
  $ laioutr rc fetch
436
568
  ```
437
569
 
438
- _See code: [src/commands/rc/fetch.ts](https://github.com/laioutr/laioutr/blob/v0.3.2/src/commands/rc/fetch.ts)_
570
+ _See code: [src/commands/rc/fetch.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/rc/fetch.ts)_
439
571
 
440
572
  ## `laioutr rc update [FILENAME]`
441
573
 
@@ -447,7 +579,7 @@ USAGE
447
579
 
448
580
  FLAGS
449
581
  --cockpitApiHost=<value> (required) [default: https://cockpit.laioutr.cloud] cockpit api host
450
- --cwd=<value> (required) [default: /Users/sl/src/laioutr/apps/cli] current working directory
582
+ --cwd=<value> (required) [default: /home/runner/work/laioutr/laioutr/apps/cli] current working directory
451
583
 
452
584
  DESCRIPTION
453
585
  Updates an existing laioutrrc.json file with the latest from the cockpit api.
@@ -456,5 +588,5 @@ EXAMPLES
456
588
  $ laioutr rc update
457
589
  ```
458
590
 
459
- _See code: [src/commands/rc/update.ts](https://github.com/laioutr/laioutr/blob/v0.3.2/src/commands/rc/update.ts)_
591
+ _See code: [src/commands/rc/update.ts](https://github.com/laioutr/laioutr/blob/v0.4.0/src/commands/rc/update.ts)_
460
592
  <!-- commandsstop -->
@@ -0,0 +1,65 @@
1
+ export interface DeployApiOptions {
2
+ cockpitApiHost: string;
3
+ apiKey: string;
4
+ organizationSlug: string;
5
+ projectSlug: string;
6
+ }
7
+ export interface TriggerOptions extends DeployApiOptions {
8
+ previewName?: string;
9
+ }
10
+ export declare function triggerDeployment(options: TriggerOptions): Promise<{
11
+ deploymentId: string;
12
+ status: string;
13
+ environment: string;
14
+ previewName: string | null;
15
+ }>;
16
+ export declare function fetchDeploymentStatus(options: DeployApiOptions & {
17
+ deploymentId: string;
18
+ }): Promise<{
19
+ deploymentId: string;
20
+ status: string;
21
+ environment: string;
22
+ previewName: string | null;
23
+ url: string | null;
24
+ lastError: string | null;
25
+ createdAt: string;
26
+ stoppedAt: string | null;
27
+ attribution: {
28
+ principalType: "user" | "api-key" | "system";
29
+ displayName: string;
30
+ email: string | null;
31
+ agentName: string | null;
32
+ redacted: boolean;
33
+ } | null;
34
+ }>;
35
+ export declare function fetchDeployments(options: DeployApiOptions & {
36
+ limit?: number;
37
+ }): Promise<{
38
+ deployments: {
39
+ deploymentId: string;
40
+ status: string;
41
+ environment: string;
42
+ previewName: string | null;
43
+ url: string | null;
44
+ createdAt: string;
45
+ stoppedAt: string | null;
46
+ attribution: {
47
+ principalType: "user" | "api-key" | "system";
48
+ displayName: string;
49
+ email: string | null;
50
+ agentName: string | null;
51
+ redacted: boolean;
52
+ } | null;
53
+ }[];
54
+ }>;
55
+ export declare function fetchDeploymentLogs(options: DeployApiOptions & {
56
+ deploymentId: string;
57
+ }): Promise<{
58
+ lines: {
59
+ text: string;
60
+ level?: "error" | "warning" | null | undefined;
61
+ timestamp?: number | null | undefined;
62
+ }[] | null;
63
+ supported: boolean;
64
+ deploymentStatus: string;
65
+ }>;
@@ -0,0 +1,34 @@
1
+ import { postJson } from './http.js';
2
+ import { deploymentLogsResponseSchema, deploymentsListResponseSchema, deploymentStatusResponseSchema, deployTriggerResponseSchema, } from '../deploy/validation.js';
3
+ export async function triggerDeployment(options) {
4
+ const data = await postJson(`${options.cockpitApiHost}/api/v1/project/deploy`, {
5
+ organizationSlug: options.organizationSlug,
6
+ projectSlug: options.projectSlug,
7
+ previewName: options.previewName,
8
+ }, { apiKey: options.apiKey });
9
+ return deployTriggerResponseSchema.parse(data);
10
+ }
11
+ export async function fetchDeploymentStatus(options) {
12
+ const data = await postJson(`${options.cockpitApiHost}/api/v1/project/deployment-status`, {
13
+ organizationSlug: options.organizationSlug,
14
+ projectSlug: options.projectSlug,
15
+ deploymentId: options.deploymentId,
16
+ }, { apiKey: options.apiKey });
17
+ return deploymentStatusResponseSchema.parse(data);
18
+ }
19
+ export async function fetchDeployments(options) {
20
+ const data = await postJson(`${options.cockpitApiHost}/api/v1/project/deployments`, {
21
+ organizationSlug: options.organizationSlug,
22
+ projectSlug: options.projectSlug,
23
+ limit: options.limit,
24
+ }, { apiKey: options.apiKey });
25
+ return deploymentsListResponseSchema.parse(data);
26
+ }
27
+ export async function fetchDeploymentLogs(options) {
28
+ const data = await postJson(`${options.cockpitApiHost}/api/v1/project/deployment-logs`, {
29
+ organizationSlug: options.organizationSlug,
30
+ projectSlug: options.projectSlug,
31
+ deploymentId: options.deploymentId,
32
+ }, { apiKey: options.apiKey });
33
+ return deploymentLogsResponseSchema.parse(data);
34
+ }
@@ -1,35 +1,19 @@
1
- /* eslint-disable no-console */
2
1
  import { z } from 'zod/v4';
2
+ import { postJson } from './http.js';
3
+ import { parseProjectSlug } from '../project.js';
3
4
  const apiResponseSchema = z.object({
4
5
  content: z.string(),
5
6
  });
6
7
  export const fetchRc = async (flags) => {
7
- const url = `${flags.cockpitApiHost}/api/v1/project/fetch-rc`;
8
- const [organizationSlug, projectSlug] = flags.project.replace('/p/', '').split('/');
9
- const response = await fetch(url, {
10
- body: JSON.stringify({
11
- environmentName: flags.environmentName,
12
- organizationSlug,
13
- projectSecret: flags.projectSecret,
14
- projectSlug,
15
- }),
16
- headers: {
17
- 'Content-Type': 'application/json',
18
- },
19
- method: 'POST',
20
- });
21
- const body = await response.text();
22
- let data = null;
23
- if (response.status !== 200) {
24
- throw new Error(`Failed to fetch RC file. Status: ${response.status} ${response.statusText}. Body: ${body}`);
25
- }
26
- try {
27
- data = JSON.parse(body);
8
+ const parsed = parseProjectSlug(flags.project);
9
+ if (!parsed) {
10
+ throw new Error(`Invalid project "${flags.project}" — expected <organization slug>/<project slug>.`);
28
11
  }
29
- catch (error) {
30
- console.error(error, response);
31
- throw new Error('Failed to parse RC file. See error message above.');
32
- }
33
- const parsed = apiResponseSchema.parse(data);
34
- return parsed.content;
12
+ const data = await postJson(`${flags.cockpitApiHost}/api/v1/project/fetch-rc`, {
13
+ environmentName: flags.environmentName,
14
+ organizationSlug: parsed.organizationSlug,
15
+ projectSecret: flags.projectSecret,
16
+ projectSlug: parsed.projectSlug,
17
+ });
18
+ return apiResponseSchema.parse(data).content;
35
19
  };
@@ -0,0 +1,14 @@
1
+ export interface PostJsonOptions {
2
+ /** When set, sent as an `Authorization: Bearer <apiKey>` header. */
3
+ apiKey?: string;
4
+ /** Extra headers, merged over the defaults (`Content-Type` + auth). */
5
+ headers?: Record<string, string>;
6
+ }
7
+ /**
8
+ * POSTs a JSON body to `url` and returns the parsed JSON response.
9
+ *
10
+ * Throws if the response status is not 200 (the message carries the status and
11
+ * raw body) or if the body is not valid JSON. Callers validate the returned
12
+ * shape themselves — typically with a zod schema — so this returns `unknown`.
13
+ */
14
+ export declare function postJson(url: string, body: unknown, options?: PostJsonOptions): Promise<unknown>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * POSTs a JSON body to `url` and returns the parsed JSON response.
3
+ *
4
+ * Throws if the response status is not 200 (the message carries the status and
5
+ * raw body) or if the body is not valid JSON. Callers validate the returned
6
+ * shape themselves — typically with a zod schema — so this returns `unknown`.
7
+ */
8
+ export async function postJson(url, body, options = {}) {
9
+ const response = await fetch(url, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ ...(options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}),
14
+ ...options.headers,
15
+ },
16
+ body: JSON.stringify(body),
17
+ });
18
+ const text = await response.text();
19
+ if (response.status !== 200) {
20
+ throw new Error(`Request failed (${response.status}): ${text}`);
21
+ }
22
+ try {
23
+ return JSON.parse(text);
24
+ }
25
+ catch {
26
+ throw new Error(`Failed to parse JSON response from ${url}`);
27
+ }
28
+ }
@@ -1,27 +1,16 @@
1
1
  import { z } from 'zod/v4';
2
+ import { postJson } from './http.js';
2
3
  const releaseResponseSchema = z.object({
3
4
  message: z.string(),
4
5
  versionId: z.string().optional(),
5
6
  });
6
7
  export const releaseAppVersion = async (options) => {
7
- const url = `${options.cockpitApiHost}/api/v1/app/release`;
8
- const response = await fetch(url, {
9
- method: 'POST',
10
- headers: {
11
- 'Content-Type': 'application/json',
12
- Authorization: `Bearer ${options.apiKey}`,
13
- },
14
- body: JSON.stringify({
15
- packageName: options.packageName,
16
- version: options.version,
17
- configSchema: options.configSchema,
18
- peerDependencies: options.peerDependencies,
19
- channel: options.channel,
20
- }),
21
- });
22
- const body = await response.text();
23
- if (response.status !== 200) {
24
- throw new Error(`Release failed (${response.status}): ${body}`);
25
- }
26
- return releaseResponseSchema.parse(JSON.parse(body));
8
+ const data = await postJson(`${options.cockpitApiHost}/api/v1/app/release`, {
9
+ packageName: options.packageName,
10
+ version: options.version,
11
+ configSchema: options.configSchema,
12
+ peerDependencies: options.peerDependencies,
13
+ channel: options.channel,
14
+ }, { apiKey: options.apiKey });
15
+ return releaseResponseSchema.parse(data);
27
16
  };
@@ -0,0 +1,31 @@
1
+ import DeployBaseCommand from '../../deploy-base-command.js';
2
+ export default class DeployList extends DeployBaseCommand {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ project: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ key: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ cockpitApiHost: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ cwd: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<{
14
+ deployments: {
15
+ deploymentId: string;
16
+ status: string;
17
+ environment: string;
18
+ previewName: string | null;
19
+ url: string | null;
20
+ createdAt: string;
21
+ stoppedAt: string | null;
22
+ attribution: {
23
+ principalType: "user" | "api-key" | "system";
24
+ displayName: string;
25
+ email: string | null;
26
+ agentName: string | null;
27
+ redacted: boolean;
28
+ } | null;
29
+ }[];
30
+ }>;
31
+ }
@@ -0,0 +1,54 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { makeTable } from '@oclif/table';
3
+ import { fetchDeployments } from '../../api/deploy.js';
4
+ import { formatAttribution } from '../../deploy/attribution.js';
5
+ import DeployBaseCommand from '../../deploy-base-command.js';
6
+ export default class DeployList extends DeployBaseCommand {
7
+ static description = 'List recent deployments for a project.';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> deploy list --project org/project --key orgKey_xxx',
11
+ '<%= config.bin %> deploy list --project org/project --key orgKey_xxx --limit 50',
12
+ ];
13
+ static flags = {
14
+ ...DeployBaseCommand.flags,
15
+ limit: Flags.integer({
16
+ default: 20,
17
+ description: 'Maximum number of deployments to list (1-100)',
18
+ max: 100,
19
+ min: 1,
20
+ }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(DeployList);
24
+ const { organizationSlug, projectSlug } = this.parseProject(flags.project);
25
+ const result = await fetchDeployments({
26
+ cockpitApiHost: flags.cockpitApiHost,
27
+ apiKey: flags.key,
28
+ organizationSlug,
29
+ projectSlug,
30
+ limit: flags.limit,
31
+ });
32
+ if (result.deployments.length === 0) {
33
+ this.log('No deployments found.');
34
+ return result;
35
+ }
36
+ const rows = result.deployments.map((d) => ({
37
+ ID: d.deploymentId,
38
+ Type: d.environment,
39
+ Preview: d.previewName ?? '—',
40
+ Status: d.status,
41
+ URL: d.url ?? '—',
42
+ 'Created by': formatAttribution(d.attribution),
43
+ }));
44
+ this.log(makeTable({
45
+ data: rows,
46
+ // Keep our exact header casing (ID / URL / Created by) instead of the
47
+ // default case-formatter, and truncate the middle of long cells (URLs)
48
+ // so both ends stay visible.
49
+ headerOptions: { formatter: (header) => header },
50
+ overflow: 'truncate-middle',
51
+ }));
52
+ return result;
53
+ }
54
+ }
@@ -0,0 +1,22 @@
1
+ import DeployBaseCommand from '../../deploy-base-command.js';
2
+ export default class DeployLogs extends DeployBaseCommand {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ deploymentId: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ project: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ key: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ cockpitApiHost: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ cwd: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<{
14
+ lines: {
15
+ text: string;
16
+ level?: "error" | "warning" | null | undefined;
17
+ timestamp?: number | null | undefined;
18
+ }[] | null;
19
+ supported: boolean;
20
+ deploymentStatus: string;
21
+ }>;
22
+ }
@@ -0,0 +1,51 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { fetchDeploymentLogs } from '../../api/deploy.js';
3
+ import { LogRenderer } from '../../deploy/log-renderer.js';
4
+ import { isTerminalStatus, POLL_INTERVAL_MS, sleep } from '../../deploy/polling.js';
5
+ import DeployBaseCommand from '../../deploy-base-command.js';
6
+ export default class DeployLogs extends DeployBaseCommand {
7
+ static description = 'Fetch and display build logs for a deployment.';
8
+ static enableJsonFlag = true;
9
+ static examples = ['<%= config.bin %> deploy logs --project org/project --key orgKey_xxx --deploymentId dep_abc123'];
10
+ static flags = {
11
+ ...DeployBaseCommand.flags,
12
+ deploymentId: Flags.string({
13
+ description: 'Deployment ID',
14
+ required: true,
15
+ }),
16
+ };
17
+ async run() {
18
+ const { flags } = await this.parse(DeployLogs);
19
+ const { organizationSlug, projectSlug } = this.parseProject(flags.project);
20
+ const apiOptions = {
21
+ cockpitApiHost: flags.cockpitApiHost,
22
+ apiKey: flags.key,
23
+ organizationSlug,
24
+ projectSlug,
25
+ deploymentId: flags.deploymentId,
26
+ };
27
+ // Initial fetch
28
+ const initial = await fetchDeploymentLogs(apiOptions);
29
+ if (!initial.supported) {
30
+ this.log("Build logs are not available for this deployment's hosting provider.");
31
+ return initial;
32
+ }
33
+ const isFollowing = !isTerminalStatus(initial.deploymentStatus);
34
+ this.log(`Build log for deployment ${flags.deploymentId}${isFollowing ? ' (following)' : ''}`);
35
+ this.log('');
36
+ const renderer = new LogRenderer();
37
+ renderer.render(initial.lines, (msg) => this.log(msg));
38
+ if (!isFollowing) {
39
+ return initial;
40
+ }
41
+ // Follow mode — poll until terminal
42
+ while (true) {
43
+ await sleep(POLL_INTERVAL_MS);
44
+ const result = await fetchDeploymentLogs(apiOptions);
45
+ renderer.render(result.lines, (msg) => this.log(msg));
46
+ if (isTerminalStatus(result.deploymentStatus)) {
47
+ return result;
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,30 @@
1
+ import DeployBaseCommand from '../../deploy-base-command.js';
2
+ export default class DeployStatus extends DeployBaseCommand {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ deploymentId: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ project: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ key: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ cockpitApiHost: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ cwd: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<{
14
+ deploymentId: string;
15
+ status: string;
16
+ environment: string;
17
+ previewName: string | null;
18
+ url: string | null;
19
+ lastError: string | null;
20
+ createdAt: string;
21
+ stoppedAt: string | null;
22
+ attribution: {
23
+ principalType: "user" | "api-key" | "system";
24
+ displayName: string;
25
+ email: string | null;
26
+ agentName: string | null;
27
+ redacted: boolean;
28
+ } | null;
29
+ }>;
30
+ }