@amerged/ohmyhost-mcp 0.1.18 → 0.1.20

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.
@@ -1,5 +1,7 @@
1
1
  import type { ManagedMailCommand } from "@ohmyhost/sdk-ts";
2
2
  import { USER_API_KEY_ID } from "@ohmyhost/contracts/user-api-keys";
3
+ import { WORKOS_USER_API_KEY_PATTERN } from "@ohmyhost/workos-auth-contracts/user-api-keys";
4
+ import { isProfileName, isProfileUserId } from "./profile-store.js";
3
5
  export type ProductCliCommand =
4
6
  | {
5
7
  readonly kind: "github-connect";
@@ -182,8 +184,11 @@ export type ProductCliCommand =
182
184
  | {
183
185
  readonly kind: "login";
184
186
  readonly organizationId?: string;
187
+ /** The user the browser must sign in as; a different account stores nothing. */
188
+ readonly userId?: string;
185
189
  readonly credentialStore: "native";
186
190
  }
191
+ | { readonly kind: "profile-list" }
187
192
  | {
188
193
  readonly kind: "logout";
189
194
  readonly revoke: boolean;
@@ -421,6 +426,10 @@ export type ProductCliCommand =
421
426
  readonly idempotencyKey: string;
422
427
  readonly stdin: true;
423
428
  readonly wait: boolean;
429
+ /** Handed over from an OHMYHOST_TOKEN process: only a key of this account may run it. */
430
+ readonly environmentToken?: HandedAccount;
431
+ /** Handed over for a saved login: the login that runs it must belong to this account. */
432
+ readonly profileAccount?: HandedAccount;
424
433
  readonly credentialStore: "native";
425
434
  }
426
435
  | {
@@ -497,6 +506,25 @@ export type ProductCliCommand =
497
506
  readonly credentialStore: "native";
498
507
  };
499
508
 
509
+ /**
510
+ * Remove the global login selector from argv. Every authenticated command accepts it; `login` uses
511
+ * the same flag to name the login it adds. The value is an alias, never a credential.
512
+ */
513
+ export const extractProfileName = (
514
+ argv: readonly string[],
515
+ ): { readonly argv: readonly string[]; readonly profileName?: string } => {
516
+ const index = argv.indexOf("--profile-name");
517
+ if (index < 0) return { argv };
518
+ const value = argv[index + 1];
519
+ if (
520
+ value === undefined ||
521
+ !isProfileName(value) ||
522
+ argv.indexOf("--profile-name", index + 1) >= 0
523
+ )
524
+ throw new InvalidCommandError();
525
+ return { argv: [...argv.slice(0, index), ...argv.slice(index + 2)], profileName: value };
526
+ };
527
+
500
528
  export class InvalidCommandError extends Error {
501
529
  public constructor() {
502
530
  super("The command arguments are invalid.");
@@ -528,6 +556,7 @@ export type RecognizedCommandLabel =
528
556
  | "init"
529
557
  | "login"
530
558
  | "logout"
559
+ | "profile list"
531
560
  | "operation get"
532
561
  | "operation reconcile"
533
562
  | "token create"
@@ -598,6 +627,10 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
598
627
  if (values.includes("--version")) return "version";
599
628
  while (values.length > 0) {
600
629
  const value = values.shift();
630
+ if (value === "--profile-name") {
631
+ values.shift();
632
+ continue;
633
+ }
601
634
  if (
602
635
  value === "--json" ||
603
636
  value === "--dry-run" ||
@@ -618,6 +651,7 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
618
651
  value === "logout"
619
652
  )
620
653
  return value;
654
+ if (value === "profile" && values[0] === "list") return "profile list";
621
655
  if (value === "github" && values[0] === "connect") return "github connect";
622
656
  if (value === "github" && values[0] === "status") return "github status";
623
657
  if (value === "operation" && values[0] === "get") return "operation get";
@@ -788,16 +822,32 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
788
822
  }
789
823
  if (dryRun) throw new InvalidCommandError();
790
824
  if (first === "login" && !revoke && !yes && !wait && !follow && !stdin) {
791
- const options = parseOptionalOptions(values, ["--organization"]);
825
+ const options = parseOptionalOptions(values, ["--organization", "--user"]);
792
826
  const organizationId = options["--organization"];
793
- if (organizationId !== undefined && !ORGANIZATION_ULID.test(organizationId))
827
+ const userId = options["--user"];
828
+ if (
829
+ (organizationId !== undefined && !ORGANIZATION_ULID.test(organizationId)) ||
830
+ (userId !== undefined && !isProfileUserId(userId))
831
+ )
794
832
  throw new InvalidCommandError();
795
833
  return {
796
834
  kind: "login",
797
835
  ...(organizationId === undefined ? {} : { organizationId }),
836
+ ...(userId === undefined ? {} : { userId }),
798
837
  credentialStore,
799
838
  };
800
839
  }
840
+ if (
841
+ first === "profile" &&
842
+ values.length === 1 &&
843
+ values[0] === "list" &&
844
+ !revoke &&
845
+ !yes &&
846
+ !wait &&
847
+ !follow &&
848
+ !stdin
849
+ )
850
+ return { kind: "profile-list" };
801
851
  if (first === "logout") {
802
852
  if (!yes && !wait && !follow && !stdin && values.length === 0)
803
853
  return { kind: "logout", revoke, credentialStore };
@@ -1781,15 +1831,44 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
1781
1831
  values.shift();
1782
1832
  const name = values.shift();
1783
1833
  if (!isUserSecretName(name)) throw new InvalidCommandError();
1784
- const options = parseOptions(values, ["--project", "--environment", "--idempotency-key"]);
1834
+ const options = parseOptionalOptions(values, [
1835
+ "--project",
1836
+ "--environment",
1837
+ "--idempotency-key",
1838
+ "--token-user",
1839
+ "--token-organization",
1840
+ "--profile-user",
1841
+ "--profile-organization",
1842
+ ]);
1843
+ const projectId = options["--project"];
1844
+ const environmentId = options["--environment"];
1845
+ const idempotencyKey = options["--idempotency-key"];
1846
+ const environmentToken = handedAccount(
1847
+ options["--token-user"],
1848
+ options["--token-organization"],
1849
+ );
1850
+ const profileAccount = handedAccount(
1851
+ options["--profile-user"],
1852
+ options["--profile-organization"],
1853
+ );
1854
+ // A key form and a saved-login form contradict each other; neither wins.
1855
+ if (
1856
+ projectId === undefined ||
1857
+ environmentId === undefined ||
1858
+ idempotencyKey === undefined ||
1859
+ (environmentToken !== undefined && profileAccount !== undefined)
1860
+ )
1861
+ throw new InvalidCommandError();
1785
1862
  return {
1786
1863
  kind: "secret-set",
1787
- projectId: options["--project"],
1788
- environmentId: options["--environment"],
1864
+ projectId,
1865
+ environmentId,
1789
1866
  name,
1790
- idempotencyKey: options["--idempotency-key"],
1867
+ idempotencyKey,
1791
1868
  stdin,
1792
1869
  wait,
1870
+ ...(environmentToken === undefined ? {} : { environmentToken }),
1871
+ ...(profileAccount === undefined ? {} : { profileAccount }),
1793
1872
  credentialStore,
1794
1873
  };
1795
1874
  }
@@ -2034,6 +2113,32 @@ const accessTtlSeconds = (value: string | undefined): number => {
2034
2113
  /** A platform organization identifier as the API returns it. */
2035
2114
  const ORGANIZATION_ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
2036
2115
 
2116
+ /** The account a command handed to a terminal must run as; never a credential. */
2117
+ export interface HandedAccount {
2118
+ readonly userId: string;
2119
+ readonly organizationId: string;
2120
+ }
2121
+
2122
+ /**
2123
+ * A user and an organization named together, or neither. A pasted key is refused here, before any
2124
+ * message could repeat it.
2125
+ */
2126
+ const handedAccount = (
2127
+ userId: string | undefined,
2128
+ organizationId: string | undefined,
2129
+ ): HandedAccount | undefined => {
2130
+ if (userId === undefined && organizationId === undefined) return undefined;
2131
+ if (
2132
+ userId === undefined ||
2133
+ organizationId === undefined ||
2134
+ !isProfileUserId(userId) ||
2135
+ WORKOS_USER_API_KEY_PATTERN.test(userId) ||
2136
+ !ORGANIZATION_ULID.test(organizationId)
2137
+ )
2138
+ throw new InvalidCommandError();
2139
+ return { userId, organizationId };
2140
+ };
2141
+
2037
2142
  const isValue = (value: string | undefined): value is string =>
2038
2143
  value !== undefined && value.length > 0 && !value.startsWith("--");
2039
2144