@amerged/ohmyhost-mcp 0.1.49 → 0.1.52

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 (29) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-GLPFLQD6.js → chunk-EAD7RRAF.js} +5 -5
  3. package/dist/index.js +1 -1
  4. package/dist/stdio-main.js +555 -5
  5. package/package.json +1 -1
  6. package/src/apps/mcp/package.json +1 -1
  7. package/src/apps/mcp/src/local-client.ts +5 -0
  8. package/src/apps/mcp/src/local-server.ts +307 -1
  9. package/src/apps/product-cli/package.json +1 -1
  10. package/src/apps/product-cli/src/cli.ts +22 -0
  11. package/src/apps/product-cli/src/command.ts +235 -0
  12. package/src/apps/product-cli/src/composition.ts +7 -2
  13. package/src/apps/product-cli/src/index.ts +1 -0
  14. package/src/apps/product-cli/src/native-auth.ts +172 -0
  15. package/src/apps/product-cli/src/product-api.ts +6 -0
  16. package/src/packages/agent-skills/src/generated-skill-resources.ts +4 -4
  17. package/src/packages/contracts/package.json +10 -0
  18. package/src/packages/contracts/src/framework-admission.ts +5 -0
  19. package/src/packages/contracts/src/framework-source-rules.ts +9 -0
  20. package/src/packages/contracts/src/index.ts +1 -1
  21. package/src/packages/contracts/src/native-auth.ts +158 -0
  22. package/src/packages/contracts/src/previews.ts +179 -0
  23. package/src/packages/contracts/src/problem-policy.ts +8 -1
  24. package/src/packages/contracts/src/project-exports.ts +11 -3
  25. package/src/packages/sdk-ts/package.json +1 -1
  26. package/src/packages/sdk-ts/src/generated/index.ts +81 -0
  27. package/src/packages/sdk-ts/src/generated/sdk.gen.ts +604 -0
  28. package/src/packages/sdk-ts/src/generated/types.gen.ts +995 -4
  29. package/src/packages/workos-auth-contracts/src/webhooks.ts +30 -13
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/mcp",
3
- "version": "0.1.49",
3
+ "version": "0.1.52",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -4,6 +4,7 @@ import {
4
4
  type ManagedMailCommand,
5
5
  type SharedMailCommand,
6
6
  } from "@ohmyhost/sdk-ts";
7
+ import { executeNativeAuth, type NativeAuthCommand } from "@ohmyhost/product-cli";
7
8
  import {
8
9
  assertProjectDatabaseAccess,
9
10
  assertProjectDatabaseAccessCredential,
@@ -1156,6 +1157,10 @@ export class GeneratedSdkLocalMcpClient implements LocalMcpProductClient {
1156
1157
  public managedMail(input: ManagedMailCommand) {
1157
1158
  return executeManagedMail(this.#client, input);
1158
1159
  }
1160
+
1161
+ public nativeAuth(input: NativeAuthCommand) {
1162
+ return executeNativeAuth(this.#client, input);
1163
+ }
1159
1164
  public planDelete(projectId: string) {
1160
1165
  return planProjectDeletion({ project_id: projectId }, { client: this.#client });
1161
1166
  }
@@ -1,4 +1,5 @@
1
1
  import { isUserSecretName, USER_SECRET_NAME_PATTERN } from "@ohmyhost/contracts/secret-names";
2
+ import { isNativeAuthRedirectPath } from "@ohmyhost/contracts/native-auth";
2
3
  import type { DatabaseComputeSelection } from "@ohmyhost/contracts/database-sizes";
3
4
  import {
4
5
  DATABASE_WEBHOOK_EVENT_TYPES,
@@ -136,6 +137,7 @@ interface DatabaseScheduleSetInput extends DatabaseScheduleTarget {
136
137
  }
137
138
 
138
139
  export interface LocalMcpProductClient {
140
+ nativeAuth?(input: import("@ohmyhost/product-cli").NativeAuthCommand): Promise<unknown>;
139
141
  readonly managedSource?: ManagedSourceApi;
140
142
  readonly productApiOrigin?: string;
141
143
  checkProjectHandle(input: { handle: string }): Promise<unknown>;
@@ -1952,7 +1954,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
1952
1954
  server,
1953
1955
  dependencies,
1954
1956
  "deployment_plan",
1955
- "Plan an immutable deployment. Preserve the customer's existing authentication integration, such as Better Auth or WorkOS, and use the same generic Next.js/Vite/TanStack runtime contract. ohmyho.st login is separate. Read the app Skill's auth guidance and report actual plan blockers, required secret names/callback configuration and unverified runtime compatibility. The explicitly selected managed Better Auth bridge requires the managed database; managed mail is optional, and its verification or password-reset send actions require a configured platform sender. Application-owned Better Auth handlers can use their own sender, including Resend, with auth.provider set to none. Never assume a working platform login proves application login or silently replace its auth provider. The plan builds into Dev unless environment is prod; prod builds straight into Prod without a Dev deployment (a project with shared data and a database must deploy to Dev and promote instead). The plan stays valid for 24 hours.",
1957
+ "Plan an immutable deployment. Preserve the customer's existing authentication integration, such as Better Auth or WorkOS, and use the same generic Next.js/Vite/TanStack runtime contract. ohmyho.st login is separate. Read the app Skill's auth guidance and report actual plan blockers, required secret names/callback configuration and unverified runtime compatibility. The explicitly selected managed Better Auth bridge requires the managed database; managed mail is optional, and its verification or password-reset send actions require a configured platform sender. Application-owned Better Auth handlers can use their own sender, including Resend, with auth.provider set to none. The unreleased omh-native capability is an explicit choice requiring Paid access, a verified sender and its separately metered Auth database; verify readiness and hosted support before claiming it works. Never assume a working platform login proves application login or silently replace its auth provider. The plan builds into Dev unless environment is prod; prod builds straight into Prod without a Dev deployment (a project with shared data and a database must deploy to Dev and promote instead). The plan stays valid for 24 hours.",
1956
1958
  z.object({
1957
1959
  project_id: identifier,
1958
1960
  commit_sha: commitSha,
@@ -2653,6 +2655,310 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
2653
2655
  },
2654
2656
  destructiveAnnotations(),
2655
2657
  );
2658
+ const nativeScope = { project_id: identifier, environment_id: identifier };
2659
+ const nativeProvider = z.enum(["google", "github"]);
2660
+ const nativeRun = (
2661
+ client: LocalMcpProductClient,
2662
+ command: import("@ohmyhost/product-cli").NativeAuthCommand,
2663
+ ) => {
2664
+ if (!client.nativeAuth) throw new Error("Native Auth unavailable");
2665
+ return client.nativeAuth(command);
2666
+ };
2667
+ registerProductTool(
2668
+ server,
2669
+ dependencies,
2670
+ "native_auth_config_get",
2671
+ "Read Owner Native Auth configuration and readiness.",
2672
+ z.object(nativeScope),
2673
+ (client, input) =>
2674
+ nativeRun(client, {
2675
+ action: "config_get",
2676
+ projectId: input.project_id,
2677
+ environmentId: input.environment_id,
2678
+ }),
2679
+ );
2680
+ registerProductTool(
2681
+ server,
2682
+ dependencies,
2683
+ "native_auth_config_set",
2684
+ "Update Owner Native Auth configuration at the current revision.",
2685
+ z.object({
2686
+ ...nativeScope,
2687
+ configuration_revision: z.number().int().min(0),
2688
+ enabled: z.boolean(),
2689
+ signup_policy: z.enum(["open", "invite-only"]),
2690
+ application_name: z
2691
+ .string()
2692
+ .trim()
2693
+ .min(1)
2694
+ .max(128)
2695
+ .refine((value) => !/\p{Cc}/u.test(value)),
2696
+ canonical_origin: z
2697
+ .string()
2698
+ .url()
2699
+ .refine((value) => {
2700
+ try {
2701
+ const url = new URL(value);
2702
+ return (
2703
+ url.protocol === "https:" && url.origin === value && !url.username && !url.password
2704
+ );
2705
+ } catch {
2706
+ return false;
2707
+ }
2708
+ })
2709
+ .nullable()
2710
+ .optional(),
2711
+ allowed_redirect_paths: z
2712
+ .array(z.string().refine(isNativeAuthRedirectPath))
2713
+ .min(1)
2714
+ .max(16)
2715
+ .refine((paths) => new Set(paths).size === paths.length)
2716
+ .optional(),
2717
+ reset_password_path: z.string().refine(isNativeAuthRedirectPath).optional(),
2718
+ invitation_path: z.string().refine(isNativeAuthRedirectPath).optional(),
2719
+ idempotency_key: idempotencyKey,
2720
+ }),
2721
+ (client, input) => {
2722
+ const paths = input.allowed_redirect_paths;
2723
+ if (
2724
+ paths &&
2725
+ [input.reset_password_path, input.invitation_path].some(
2726
+ (path) => path !== undefined && !paths.includes(path),
2727
+ )
2728
+ )
2729
+ throw new TypeError("Native Auth form path must be in the redirect allowlist");
2730
+ return nativeRun(client, {
2731
+ action: "config_set",
2732
+ projectId: input.project_id,
2733
+ environmentId: input.environment_id,
2734
+ configurationRevision: input.configuration_revision,
2735
+ enabled: input.enabled,
2736
+ signupPolicy: input.signup_policy,
2737
+ applicationName: input.application_name,
2738
+ ...(input.canonical_origin === undefined
2739
+ ? {}
2740
+ : { canonicalOrigin: input.canonical_origin }),
2741
+ ...(input.allowed_redirect_paths === undefined
2742
+ ? {}
2743
+ : { allowedRedirectPaths: input.allowed_redirect_paths }),
2744
+ ...(input.reset_password_path === undefined
2745
+ ? {}
2746
+ : { resetPasswordPath: input.reset_password_path }),
2747
+ ...(input.invitation_path === undefined ? {} : { invitationPath: input.invitation_path }),
2748
+ idempotencyKey: input.idempotency_key,
2749
+ });
2750
+ },
2751
+ mutationAnnotations(),
2752
+ );
2753
+ registerProductTool(
2754
+ server,
2755
+ dependencies,
2756
+ "native_auth_provider_get",
2757
+ "Read provider setup without its secret.",
2758
+ z.object({ ...nativeScope, provider: nativeProvider }),
2759
+ (client, input) =>
2760
+ nativeRun(client, {
2761
+ action: "provider_get",
2762
+ projectId: input.project_id,
2763
+ environmentId: input.environment_id,
2764
+ provider: input.provider,
2765
+ }),
2766
+ );
2767
+ registerProductTool(
2768
+ server,
2769
+ dependencies,
2770
+ "native_auth_provider_set",
2771
+ "Update provider settings without submitting a secret.",
2772
+ z
2773
+ .object({
2774
+ ...nativeScope,
2775
+ provider: nativeProvider,
2776
+ configuration_revision: z.number().int().min(0),
2777
+ enabled: z.boolean(),
2778
+ client_id: z.string().min(1).max(1024).optional(),
2779
+ idempotency_key: idempotencyKey,
2780
+ })
2781
+ .strict(),
2782
+ (client, input) =>
2783
+ nativeRun(client, {
2784
+ action: "provider_set",
2785
+ projectId: input.project_id,
2786
+ environmentId: input.environment_id,
2787
+ provider: input.provider,
2788
+ configurationRevision: input.configuration_revision,
2789
+ enabled: input.enabled,
2790
+ ...(input.client_id === undefined ? {} : { clientId: input.client_id }),
2791
+ idempotencyKey: input.idempotency_key,
2792
+ }),
2793
+ mutationAnnotations(),
2794
+ );
2795
+ registerProductTool(
2796
+ server,
2797
+ dependencies,
2798
+ "native_auth_users_list",
2799
+ "List Native Auth users with bounded pagination.",
2800
+ z.object({
2801
+ ...nativeScope,
2802
+ limit: z.number().int().min(1).max(100).optional(),
2803
+ cursor: z.string().min(1).max(512).optional(),
2804
+ search: z.string().min(1).max(320).optional(),
2805
+ }),
2806
+ (client, input) =>
2807
+ nativeRun(client, {
2808
+ action: "users_list",
2809
+ projectId: input.project_id,
2810
+ environmentId: input.environment_id,
2811
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
2812
+ ...(input.cursor === undefined ? {} : { cursor: input.cursor }),
2813
+ ...(input.search === undefined ? {} : { search: input.search }),
2814
+ }),
2815
+ );
2816
+ registerProductTool(
2817
+ server,
2818
+ dependencies,
2819
+ "native_auth_user_create",
2820
+ "Create and invite a Native Auth user without choosing a password.",
2821
+ z.object({
2822
+ ...nativeScope,
2823
+ email: z.string().email().max(254),
2824
+ idempotency_key: idempotencyKey,
2825
+ }),
2826
+ (client, input) =>
2827
+ nativeRun(client, {
2828
+ action: "user_create",
2829
+ projectId: input.project_id,
2830
+ environmentId: input.environment_id,
2831
+ email: input.email,
2832
+ idempotencyKey: input.idempotency_key,
2833
+ }),
2834
+ mutationAnnotations(),
2835
+ );
2836
+ registerProductTool(
2837
+ server,
2838
+ dependencies,
2839
+ "native_auth_user_get",
2840
+ "Read a Native Auth user.",
2841
+ z.object({ ...nativeScope, user_id: z.string().uuid() }),
2842
+ (client, input) =>
2843
+ nativeRun(client, {
2844
+ action: "user_get",
2845
+ projectId: input.project_id,
2846
+ environmentId: input.environment_id,
2847
+ userId: input.user_id,
2848
+ }),
2849
+ );
2850
+ const nativeUserActionInput = z.object({
2851
+ ...nativeScope,
2852
+ user_id: z.string().uuid(),
2853
+ idempotency_key: idempotencyKey,
2854
+ confirm: z.literal(true),
2855
+ });
2856
+ const nativeUserAction =
2857
+ (
2858
+ action:
2859
+ | "user_delete"
2860
+ | "user_invite"
2861
+ | "user_suspend"
2862
+ | "user_reactivate"
2863
+ | "user_revoke_sessions",
2864
+ ) =>
2865
+ (client: LocalMcpProductClient, input: z.infer<typeof nativeUserActionInput>) =>
2866
+ nativeRun(client, {
2867
+ action,
2868
+ projectId: input.project_id,
2869
+ environmentId: input.environment_id,
2870
+ userId: input.user_id,
2871
+ idempotencyKey: input.idempotency_key,
2872
+ });
2873
+ registerProductTool(
2874
+ server,
2875
+ dependencies,
2876
+ "native_auth_user_delete",
2877
+ "Delete an Owner-authorized Native Auth user.",
2878
+ nativeUserActionInput,
2879
+ nativeUserAction("user_delete"),
2880
+ destructiveAnnotations(),
2881
+ );
2882
+ registerProductTool(
2883
+ server,
2884
+ dependencies,
2885
+ "native_auth_user_invite",
2886
+ "Resend an Owner-authorized Native Auth user's invitation.",
2887
+ nativeUserActionInput,
2888
+ nativeUserAction("user_invite"),
2889
+ destructiveAnnotations(),
2890
+ );
2891
+ registerProductTool(
2892
+ server,
2893
+ dependencies,
2894
+ "native_auth_user_suspend",
2895
+ "Suspend an Owner-authorized Native Auth user.",
2896
+ nativeUserActionInput,
2897
+ nativeUserAction("user_suspend"),
2898
+ destructiveAnnotations(),
2899
+ );
2900
+ registerProductTool(
2901
+ server,
2902
+ dependencies,
2903
+ "native_auth_user_reactivate",
2904
+ "Reactivate an Owner-authorized Native Auth user.",
2905
+ nativeUserActionInput,
2906
+ nativeUserAction("user_reactivate"),
2907
+ destructiveAnnotations(),
2908
+ );
2909
+ registerProductTool(
2910
+ server,
2911
+ dependencies,
2912
+ "native_auth_user_revoke_sessions",
2913
+ "Revoke an Owner-authorized Native Auth user's sessions.",
2914
+ nativeUserActionInput,
2915
+ nativeUserAction("user_revoke_sessions"),
2916
+ destructiveAnnotations(),
2917
+ );
2918
+ registerProductTool(
2919
+ server,
2920
+ dependencies,
2921
+ "native_auth_provider_secret_input_request",
2922
+ "Return an account-bound stdin-only command; never place a provider secret in MCP arguments.",
2923
+ z
2924
+ .object({
2925
+ ...nativeScope,
2926
+ provider: nativeProvider,
2927
+ configuration_revision: z.number().int().min(0),
2928
+ enabled: z.boolean(),
2929
+ client_id: z.string().min(1).max(1024).optional(),
2930
+ idempotency_key: idempotencyKey,
2931
+ })
2932
+ .strict(),
2933
+ async (client, input, signal) => {
2934
+ await client.getProject(input.project_id);
2935
+ const account = await handoverAccount(client, signal);
2936
+ return {
2937
+ command: [
2938
+ `${commandPrefix} auth provider set`,
2939
+ "--project",
2940
+ shellQuote(input.project_id),
2941
+ "--environment",
2942
+ shellQuote(input.environment_id),
2943
+ "--provider",
2944
+ input.provider,
2945
+ "--revision",
2946
+ String(input.configuration_revision),
2947
+ "--enabled",
2948
+ String(input.enabled),
2949
+ ...(input.client_id === undefined ? [] : ["--client-id", shellQuote(input.client_id)]),
2950
+ "--idempotency-key",
2951
+ shellQuote(input.idempotency_key),
2952
+ ...account.flags,
2953
+ "--stdin",
2954
+ "--json",
2955
+ ].join(" "),
2956
+ acting_as: client.actingAs?.() ?? null,
2957
+ instruction: `Pass the provider secret on stdin in a local terminal; ${account.runs}. Never put it in MCP, arguments, source, or logs.`,
2958
+ };
2959
+ },
2960
+ );
2961
+
2656
2962
  registerProductTool(
2657
2963
  server,
2658
2964
  dependencies,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/product-cli",
3
- "version": "0.1.49",
3
+ "version": "0.1.52",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -518,6 +518,7 @@ const HELP_USAGE = Object.freeze({
518
518
  "ohmyhost function runs --project ULID --environment ENVIRONMENT_ULID [--limit 1-100] --json",
519
519
  "secret set":
520
520
  "printf '%s' \"$SECRET_VALUE\" | ohmyhost secret set NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--profile-user USER_ID --profile-organization ULID | --token-user USER_ID --token-organization ULID] --stdin [--wait] --json (with --profile-user and --profile-organization the saved login that runs it must belong to that user and organization; with --token-user and --token-organization it runs only with an OHMYHOST_TOKEN of that user and organization, never with a saved login)",
521
+ auth: "ohmyhost auth config get|set --project ULID --environment ULID [--revision N --enabled true|false --signup-policy open|invite-only --application-name NAME --idempotency-key KEY] [--canonical-origin URL|primary] [--allowed-redirect-paths JSON_ARRAY --reset-password-path PATH --invitation-path PATH]; auth provider get|set --project ULID --environment ULID --provider google|github [--revision N --enabled true|false --client-id ID --idempotency-key KEY --stdin]; auth users list --project ULID --environment ULID [--limit N --cursor CURSOR --search TEXT]; auth user create|get|delete|invite|suspend|reactivate|revoke-sessions --project ULID --environment ULID [--email EMAIL | --user UUID] [--idempotency-key KEY]. Provider secrets are read only from stdin; use --json.",
521
522
  "secret delete":
522
523
  "ohmyhost secret delete NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--wait] --json",
523
524
  "mail workspace list": "ohmyhost mail workspace list --organization ULID --json",
@@ -1276,6 +1277,26 @@ export class ProductCli {
1276
1277
  if (command.kind === "secret-list") return await this.listEnvironmentSecrets(command, signal);
1277
1278
  if (command.kind === "function-runs") return await this.listFunctionRuns(command, signal);
1278
1279
  if (command.kind === "secret-set") return await this.putEnvironmentSecret(command, signal);
1280
+ if (command.kind === "native-auth") {
1281
+ const api = await this.authenticatedApi(command.credentialStore, signal);
1282
+ if (!api.nativeAuth) throw new Error("Native Auth unavailable");
1283
+ if (command.stdin && command.request.action === "provider_set") {
1284
+ const secret = await this.dependencies.readSecretValue(signal);
1285
+ const bytes = new TextEncoder().encode(secret);
1286
+ if (bytes.length === 0 || bytes.length > 5120) {
1287
+ bytes.fill(0);
1288
+ throw new SecretInputError();
1289
+ }
1290
+ try {
1291
+ return success("auth", {
1292
+ auth: await api.nativeAuth({ ...command.request, clientSecret: secret }),
1293
+ });
1294
+ } finally {
1295
+ bytes.fill(0);
1296
+ }
1297
+ }
1298
+ return success("auth", { auth: await api.nativeAuth(command.request) });
1299
+ }
1279
1300
  if (command.kind === "shared-mail") {
1280
1301
  const api = await this.authenticatedApi(command.credentialStore, signal);
1281
1302
  if (!api.sharedMail) throw new Error("Shared mail unavailable");
@@ -3693,6 +3714,7 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
3693
3714
  if (command.kind === "function-runs") return "function runs";
3694
3715
  if (command.kind === "secret-set") return "secret set";
3695
3716
  if (command.kind === "secret-delete") return "secret delete";
3717
+ if (command.kind === "native-auth") return "auth";
3696
3718
  if (command.kind === "shared-mail" || command.kind === "managed-mail") return command.label;
3697
3719
  if (command.kind === "profile-list") return "profile list";
3698
3720
  return command.kind;
@@ -26,6 +26,8 @@ import {
26
26
  type ProjectDataChangeKind,
27
27
  } from "@ohmyhost/contracts/project-data-changes";
28
28
  import type { ManagedMailCommand, SharedMailCommand } from "@ohmyhost/sdk-ts";
29
+ import type { NativeAuthCommand } from "./native-auth.js";
30
+ import { isNativeAuthRedirectPath } from "@ohmyhost/contracts/native-auth";
29
31
  import { USER_API_KEY_ID } from "@ohmyhost/contracts/user-api-keys";
30
32
  import { WORKOS_USER_API_KEY_PATTERN } from "@ohmyhost/workos-auth-contracts/user-api-keys";
31
33
  import { isProfileName, isProfileUserId } from "./profile-store.js";
@@ -86,6 +88,14 @@ export type SourceCliCommand =
86
88
  };
87
89
  export type ProductCliCommand =
88
90
  | SourceCliCommand
91
+ | {
92
+ readonly kind: "native-auth";
93
+ readonly request: NativeAuthCommand;
94
+ readonly stdin: boolean;
95
+ readonly credentialStore: "native";
96
+ readonly environmentToken?: HandedAccount;
97
+ readonly profileAccount?: HandedAccount;
98
+ }
89
99
  | {
90
100
  readonly kind: "project-data-plan";
91
101
  readonly projectId: string;
@@ -978,6 +988,7 @@ function parseSourceCommand(values: string[], wait: boolean): SourceCliCommand {
978
988
  }
979
989
 
980
990
  export type RecognizedCommandLabel =
991
+ | "auth"
981
992
  | "source inspect"
982
993
  | "source status"
983
994
  | "source files"
@@ -1291,6 +1302,7 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
1291
1302
  if (values[0] === "set") return "secret set";
1292
1303
  if (values[0] === "delete") return "secret delete";
1293
1304
  }
1305
+ if (value === "auth") return "auth";
1294
1306
  if (value === "function" && values[0] === "runs") return "function runs";
1295
1307
  if (value === "mail") {
1296
1308
  if (["workspace", "binding", "identity"].includes(values[0] ?? "")) {
@@ -1350,6 +1362,229 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
1350
1362
  const follow = removeFlag(values, "--follow");
1351
1363
  const stdin = removeFlag(values, "--stdin");
1352
1364
  const first = values.shift();
1365
+ if (first === "auth") {
1366
+ if (dryRun || revoke || yes || wait || follow) throw new InvalidCommandError();
1367
+ const area = values.shift();
1368
+ const action = values.shift();
1369
+ const actionKey = `${area}_${action}`;
1370
+ const options = parseOptionalOptions(values, [
1371
+ "--project",
1372
+ "--environment",
1373
+ "--idempotency-key",
1374
+ "--revision",
1375
+ "--enabled",
1376
+ "--signup-policy",
1377
+ "--application-name",
1378
+ "--canonical-origin",
1379
+ "--allowed-redirect-paths",
1380
+ "--reset-password-path",
1381
+ "--invitation-path",
1382
+ "--provider",
1383
+ "--client-id",
1384
+ "--user",
1385
+ "--email",
1386
+ "--limit",
1387
+ "--cursor",
1388
+ "--search",
1389
+ "--token-user",
1390
+ "--token-organization",
1391
+ "--profile-user",
1392
+ "--profile-organization",
1393
+ ]);
1394
+ const actionOptions: Readonly<Record<string, readonly string[]>> = {
1395
+ config_get: [],
1396
+ config_set: [
1397
+ "--idempotency-key",
1398
+ "--revision",
1399
+ "--enabled",
1400
+ "--signup-policy",
1401
+ "--application-name",
1402
+ "--canonical-origin",
1403
+ "--allowed-redirect-paths",
1404
+ "--reset-password-path",
1405
+ "--invitation-path",
1406
+ ],
1407
+ provider_get: ["--provider"],
1408
+ provider_set: [
1409
+ "--provider",
1410
+ "--idempotency-key",
1411
+ "--revision",
1412
+ "--enabled",
1413
+ "--client-id",
1414
+ "--token-user",
1415
+ "--token-organization",
1416
+ "--profile-user",
1417
+ "--profile-organization",
1418
+ ],
1419
+ users_list: ["--limit", "--cursor", "--search"],
1420
+ user_create: ["--idempotency-key", "--email"],
1421
+ user_get: ["--user"],
1422
+ user_delete: ["--user", "--idempotency-key"],
1423
+ user_invite: ["--user", "--idempotency-key"],
1424
+ user_suspend: ["--user", "--idempotency-key"],
1425
+ user_reactivate: ["--user", "--idempotency-key"],
1426
+ "user_revoke-sessions": ["--user", "--idempotency-key"],
1427
+ };
1428
+ const admittedOptions = actionOptions[actionKey];
1429
+ if (
1430
+ !admittedOptions ||
1431
+ Object.keys(options).some(
1432
+ (key) => key !== "--project" && key !== "--environment" && !admittedOptions.includes(key),
1433
+ )
1434
+ )
1435
+ throw new InvalidCommandError();
1436
+ const projectId = options["--project"],
1437
+ environmentId = options["--environment"];
1438
+ if (!projectId || !environmentId) throw new InvalidCommandError();
1439
+ const scope = { projectId, environmentId };
1440
+ const idempotencyKey = options["--idempotency-key"];
1441
+ const revision = Number(options["--revision"]);
1442
+ const enabled =
1443
+ options["--enabled"] === "true" ? true : options["--enabled"] === "false" ? false : null;
1444
+ const provider = options["--provider"];
1445
+ const userId = options["--user"];
1446
+ let request: NativeAuthCommand;
1447
+ if (actionKey === "config_get") request = { ...scope, action: "config_get" };
1448
+ else if (
1449
+ actionKey === "config_set" &&
1450
+ idempotencyKey &&
1451
+ Number.isSafeInteger(revision) &&
1452
+ revision >= 0 &&
1453
+ enabled !== null &&
1454
+ (options["--signup-policy"] === "open" || options["--signup-policy"] === "invite-only") &&
1455
+ options["--application-name"]
1456
+ ) {
1457
+ const rawPaths = options["--allowed-redirect-paths"];
1458
+ let paths: unknown;
1459
+ if (rawPaths !== undefined) {
1460
+ try {
1461
+ paths = JSON.parse(rawPaths);
1462
+ } catch {
1463
+ throw new InvalidCommandError();
1464
+ }
1465
+ if (
1466
+ !Array.isArray(paths) ||
1467
+ paths.length < 1 ||
1468
+ paths.length > 16 ||
1469
+ !paths.every(isNativeAuthRedirectPath) ||
1470
+ new Set(paths).size !== paths.length
1471
+ )
1472
+ throw new InvalidCommandError();
1473
+ }
1474
+ for (const path of [options["--reset-password-path"], options["--invitation-path"]]) {
1475
+ if (
1476
+ path !== undefined &&
1477
+ (!isNativeAuthRedirectPath(path) || (Array.isArray(paths) && !paths.includes(path)))
1478
+ )
1479
+ throw new InvalidCommandError();
1480
+ }
1481
+ const origin = options["--canonical-origin"];
1482
+ if (origin !== undefined && origin !== "primary") {
1483
+ try {
1484
+ const url = new URL(origin);
1485
+ if (url.protocol !== "https:" || url.origin !== origin || url.username || url.password)
1486
+ throw new InvalidCommandError();
1487
+ } catch {
1488
+ throw new InvalidCommandError();
1489
+ }
1490
+ }
1491
+ request = {
1492
+ ...scope,
1493
+ action: "config_set",
1494
+ idempotencyKey,
1495
+ configurationRevision: revision,
1496
+ enabled,
1497
+ signupPolicy: options["--signup-policy"],
1498
+ applicationName: options["--application-name"],
1499
+ ...(options["--canonical-origin"] === undefined
1500
+ ? {}
1501
+ : {
1502
+ canonicalOrigin:
1503
+ options["--canonical-origin"] === "primary" ? null : options["--canonical-origin"],
1504
+ }),
1505
+ ...(paths === undefined ? {} : { allowedRedirectPaths: paths as string[] }),
1506
+ ...(options["--reset-password-path"] === undefined
1507
+ ? {}
1508
+ : { resetPasswordPath: options["--reset-password-path"] }),
1509
+ ...(options["--invitation-path"] === undefined
1510
+ ? {}
1511
+ : { invitationPath: options["--invitation-path"] }),
1512
+ };
1513
+ } else if (actionKey === "provider_get" && (provider === "google" || provider === "github"))
1514
+ request = { ...scope, action: "provider_get", provider };
1515
+ else if (
1516
+ actionKey === "provider_set" &&
1517
+ idempotencyKey &&
1518
+ Number.isSafeInteger(revision) &&
1519
+ revision >= 0 &&
1520
+ enabled !== null &&
1521
+ (provider === "google" || provider === "github")
1522
+ )
1523
+ request = {
1524
+ ...scope,
1525
+ action: "provider_set",
1526
+ provider,
1527
+ idempotencyKey,
1528
+ configurationRevision: revision,
1529
+ enabled,
1530
+ ...(options["--client-id"] ? { clientId: options["--client-id"] } : {}),
1531
+ };
1532
+ else if (actionKey === "users_list") {
1533
+ const limit = options["--limit"] === undefined ? undefined : Number(options["--limit"]);
1534
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 100))
1535
+ throw new InvalidCommandError();
1536
+ request = {
1537
+ ...scope,
1538
+ action: "users_list",
1539
+ ...(limit === undefined ? {} : { limit }),
1540
+ ...(options["--cursor"] ? { cursor: options["--cursor"] } : {}),
1541
+ ...(options["--search"] ? { search: options["--search"] } : {}),
1542
+ };
1543
+ } else if (actionKey === "user_create" && idempotencyKey && options["--email"])
1544
+ request = { ...scope, action: "user_create", idempotencyKey, email: options["--email"] };
1545
+ else if (actionKey === "user_get" && userId) request = { ...scope, action: "user_get", userId };
1546
+ else if (
1547
+ idempotencyKey &&
1548
+ userId &&
1549
+ [
1550
+ "user_delete",
1551
+ "user_invite",
1552
+ "user_suspend",
1553
+ "user_reactivate",
1554
+ "user_revoke-sessions",
1555
+ ].includes(actionKey)
1556
+ )
1557
+ request = {
1558
+ ...scope,
1559
+ action: (actionKey === "user_revoke-sessions" ? "user_revoke_sessions" : actionKey) as
1560
+ | "user_delete"
1561
+ | "user_invite"
1562
+ | "user_suspend"
1563
+ | "user_reactivate"
1564
+ | "user_revoke_sessions",
1565
+ userId,
1566
+ idempotencyKey,
1567
+ };
1568
+ else throw new InvalidCommandError();
1569
+ if (stdin && request.action !== "provider_set") throw new InvalidCommandError();
1570
+ const environmentToken = handedAccount(
1571
+ options["--token-user"],
1572
+ options["--token-organization"],
1573
+ );
1574
+ const profileAccount = handedAccount(
1575
+ options["--profile-user"],
1576
+ options["--profile-organization"],
1577
+ );
1578
+ if (environmentToken && profileAccount) throw new InvalidCommandError();
1579
+ return {
1580
+ kind: "native-auth",
1581
+ request,
1582
+ stdin,
1583
+ credentialStore,
1584
+ ...(environmentToken ? { environmentToken } : {}),
1585
+ ...(profileAccount ? { profileAccount } : {}),
1586
+ };
1587
+ }
1353
1588
  if (first === "source" && values[0] !== "auto-deploy") {
1354
1589
  if (dryRun || revoke || yes || follow || stdin) throw new InvalidCommandError();
1355
1590
  return parseSourceCommand(values, wait);