@mittwald/cli 1.5.1 → 1.7.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 (130) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/app/dependency/list.d.ts +1 -0
  3. package/dist/commands/app/dependency/versions.d.ts +1 -0
  4. package/dist/commands/app/get.js +1 -2
  5. package/dist/commands/app/list-upgrade-candidates.d.ts +1 -0
  6. package/dist/commands/app/list.d.ts +1 -0
  7. package/dist/commands/app/upgrade.d.ts +14 -0
  8. package/dist/commands/app/upgrade.js +35 -22
  9. package/dist/commands/backup/delete.d.ts +1 -0
  10. package/dist/commands/backup/get.js +1 -2
  11. package/dist/commands/backup/list.d.ts +1 -0
  12. package/dist/commands/backup/schedule/list.d.ts +1 -0
  13. package/dist/commands/container/delete.d.ts +1 -0
  14. package/dist/commands/container/exec.d.ts +25 -0
  15. package/dist/commands/container/exec.js +93 -0
  16. package/dist/commands/container/list.d.ts +1 -0
  17. package/dist/commands/container/logs.d.ts +1 -0
  18. package/dist/commands/container/port-forward.d.ts +20 -0
  19. package/dist/commands/container/port-forward.js +68 -0
  20. package/dist/commands/container/run.d.ts +12 -0
  21. package/dist/commands/container/run.js +27 -8
  22. package/dist/commands/container/ssh.d.ts +17 -0
  23. package/dist/commands/container/ssh.js +61 -0
  24. package/dist/commands/conversation/categories.d.ts +1 -0
  25. package/dist/commands/conversation/create.d.ts +1 -0
  26. package/dist/commands/conversation/list.d.ts +1 -0
  27. package/dist/commands/conversation/reply.d.ts +1 -0
  28. package/dist/commands/cronjob/delete.d.ts +1 -0
  29. package/dist/commands/cronjob/execution/get.d.ts +1 -1
  30. package/dist/commands/cronjob/execution/list.d.ts +1 -0
  31. package/dist/commands/cronjob/execution/logs.d.ts +1 -0
  32. package/dist/commands/cronjob/get.js +1 -2
  33. package/dist/commands/cronjob/list.d.ts +1 -0
  34. package/dist/commands/database/list.d.ts +1 -0
  35. package/dist/commands/database/mysql/charsets.d.ts +1 -0
  36. package/dist/commands/database/mysql/delete.d.ts +1 -0
  37. package/dist/commands/database/mysql/get.d.ts +1 -1
  38. package/dist/commands/database/mysql/list.d.ts +1 -0
  39. package/dist/commands/database/mysql/user/delete.d.ts +1 -0
  40. package/dist/commands/database/mysql/user/get.d.ts +1 -1
  41. package/dist/commands/database/mysql/user/list.d.ts +1 -0
  42. package/dist/commands/database/mysql/versions.d.ts +1 -0
  43. package/dist/commands/database/redis/get.d.ts +1 -1
  44. package/dist/commands/database/redis/list.d.ts +1 -0
  45. package/dist/commands/database/redis/versions.d.ts +1 -0
  46. package/dist/commands/domain/dnszone/get.js +1 -2
  47. package/dist/commands/domain/dnszone/list.d.ts +1 -0
  48. package/dist/commands/domain/dnszone/update.js +3 -2
  49. package/dist/commands/domain/get.js +1 -2
  50. package/dist/commands/domain/list.d.ts +1 -0
  51. package/dist/commands/domain/virtualhost/delete.d.ts +1 -0
  52. package/dist/commands/domain/virtualhost/get.js +1 -2
  53. package/dist/commands/domain/virtualhost/list.d.ts +1 -0
  54. package/dist/commands/extension/list-installed.d.ts +1 -0
  55. package/dist/commands/extension/list.d.ts +1 -0
  56. package/dist/commands/mail/address/delete.d.ts +1 -0
  57. package/dist/commands/mail/address/get.d.ts +1 -1
  58. package/dist/commands/mail/address/list.d.ts +1 -0
  59. package/dist/commands/mail/deliverybox/delete.d.ts +1 -0
  60. package/dist/commands/mail/deliverybox/get.d.ts +1 -1
  61. package/dist/commands/mail/deliverybox/list.d.ts +1 -0
  62. package/dist/commands/org/delete.d.ts +1 -0
  63. package/dist/commands/org/get.js +1 -2
  64. package/dist/commands/org/invite/list-own.d.ts +1 -0
  65. package/dist/commands/org/invite/list.d.ts +1 -0
  66. package/dist/commands/org/list.d.ts +1 -0
  67. package/dist/commands/org/membership/list-own.d.ts +1 -0
  68. package/dist/commands/org/membership/list.d.ts +1 -0
  69. package/dist/commands/project/delete.d.ts +1 -0
  70. package/dist/commands/project/filesystem/usage.js +1 -2
  71. package/dist/commands/project/get.js +1 -2
  72. package/dist/commands/project/invite/get.d.ts +1 -1
  73. package/dist/commands/project/invite/list-own.d.ts +1 -0
  74. package/dist/commands/project/invite/list.d.ts +1 -0
  75. package/dist/commands/project/list.d.ts +1 -0
  76. package/dist/commands/project/membership/get-own.d.ts +1 -0
  77. package/dist/commands/project/membership/get.d.ts +1 -1
  78. package/dist/commands/project/membership/list-own.d.ts +1 -0
  79. package/dist/commands/project/membership/list.d.ts +1 -0
  80. package/dist/commands/registry/delete.d.ts +1 -0
  81. package/dist/commands/registry/list.d.ts +1 -0
  82. package/dist/commands/server/get.d.ts +1 -1
  83. package/dist/commands/server/list.d.ts +1 -0
  84. package/dist/commands/sftp-user/delete.d.ts +1 -0
  85. package/dist/commands/sftp-user/list.d.ts +1 -0
  86. package/dist/commands/ssh-user/delete.d.ts +1 -0
  87. package/dist/commands/ssh-user/list.d.ts +1 -0
  88. package/dist/commands/stack/delete.d.ts +1 -0
  89. package/dist/commands/stack/deploy.js +1 -8
  90. package/dist/commands/stack/list.d.ts +1 -0
  91. package/dist/commands/stack/ps.d.ts +1 -0
  92. package/dist/commands/stack/ps.js +1 -1
  93. package/dist/commands/user/api-token/get.d.ts +1 -1
  94. package/dist/commands/user/api-token/list.d.ts +1 -0
  95. package/dist/commands/user/api-token/revoke.d.ts +1 -0
  96. package/dist/commands/user/get.d.ts +1 -1
  97. package/dist/commands/user/session/get.d.ts +1 -1
  98. package/dist/commands/user/session/list.d.ts +1 -0
  99. package/dist/commands/user/ssh-key/delete.d.ts +1 -0
  100. package/dist/commands/user/ssh-key/get.d.ts +1 -1
  101. package/dist/commands/user/ssh-key/list.d.ts +1 -0
  102. package/dist/lib/basecommands/BaseCommand.d.ts +7 -5
  103. package/dist/lib/basecommands/BaseCommand.js +26 -8
  104. package/dist/lib/basecommands/CommandFlags.d.ts +3 -3
  105. package/dist/lib/basecommands/CoreBaseCommand.d.ts +12 -0
  106. package/dist/lib/basecommands/CoreBaseCommand.js +16 -0
  107. package/dist/lib/basecommands/DeleteBaseCommand.d.ts +1 -0
  108. package/dist/lib/basecommands/DeleteBaseCommand.js +1 -0
  109. package/dist/lib/basecommands/ExecRenderBaseCommand.d.ts +3 -0
  110. package/dist/lib/basecommands/ExecRenderBaseCommand.js +3 -0
  111. package/dist/lib/basecommands/ExtendedBaseCommand.d.ts +3 -0
  112. package/dist/lib/basecommands/ExtendedBaseCommand.js +3 -0
  113. package/dist/lib/basecommands/GetBaseCommand.d.ts +1 -1
  114. package/dist/lib/basecommands/GetBaseCommand.js +1 -0
  115. package/dist/lib/basecommands/ListBaseCommand.d.ts +1 -0
  116. package/dist/lib/basecommands/ListBaseCommand.js +1 -0
  117. package/dist/lib/basecommands/UnauthenticatedBaseCommand.d.ts +10 -0
  118. package/dist/lib/basecommands/UnauthenticatedBaseCommand.js +16 -0
  119. package/dist/lib/error/NoTokenFoundError.d.ts +11 -0
  120. package/dist/lib/error/NoTokenFoundError.js +13 -0
  121. package/dist/lib/resources/ssh/container.d.ts +3 -0
  122. package/dist/lib/resources/ssh/container.js +29 -0
  123. package/dist/lib/resources/stack/enrich.d.ts +2 -2
  124. package/dist/lib/resources/stack/enrich.js +1 -31
  125. package/dist/lib/units/PortMapping.d.ts +11 -0
  126. package/dist/lib/units/PortMapping.js +38 -0
  127. package/dist/lib/units/PortMapping.test.d.ts +1 -0
  128. package/dist/lib/units/PortMapping.test.js +28 -0
  129. package/dist/rendering/react/components/ErrorBox.js +4 -0
  130. package/package.json +67 -19
@@ -5,6 +5,7 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
5
5
  static flags: {
6
6
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
7
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
9
10
  static args: {
10
11
  "registry-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -16,6 +16,7 @@ export declare class List extends ListBaseCommand<typeof List, ResponseItem, Res
16
16
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
19
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
20
  };
20
21
  getData(): Promise<Response>;
21
22
  protected getColumns(data: ResponseItem[]): ListColumns<ResponseItem>;
@@ -4,7 +4,7 @@ type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["project"]["getServer"
4
4
  export default class Get extends GetBaseCommand<typeof Get, APIResponse> {
5
5
  static description: string;
6
6
  static flags: {
7
- [x: string]: import("@oclif/core/interfaces").Flag<any>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  };
9
9
  static args: {
10
10
  "server-id": import("@oclif/core/interfaces").Arg<string>;
@@ -14,6 +14,7 @@ export default class List extends ListBaseCommand<typeof List, ResponseItem, Res
14
14
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
15
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
17
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
18
  };
18
19
  getData(): Promise<Response>;
19
20
  protected getColumns(ignoredData: ResponseItem[]): ListColumns<ResponseItem>;
@@ -5,6 +5,7 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
5
5
  static flags: {
6
6
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
7
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
9
10
  static args: {
10
11
  "sftp-user-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -15,6 +15,7 @@ export default class List extends ListBaseCommand<typeof List, SftpUserResponseI
15
15
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
18
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  };
19
20
  static aliases: string[];
20
21
  static deprecateAliases: boolean;
@@ -5,6 +5,7 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
5
5
  static flags: {
6
6
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
7
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
9
10
  static args: {
10
11
  "ssh-user-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -15,6 +15,7 @@ export declare class List extends ListBaseCommand<typeof List, SshUserResponseIt
15
15
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
18
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  };
19
20
  static aliases: string[];
20
21
  static deprecateAliases: boolean;
@@ -7,6 +7,7 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
7
7
  "with-volumes": import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
8
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
9
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  static args: {
12
13
  "stack-id": import("@oclif/core/interfaces").Arg<string>;
@@ -32,17 +32,10 @@ export class Deploy extends ExecRenderBaseCommand {
32
32
  const { "compose-file": composeFile, "env-file": envFile } = this.flags;
33
33
  const r = makeProcessRenderer(this.flags, "Deploying container stack");
34
34
  const result = { restartedServices: [] };
35
- const stack = await r.runStep("getting stack", async () => {
36
- const resp = await this.apiClient.container.getStack({ stackId });
37
- assertStatus(resp, 200);
38
- return resp.data;
39
- });
40
35
  const env = await collectEnvironment(process.env, envFile);
41
36
  let stackDefinition = await loadStackFromFile(composeFile, env);
42
37
  stackDefinition = sanitizeStackDefinition(stackDefinition);
43
- stackDefinition = await r.runStep("getting image configurations", async () => {
44
- return enrichStackDefinition(this.apiClient, stack.projectId, stackDefinition);
45
- });
38
+ stackDefinition = await r.runStep("getting image configurations", () => enrichStackDefinition(stackDefinition));
46
39
  this.debug("complete stack definition: %O", stackDefinition);
47
40
  const declaredStack = await r.runStep("deploying stack", async () => {
48
41
  const resp = await this.apiClient.container.declareStack({
@@ -17,6 +17,7 @@ export declare class List extends ListBaseCommand<typeof List, ListItem, ListRes
17
17
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
19
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
20
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
21
  };
21
22
  getData(): Promise<ListResponse>;
22
23
  protected getColumns(data: ListItem[]): ListColumns<ListItem>;
@@ -16,6 +16,7 @@ export declare class ListContainers extends ListBaseCommand<typeof ListContainer
16
16
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
19
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
20
  };
20
21
  getData(): Promise<ListItem[]>;
21
22
  protected getColumns(data: ListItem[]): ListColumns<ListItem>;
@@ -29,7 +29,7 @@ export class ListContainers extends ListBaseCommand {
29
29
  get: (svc) => svc.deployedState.image,
30
30
  },
31
31
  command: {
32
- get: (svc) => svc.deployedState.command?.join(" "),
32
+ get: (svc) => svc.deployedState.command?.join(" ") ?? "(from image)",
33
33
  },
34
34
  description: {},
35
35
  ports: {
@@ -4,7 +4,7 @@ type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["user"]["getApiToken"]
4
4
  export default class Get extends GetBaseCommand<typeof Get, APIResponse> {
5
5
  static description: string;
6
6
  static flags: {
7
- [x: string]: import("@oclif/core/interfaces").Flag<any>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  };
9
9
  static args: {
10
10
  "token-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -14,6 +14,7 @@ export default class List extends ListBaseCommand<typeof List, ResponseItem, Res
14
14
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
15
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
17
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
18
  };
18
19
  getData(): Promise<Response>;
19
20
  protected getColumns(): ListColumns<ResponseItem>;
@@ -8,6 +8,7 @@ export default class Revoke extends DeleteBaseCommand<typeof Revoke> {
8
8
  static flags: {
9
9
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  protected deleteResource(): Promise<void>;
13
14
  }
@@ -5,7 +5,7 @@ type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["user"]["getUser"]>>;
5
5
  export default class Get extends GetBaseCommand<typeof Get, APIResponse> {
6
6
  static description: string;
7
7
  static flags: {
8
- [x: string]: import("@oclif/core/interfaces").Flag<any>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
10
  static args: {
11
11
  "user-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -4,7 +4,7 @@ type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["user"]["getSession"]>
4
4
  export default class Get extends GetBaseCommand<typeof Get, APIResponse> {
5
5
  static description: string;
6
6
  static flags: {
7
- [x: string]: import("@oclif/core/interfaces").Flag<any>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  };
9
9
  static args: {
10
10
  "token-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -14,6 +14,7 @@ export default class List extends ListBaseCommand<typeof List, ResponseItem, Res
14
14
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
15
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
17
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
18
  };
18
19
  getData(): Promise<Response>;
19
20
  protected getColumns(): ListColumns<ResponseItem>;
@@ -8,6 +8,7 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
8
8
  static flags: {
9
9
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  protected deleteResource(): Promise<void>;
13
14
  }
@@ -4,7 +4,7 @@ type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["user"]["getSshKey"]>>
4
4
  export default class Get extends GetBaseCommand<typeof Get, APIResponse> {
5
5
  static description: string;
6
6
  static flags: {
7
- [x: string]: import("@oclif/core/interfaces").Flag<any>;
7
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  };
9
9
  static args: {
10
10
  "key-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -15,6 +15,7 @@ export default class List extends ListBaseCommand<typeof List, ResponseItem, Res
15
15
  "no-truncate": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
16
  "no-relative-dates": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  "csv-separator": import("@oclif/core/interfaces").OptionFlag<"," | ";">;
18
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  };
19
20
  getData(): Promise<Response>;
20
21
  protected mapData(data: SuccessfulResponse<Response, 200>["data"]): MittwaldAPIV2.Components.Schemas.SignupSshKey[];
@@ -1,7 +1,9 @@
1
- import { Command } from "@oclif/core";
2
- import { MittwaldAPIV2Client } from "@mittwald/api-client";
3
- export declare abstract class BaseCommand extends Command {
4
- protected authenticationRequired: boolean;
5
- protected apiClient: MittwaldAPIV2Client;
1
+ import { CoreBaseCommand } from "./CoreBaseCommand.js";
2
+ /** Base command class for authenticated commands that includes the --token flag. */
3
+ export declare abstract class BaseCommand extends CoreBaseCommand {
4
+ static baseFlags: {
5
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ };
6
7
  init(): Promise<void>;
8
+ private getEffectiveTokenWithFlag;
7
9
  }
@@ -1,18 +1,28 @@
1
- import { Command } from "@oclif/core";
1
+ import { Flags } from "@oclif/core";
2
2
  import { MittwaldAPIV2Client } from "@mittwald/api-client";
3
- import { configureAxiosRetry } from "../apiutil/api_retry.js";
4
- import { configureConsistencyHandling } from "../apiutil/api_consistency.js";
5
3
  import { getTokenFilename, readApiToken } from "../auth/token.js";
4
+ import { CoreBaseCommand } from "./CoreBaseCommand.js";
6
5
  import { configureAxiosLogging } from "../apiutil/api_logging.js";
7
- export class BaseCommand extends Command {
8
- authenticationRequired = true;
9
- apiClient = MittwaldAPIV2Client.newUnauthenticated();
6
+ import { configureAxiosRetry } from "../apiutil/api_retry.js";
7
+ import { configureConsistencyHandling } from "../apiutil/api_consistency.js";
8
+ import NoTokenFoundError from "../error/NoTokenFoundError.js";
9
+ /** Base command class for authenticated commands that includes the --token flag. */
10
+ export class BaseCommand extends CoreBaseCommand {
11
+ static baseFlags = {
12
+ token: Flags.string({
13
+ description: "API token to use for authentication (overrides environment and config file). NOTE: watch out that tokens passed via this flag might be logged in your shell history.",
14
+ required: false,
15
+ helpGroup: "AUTHENTICATION",
16
+ }),
17
+ };
10
18
  async init() {
11
19
  await super.init();
20
+ // Override the parent's auth behavior to include --token flag support
12
21
  if (this.authenticationRequired) {
13
- const token = await readApiToken(this.config);
22
+ const { flags } = await this.parse();
23
+ const token = await this.getEffectiveTokenWithFlag(flags);
14
24
  if (token === undefined) {
15
- throw new Error(`Could not get token from either config file (${getTokenFilename(this.config)}) or environment`);
25
+ throw new NoTokenFoundError(getTokenFilename(this.config));
16
26
  }
17
27
  this.apiClient = MittwaldAPIV2Client.newWithToken(token);
18
28
  this.apiClient.axios.defaults.headers["User-Agent"] =
@@ -22,4 +32,12 @@ export class BaseCommand extends Command {
22
32
  configureConsistencyHandling(this.apiClient.axios);
23
33
  }
24
34
  }
35
+ async getEffectiveTokenWithFlag(flags) {
36
+ // 1. Check --token flag first (highest precedence)
37
+ if (flags.token) {
38
+ return flags.token;
39
+ }
40
+ // 2. Fall back to existing readApiToken logic (env then file)
41
+ return await readApiToken(this.config);
42
+ }
25
43
  }
@@ -1,6 +1,6 @@
1
- import { BaseCommand } from "./BaseCommand.js";
1
+ import { CoreBaseCommand } from "./CoreBaseCommand.js";
2
2
  import { Interfaces } from "@oclif/core";
3
3
  /** CommandFlags is a helper type that extracts the flags from a command class. */
4
- export type CommandFlags<T extends typeof BaseCommand> = Interfaces.InferredFlags<T["flags"]>;
4
+ export type CommandFlags<T extends typeof CoreBaseCommand> = Interfaces.InferredFlags<T["flags"]>;
5
5
  /** CommandArgs is a helper type that extracts the args from a command class. */
6
- export type CommandArgs<T extends typeof BaseCommand> = Interfaces.InferredArgs<T["args"]>;
6
+ export type CommandArgs<T extends typeof CoreBaseCommand> = Interfaces.InferredArgs<T["args"]>;
@@ -0,0 +1,12 @@
1
+ import { Command } from "@oclif/core";
2
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
3
+ /**
4
+ * Core base command class that provides common functionality but no
5
+ * authentication flags. This is the base for both authenticated and
6
+ * unauthenticated command hierarchies.
7
+ */
8
+ export declare abstract class CoreBaseCommand extends Command {
9
+ protected authenticationRequired: boolean;
10
+ protected apiClient: MittwaldAPIV2Client;
11
+ protected getEffectiveToken(): Promise<string | undefined>;
12
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from "@oclif/core";
2
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
3
+ import { readApiToken } from "../auth/token.js";
4
+ /**
5
+ * Core base command class that provides common functionality but no
6
+ * authentication flags. This is the base for both authenticated and
7
+ * unauthenticated command hierarchies.
8
+ */
9
+ export class CoreBaseCommand extends Command {
10
+ authenticationRequired = true;
11
+ apiClient = MittwaldAPIV2Client.newUnauthenticated();
12
+ async getEffectiveToken() {
13
+ // Only check env and file - no --token flag in this base class
14
+ return await readApiToken(this.config);
15
+ }
16
+ }
@@ -5,6 +5,7 @@ export declare abstract class DeleteBaseCommand<T extends typeof BaseCommand> ex
5
5
  static baseFlags: {
6
6
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
7
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
9
10
  protected exec(): Promise<void>;
10
11
  protected render(): null;
@@ -7,6 +7,7 @@ import { Text } from "ink";
7
7
  export class DeleteBaseCommand extends ExecRenderBaseCommand {
8
8
  static resourceName = "object";
9
9
  static baseFlags = {
10
+ ...ExecRenderBaseCommand.baseFlags,
10
11
  ...processFlags,
11
12
  force: Flags.boolean({
12
13
  char: "f",
@@ -2,6 +2,9 @@ import { BaseCommand } from "./BaseCommand.js";
2
2
  import { ReactNode } from "react";
3
3
  import { ExtendedBaseCommand } from "./ExtendedBaseCommand.js";
4
4
  export declare abstract class ExecRenderBaseCommand<T extends typeof BaseCommand, TRes> extends ExtendedBaseCommand<T> {
5
+ static baseFlags: {
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
5
8
  protected abstract exec(): Promise<TRes>;
6
9
  run(): Promise<void>;
7
10
  protected abstract render(executionResult: TRes): ReactNode;
@@ -14,6 +14,9 @@ function wrapRender(fn) {
14
14
  };
15
15
  }
16
16
  export class ExecRenderBaseCommand extends ExtendedBaseCommand {
17
+ static baseFlags = {
18
+ ...ExtendedBaseCommand.baseFlags,
19
+ };
17
20
  async run() {
18
21
  const result = await this.exec();
19
22
  const wrappedRender = wrapRender(this.render.bind(this));
@@ -2,6 +2,9 @@ import { BaseCommand } from "./BaseCommand.js";
2
2
  import { CommandArgs, CommandFlags } from "./CommandFlags.js";
3
3
  import { CommandType } from "../context/FlagSetBuilder.js";
4
4
  export declare abstract class ExtendedBaseCommand<T extends typeof BaseCommand> extends BaseCommand {
5
+ static baseFlags: {
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
5
8
  protected flags: CommandFlags<T>;
6
9
  protected args: CommandArgs<T>;
7
10
  init(): Promise<void>;
@@ -4,6 +4,9 @@ import { withProjectId } from "../resources/project/flags.js";
4
4
  import { withServerId } from "../resources/server/flags.js";
5
5
  import { withStackId } from "../resources/stack/flags.js";
6
6
  export class ExtendedBaseCommand extends BaseCommand {
7
+ static baseFlags = {
8
+ ...BaseCommand.baseFlags,
9
+ };
7
10
  flags;
8
11
  args;
9
12
  async init() {
@@ -7,7 +7,7 @@ export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof G
7
7
  export type Args<T extends typeof Command> = Interfaces.InferredArgs<T["args"]>;
8
8
  export declare abstract class GetBaseCommand<T extends typeof BaseCommand, TAPIResponse extends Response> extends ExtendedBaseCommand<T> {
9
9
  static baseFlags: {
10
- [x: string]: Interfaces.Flag<any>;
10
+ token: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
11
11
  };
12
12
  protected formatter: GetFormatter;
13
13
  init(): Promise<void>;
@@ -3,6 +3,7 @@ import { ExtendedBaseCommand } from "./ExtendedBaseCommand.js";
3
3
  import { GetFormatter } from "../../rendering/formatter/GetFormatter.js";
4
4
  export class GetBaseCommand extends ExtendedBaseCommand {
5
5
  static baseFlags = {
6
+ ...ExtendedBaseCommand.baseFlags,
6
7
  ...GetFormatter.flags,
7
8
  };
8
9
  formatter = new GetFormatter();
@@ -19,6 +19,7 @@ export declare abstract class ListBaseCommand<T extends typeof BaseCommand, TIte
19
19
  "no-truncate": Interfaces.BooleanFlag<boolean>;
20
20
  "no-relative-dates": Interfaces.BooleanFlag<boolean>;
21
21
  "csv-separator": Interfaces.OptionFlag<"," | ";">;
22
+ token: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
22
23
  };
23
24
  protected formatter: ListFormatter;
24
25
  protected sorter?: SorterFunction<TItem>;
@@ -4,6 +4,7 @@ import { ExtendedBaseCommand } from "./ExtendedBaseCommand.js";
4
4
  import ListDateColumnFormatter, { isResourceWithCreatedAt, } from "../../rendering/formatter/ListDateColumnFormatter.js";
5
5
  export class ListBaseCommand extends ExtendedBaseCommand {
6
6
  static baseFlags = {
7
+ ...ExtendedBaseCommand.baseFlags,
7
8
  ...ListFormatter.flags,
8
9
  };
9
10
  formatter = new ListFormatter();
@@ -0,0 +1,10 @@
1
+ import { CoreBaseCommand } from "./CoreBaseCommand.js";
2
+ /**
3
+ * Base command class for commands that do not require authentication. This
4
+ * class extends CoreBaseCommand directly, bypassing the --token flag from
5
+ * BaseCommand.
6
+ */
7
+ export declare abstract class UnauthenticatedBaseCommand extends CoreBaseCommand {
8
+ protected authenticationRequired: boolean;
9
+ init(): Promise<void>;
10
+ }
@@ -0,0 +1,16 @@
1
+ import { CoreBaseCommand } from "./CoreBaseCommand.js";
2
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
3
+ /**
4
+ * Base command class for commands that do not require authentication. This
5
+ * class extends CoreBaseCommand directly, bypassing the --token flag from
6
+ * BaseCommand.
7
+ */
8
+ export class UnauthenticatedBaseCommand extends CoreBaseCommand {
9
+ // No baseFlags defined - we don't want the --token flag for these commands
10
+ authenticationRequired = false;
11
+ async init() {
12
+ await super.init();
13
+ // For unauthenticated commands, keep the unauthenticated API client
14
+ this.apiClient = this.apiClient || MittwaldAPIV2Client.newUnauthenticated();
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This class represents a custom error thrown when no token can be found. The
3
+ * error specifically occurs if the token is not provided via the --token flag,
4
+ * the MITTWALD_API_TOKEN environment variable, or the configuration file.
5
+ *
6
+ * This is a dedicated error class so that the error handler can check for this
7
+ * specific type of error and present it differently to the user.
8
+ */
9
+ export default class NoTokenFoundError extends Error {
10
+ constructor(tokenFilename: string);
11
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * This class represents a custom error thrown when no token can be found. The
3
+ * error specifically occurs if the token is not provided via the --token flag,
4
+ * the MITTWALD_API_TOKEN environment variable, or the configuration file.
5
+ *
6
+ * This is a dedicated error class so that the error handler can check for this
7
+ * specific type of error and present it differently to the user.
8
+ */
9
+ export default class NoTokenFoundError extends Error {
10
+ constructor(tokenFilename) {
11
+ super(`Could not get token from --token flag, MITTWALD_API_TOKEN env var, or config file (${tokenFilename}). Please run "mw login token" or use --token.`);
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ import { SSHConnectionData } from "./types.js";
3
+ export declare function getSSHConnectionForContainer(client: MittwaldAPIV2Client, containerId: string, stackId: string, sshUser: string | undefined): Promise<SSHConnectionData>;
@@ -0,0 +1,29 @@
1
+ import { assertStatus } from "@mittwald/api-client";
2
+ export async function getSSHConnectionForContainer(client, containerId, stackId, sshUser) {
3
+ // Get container details
4
+ const containerResponse = await client.container.getService({
5
+ stackId,
6
+ serviceId: containerId,
7
+ });
8
+ assertStatus(containerResponse, 200);
9
+ // Get project details
10
+ const projectResponse = await client.project.getProject({
11
+ projectId: containerResponse.data.projectId,
12
+ });
13
+ assertStatus(projectResponse, 200);
14
+ // If no SSH user is provided, use the current user's email
15
+ if (sshUser === undefined) {
16
+ const userResponse = await client.user.getUser({ userId: "self" });
17
+ assertStatus(userResponse, 200);
18
+ sshUser = userResponse.data.email;
19
+ }
20
+ // Construct the SSH connection data
21
+ const host = `ssh.${projectResponse.data.clusterID}.${projectResponse.data.clusterDomain}`;
22
+ const user = `${sshUser}@${containerResponse.data.shortId}`;
23
+ const directory = "/";
24
+ return {
25
+ host,
26
+ user,
27
+ directory,
28
+ };
29
+ }
@@ -1,4 +1,4 @@
1
- import { type MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
1
+ import { type MittwaldAPIV2 } from "@mittwald/api-client";
2
2
  type StackRequest = MittwaldAPIV2.Paths.V2StacksStackId.Put.Parameters.RequestBody;
3
- export declare function enrichStackDefinition(apiClient: MittwaldAPIV2Client, projectId: string, input: StackRequest): Promise<StackRequest>;
3
+ export declare function enrichStackDefinition(input: StackRequest): Promise<StackRequest>;
4
4
  export {};
@@ -1,11 +1,9 @@
1
- import { assertStatus, } from "@mittwald/api-client";
2
1
  import { readFile } from "fs/promises";
3
2
  import { parse } from "envfile";
4
- export async function enrichStackDefinition(apiClient, projectId, input) {
3
+ export async function enrichStackDefinition(input) {
5
4
  const enriched = structuredClone(input);
6
5
  for (const serviceName of Object.keys(input.services ?? {})) {
7
6
  let service = enriched.services[serviceName];
8
- service = await setCommandAndEntrypointFromImage(apiClient, projectId, service);
9
7
  service = await setEnvironmentFromEnvFile(service);
10
8
  enriched.services[serviceName] = service;
11
9
  }
@@ -25,31 +23,3 @@ async function setEnvironmentFromEnvFile(service) {
25
23
  };
26
24
  return enriched;
27
25
  }
28
- async function setCommandAndEntrypointFromImage(apiClient, projectId, service) {
29
- const enriched = structuredClone(service);
30
- const resp = await apiClient.container.getContainerImageConfig({
31
- queryParameters: {
32
- imageReference: service.image,
33
- useCredentialsForProjectId: projectId,
34
- },
35
- });
36
- assertStatus(resp, 200);
37
- if (service.ports === undefined) {
38
- enriched.ports = (resp.data.exposedPorts ?? []).map((p) => p.port);
39
- }
40
- if (service.command === undefined) {
41
- let command = resp.data.command;
42
- if (typeof command === "string") {
43
- command = [command];
44
- }
45
- enriched.command = command;
46
- }
47
- if (service.entrypoint === undefined) {
48
- let entrypoint = resp.data.entrypoint;
49
- if (typeof entrypoint === "string") {
50
- entrypoint = [entrypoint];
51
- }
52
- enriched.entrypoint = entrypoint;
53
- }
54
- return enriched;
55
- }
@@ -0,0 +1,11 @@
1
+ /** Represents a mapping between a local port and a remote port. */
2
+ export default class PortMapping {
3
+ readonly localPort: number;
4
+ readonly remotePort: number;
5
+ constructor(localPort: number, remotePort: number);
6
+ private static validatePort;
7
+ static arg: import("@oclif/core/interfaces").ArgDefinition<PortMapping, Record<string, unknown>>;
8
+ /** @param str Port and protocol; example: `8080/tcp` */
9
+ static fromPortAndProtocol(str: string): PortMapping;
10
+ static fromString(str: string): PortMapping;
11
+ }