@mittwald/cli 1.5.0 → 1.6.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 (117) hide show
  1. package/dist/commands/app/dependency/list.d.ts +1 -0
  2. package/dist/commands/app/dependency/versions.d.ts +1 -0
  3. package/dist/commands/app/get.js +1 -2
  4. package/dist/commands/app/list-upgrade-candidates.d.ts +1 -0
  5. package/dist/commands/app/list.d.ts +1 -0
  6. package/dist/commands/backup/delete.d.ts +1 -0
  7. package/dist/commands/backup/get.js +1 -2
  8. package/dist/commands/backup/list.d.ts +1 -0
  9. package/dist/commands/backup/schedule/list.d.ts +1 -0
  10. package/dist/commands/container/delete.d.ts +1 -0
  11. package/dist/commands/container/list.d.ts +1 -0
  12. package/dist/commands/container/logs.d.ts +1 -0
  13. package/dist/commands/container/run.d.ts +0 -16
  14. package/dist/commands/container/run.js +4 -61
  15. package/dist/commands/container/update.d.ts +36 -0
  16. package/dist/commands/container/update.js +174 -0
  17. package/dist/commands/conversation/categories.d.ts +1 -0
  18. package/dist/commands/conversation/create.d.ts +1 -0
  19. package/dist/commands/conversation/list.d.ts +1 -0
  20. package/dist/commands/conversation/reply.d.ts +1 -0
  21. package/dist/commands/cronjob/delete.d.ts +1 -0
  22. package/dist/commands/cronjob/execution/get.d.ts +1 -1
  23. package/dist/commands/cronjob/execution/list.d.ts +1 -0
  24. package/dist/commands/cronjob/execution/logs.d.ts +1 -0
  25. package/dist/commands/cronjob/get.js +1 -2
  26. package/dist/commands/cronjob/list.d.ts +1 -0
  27. package/dist/commands/database/list.d.ts +1 -0
  28. package/dist/commands/database/mysql/charsets.d.ts +1 -0
  29. package/dist/commands/database/mysql/delete.d.ts +1 -0
  30. package/dist/commands/database/mysql/get.d.ts +1 -1
  31. package/dist/commands/database/mysql/list.d.ts +1 -0
  32. package/dist/commands/database/mysql/user/delete.d.ts +1 -0
  33. package/dist/commands/database/mysql/user/get.d.ts +1 -1
  34. package/dist/commands/database/mysql/user/list.d.ts +1 -0
  35. package/dist/commands/database/mysql/versions.d.ts +1 -0
  36. package/dist/commands/database/redis/get.d.ts +1 -1
  37. package/dist/commands/database/redis/list.d.ts +1 -0
  38. package/dist/commands/database/redis/versions.d.ts +1 -0
  39. package/dist/commands/domain/dnszone/get.js +1 -2
  40. package/dist/commands/domain/dnszone/list.d.ts +1 -0
  41. package/dist/commands/domain/get.js +1 -2
  42. package/dist/commands/domain/list.d.ts +1 -0
  43. package/dist/commands/domain/virtualhost/delete.d.ts +1 -0
  44. package/dist/commands/domain/virtualhost/get.js +1 -2
  45. package/dist/commands/domain/virtualhost/list.d.ts +1 -0
  46. package/dist/commands/extension/list-installed.d.ts +1 -0
  47. package/dist/commands/extension/list.d.ts +1 -0
  48. package/dist/commands/mail/address/create.d.ts +2 -1
  49. package/dist/commands/mail/address/create.js +2 -1
  50. package/dist/commands/mail/address/delete.d.ts +1 -0
  51. package/dist/commands/mail/address/get.d.ts +1 -1
  52. package/dist/commands/mail/address/list.d.ts +1 -0
  53. package/dist/commands/mail/deliverybox/delete.d.ts +1 -0
  54. package/dist/commands/mail/deliverybox/get.d.ts +1 -1
  55. package/dist/commands/mail/deliverybox/list.d.ts +1 -0
  56. package/dist/commands/org/delete.d.ts +1 -0
  57. package/dist/commands/org/get.js +1 -2
  58. package/dist/commands/org/invite/list-own.d.ts +1 -0
  59. package/dist/commands/org/invite/list.d.ts +1 -0
  60. package/dist/commands/org/list.d.ts +1 -0
  61. package/dist/commands/org/membership/list-own.d.ts +4 -0
  62. package/dist/commands/org/membership/list.d.ts +4 -0
  63. package/dist/commands/project/delete.d.ts +1 -0
  64. package/dist/commands/project/filesystem/usage.js +1 -2
  65. package/dist/commands/project/get.js +1 -2
  66. package/dist/commands/project/invite/get.d.ts +1 -1
  67. package/dist/commands/project/invite/list-own.d.ts +1 -0
  68. package/dist/commands/project/invite/list.d.ts +1 -0
  69. package/dist/commands/project/list.d.ts +1 -0
  70. package/dist/commands/project/membership/get-own.d.ts +1 -0
  71. package/dist/commands/project/membership/get.d.ts +1 -1
  72. package/dist/commands/project/membership/list-own.d.ts +1 -0
  73. package/dist/commands/project/membership/list.d.ts +1 -0
  74. package/dist/commands/registry/delete.d.ts +1 -0
  75. package/dist/commands/registry/list.d.ts +1 -0
  76. package/dist/commands/server/get.d.ts +1 -1
  77. package/dist/commands/server/list.d.ts +1 -0
  78. package/dist/commands/sftp-user/delete.d.ts +1 -0
  79. package/dist/commands/sftp-user/list.d.ts +1 -0
  80. package/dist/commands/ssh-user/delete.d.ts +1 -0
  81. package/dist/commands/ssh-user/list.d.ts +1 -0
  82. package/dist/commands/stack/delete.d.ts +1 -0
  83. package/dist/commands/stack/deploy.js +1 -8
  84. package/dist/commands/stack/list.d.ts +1 -0
  85. package/dist/commands/stack/ps.d.ts +1 -0
  86. package/dist/commands/stack/ps.js +1 -1
  87. package/dist/commands/user/api-token/get.d.ts +1 -1
  88. package/dist/commands/user/api-token/list.d.ts +1 -0
  89. package/dist/commands/user/api-token/revoke.d.ts +1 -0
  90. package/dist/commands/user/get.d.ts +1 -1
  91. package/dist/commands/user/session/get.d.ts +1 -1
  92. package/dist/commands/user/session/list.d.ts +1 -0
  93. package/dist/commands/user/ssh-key/delete.d.ts +1 -0
  94. package/dist/commands/user/ssh-key/get.d.ts +1 -1
  95. package/dist/commands/user/ssh-key/list.d.ts +1 -0
  96. package/dist/lib/basecommands/BaseCommand.d.ts +7 -5
  97. package/dist/lib/basecommands/BaseCommand.js +25 -8
  98. package/dist/lib/basecommands/CommandFlags.d.ts +3 -3
  99. package/dist/lib/basecommands/CoreBaseCommand.d.ts +12 -0
  100. package/dist/lib/basecommands/CoreBaseCommand.js +16 -0
  101. package/dist/lib/basecommands/DeleteBaseCommand.d.ts +1 -0
  102. package/dist/lib/basecommands/DeleteBaseCommand.js +1 -0
  103. package/dist/lib/basecommands/ExecRenderBaseCommand.d.ts +3 -0
  104. package/dist/lib/basecommands/ExecRenderBaseCommand.js +3 -0
  105. package/dist/lib/basecommands/ExtendedBaseCommand.d.ts +3 -0
  106. package/dist/lib/basecommands/ExtendedBaseCommand.js +3 -0
  107. package/dist/lib/basecommands/GetBaseCommand.d.ts +1 -1
  108. package/dist/lib/basecommands/GetBaseCommand.js +1 -0
  109. package/dist/lib/basecommands/ListBaseCommand.d.ts +1 -0
  110. package/dist/lib/basecommands/ListBaseCommand.js +1 -0
  111. package/dist/lib/basecommands/UnauthenticatedBaseCommand.d.ts +10 -0
  112. package/dist/lib/basecommands/UnauthenticatedBaseCommand.js +16 -0
  113. package/dist/lib/resources/container/containerconfig.d.ts +43 -0
  114. package/dist/lib/resources/container/containerconfig.js +82 -0
  115. package/dist/lib/resources/stack/enrich.d.ts +2 -2
  116. package/dist/lib/resources/stack/enrich.js +1 -31
  117. package/package.json +8 -8
@@ -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,27 @@
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
+ /** Base command class for authenticated commands that includes the --token flag. */
9
+ export class BaseCommand extends CoreBaseCommand {
10
+ static baseFlags = {
11
+ token: Flags.string({
12
+ 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.",
13
+ required: false,
14
+ helpGroup: "AUTHENTICATION",
15
+ }),
16
+ };
10
17
  async init() {
11
18
  await super.init();
19
+ // Override the parent's auth behavior to include --token flag support
12
20
  if (this.authenticationRequired) {
13
- const token = await readApiToken(this.config);
21
+ const { flags } = await this.parse();
22
+ const token = await this.getEffectiveTokenWithFlag(flags);
14
23
  if (token === undefined) {
15
- throw new Error(`Could not get token from either config file (${getTokenFilename(this.config)}) or environment`);
24
+ throw new Error(`Could not get token from --token flag, MITTWALD_API_TOKEN env var, or config file (${getTokenFilename(this.config)}). Please run "mw login token" or use --token.`);
16
25
  }
17
26
  this.apiClient = MittwaldAPIV2Client.newWithToken(token);
18
27
  this.apiClient.axios.defaults.headers["User-Agent"] =
@@ -22,4 +31,12 @@ export class BaseCommand extends Command {
22
31
  configureConsistencyHandling(this.apiClient.axios);
23
32
  }
24
33
  }
34
+ async getEffectiveTokenWithFlag(flags) {
35
+ // 1. Check --token flag first (highest precedence)
36
+ if (flags.token) {
37
+ return flags.token;
38
+ }
39
+ // 2. Fall back to existing readApiToken logic (env then file)
40
+ return await readApiToken(this.config);
41
+ }
25
42
  }
@@ -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,43 @@
1
+ import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ type ContainerContainerImageConfig = MittwaldAPIV2.Components.Schemas.ContainerContainerImageConfig;
3
+ /**
4
+ * Parses environment variables from command line flags and env files
5
+ *
6
+ * @param envFlags Array of environment variable strings in KEY=VALUE format
7
+ * @param envFiles Array of paths to env files
8
+ * @returns An object containing environment variable key-value pairs
9
+ */
10
+ export declare function parseEnvironmentVariables(envFlags?: string[], envFiles?: string[]): Promise<Record<string, string>>;
11
+ /**
12
+ * Parses environment variables from env files
13
+ *
14
+ * @param envFiles Array of paths to env files
15
+ * @returns An object containing environment variable key-value pairs
16
+ */
17
+ export declare function parseEnvironmentVariablesFromFile(envFiles?: string[]): Promise<Record<string, string>>;
18
+ /**
19
+ * Parses environment variables from command line flags
20
+ *
21
+ * @param envFlags Array of environment variable strings in KEY=VALUE format
22
+ * @returns An object containing environment variable key-value pairs
23
+ */
24
+ export declare function parseEnvironmentVariablesFromEnvFlags(envFlags?: string[]): Record<string, string>;
25
+ /**
26
+ * Determines which ports to expose based on image metadata
27
+ *
28
+ * @param imageMeta Metadata about the container image
29
+ * @param publishAll Whether to publish all ports defined in the image
30
+ * @param publishPorts Array of port mappings specified by the user
31
+ * @returns An array of port mappings
32
+ */
33
+ export declare function getPortMappings(imageMeta: ContainerContainerImageConfig, publishAll?: boolean, publishPorts?: string[]): string[];
34
+ /**
35
+ * Retrieves metadata for a container image
36
+ *
37
+ * @param apiClient The API client instance
38
+ * @param image The container image reference
39
+ * @param projectId The project ID for credentials
40
+ * @returns Metadata about the container image
41
+ */
42
+ export declare function getImageMeta(apiClient: MittwaldAPIV2Client, image: string, projectId: string): Promise<ContainerContainerImageConfig>;
43
+ export {};
@@ -0,0 +1,82 @@
1
+ import { assertStatus, } from "@mittwald/api-client";
2
+ import * as fs from "fs/promises";
3
+ import { parse } from "envfile";
4
+ import { pathExists } from "../../util/fs/pathExists.js";
5
+ /**
6
+ * Parses environment variables from command line flags and env files
7
+ *
8
+ * @param envFlags Array of environment variable strings in KEY=VALUE format
9
+ * @param envFiles Array of paths to env files
10
+ * @returns An object containing environment variable key-value pairs
11
+ */
12
+ export async function parseEnvironmentVariables(envFlags = [], envFiles = []) {
13
+ return {
14
+ ...parseEnvironmentVariablesFromEnvFlags(envFlags),
15
+ ...(await parseEnvironmentVariablesFromFile(envFiles)),
16
+ };
17
+ }
18
+ /**
19
+ * Parses environment variables from env files
20
+ *
21
+ * @param envFiles Array of paths to env files
22
+ * @returns An object containing environment variable key-value pairs
23
+ */
24
+ export async function parseEnvironmentVariablesFromFile(envFiles = []) {
25
+ const result = {};
26
+ for (const envFile of envFiles) {
27
+ if (!(await pathExists(envFile))) {
28
+ throw new Error(`Env file not found: ${envFile}`);
29
+ }
30
+ const fileContent = await fs.readFile(envFile, { encoding: "utf-8" });
31
+ const parsed = parse(fileContent);
32
+ Object.assign(result, parsed);
33
+ }
34
+ return result;
35
+ }
36
+ /**
37
+ * Parses environment variables from command line flags
38
+ *
39
+ * @param envFlags Array of environment variable strings in KEY=VALUE format
40
+ * @returns An object containing environment variable key-value pairs
41
+ */
42
+ export function parseEnvironmentVariablesFromEnvFlags(envFlags = []) {
43
+ const splitIntoKeyAndValue = (e) => e.split("=", 2);
44
+ return Object.fromEntries(envFlags.map(splitIntoKeyAndValue));
45
+ }
46
+ /**
47
+ * Determines which ports to expose based on image metadata
48
+ *
49
+ * @param imageMeta Metadata about the container image
50
+ * @param publishAll Whether to publish all ports defined in the image
51
+ * @param publishPorts Array of port mappings specified by the user
52
+ * @returns An array of port mappings
53
+ */
54
+ export function getPortMappings(imageMeta, publishAll = false, publishPorts = []) {
55
+ if (publishAll) {
56
+ const definedPorts = imageMeta.exposedPorts ?? [];
57
+ const concatPort = (p) => {
58
+ const [port, protocol = "tcp"] = p.port.split("/", 2);
59
+ return `${port}:${port}/${protocol}`;
60
+ };
61
+ return definedPorts.map(concatPort);
62
+ }
63
+ return publishPorts;
64
+ }
65
+ /**
66
+ * Retrieves metadata for a container image
67
+ *
68
+ * @param apiClient The API client instance
69
+ * @param image The container image reference
70
+ * @param projectId The project ID for credentials
71
+ * @returns Metadata about the container image
72
+ */
73
+ export async function getImageMeta(apiClient, image, projectId) {
74
+ const resp = await apiClient.container.getContainerImageConfig({
75
+ queryParameters: {
76
+ imageReference: image,
77
+ useCredentialsForProjectId: projectId,
78
+ },
79
+ });
80
+ assertStatus(resp, 200);
81
+ return resp.data;
82
+ }
@@ -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
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/cli",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Hand-crafted CLI for the mittwald API",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "type": "module",
18
18
  "engines": {
19
- "node": ">=18.0.0"
19
+ "node": ">=20.0.0"
20
20
  },
21
21
  "scripts": {
22
22
  "clean": "(rimraf --glob dist tsconfig.*.tsbuildinfo) | true",
@@ -62,7 +62,7 @@
62
62
  "marked-terminal": "^7.3.0",
63
63
  "open": "^10.0.3",
64
64
  "parse-duration": "^2.0.1",
65
- "pretty-bytes": "^6.1.0",
65
+ "pretty-bytes": "^7.0.0",
66
66
  "react": "^18.2.0",
67
67
  "semver": "^7.5.4",
68
68
  "semver-parser": "^4.1.6",
@@ -73,10 +73,10 @@
73
73
  "uuid": "^11.0.3"
74
74
  },
75
75
  "devDependencies": {
76
- "@jest/globals": "^29.7.0",
76
+ "@jest/globals": "^30.0.4",
77
77
  "@oclif/test": "^4.0.4",
78
78
  "@types/js-yaml": "^4.0.9",
79
- "@types/node": "^22.7.5",
79
+ "@types/node": "^24.0.10",
80
80
  "@types/react": "^18",
81
81
  "@types/semver": "^7.5.0",
82
82
  "@types/shell-escape": "^0.2.3",
@@ -88,11 +88,11 @@
88
88
  "eslint-plugin-json": "^4.0.1",
89
89
  "eslint-plugin-prettier": "^5.5.1",
90
90
  "globals": "^16.0.0",
91
- "jest": "^29.7.0",
91
+ "jest": "^30.0.4",
92
92
  "license-checker-rseidelsohn": "^4.2.6",
93
93
  "nock": "^14.0.0",
94
94
  "oclif": "^4.14.31",
95
- "prettier": "~3.5.3",
95
+ "prettier": "~3.6.2",
96
96
  "prettier-plugin-jsdoc": "^1.3.2",
97
97
  "prettier-plugin-package": "^1.4.0",
98
98
  "prettier-plugin-sort-json": "^4.1.1",
@@ -223,7 +223,7 @@
223
223
  ],
224
224
  "update": {
225
225
  "node": {
226
- "version": "18.20.4"
226
+ "version": "20.19.3"
227
227
  },
228
228
  "s3": {
229
229
  "xz": true,