@keystrokehq/cli 0.0.134 → 0.0.137

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.
package/dist/index.mjs CHANGED
@@ -2105,7 +2105,6 @@ const UserOrganizationSchema = object({
2105
2105
  });
2106
2106
  const ActiveOrganizationResponseSchema = object({ organization: UserOrganizationSchema.nullable() });
2107
2107
  const ListOrganizationsResponseSchema = object({ organizations: array(UserOrganizationSchema) });
2108
- const SetActiveOrganizationRequestSchema = object({ organizationId: string().min(1) });
2109
2108
  const CreateOrganizationRequestSchema = object({
2110
2109
  name: string().trim().min(1),
2111
2110
  /** Format-only at the wire boundary; reserved-name checks run in platform-database. */
@@ -4782,9 +4781,11 @@ async function runDeviceLogin(options) {
4782
4781
  });
4783
4782
  if (!tokenResult.error) {
4784
4783
  const token = tokenResult.data;
4784
+ const organizationId = token.scope?.trim();
4785
4785
  return {
4786
4786
  accessToken: token.access_token,
4787
- expiresIn: token.expires_in
4787
+ expiresIn: token.expires_in,
4788
+ organizationId: organizationId || void 0
4788
4789
  };
4789
4790
  }
4790
4791
  const errorPayload = tokenResult.error;
@@ -5071,10 +5072,22 @@ function createHealthResource(http) {
5071
5072
  } };
5072
5073
  }
5073
5074
  function createOrganizationsResource(http, options = {}) {
5075
+ async function get(organizationId) {
5076
+ try {
5077
+ const data = await http.get(`/api/organizations/${organizationId}`).json();
5078
+ const organization = ActiveOrganizationResponseSchema.parse(data).organization;
5079
+ if (!organization) throw new Error("Organization was not returned");
5080
+ return organization;
5081
+ } catch (error) {
5082
+ throw await toPlatformError(error);
5083
+ }
5084
+ }
5074
5085
  async function update(input) {
5075
5086
  const body = UpdateOrganizationRequestSchema.parse(input);
5087
+ const organizationId = options.getActiveOrganizationId?.();
5088
+ if (!organizationId) throw new Error("No active organization");
5076
5089
  try {
5077
- const data = await http.patch("/api/organizations/active", { json: body }).json();
5090
+ const data = await http.patch(`/api/organizations/${organizationId}`, { json: body }).json();
5078
5091
  const organization = ActiveOrganizationResponseSchema.parse(data).organization;
5079
5092
  if (!organization) throw new Error("Active organization was not returned");
5080
5093
  return organization;
@@ -5091,34 +5104,27 @@ function createOrganizationsResource(http, options = {}) {
5091
5104
  throw await toPlatformError(error);
5092
5105
  }
5093
5106
  },
5107
+ get,
5094
5108
  async getActive() {
5109
+ const organizationId = options.getActiveOrganizationId?.();
5110
+ if (!organizationId) return null;
5095
5111
  try {
5096
- const data = await http.get("/api/organizations/active").json();
5097
- return ActiveOrganizationResponseSchema.parse(data).organization;
5112
+ return await get(organizationId);
5098
5113
  } catch (error) {
5099
- if (isHTTPError(error) && error.response.status === 401) return null;
5100
- throw await toPlatformError(error);
5114
+ if (error instanceof PlatformError && (error.status === 401 || error.status === 403)) return null;
5115
+ if (isHTTPError(error) && (error.response.status === 401 || error.response.status === 403)) return null;
5116
+ throw error instanceof PlatformError ? error : await toPlatformError(error);
5101
5117
  }
5102
5118
  },
5103
5119
  async setActive(input) {
5104
- const body = SetActiveOrganizationRequestSchema.parse(input);
5105
- try {
5106
- const data = await http.post("/api/organizations/active", { json: body }).json();
5107
- const organization = ActiveOrganizationResponseSchema.parse(data).organization;
5108
- if (!organization) throw new Error("Active organization was not returned");
5109
- options.onActiveOrganizationChange?.(organization.organization.id);
5110
- return organization;
5111
- } catch (error) {
5112
- throw await toPlatformError(error);
5113
- }
5120
+ const organizationId = input.organizationId.trim();
5121
+ if (!organizationId) throw new Error("Organization id is required");
5122
+ const membership = await get(organizationId);
5123
+ options.onActiveOrganizationChange?.(organizationId);
5124
+ return membership;
5114
5125
  },
5115
5126
  async clearActive() {
5116
- try {
5117
- await http.delete("/api/organizations/active");
5118
- options.onActiveOrganizationChange?.(null);
5119
- } catch (error) {
5120
- throw await toPlatformError(error);
5121
- }
5127
+ options.onActiveOrganizationChange?.(null);
5122
5128
  },
5123
5129
  update,
5124
5130
  async create(input) {
@@ -5130,9 +5136,9 @@ function createOrganizationsResource(http, options = {}) {
5130
5136
  throw await toPlatformError(error);
5131
5137
  }
5132
5138
  },
5133
- async checkSlug(slug, options = {}) {
5139
+ async checkSlug(slug, slugOptions = {}) {
5134
5140
  const searchParams = new URLSearchParams({ slug });
5135
- if (options.excludeOrganizationId) searchParams.set("excludeOrganizationId", options.excludeOrganizationId);
5141
+ if (slugOptions.excludeOrganizationId) searchParams.set("excludeOrganizationId", slugOptions.excludeOrganizationId);
5136
5142
  try {
5137
5143
  const data = await http.get(`/api/organizations/slug-available?${searchParams}`).json();
5138
5144
  return SlugAvailabilityResponseSchema.parse(data);
@@ -7740,19 +7746,24 @@ async function presignPutAndFinalize(input) {
7740
7746
  throw await input.toPlatformError(error);
7741
7747
  }
7742
7748
  }
7743
- function createOrganizationSidebarBrandingResource(http) {
7749
+ function createOrganizationSidebarBrandingResource(http, options = {}) {
7750
+ function brandingPath() {
7751
+ const organizationId = options.getActiveOrganizationId?.();
7752
+ if (!organizationId) throw new Error("No active organization");
7753
+ return `/api/organizations/${organizationId}/branding`;
7754
+ }
7744
7755
  async function presignLogoUpload(variant, contentType) {
7745
7756
  const body = PresignOrgLogoRequestSchema.parse({
7746
7757
  variant,
7747
7758
  contentType
7748
7759
  });
7749
- const data = await http.post("/api/organizations/active/branding/logo/presign", { json: body }).json();
7760
+ const data = await http.post(`${brandingPath()}/logo/presign`, { json: body }).json();
7750
7761
  return PresignOrgLogoResponseSchema.parse(data);
7751
7762
  }
7752
7763
  return {
7753
7764
  async get() {
7754
7765
  try {
7755
- const data = await http.get("/api/organizations/active/branding").json();
7766
+ const data = await http.get(brandingPath()).json();
7756
7767
  return OrganizationSidebarBrandingSchema.parse(data);
7757
7768
  } catch (error) {
7758
7769
  throw await toPlatformError(error);
@@ -7779,7 +7790,7 @@ function createOrganizationSidebarBrandingResource(http) {
7779
7790
  async update(patch) {
7780
7791
  const body = OrganizationSidebarBrandingPatchSchema.parse(patch);
7781
7792
  try {
7782
- const data = await http.patch("/api/organizations/active/branding", { json: body }).json();
7793
+ const data = await http.patch(brandingPath(), { json: body }).json();
7783
7794
  return OrganizationSidebarBrandingSchema.parse(data);
7784
7795
  } catch (error) {
7785
7796
  throw await toPlatformError(error);
@@ -8596,7 +8607,7 @@ function createPlatformClient(options) {
8596
8607
  recents: createRecentsResource(http),
8597
8608
  userPreferences: createUserPreferencesResource({ getCurrentUserId: options.getCurrentUserId }),
8598
8609
  userAvatar: createUserAvatarResource(http),
8599
- organizationSidebarBranding: createOrganizationSidebarBrandingResource(http),
8610
+ organizationSidebarBranding: createOrganizationSidebarBrandingResource(http, { getActiveOrganizationId: () => activeOrganizationId }),
8600
8611
  getActiveOrganizationId: () => activeOrganizationId,
8601
8612
  setActiveOrganizationId
8602
8613
  };
@@ -8665,6 +8676,15 @@ async function activateOrganization(config, organizationId) {
8665
8676
  platform.setActiveOrganizationId(membership.organization.id);
8666
8677
  return membership;
8667
8678
  }
8679
+ function resolveOrganizationIdForLogin(organizations, options = {}) {
8680
+ if (options.organizationRef ?? options.organizationId) return resolveOrganizationRef(organizations, options.organizationRef ?? options.organizationId).organization.id;
8681
+ for (const candidateId of [options.deviceOrganizationId, options.storedOrganizationId]) {
8682
+ const organizationId = candidateId?.trim();
8683
+ if (organizationId && organizations.some((entry) => entry.organization.id === organizationId)) return organizationId;
8684
+ }
8685
+ if (organizations.length === 1) return organizations[0].organization.id;
8686
+ return [...organizations].sort((left, right) => left.organization.slug.localeCompare(right.organization.slug))[0].organization.id;
8687
+ }
8668
8688
  async function pickOrganization(config, organizations, organizationRef) {
8669
8689
  if (organizationRef) return activateOrganization(config, resolveOrganizationRef(organizations, organizationRef).organization.id);
8670
8690
  if (!process.stdin.isTTY) {
@@ -8688,33 +8708,21 @@ async function selectActiveOrganization(config, options = {}) {
8688
8708
  async function syncActiveOrganizationAfterLogin(config, options = {}) {
8689
8709
  const organizations = await listOrganizations(config);
8690
8710
  if (organizations.length === 0) throw new Error(`No organization found — create one at ${getWebUrl(config)}`);
8691
- const storedId = config.get("activeOrganizationId");
8692
- if (storedId && organizations.some((entry) => entry.organization.id === storedId)) {
8693
- const membership = await activateOrganization(config, storedId);
8694
- process.stdout.write(`Using organization ${membership.organization.name}\n`);
8695
- return membership;
8696
- }
8697
- if (organizations.length === 1) {
8698
- const membership = await activateOrganization(config, organizations[0].organization.id);
8699
- process.stdout.write(`Using organization ${membership.organization.name}\n`);
8700
- return membership;
8701
- }
8702
- if (options.organizationRef ?? options.organizationId) {
8703
- const membership = await pickOrganization(config, organizations, options.organizationRef ?? options.organizationId);
8704
- process.stdout.write(`Using organization ${membership.organization.name}\n`);
8705
- return membership;
8706
- }
8707
- const membership = await pickOrganization(config, organizations);
8708
- process.stdout.write(`Using organization ${membership.organization.name}\n`);
8709
- return membership;
8711
+ return activateOrganization(config, resolveOrganizationIdForLogin(organizations, {
8712
+ organizationRef: options.organizationRef,
8713
+ organizationId: options.organizationId,
8714
+ deviceOrganizationId: options.deviceOrganizationId,
8715
+ storedOrganizationId: config.get("activeOrganizationId")
8716
+ }));
8710
8717
  }
8711
8718
  async function ensureActiveOrganization(config) {
8719
+ const organizations = await listOrganizations(config);
8720
+ if (organizations.length === 0) throw new Error("No organization found. Run `keystroke auth login` after creating one in the dashboard.");
8712
8721
  const storedId = config.get("activeOrganizationId");
8713
- if (storedId) {
8722
+ if (storedId && organizations.some((entry) => entry.organization.id === storedId)) {
8714
8723
  await activateOrganization(config, storedId);
8715
8724
  return;
8716
8725
  }
8717
- const organizations = await listOrganizations(config);
8718
8726
  if (organizations.length === 1) {
8719
8727
  await activateOrganization(config, organizations[0].organization.id);
8720
8728
  return;
@@ -9290,7 +9298,7 @@ function resolveAuthLoginTargets(options, config) {
9290
9298
  //#endregion
9291
9299
  //#region src/commands/auth/login.ts
9292
9300
  function registerAuthLoginCommand(auth) {
9293
- auth.command("login").description("Authenticate via browser device flow").option("--web-url <url>", "Web dashboard origin (omit to use production defaults; local dev: http://localhost:3000)").option("--platform-url <url>", "Platform API origin (derived from web URL when omitted)").option("--org <slug>", "Active organization slug (skips prompt when you belong to several)").action(async (options) => {
9301
+ auth.command("login").description("Authenticate via browser device flow").option("--web-url <url>", "Web dashboard origin (omit to use production defaults; local dev: http://localhost:3000)").option("--platform-url <url>", "Platform API origin (derived from web URL when omitted)").option("--org <slug>", "Active organization slug (overrides browser default)").action(async (options) => {
9294
9302
  const config = createCliConfig();
9295
9303
  const { webUrl, platformUrl } = resolveAuthLoginTargets(options, config);
9296
9304
  config.set("webUrl", webUrl);
@@ -9303,9 +9311,12 @@ function registerAuthLoginCommand(auth) {
9303
9311
  });
9304
9312
  setAccessToken(platformUrl, result.accessToken);
9305
9313
  const user = await getSessionWithBearer(platformUrl, result.accessToken);
9306
- if (user) process.stdout.write(`Logged in as ${user.name} (${user.email})\n`);
9307
- else process.stdout.write(`Logged in to ${webUrl}\n`);
9308
- await syncActiveOrganizationAfterLogin(config, { organizationRef: options.org });
9314
+ const membership = await syncActiveOrganizationAfterLogin(config, {
9315
+ organizationRef: options.org,
9316
+ deviceOrganizationId: result.organizationId
9317
+ });
9318
+ if (user) process.stdout.write(`Logged in as ${user.name} (${user.email}) — organization ${membership.organization.name}\n`);
9319
+ else process.stdout.write(`Logged in to ${webUrl} — organization ${membership.organization.name}\n`);
9309
9320
  } catch (error) {
9310
9321
  process.stderr.write(`${formatCliError(error, "Login failed", {
9311
9322
  webUrl,