@mittwald/cli 1.9.1 → 1.11.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.
Files changed (110) hide show
  1. package/bin/run.js +4 -1
  2. package/dist/commands/app/create/node.d.ts +1 -3
  3. package/dist/commands/app/create/php-worker.d.ts +1 -3
  4. package/dist/commands/app/create/php.d.ts +1 -3
  5. package/dist/commands/app/create/python.d.ts +1 -3
  6. package/dist/commands/app/create/static.d.ts +1 -3
  7. package/dist/commands/app/dependency/update.js +2 -4
  8. package/dist/commands/app/download.js +2 -2
  9. package/dist/commands/app/exec.d.ts +17 -0
  10. package/dist/commands/app/exec.js +71 -0
  11. package/dist/commands/app/install/contao.d.ts +1 -3
  12. package/dist/commands/app/install/joomla.d.ts +1 -3
  13. package/dist/commands/app/install/matomo.d.ts +1 -3
  14. package/dist/commands/app/install/nextcloud.d.ts +1 -3
  15. package/dist/commands/app/install/shopware5.d.ts +1 -3
  16. package/dist/commands/app/install/shopware6.d.ts +1 -3
  17. package/dist/commands/app/install/typo3.d.ts +1 -3
  18. package/dist/commands/app/install/wordpress.d.ts +1 -3
  19. package/dist/commands/app/ssh.d.ts +1 -0
  20. package/dist/commands/app/ssh.js +15 -1
  21. package/dist/commands/app/update.js +4 -8
  22. package/dist/commands/app/upgrade.js +9 -9
  23. package/dist/commands/backup/create.js +1 -2
  24. package/dist/commands/backup/download.js +4 -5
  25. package/dist/commands/container/exec.d.ts +1 -7
  26. package/dist/commands/container/exec.js +11 -22
  27. package/dist/commands/container/run.d.ts +4 -2
  28. package/dist/commands/container/run.js +7 -6
  29. package/dist/commands/database/mysql/create.js +1 -2
  30. package/dist/commands/database/mysql/dump.js +1 -2
  31. package/dist/commands/database/mysql/import.js +1 -2
  32. package/dist/commands/ddev/init.js +2 -6
  33. package/dist/commands/extension/install.js +1 -3
  34. package/dist/commands/login/reset.js +1 -2
  35. package/dist/commands/mail/address/create.js +1 -4
  36. package/dist/commands/mail/deliverybox/create.js +1 -4
  37. package/dist/commands/project/create.js +4 -6
  38. package/dist/commands/registry/create.js +1 -2
  39. package/dist/commands/registry/update.js +1 -2
  40. package/dist/commands/user/api-token/create.js +1 -1
  41. package/dist/commands/user/ssh-key/create.js +3 -8
  42. package/dist/commands/user/ssh-key/import.js +2 -3
  43. package/dist/hooks/prerun/update-brew-check.js +1 -1
  44. package/dist/lib/basecommands/DeleteBaseCommand.js +4 -4
  45. package/dist/lib/ddev/init_assert.js +1 -6
  46. package/dist/lib/ddev/init_database.js +1 -7
  47. package/dist/lib/ddev/init_projecttype.js +2 -11
  48. package/dist/lib/intellij/config.d.ts +5 -0
  49. package/dist/lib/intellij/config.js +295 -0
  50. package/dist/lib/intellij/config.test.d.ts +1 -0
  51. package/dist/lib/intellij/config.test.js +262 -0
  52. package/dist/lib/intellij/config_xml_types.d.ts +72 -0
  53. package/dist/lib/intellij/config_xml_types.js +2 -0
  54. package/dist/lib/resources/app/Installer.d.ts +6 -2
  55. package/dist/lib/resources/app/Installer.js +13 -6
  56. package/dist/lib/resources/app/flags.js +9 -12
  57. package/dist/lib/resources/app/install.d.ts +2 -1
  58. package/dist/lib/resources/app/install.js +3 -1
  59. package/dist/lib/resources/app/versions.js +1 -4
  60. package/dist/lib/resources/app/wait.js +1 -3
  61. package/dist/lib/resources/mail/commons.js +1 -4
  62. package/dist/lib/resources/ssh/appinstall.d.ts +3 -1
  63. package/dist/lib/resources/ssh/appinstall.js +1 -0
  64. package/dist/lib/resources/ssh/environment.d.ts +7 -0
  65. package/dist/lib/resources/ssh/environment.js +21 -0
  66. package/dist/rendering/process/components/ProcessStateIcon.js +1 -1
  67. package/dist/rendering/process/process.d.ts +16 -16
  68. package/dist/rendering/process/process_exec.d.ts +1 -2
  69. package/dist/rendering/process/process_fancy.d.ts +9 -9
  70. package/dist/rendering/process/process_flags.js +4 -0
  71. package/dist/rendering/process/process_quiet.d.ts +3 -4
  72. package/dist/rendering/process/process_simple.d.ts +26 -0
  73. package/dist/rendering/process/process_simple.js +143 -0
  74. package/dist/rendering/process/process_simple.test.d.ts +1 -0
  75. package/dist/rendering/process/process_simple.test.js +149 -0
  76. package/dist/rendering/react/components/AppInstallation/AppBackendAccessHints.d.ts +15 -0
  77. package/dist/rendering/react/components/AppInstallation/AppBackendAccessHints.js +13 -0
  78. package/dist/rendering/react/components/AppInstallation/AppDomainConnectionHints.d.ts +12 -0
  79. package/dist/rendering/react/components/AppInstallation/AppDomainConnectionHints.js +21 -0
  80. package/dist/rendering/react/components/AppInstallation/AppManagementCommands.d.ts +12 -0
  81. package/dist/rendering/react/components/AppInstallation/AppManagementCommands.js +17 -0
  82. package/dist/rendering/react/components/AppInstallation/AppUsageHints.d.ts +17 -0
  83. package/dist/rendering/react/components/AppInstallation/AppUsageHints.js +23 -0
  84. package/dist/rendering/react/components/Container/CommandHint.d.ts +14 -0
  85. package/dist/rendering/react/components/Container/CommandHint.js +13 -0
  86. package/dist/rendering/react/components/Container/ContainerManagementCommands.d.ts +22 -0
  87. package/dist/rendering/react/components/Container/ContainerManagementCommands.js +23 -0
  88. package/dist/rendering/react/components/Container/ContainerUsageHints.d.ts +12 -0
  89. package/dist/rendering/react/components/Container/ContainerUsageHints.js +35 -0
  90. package/dist/rendering/react/components/Container/DomainConnectionHints.d.ts +12 -0
  91. package/dist/rendering/react/components/Container/DomainConnectionHints.js +21 -0
  92. package/dist/rendering/react/components/Container/InternalConnectionHints.d.ts +12 -0
  93. package/dist/rendering/react/components/Container/InternalConnectionHints.js +12 -0
  94. package/dist/rendering/react/components/Container/NoPortsUsageHints.d.ts +12 -0
  95. package/dist/rendering/react/components/Container/NoPortsUsageHints.js +12 -0
  96. package/dist/rendering/react/components/Container/PortConnectionHints.d.ts +13 -0
  97. package/dist/rendering/react/components/Container/PortConnectionHints.js +11 -0
  98. package/dist/rendering/react/components/Container/PortForwardingHints.d.ts +12 -0
  99. package/dist/rendering/react/components/Container/PortForwardingHints.js +18 -0
  100. package/dist/rendering/react/components/Container/types.d.ts +4 -0
  101. package/dist/rendering/react/components/Container/types.js +1 -0
  102. package/dist/rendering/react/components/Error/ErrorBox.d.ts +1 -1
  103. package/dist/rendering/react/components/Error/ErrorBox.js +3 -4
  104. package/dist/rendering/react/components/Error/GenericError.js +1 -1
  105. package/dist/rendering/react/components/ErrorBoundary.d.ts +1 -1
  106. package/dist/rendering/react/components/Success.d.ts +0 -1
  107. package/dist/rendering/react/components/Success.js +4 -2
  108. package/dist/rendering/react/styles/useDefaultBoxStyles.d.ts +11 -0
  109. package/dist/rendering/react/styles/useDefaultBoxStyles.js +23 -0
  110. package/package.json +6 -5
@@ -6,6 +6,7 @@ import { normalizeToAppVersionUuid } from "./versions.js";
6
6
  import { triggerAppInstallation } from "./install.js";
7
7
  import { waitUntilAppStateHasNormalized } from "./wait.js";
8
8
  import { Success } from "../../../rendering/react/components/Success.js";
9
+ import AppUsageHints from "../../../rendering/react/components/AppInstallation/AppUsageHints.js";
9
10
  export class AppInstaller {
10
11
  appId;
11
12
  appName;
@@ -35,22 +36,28 @@ export class AppInstaller {
35
36
  const projectId = await withProjectId(apiClient, "flag", flags, args, config);
36
37
  await autofillFlags(apiClient, process, this.appSupportedFlags, flags, projectId, this.appName, this.defaultFlagValues);
37
38
  const appVersion = await normalizeToAppVersionUuid(apiClient, "version" in flags ? flags.version : "latest", process, this.appId);
38
- const appInstallationId = await triggerAppInstallation(apiClient, process, projectId, flags, appVersion);
39
+ const appInstallation = await triggerAppInstallation(apiClient, process, projectId, flags, appVersion);
39
40
  let successText;
40
41
  if (flags.wait) {
41
- await waitUntilAppStateHasNormalized(apiClient, process, appInstallationId, "waiting for app installation to be ready", flags["wait-timeout"]);
42
+ await waitUntilAppStateHasNormalized(apiClient, process, appInstallation.id, "waiting for app installation to be ready", flags["wait-timeout"]);
42
43
  successText = `Your ${this.appName} installation is now complete. Have fun! 🎉`;
43
44
  }
44
45
  else {
45
46
  successText = `Your ${this.appName} installation has started. Have fun when it's ready! 🎉`;
46
47
  }
47
- process.complete(_jsx(Success, { children: successText }));
48
- return { appInstallationId };
48
+ await process.complete(_jsx(Success, { children: successText }));
49
+ return {
50
+ appInstallation,
51
+ appVersion,
52
+ host: "host" in flags && typeof flags.host === "string"
53
+ ? flags.host
54
+ : undefined,
55
+ };
49
56
  }
50
57
  render(result, flags) {
51
58
  if (flags.quiet) {
52
- return result.appInstallationId;
59
+ return result.appInstallation.id;
53
60
  }
54
- return undefined;
61
+ return (_jsx(AppUsageHints, { appInstallation: result.appInstallation, appVersion: result.appVersion, appName: this.appName, appHost: result.host }));
55
62
  }
56
63
  }
@@ -1,8 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { getDefaultIngressForProject } from "../project/ingress.js";
3
- import { Value } from "../../../rendering/react/components/Value.js";
4
2
  import { getProjectShortIdFromUuid } from "../project/shortId.js";
5
- import { Text } from "ink";
6
3
  import { assertStatus } from "@mittwald/api-client-commons";
7
4
  import { projectFlags } from "../project/flags.js";
8
5
  import { processFlags, } from "../../../rendering/process/process_flags.js";
@@ -169,7 +166,7 @@ export async function autofillFlags(apiClient, process, necessaryFlags, flags, p
169
166
  if (necessaryFlags.includes("host") && !flags.host) {
170
167
  flags.host =
171
168
  "https://" + (await getDefaultIngressForProject(apiClient, projectId));
172
- process.addInfo(_jsxs(Text, { children: ["Using default Host ", _jsx(Value, { children: flags["host"] })] }));
169
+ process.addInfo(`Using default Host ${flags["host"]}`);
173
170
  }
174
171
  // Title
175
172
  if (necessaryFlags.includes("site-title") && !flags["site-title"]) {
@@ -186,12 +183,12 @@ export async function autofillFlags(apiClient, process, necessaryFlags, flags, p
186
183
  else {
187
184
  flags["admin-user"] = await getProjectShortIdFromUuid(apiClient, projectId);
188
185
  }
189
- process.addInfo(_jsxs(Text, { children: ["Using generated Admin User: ", _jsx(Value, { children: flags["admin-user"] })] }));
186
+ process.addInfo(`Using generated Admin User: ${flags["admin-user"]}`);
190
187
  }
191
188
  // Admin Pass
192
189
  if (necessaryFlags.includes("admin-pass") && !flags["admin-pass"]) {
193
190
  flags["admin-pass"] = generatePasswordWithSpecialChars();
194
- process.addInfo(_jsxs(Text, { children: ["Using generated random Admin Pass: ", _jsx(Value, { children: flags["admin-pass"] })] }));
191
+ process.addInfo(`Using generated random Admin Pass: ${flags["admin-pass"]}`);
195
192
  }
196
193
  // Admin Firstname
197
194
  if (necessaryFlags.includes("admin-firstname") && !flags["admin-firstname"]) {
@@ -201,7 +198,7 @@ export async function autofillFlags(apiClient, process, necessaryFlags, flags, p
201
198
  else {
202
199
  flags["admin-firstname"] = "Max";
203
200
  }
204
- process.addInfo(_jsxs(Text, { children: ["Using mStudio firstname as Admin firstname (", _jsx(Value, { children: flags["admin-firstname"] }), ")"] }));
201
+ process.addInfo(`Using mStudio firstname as Admin firstname (${flags["admin-firstname"]})`);
205
202
  }
206
203
  // Admin Lastname
207
204
  if (necessaryFlags.includes("admin-lastname") && !flags["admin-lastname"]) {
@@ -211,26 +208,26 @@ export async function autofillFlags(apiClient, process, necessaryFlags, flags, p
211
208
  else {
212
209
  flags["admin-lastname"] = "Mustermann";
213
210
  }
214
- process.addInfo(_jsxs(Text, { children: ["Using mStudio lastname as Admin lastname (", _jsx(Value, { children: flags["admin-lastname"] }), ")"] }));
211
+ process.addInfo(`Using mStudio lastname as Admin lastname (${flags["admin-lastname"]})`);
215
212
  }
216
213
  // Admin E-Mail
217
214
  if (necessaryFlags.includes("admin-email") && !flags["admin-email"]) {
218
215
  flags["admin-email"] = ownUser.data.email;
219
- process.addInfo(_jsxs(Text, { children: ["Using mStudio email as Admin email (", _jsx(Value, { children: flags["admin-email"] }), ")"] }));
216
+ process.addInfo(`Using mStudio email as Admin email (${flags["admin-email"]})`);
220
217
  }
221
218
  // Shop E-Mail
222
219
  if (necessaryFlags.includes("shop-email") && !flags["shop-email"]) {
223
220
  flags["shop-email"] = ownUser.data.email;
224
- process.addInfo(_jsxs(Text, { children: ["Using mStudio email as Shop email (", _jsx(Value, { children: flags["shop-email"] }), ")"] }));
221
+ process.addInfo(`Using mStudio email as Shop email (${flags["shop-email"]})`);
225
222
  }
226
223
  // Shop Language Code
227
224
  if (necessaryFlags.includes("shop-lang") && !flags["shop-lang"]) {
228
225
  flags["shop-lang"] = defaults["shop-lang"] ?? "de-DE";
229
- process.addInfo(_jsxs(Text, { children: ["Using default shop language '", flags["shop-lang"], "'."] }));
226
+ process.addInfo(`Using default shop language '${flags["shop-lang"]}'.`);
230
227
  }
231
228
  // Shop Currency
232
229
  if (necessaryFlags.includes("shop-currency") && !flags["shop-currency"]) {
233
230
  flags["shop-currency"] = "EUR";
234
- process.addInfo(_jsx(Text, { children: "Using default shop currency '\u20AC'." }));
231
+ process.addInfo("Using default shop currency ''.");
235
232
  }
236
233
  }
@@ -1,10 +1,11 @@
1
1
  import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
2
2
  import { ProcessRenderer } from "../../../rendering/process/process.js";
3
+ type AppAppInstallation = MittwaldAPIV2.Components.Schemas.AppAppInstallation;
3
4
  type AppAppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion;
4
5
  export declare function triggerAppInstallation(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, projectId: string, flags: {
5
6
  "site-title": string;
6
7
  "document-root"?: string;
7
8
  } & {
8
9
  [k: string]: unknown;
9
- }, appVersion: AppAppVersion): Promise<string>;
10
+ }, appVersion: AppAppVersion): Promise<AppAppInstallation>;
10
11
  export {};
@@ -38,5 +38,7 @@ export async function triggerAppInstallation(apiClient, process, projectId, flag
38
38
  assertStatus(result, 204);
39
39
  });
40
40
  }
41
- return appInstallationId;
41
+ const result = await apiClient.app.getAppinstallation({ appInstallationId });
42
+ assertStatus(result, 200);
43
+ return result.data;
42
44
  }
@@ -1,8 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { assertStatus } from "@mittwald/api-client-commons";
3
2
  import { gt } from "semver";
4
- import { Value } from "../../../rendering/react/components/Value.js";
5
- import { Text } from "ink";
6
3
  import { getAppInstallationFromUuid, getAppNameFromUuid } from "./uuid.js";
7
4
  import { compare } from "semver";
8
5
  export async function normalizeToAppVersionUuid(apiClient, version, process, appUuid) {
@@ -16,7 +13,7 @@ export async function normalizeToAppVersionUuid(apiClient, version, process, app
16
13
  if (!appVersion) {
17
14
  throw new Error(`${await getAppNameFromUuid(apiClient, appUuid)} version ${version} does not seem to exist for the mStudio.`);
18
15
  }
19
- process.addInfo(_jsxs(Text, { children: ["installing version: ", _jsx(Value, { children: appVersion.externalVersion })] }));
16
+ process.addInfo(`installing version: ${appVersion.externalVersion}`);
20
17
  return appVersion;
21
18
  }
22
19
  // Get latest available Internal App Version for App UUID
@@ -1,8 +1,6 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  import { waitUntil } from "../../wait.js";
3
- import { Text } from "ink";
4
2
  export async function waitUntilAppStateHasNormalized(apiClient, process, appInstallationId, label, timeout) {
5
- const stepWaiting = process.addStep(_jsx(Text, { children: label }));
3
+ const stepWaiting = process.addStep(label);
6
4
  await waitUntil(async () => {
7
5
  const installationResponse = await apiClient.app.getAppinstallation({
8
6
  appInstallationId,
@@ -1,9 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { generatePassword } from "../../util/password/generatePassword.js";
3
- import { Value } from "../../../rendering/react/components/Value.js";
4
- import { Text } from "ink";
5
2
  export async function generateRandomPassword(process) {
6
3
  const generated = await process.runStep("generating random password", async () => generatePassword(32));
7
- process.addInfo(_jsxs(Text, { children: [" ", "generated password: ", _jsx(Value, { children: generated }), " "] }));
4
+ process.addInfo(` generated password: ${generated} `);
8
5
  return generated;
9
6
  }
@@ -1,3 +1,5 @@
1
1
  import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
2
  import { SSHConnectionData } from "./types.js";
3
- export declare function getSSHConnectionForAppInstallation(client: MittwaldAPIV2Client, appInstallationId: string, sshUser: string | undefined): Promise<SSHConnectionData>;
3
+ export declare function getSSHConnectionForAppInstallation(client: MittwaldAPIV2Client, appInstallationId: string, sshUser: string | undefined): Promise<SSHConnectionData & {
4
+ appShortId: string;
5
+ }>;
@@ -24,5 +24,6 @@ export async function getSSHConnectionForAppInstallation(client, appInstallation
24
24
  host,
25
25
  user,
26
26
  directory,
27
+ appShortId: appInstallationResponse.data.shortId,
27
28
  };
28
29
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Prepare environment variables for SSH command execution
3
+ *
4
+ * @param envVars Array of environment variables in KEY=VALUE format
5
+ * @returns Formatted string with export commands
6
+ */
7
+ export declare function prepareEnvironmentVariables(envVars: string[]): string;
@@ -0,0 +1,21 @@
1
+ import shellEscape from "shell-escape";
2
+ /**
3
+ * Prepare environment variables for SSH command execution
4
+ *
5
+ * @param envVars Array of environment variables in KEY=VALUE format
6
+ * @returns Formatted string with export commands
7
+ */
8
+ export function prepareEnvironmentVariables(envVars) {
9
+ return (envVars
10
+ .map((env) => {
11
+ const eqIdx = env.indexOf("=");
12
+ if (eqIdx === -1) {
13
+ // If no '=', treat the whole string as key with empty value
14
+ return `export ${shellEscape([env])}=`;
15
+ }
16
+ const key = env.slice(0, eqIdx);
17
+ const value = env.slice(eqIdx + 1);
18
+ return `export ${shellEscape([key])}=${shellEscape([value])}`;
19
+ })
20
+ .join("; ") + "; ");
21
+ }
@@ -10,7 +10,7 @@ export const ProcessStateIcon = ({ step }) => {
10
10
  return _jsx(Text, { children: "\u2753" });
11
11
  }
12
12
  else if (step.phase === "completed") {
13
- return _jsx(Text, { children: "\u2705" });
13
+ return _jsx(Text, { children: "\u2705 " });
14
14
  }
15
15
  else if (step.phase === "aborted") {
16
16
  return _jsx(Text, { children: "\u23E9\uFE0F " });
@@ -1,11 +1,11 @@
1
- import { ReactElement, ReactNode } from "react";
1
+ import { ReactElement } from "react";
2
2
  export type ProcessStepInfo = {
3
3
  type: "info";
4
- title: ReactNode;
4
+ title: string;
5
5
  };
6
6
  export type ProcessStepRunnable = {
7
7
  type: "step";
8
- title: ReactNode;
8
+ title: string;
9
9
  phase: "running" | "completed" | "failed" | "aborted";
10
10
  error?: unknown;
11
11
  progress?: string;
@@ -13,27 +13,27 @@ export type ProcessStepRunnable = {
13
13
  };
14
14
  export type ProcessStepConfirm = {
15
15
  type: "confirm";
16
- title: ReactNode;
16
+ title: string;
17
17
  confirmed: boolean | undefined;
18
18
  };
19
19
  export type ProcessStepInput = {
20
20
  type: "input";
21
- title: ReactNode;
21
+ title: string;
22
22
  mask?: boolean;
23
23
  value?: string;
24
24
  };
25
25
  export type ProcessStepSelect<TVal> = {
26
26
  type: "select";
27
- title: ReactNode;
27
+ title: string;
28
28
  options: {
29
29
  value: TVal;
30
- label: ReactNode;
30
+ label: string;
31
31
  }[];
32
32
  selected?: TVal;
33
33
  };
34
34
  export type ProcessStep = ProcessStepInfo | ProcessStepRunnable | ProcessStepConfirm | ProcessStepInput | ProcessStepSelect<unknown>;
35
35
  export type CleanupFunction = {
36
- title: ReactNode;
36
+ title: string;
37
37
  fn: () => Promise<unknown>;
38
38
  };
39
39
  export declare class RunnableHandler {
@@ -53,16 +53,16 @@ export declare class RunnableHandler {
53
53
  }
54
54
  export interface ProcessRenderer {
55
55
  start(): void;
56
- addStep(title: ReactNode): RunnableHandler;
57
- runStep<TRes>(title: ReactNode, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
58
- addInfo(title: ReactNode): void;
59
- addConfirmation(question: ReactNode): Promise<boolean>;
60
- addInput(question: ReactNode, mask?: boolean): Promise<string>;
61
- addSelect<TVal>(question: ReactNode, options: {
56
+ addStep(title: string): RunnableHandler;
57
+ runStep<TRes>(title: string, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
58
+ addInfo(title: string): void;
59
+ addConfirmation(question: string): Promise<boolean>;
60
+ addInput(question: string, mask?: boolean): Promise<string>;
61
+ addSelect<TVal>(question: string, options: {
62
62
  value: TVal;
63
- label: ReactNode;
63
+ label: string;
64
64
  }[]): Promise<TVal>;
65
- addCleanup(title: ReactNode, fn: () => Promise<unknown>): void;
65
+ addCleanup(title: string, fn: () => Promise<unknown>): void;
66
66
  complete(summary: ReactElement): Promise<void>;
67
67
  error(err: unknown): Promise<void>;
68
68
  }
@@ -1,3 +1,2 @@
1
1
  import { ProcessRenderer } from "./process.js";
2
- import { ReactNode } from "react";
3
- export declare function spawnInProcess(r: ProcessRenderer, title: ReactNode, cmd: string, args: string[]): Promise<void>;
2
+ export declare function spawnInProcess(r: ProcessRenderer, title: string, cmd: string, args: string[]): Promise<void>;
@@ -1,4 +1,4 @@
1
- import React, { ReactElement, ReactNode } from "react";
1
+ import { ReactElement } from "react";
2
2
  import { ProcessRenderer, RunnableHandler } from "./process.js";
3
3
  export declare class FancyProcessRenderer implements ProcessRenderer {
4
4
  private readonly title;
@@ -7,18 +7,18 @@ export declare class FancyProcessRenderer implements ProcessRenderer {
7
7
  private cleanupFns;
8
8
  constructor(title: string);
9
9
  start(): void;
10
- addStep(title: ReactNode): RunnableHandler;
11
- runStep<TRes>(title: ReactNode, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
12
- addInfo(title: ReactNode): void;
13
- addInput(question: React.ReactElement, mask?: boolean): Promise<string>;
14
- addSelect<TVal>(question: React.ReactNode, options: {
10
+ addStep(title: string): RunnableHandler;
11
+ runStep<TRes>(title: string, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
12
+ addInfo(title: string): void;
13
+ addInput(question: string, mask?: boolean): Promise<string>;
14
+ addSelect<TVal>(question: string, options: {
15
15
  value: TVal;
16
- label: React.ReactNode;
16
+ label: string;
17
17
  }[]): Promise<TVal>;
18
- addConfirmation(question: ReactElement): Promise<boolean>;
18
+ addConfirmation(question: string): Promise<boolean>;
19
19
  complete(summary: ReactElement): Promise<void>;
20
20
  error(err: unknown): Promise<void>;
21
21
  private renderStart;
22
- addCleanup(title: ReactNode, fn: () => Promise<unknown>): void;
22
+ addCleanup(title: string, fn: () => Promise<unknown>): void;
23
23
  private cleanup;
24
24
  }
@@ -1,6 +1,7 @@
1
1
  import { FancyProcessRenderer } from "./process_fancy.js";
2
2
  import { Flags } from "@oclif/core";
3
3
  import { SilentProcessRenderer } from "./process_quiet.js";
4
+ import { SimpleProcessRenderer } from "./process_simple.js";
4
5
  export const processFlags = {
5
6
  quiet: Flags.boolean({
6
7
  char: "q",
@@ -25,5 +26,8 @@ export const makeProcessRenderer = (flags, title) => {
25
26
  if (flags.quiet) {
26
27
  return new SilentProcessRenderer();
27
28
  }
29
+ if (!process.stdout.isTTY) {
30
+ return new SimpleProcessRenderer(title, process.stdout);
31
+ }
28
32
  return new FancyProcessRenderer(title);
29
33
  };
@@ -1,16 +1,15 @@
1
- import { ReactNode } from "react";
2
1
  import { ProcessRenderer, RunnableHandler } from "./process.js";
3
2
  export declare class SilentProcessRenderer implements ProcessRenderer {
4
3
  private cleanupFns;
5
4
  start(): void;
6
- addStep(title: ReactNode): RunnableHandler;
7
- runStep<TRes>(unusedTitle: ReactNode, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
5
+ addStep(title: string): RunnableHandler;
6
+ runStep<TRes>(unusedTitle: string, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
8
7
  addInfo(): void;
9
8
  complete(): Promise<void>;
10
9
  error(err: unknown): Promise<void>;
11
10
  addConfirmation(): Promise<boolean>;
12
11
  addInput(): Promise<string>;
13
12
  addSelect<TVal>(): Promise<TVal>;
14
- addCleanup(_: ReactNode, fn: () => Promise<unknown>): void;
13
+ addCleanup(_: string, fn: () => Promise<unknown>): void;
15
14
  private cleanup;
16
15
  }
@@ -0,0 +1,26 @@
1
+ import { ReactElement } from "react";
2
+ import { ProcessRenderer, RunnableHandler } from "./process.js";
3
+ import { Writable } from "stream";
4
+ export declare class SimpleProcessRenderer implements ProcessRenderer {
5
+ private readonly title;
6
+ private readonly output;
7
+ private started;
8
+ private cleanupFns;
9
+ private stepCounter;
10
+ constructor(title: string, output: Writable);
11
+ start(): void;
12
+ addStep(title: string): RunnableHandler;
13
+ runStep<TRes>(title: string, fn: (() => Promise<TRes>) | Promise<TRes>): Promise<TRes>;
14
+ addInfo(title: string): void;
15
+ addConfirmation(question: string): Promise<boolean>;
16
+ addInput(question: string, mask?: boolean): Promise<string>;
17
+ addSelect<TVal>(question: string, options: {
18
+ value: TVal;
19
+ label: string;
20
+ }[]): Promise<TVal>;
21
+ addCleanup(title: string, fn: () => Promise<unknown>): void;
22
+ complete(summary: ReactElement): Promise<void>;
23
+ error(err: unknown): Promise<void>;
24
+ private cleanup;
25
+ private renderElementToText;
26
+ }
@@ -0,0 +1,143 @@
1
+ import { RunnableHandler, } from "./process.js";
2
+ export class SimpleProcessRenderer {
3
+ title;
4
+ output;
5
+ started = false;
6
+ cleanupFns = [];
7
+ stepCounter = 0;
8
+ constructor(title, output) {
9
+ this.title = title;
10
+ this.output = output;
11
+ }
12
+ start() {
13
+ if (this.started) {
14
+ return;
15
+ }
16
+ this.started = true;
17
+ this.output.write(`Starting: ${this.title}\n\n`);
18
+ }
19
+ addStep(title) {
20
+ this.start();
21
+ this.stepCounter++;
22
+ const titleText = title;
23
+ this.output.write(`Step ${this.stepCounter}: ${titleText}... `);
24
+ const state = {
25
+ type: "step",
26
+ title,
27
+ phase: "running",
28
+ };
29
+ return new RunnableHandler(state, () => {
30
+ if (state.phase === "completed") {
31
+ this.output.write("completed\n");
32
+ }
33
+ else if (state.phase === "failed") {
34
+ this.output.write("FAILED\n");
35
+ if (state.error) {
36
+ this.output.write(` Error: ${state.error}\n`);
37
+ }
38
+ }
39
+ else if (state.phase === "aborted") {
40
+ this.output.write("aborted\n");
41
+ }
42
+ if (state.progress) {
43
+ this.output.write(` Progress: ${state.progress}\n`);
44
+ }
45
+ if (state.output) {
46
+ const lines = state.output
47
+ .split("\n")
48
+ .filter((line) => line.trim());
49
+ this.output.write("got output:\n");
50
+ for (const line of lines) {
51
+ this.output.write(` ${line}\n`);
52
+ }
53
+ }
54
+ });
55
+ }
56
+ async runStep(title, fn) {
57
+ const step = this.addStep(title);
58
+ try {
59
+ const promise = typeof fn === "function" ? fn() : fn;
60
+ const result = await promise;
61
+ step.complete();
62
+ return result;
63
+ }
64
+ catch (err) {
65
+ step.error(err);
66
+ throw err;
67
+ }
68
+ }
69
+ addInfo(title) {
70
+ this.start();
71
+ const titleText = title;
72
+ this.output.write(`Info: ${titleText}\n`);
73
+ }
74
+ async addConfirmation(question) {
75
+ this.start();
76
+ const questionText = question;
77
+ this.output.write(`Confirm: ${questionText}; automatically confirmed\n`);
78
+ return true;
79
+ }
80
+ async addInput(question, mask) {
81
+ this.start();
82
+ const questionText = question;
83
+ const maskText = mask ? " (masked)" : "";
84
+ this.output.write(`Input: ${questionText}${maskText}; no input available\n`);
85
+ // For non-interactive use, throw an error
86
+ throw new Error("Interactive input not available in simple process renderer");
87
+ }
88
+ async addSelect(question, options) {
89
+ this.start();
90
+ const questionText = question;
91
+ this.output.write(`Selection: ${questionText}\n`);
92
+ this.output.write("Available options:\n");
93
+ options.forEach((option, index) => {
94
+ const labelText = option.label;
95
+ this.output.write(` ${index + 1}. ${labelText}\n`);
96
+ });
97
+ throw new Error("Interactive selection not available in simple process renderer");
98
+ }
99
+ addCleanup(title, fn) {
100
+ this.cleanupFns.push(fn);
101
+ }
102
+ async complete(summary) {
103
+ await this.cleanup();
104
+ const summaryText = this.renderElementToText(summary);
105
+ this.output.write("Completed: Process completed successfully\n\n");
106
+ this.output.write(`Summary: ${summaryText}\n`);
107
+ }
108
+ async error(err) {
109
+ await this.cleanup();
110
+ this.output.write(`ERROR: Process failed: ${err?.toString()}\n`);
111
+ }
112
+ async cleanup() {
113
+ if (this.cleanupFns.length === 0) {
114
+ return;
115
+ }
116
+ this.output.write("Cleanup: Running cleanup tasks... ");
117
+ for (let i = 0; i < this.cleanupFns.length; i++) {
118
+ try {
119
+ await this.cleanupFns[i]();
120
+ }
121
+ catch (err) {
122
+ this.output.write(`task ${i + 1} failed: ${err}`);
123
+ }
124
+ }
125
+ this.output.write("completed\n");
126
+ }
127
+ renderElementToText(node) {
128
+ if (node === null || node === undefined) {
129
+ return "";
130
+ }
131
+ // Check if it's a React element with props.children
132
+ if (typeof node === "object" &&
133
+ "props" in node &&
134
+ node.props &&
135
+ typeof node.props === "object") {
136
+ const props = node.props;
137
+ if (props.children && typeof props.children === "string") {
138
+ return props.children;
139
+ }
140
+ }
141
+ return "[Complex content]";
142
+ }
143
+ }
@@ -0,0 +1 @@
1
+ export {};