@keystrokehq/cli 0.1.4 → 0.1.5

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
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { $ as ListProjectsResponseSchema, A as DownloadActiveProjectArtifactResponseSchema, At as TriggerRunListResponseSchema, B as InviteOrganizationMembersResponseSchema, Bt as UpdateProjectSettingsRequestSchema, C as CreateOrganizationResponseSchema, Ct as StartOAuthConnectionInputSchema, D as CredentialInstanceListResponseSchema, Dt as TriggerDetailResponseSchema, E as CreateProjectResponseSchema, Et as SubmitTeamRequestRequestSchema, F as HistoryRunCancelResponseSchema, Ft as UpdateOrganizationMemberResponseSchema, G as ListCredentialsResponseSchema, Gt as UserAvatarSchema, H as InviteProjectMembersResponseSchema, Ht as UploadProjectSourceResponseSchema, I as HistoryRunDetailResponseSchema, It as UpdateOrganizationRequestSchema, J as ListOrganizationsResponseSchema, Jt as WorkflowRunDetailResponseSchema, K as ListOrganizationInvitationsResponseSchema, Kt as UserPreferencesPatchSchema, L as HistoryRunListQuerySchema, Lt as UpdateProjectMemberRequestSchema, M as GatewayAttachmentRecordSchema, Mt as UpdateCredentialInstanceBodySchema, N as GetCredentialResponseSchema, Nt as UpdateCredentialRequestSchema, O as CredentialInstanceRecordSchema, Ot as TriggerListResponseSchema, P as HealthResponseSchema, Pt as UpdateOrganizationMemberRequestSchema, Q as ListProjectMetricsResponseSchema, Qt as listenPortFromPublicUrl, R as HistoryRunListResponseSchema, Rt as UpdateProjectMemberResponseSchema, S as CreateOrganizationRequestSchema, St as SlugAvailabilityResponseSchema, T as CreateProjectRequestSchema, Tt as StoredRouteManifestSchema, U as ListApiKeysResponseSchema, Ut as UpsertGatewayAttachmentBodySchema, V as InviteProjectMembersRequestSchema, Vt as UploadProjectSourceManifestRequestSchema, W as ListAppsResponseSchema, Wt as UserAvatarPatchSchema, X as ListProjectFilesResponseSchema, Xt as WorkflowSummaryDetailResponseSchema, Y as ListProjectDeploymentsResponseSchema, Yt as WorkflowRunListResponseSchema, Z as ListProjectMembersResponseSchema, Zt as WorkflowSummaryListResponseSchema, _ as CreateApiKeyRequestSchema, _t as QueuedRunResponseSchema, a as AgentSessionListResponseSchema, at as PresignOrgLogoResponseSchema, b as CreateCredentialsRequestSchema, bt as SkillSummaryDetailResponseSchema, c as BindChannelBodySchema, ct as PresignUserAvatarRequestSchema, d as ChannelConnectionSchema, dt as ProjectResponseSchema, et as OrganizationSidebarBrandingPatchSchema, f as ChannelDirectoryListResponseSchema, ft as ProjectSettingsResponseSchema, g as ConnectProvidersResponseSchema, gt as QueuedAgentPromptResponseSchema, h as ConnectAuthorizeUrlResponseSchema, ht as PromptResponseSchema, i as AgentSessionDetailResponseSchema, it as PresignOrgLogoRequestSchema, j as ErrorResponseSchema, jt as UpdateChannelBindingBodySchema, k as DeclineOrganizationInvitationResponseSchema, kt as TriggerRunDetailResponseSchema, l as ChannelAccountListResponseSchema, lt as PresignUserAvatarResponseSchema, m as CompleteProjectArtifactResponseSchema, mt as PromptInputSchema, n as AcceptOrganizationInvitationResponseSchema, nt as PROJECT_REACHABILITY_REQUEST_TIMEOUT_MS, o as AgentSummaryDetailResponseSchema, ot as PresignProjectSourceRequestSchema, p as ChannelPlatformSchema, pt as ProjectSlugAvailabilityResponseSchema, q as ListOrganizationMembersResponseSchema, qt as UserPreferencesSchema, r as ActiveOrganizationResponseSchema, rt as PollRunResponseSchema, s as AgentSummaryListResponseSchema, st as PresignProjectSourceResponseSchema, t as ACTIVE_ORG_HEADER, tn as parseErrorResponse, tt as OrganizationSidebarBrandingSchema, u as ChannelConnectionListResponseSchema, ut as ProjectReachabilityResponseSchema, v as CreateApiKeyResponseSchema, vt as ROUTE_MANIFEST_REL_PATH, w as CreateProjectArtifactResponseSchema, wt as StartOAuthConnectionResultSchema, x as CreateCredentialsResponseSchema, xt as SkillSummaryListResponseSchema, y as CreateCredentialInstanceBodySchema, yt as RecentResourceListResponseSchema, z as InviteOrganizationMembersRequestSchema, zt as UpdateProjectRequestSchema } from "./dist-C47GdlWY.mjs";
3
- import { _ as resolvePlatformUrlForWebUrl, a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, h as DEFAULT_PLATFORM_URL, i as installDependencies, l as getCliConfigDir, m as getWebUrl, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getServerUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-Dxl3y5p6.mjs";
2
+ import { $ as ListCredentialsResponseSchema, $t as UpsertGatewayAttachmentBodySchema, A as CreateOrganizationResponseSchema, At as SlugAvailabilityResponseSchema, B as GetCredentialResponseSchema, Bt as TriggerRunListResponseSchema, C as CreateApiKeyResponseSchema, Ct as PromptResponseSchema, D as CreateCustomAppRequestSchema, Dt as RecentResourceListResponseSchema, E as CreateCredentialsResponseSchema, Et as ROUTE_MANIFEST_REL_PATH, F as CredentialInstanceRecordSchema, Ft as StoredRouteManifestSchema, G as HistoryRunListQuerySchema, Gt as UpdateOrganizationMemberResponseSchema, H as HealthResponseSchema, Ht as UpdateCredentialInstanceBodySchema, I as DeclineOrganizationInvitationResponseSchema, It as SubmitTeamRequestRequestSchema, J as InviteOrganizationMembersResponseSchema, Jt as UpdateProjectMemberResponseSchema, K as HistoryRunListResponseSchema, Kt as UpdateOrganizationRequestSchema, L as DownloadActiveProjectArtifactResponseSchema, Lt as TriggerDetailResponseSchema, M as CreateProjectRequestSchema, Mt as StartKeystrokeConnectionResultSchema, N as CreateProjectResponseSchema, Nt as StartOAuthConnectionInputSchema, O as CreateCustomAppResponseSchema, Ot as SkillSummaryDetailResponseSchema, P as CredentialInstanceListResponseSchema, Pt as StartOAuthConnectionResultSchema, Q as ListAppsResponseSchema, Qt as UploadProjectSourceResponseSchema, R as ErrorResponseSchema, Rt as TriggerListResponseSchema, S as CreateApiKeyRequestSchema, St as PromptInputSchema, T as CreateCredentialsRequestSchema, Tt as QueuedRunResponseSchema, U as HistoryRunCancelResponseSchema, Ut as UpdateCredentialRequestSchema, V as GetCustomAppResponseSchema, Vt as UpdateChannelBindingBodySchema, W as HistoryRunDetailResponseSchema, Wt as UpdateOrganizationMemberRequestSchema, X as InviteProjectMembersResponseSchema, Xt as UpdateProjectSettingsRequestSchema, Y as InviteProjectMembersRequestSchema, Yt as UpdateProjectRequestSchema, Z as ListApiKeysResponseSchema, Zt as UploadProjectSourceManifestRequestSchema, _ as ChannelDirectoryListResponseSchema, _t as PresignUserAvatarResponseSchema, a as AgentSessionListResponseSchema, an as WorkflowRunListResponseSchema, at as ListProjectMembersResponseSchema, b as ConnectAuthorizeUrlResponseSchema, bt as ProjectSettingsResponseSchema, c as AppSlugAvailabilityResponseSchema, cn as listenPortFromPublicUrl, ct as OrganizationSidebarBrandingPatchSchema, d as CatalogActionsPageResponseSchema, dn as parseAppSlug, dt as PollRunResponseSchema, en as UserAvatarPatchSchema, et as ListOrganizationInvitationsResponseSchema, f as CatalogAppDetailResponseSchema, fn as parseErrorResponse, ft as PresignOrgLogoRequestSchema, g as ChannelConnectionSchema, gt as PresignUserAvatarRequestSchema, h as ChannelConnectionListResponseSchema, ht as PresignProjectSourceResponseSchema, i as AgentSessionDetailResponseSchema, in as WorkflowRunDetailResponseSchema, it as ListProjectFilesResponseSchema, j as CreateProjectArtifactResponseSchema, jt as StartKeystrokeConnectionInputSchema, k as CreateOrganizationRequestSchema, kt as SkillSummaryListResponseSchema, l as BindChannelBodySchema, lt as OrganizationSidebarBrandingSchema, m as ChannelAccountListResponseSchema, mt as PresignProjectSourceRequestSchema, n as AcceptOrganizationInvitationResponseSchema, nn as UserPreferencesPatchSchema, nt as ListOrganizationsResponseSchema, o as AgentSummaryDetailResponseSchema, on as WorkflowSummaryDetailResponseSchema, ot as ListProjectMetricsResponseSchema, p as CatalogAppsPageResponseSchema, pn as slugifyAppName, pt as PresignOrgLogoResponseSchema, q as InviteOrganizationMembersRequestSchema, qt as UpdateProjectMemberRequestSchema, r as ActiveOrganizationResponseSchema, rn as UserPreferencesSchema, rt as ListProjectDeploymentsResponseSchema, s as AgentSummaryListResponseSchema, sn as WorkflowSummaryListResponseSchema, st as ListProjectsResponseSchema, t as ACTIVE_ORG_HEADER, tn as UserAvatarSchema, tt as ListOrganizationMembersResponseSchema, u as CatalogActionDetailResponseSchema, ut as PROJECT_REACHABILITY_REQUEST_TIMEOUT_MS, v as ChannelPlatformSchema, vt as ProjectReachabilityResponseSchema, w as CreateCredentialInstanceBodySchema, wt as QueuedAgentPromptResponseSchema, x as ConnectProvidersResponseSchema, xt as ProjectSlugAvailabilityResponseSchema, y as CompleteProjectArtifactResponseSchema, yt as ProjectResponseSchema, z as GatewayAttachmentRecordSchema, zt as TriggerRunDetailResponseSchema } from "./dist-C3YClLXV.mjs";
3
+ import { _ as resolvePlatformUrlForWebUrl, a as installPlaygroundDependencies, c as createCliConfig, d as getEffectiveApiTarget, f as getPlatformUrl, h as DEFAULT_PLATFORM_URL, i as installDependencies, l as getCliConfigDir, m as getWebUrl, n as buildPlaygroundWorkspace, o as resolvePackageManager, p as getServerUrl, s as resolveCliRoot, t as readCliVersion, u as getConfigDir } from "./version-BOm_5ar9.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { Command } from "commander";
6
6
  import { platform, tmpdir } from "node:os";
@@ -8,9 +8,9 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
8
8
  import { Entry } from "@napi-rs/keyring";
9
9
  import { confirm, input, select } from "@inquirer/prompts";
10
10
  import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
11
+ import { access, copyFile, cp, lstat, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
11
12
  import { spawn, spawnSync } from "node:child_process";
12
13
  import { pathToFileURL } from "node:url";
13
- import { access, copyFile, cp, lstat, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
14
14
  //#region ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
15
15
  /**
16
16
  Base class for all Ky-specific errors. `HTTPError`, `NetworkError`, `TimeoutError`, and `ForceRetryError` extend this class.
@@ -3351,6 +3351,7 @@ var PlatformError = class extends Error {
3351
3351
  }
3352
3352
  };
3353
3353
  async function toPlatformError(error) {
3354
+ if (error instanceof PlatformError) return error;
3354
3355
  if (!isHTTPError(error)) return new PlatformError(error instanceof Error ? error.message : "Request failed", 0, null);
3355
3356
  const status = error.response.status;
3356
3357
  const body = error.data ?? null;
@@ -3421,7 +3422,7 @@ function createDeploymentsResource(http) {
3421
3422
  return { async listForProject(projectId) {
3422
3423
  try {
3423
3424
  const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/deployments`).json();
3424
- return ListProjectDeploymentsResponseSchema.parse(data).deployments;
3425
+ return ListProjectDeploymentsResponseSchema.parse(data);
3425
3426
  } catch (error) {
3426
3427
  throw await toPlatformError(error);
3427
3428
  }
@@ -3474,7 +3475,7 @@ function createOrganizationsResource(http, options = {}) {
3474
3475
  async function get(organizationId) {
3475
3476
  try {
3476
3477
  const data = await http.get(`/api/organizations/${organizationId}`).json();
3477
- const organization = ActiveOrganizationResponseSchema.parse(data).organization;
3478
+ const organization = ActiveOrganizationResponseSchema.parse(data);
3478
3479
  if (!organization) throw new Error("Organization was not returned");
3479
3480
  return organization;
3480
3481
  } catch (error) {
@@ -3487,7 +3488,7 @@ function createOrganizationsResource(http, options = {}) {
3487
3488
  if (!organizationId) throw new Error("No active organization");
3488
3489
  try {
3489
3490
  const data = await http.patch(`/api/organizations/${organizationId}`, { json: body }).json();
3490
- const organization = ActiveOrganizationResponseSchema.parse(data).organization;
3491
+ const organization = ActiveOrganizationResponseSchema.parse(data);
3491
3492
  if (!organization) throw new Error("Active organization was not returned");
3492
3493
  return organization;
3493
3494
  } catch (error) {
@@ -3498,7 +3499,7 @@ function createOrganizationsResource(http, options = {}) {
3498
3499
  async list() {
3499
3500
  try {
3500
3501
  const data = await http.get("/api/organizations").json();
3501
- return ListOrganizationsResponseSchema.parse(data).organizations;
3502
+ return ListOrganizationsResponseSchema.parse(data);
3502
3503
  } catch (error) {
3503
3504
  throw await toPlatformError(error);
3504
3505
  }
@@ -3530,7 +3531,7 @@ function createOrganizationsResource(http, options = {}) {
3530
3531
  const body = CreateOrganizationRequestSchema.parse(input);
3531
3532
  try {
3532
3533
  const data = await http.post("/api/organizations", { json: body }).json();
3533
- return CreateOrganizationResponseSchema.parse(data).organization;
3534
+ return CreateOrganizationResponseSchema.parse(data);
3534
3535
  } catch (error) {
3535
3536
  throw await toPlatformError(error);
3536
3537
  }
@@ -3589,7 +3590,7 @@ function createProjectsResource(http) {
3589
3590
  async list(options) {
3590
3591
  try {
3591
3592
  const data = await http.get("/api/projects", { searchParams: listSearchParams(options) }).json();
3592
- return ListProjectsResponseSchema.parse(data).projects;
3593
+ return ListProjectsResponseSchema.parse(data);
3593
3594
  } catch (error) {
3594
3595
  throw await toPlatformError(error);
3595
3596
  }
@@ -3598,7 +3599,7 @@ function createProjectsResource(http) {
3598
3599
  const body = CreateProjectRequestSchema.parse(input);
3599
3600
  try {
3600
3601
  const data = await http.post("/api/projects", { json: body }).json();
3601
- return CreateProjectResponseSchema.parse(data).project;
3602
+ return CreateProjectResponseSchema.parse(data);
3602
3603
  } catch (error) {
3603
3604
  throw await toPlatformError(error);
3604
3605
  }
@@ -3606,7 +3607,7 @@ function createProjectsResource(http) {
3606
3607
  async get(projectId) {
3607
3608
  try {
3608
3609
  const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}`).json();
3609
- return ProjectResponseSchema.parse(data).project;
3610
+ return ProjectResponseSchema.parse(data);
3610
3611
  } catch (error) {
3611
3612
  throw await toPlatformError(error);
3612
3613
  }
@@ -3615,7 +3616,7 @@ function createProjectsResource(http) {
3615
3616
  const body = UpdateProjectRequestSchema.parse(patch);
3616
3617
  try {
3617
3618
  const data = await http.patch(`/api/projects/${encodeURIComponent(projectId)}`, { json: body }).json();
3618
- return ProjectResponseSchema.parse(data).project;
3619
+ return ProjectResponseSchema.parse(data);
3619
3620
  } catch (error) {
3620
3621
  throw await toPlatformError(error);
3621
3622
  }
@@ -3651,7 +3652,7 @@ function createProjectMetricsResource(http) {
3651
3652
  return { async list(options) {
3652
3653
  try {
3653
3654
  const data = await http.get("/api/projects/metrics", { searchParams: listSearchParams(options) }).json();
3654
- return ListProjectMetricsResponseSchema.parse(data).metrics;
3655
+ return ListProjectMetricsResponseSchema.parse(data);
3655
3656
  } catch (error) {
3656
3657
  throw await toPlatformError(error);
3657
3658
  }
@@ -3737,6 +3738,8 @@ function buildOAuthAuthorizeSearchParams(input) {
3737
3738
  if (projectIds.length > 0) params.projects = projectIds.join(",");
3738
3739
  if (input.createOrganizationCredential) params.org = "true";
3739
3740
  if (input.createUserProvidedCredential) params.user = "true";
3741
+ const label = input.label?.trim();
3742
+ if (label) params.label = label;
3740
3743
  return params;
3741
3744
  }
3742
3745
  function createCredentialsResource(http) {
@@ -3744,7 +3747,7 @@ function createCredentialsResource(http) {
3744
3747
  async list() {
3745
3748
  try {
3746
3749
  const data = await http.get("/api/credentials").json();
3747
- return ListCredentialsResponseSchema.parse(data).credentials;
3750
+ return ListCredentialsResponseSchema.parse(data);
3748
3751
  } catch (error) {
3749
3752
  throw await toPlatformError(error);
3750
3753
  }
@@ -3752,7 +3755,7 @@ function createCredentialsResource(http) {
3752
3755
  async get(credentialId) {
3753
3756
  try {
3754
3757
  const data = await http.get(`/api/credentials/${encodeURIComponent(credentialId)}`).json();
3755
- return GetCredentialResponseSchema.parse(data).credential;
3758
+ return GetCredentialResponseSchema.parse(data);
3756
3759
  } catch (error) {
3757
3760
  throw await toPlatformError(error);
3758
3761
  }
@@ -3761,7 +3764,7 @@ function createCredentialsResource(http) {
3761
3764
  const body = CreateCredentialsRequestSchema.parse(input);
3762
3765
  try {
3763
3766
  const data = await http.post("/api/credentials", { json: body }).json();
3764
- return CreateCredentialsResponseSchema.parse(data).credentials;
3767
+ return CreateCredentialsResponseSchema.parse(data);
3765
3768
  } catch (error) {
3766
3769
  throw await toPlatformError(error);
3767
3770
  }
@@ -3770,7 +3773,7 @@ function createCredentialsResource(http) {
3770
3773
  const body = UpdateCredentialRequestSchema.parse(patch);
3771
3774
  try {
3772
3775
  const data = await http.patch(`/api/credentials/${encodeURIComponent(credentialId)}`, { json: body }).json();
3773
- return GetCredentialResponseSchema.parse(data).credential;
3776
+ return GetCredentialResponseSchema.parse(data);
3774
3777
  } catch (error) {
3775
3778
  throw await toPlatformError(error);
3776
3779
  }
@@ -3794,25 +3797,128 @@ function createCredentialsResource(http) {
3794
3797
  } catch (error) {
3795
3798
  throw await toPlatformError(error);
3796
3799
  }
3800
+ },
3801
+ async startKeystrokeConnection(input) {
3802
+ const body = StartKeystrokeConnectionInputSchema.parse(input);
3803
+ try {
3804
+ const data = await http.post("/mcp/connections", { json: body }).json();
3805
+ return StartKeystrokeConnectionResultSchema.parse(data);
3806
+ } catch (error) {
3807
+ throw await toPlatformError(error);
3808
+ }
3797
3809
  }
3798
3810
  };
3799
3811
  }
3812
+ function parseCatalogData(schema, data) {
3813
+ const parsed = schema.safeParse(data);
3814
+ if (parsed.success) return parsed.data;
3815
+ throw new PlatformError(`Invalid catalog response: ${parsed.error.message}`, 502, data);
3816
+ }
3817
+ function catalogSearchParams(params) {
3818
+ const searchParams = new URLSearchParams();
3819
+ for (const [key, value] of Object.entries(params)) {
3820
+ if (value === void 0 || value === "") continue;
3821
+ searchParams.set(key, String(value));
3822
+ }
3823
+ return searchParams;
3824
+ }
3800
3825
  function createAppsResource(http) {
3801
- return { async listCatalog() {
3802
- try {
3803
- const data = await http.get("/api/apps").json();
3804
- return ListAppsResponseSchema.parse(data).apps;
3805
- } catch (error) {
3806
- throw await toPlatformError(error);
3826
+ return {
3827
+ async listCatalog() {
3828
+ try {
3829
+ const data = await http.get("/api/apps").json();
3830
+ return ListAppsResponseSchema.parse(data);
3831
+ } catch (error) {
3832
+ throw await toPlatformError(error);
3833
+ }
3834
+ },
3835
+ async searchCatalog(options) {
3836
+ try {
3837
+ const searchParams = catalogSearchParams({
3838
+ search: options.search,
3839
+ category: options.category,
3840
+ sort_by: options.sortBy,
3841
+ limit: options.limit,
3842
+ cursor: options.cursor
3843
+ });
3844
+ return parseCatalogData(CatalogAppsPageResponseSchema, await http.get(`/mcp/catalog/apps?${searchParams}`).json());
3845
+ } catch (error) {
3846
+ throw await toPlatformError(error);
3847
+ }
3848
+ },
3849
+ async getCatalogApp(slug) {
3850
+ try {
3851
+ return parseCatalogData(CatalogAppDetailResponseSchema, await http.get(`/mcp/catalog/apps/${encodeURIComponent(slug)}`).json());
3852
+ } catch (error) {
3853
+ throw await toPlatformError(error);
3854
+ }
3855
+ },
3856
+ async listCatalogActions(slug, options) {
3857
+ try {
3858
+ const searchParams = catalogSearchParams({
3859
+ search: options?.search,
3860
+ limit: options?.limit,
3861
+ cursor: options?.cursor
3862
+ });
3863
+ const query = searchParams.size > 0 ? `?${searchParams}` : "";
3864
+ return parseCatalogData(CatalogActionsPageResponseSchema, await http.get(`/mcp/catalog/apps/${encodeURIComponent(slug)}/actions${query}`).json());
3865
+ } catch (error) {
3866
+ throw await toPlatformError(error);
3867
+ }
3868
+ },
3869
+ async searchCatalogActions(options) {
3870
+ try {
3871
+ const searchParams = catalogSearchParams({
3872
+ search: options.search,
3873
+ limit: options.limit,
3874
+ cursor: options.cursor
3875
+ });
3876
+ return parseCatalogData(CatalogActionsPageResponseSchema, await http.get(`/mcp/catalog/actions?${searchParams}`).json());
3877
+ } catch (error) {
3878
+ throw await toPlatformError(error);
3879
+ }
3880
+ },
3881
+ async getCatalogAction(toolSlug) {
3882
+ try {
3883
+ return parseCatalogData(CatalogActionDetailResponseSchema, await http.get(`/mcp/catalog/actions/${encodeURIComponent(toolSlug)}`).json());
3884
+ } catch (error) {
3885
+ throw await toPlatformError(error);
3886
+ }
3887
+ },
3888
+ async checkSlug(slug) {
3889
+ try {
3890
+ const searchParams = new URLSearchParams({ slug });
3891
+ const data = await http.get(`/api/apps/slug-available?${searchParams}`).json();
3892
+ return AppSlugAvailabilityResponseSchema.parse(data);
3893
+ } catch (error) {
3894
+ throw await toPlatformError(error);
3895
+ }
3896
+ },
3897
+ async create(input) {
3898
+ try {
3899
+ const body = CreateCustomAppRequestSchema.parse(input);
3900
+ const data = await http.post("/api/apps", { json: body }).json();
3901
+ return CreateCustomAppResponseSchema.parse(data);
3902
+ } catch (error) {
3903
+ throw await toPlatformError(error);
3904
+ }
3905
+ },
3906
+ async get(slug) {
3907
+ try {
3908
+ const data = await http.get(`/api/apps/${encodeURIComponent(slug)}`).json();
3909
+ return GetCustomAppResponseSchema.parse(data);
3910
+ } catch (error) {
3911
+ throw await toPlatformError(error);
3912
+ }
3807
3913
  }
3808
- } };
3914
+ };
3809
3915
  }
3810
3916
  function createMembersResource(http) {
3811
3917
  return {
3812
3918
  async listOrganizationMembers() {
3813
3919
  try {
3814
3920
  const data = await http.get("/api/members").json();
3815
- return ListOrganizationMembersResponseSchema.parse(data).members;
3921
+ return ListOrganizationMembersResponseSchema.parse(data);
3816
3922
  } catch (error) {
3817
3923
  throw await toPlatformError(error);
3818
3924
  }
@@ -3830,7 +3936,7 @@ function createMembersResource(http) {
3830
3936
  const body = UpdateOrganizationMemberRequestSchema.parse(input);
3831
3937
  try {
3832
3938
  const data = await http.patch(`/api/members/${encodeURIComponent(userId)}`, { json: body }).json();
3833
- return UpdateOrganizationMemberResponseSchema.parse(data).member;
3939
+ return UpdateOrganizationMemberResponseSchema.parse(data);
3834
3940
  } catch (error) {
3835
3941
  throw await toPlatformError(error);
3836
3942
  }
@@ -3845,7 +3951,7 @@ function createMembersResource(http) {
3845
3951
  async listForProject(projectId) {
3846
3952
  try {
3847
3953
  const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/members`).json();
3848
- return ListProjectMembersResponseSchema.parse(data).members;
3954
+ return ListProjectMembersResponseSchema.parse(data);
3849
3955
  } catch (error) {
3850
3956
  throw await toPlatformError(error);
3851
3957
  }
@@ -3866,7 +3972,7 @@ function createMembersResource(http) {
3866
3972
  const body = UpdateProjectMemberRequestSchema.parse(input);
3867
3973
  try {
3868
3974
  const data = await http.patch(`/api/projects/${encodeURIComponent(projectId)}/members/${encodeURIComponent(userId)}`, { json: body }).json();
3869
- return UpdateProjectMemberResponseSchema.parse(data).member;
3975
+ return UpdateProjectMemberResponseSchema.parse(data);
3870
3976
  } catch (error) {
3871
3977
  throw await toPlatformError(error);
3872
3978
  }
@@ -3892,7 +3998,7 @@ function createProjectSettingsResource(http) {
3892
3998
  async get(projectId) {
3893
3999
  try {
3894
4000
  const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/settings`).json();
3895
- return ProjectSettingsResponseSchema.parse(data).settings;
4001
+ return ProjectSettingsResponseSchema.parse(data);
3896
4002
  } catch (error) {
3897
4003
  throw await toPlatformError(error);
3898
4004
  }
@@ -3901,7 +4007,7 @@ function createProjectSettingsResource(http) {
3901
4007
  const body = UpdateProjectSettingsRequestSchema.parse(patch);
3902
4008
  try {
3903
4009
  const data = await http.patch(`/api/projects/${encodeURIComponent(projectId)}/settings`, { json: body }).json();
3904
- return ProjectSettingsResponseSchema.parse(data).settings;
4010
+ return ProjectSettingsResponseSchema.parse(data);
3905
4011
  } catch (error) {
3906
4012
  throw await toPlatformError(error);
3907
4013
  }
@@ -3913,7 +4019,7 @@ function createInvitationsResource(http) {
3913
4019
  async list() {
3914
4020
  try {
3915
4021
  const data = await http.get("/api/invitations").json();
3916
- return ListOrganizationInvitationsResponseSchema.parse(data).invitations;
4022
+ return ListOrganizationInvitationsResponseSchema.parse(data);
3917
4023
  } catch (error) {
3918
4024
  throw await toPlatformError(error);
3919
4025
  }
@@ -3921,7 +4027,7 @@ function createInvitationsResource(http) {
3921
4027
  async accept(invitationId) {
3922
4028
  try {
3923
4029
  const data = await http.post(`/api/invitations/${encodeURIComponent(invitationId)}/accept`).json();
3924
- return AcceptOrganizationInvitationResponseSchema.parse(data).organization;
4030
+ return AcceptOrganizationInvitationResponseSchema.parse(data);
3925
4031
  } catch (error) {
3926
4032
  throw await toPlatformError(error);
3927
4033
  }
@@ -3941,7 +4047,7 @@ function createApiKeysResource(http) {
3941
4047
  async list() {
3942
4048
  try {
3943
4049
  const data = await http.get("/api/api-keys").json();
3944
- return ListApiKeysResponseSchema.parse(data).apiKeys;
4050
+ return ListApiKeysResponseSchema.parse(data);
3945
4051
  } catch (error) {
3946
4052
  throw await toPlatformError(error);
3947
4053
  }
@@ -4148,7 +4254,7 @@ function createChannelsResource(http) {
4148
4254
  async function listGatewayPlatforms() {
4149
4255
  try {
4150
4256
  const data = await http.get("/api/apps").json();
4151
- return ListAppsResponseSchema.parse(data).apps.filter((app) => app.gateway).map((app) => projectChannelPlatform(app));
4257
+ return ListAppsResponseSchema.parse(data).filter((app) => app.gateway).map((app) => projectChannelPlatform(app));
4152
4258
  } catch (error) {
4153
4259
  throw await toPlatformError(error);
4154
4260
  }
@@ -4160,7 +4266,7 @@ function createChannelsResource(http) {
4160
4266
  async listAccounts(projectId, platform) {
4161
4267
  try {
4162
4268
  const data = await http.get(`${projectPrefix(projectId)}/channels/${encodeURIComponent(platform)}/accounts`).json();
4163
- return ChannelAccountListResponseSchema.parse(data).accounts;
4269
+ return ChannelAccountListResponseSchema.parse(data);
4164
4270
  } catch (error) {
4165
4271
  throw await toPlatformError(error);
4166
4272
  }
@@ -4168,7 +4274,7 @@ function createChannelsResource(http) {
4168
4274
  async listForAgent(projectId, agentId) {
4169
4275
  try {
4170
4276
  const data = await http.get(`${projectPrefix(projectId)}/agents/${encodeURIComponent(agentId)}/channels`).json();
4171
- return ChannelConnectionListResponseSchema.parse(data).connections;
4277
+ return ChannelConnectionListResponseSchema.parse(data);
4172
4278
  } catch (error) {
4173
4279
  throw await toPlatformError(error);
4174
4280
  }
@@ -4176,7 +4282,7 @@ function createChannelsResource(http) {
4176
4282
  async listChannels(projectId, platform, accountId) {
4177
4283
  try {
4178
4284
  const data = await http.get(`${projectPrefix(projectId)}/channels/${encodeURIComponent(platform)}/directory`, { searchParams: { accountId } }).json();
4179
- return ChannelDirectoryListResponseSchema.parse(data).channels;
4285
+ return ChannelDirectoryListResponseSchema.parse(data);
4180
4286
  } catch (error) {
4181
4287
  throw await toPlatformError(error);
4182
4288
  }
@@ -4560,7 +4666,10 @@ function formatHttpClientError(error, context) {
4560
4666
  if (origin && !getAccessToken(origin)) return "Not logged in. Run `keystroke auth login` first.";
4561
4667
  return "Authentication failed. Run `keystroke auth login` again.";
4562
4668
  }
4563
- if (error.status === 0) return unreachableServerMessage(unreachableTarget(context));
4669
+ if (error.status === 0) {
4670
+ if (error.message && !isUnreachableServerError(error)) return error.message;
4671
+ return unreachableServerMessage(unreachableTarget(context));
4672
+ }
4564
4673
  const parsed = parseErrorResponse(error.body);
4565
4674
  if (parsed) return formatApiErrorMessage(parsed, error.message, context);
4566
4675
  return error.message;
@@ -4845,6 +4954,120 @@ function registerApiKeyCommand(program) {
4845
4954
  });
4846
4955
  }
4847
4956
  //#endregion
4957
+ //#region src/commands/app/run-app-catalog.ts
4958
+ async function runAppSearch(client, query, options) {
4959
+ const trimmed = query.trim();
4960
+ if (!trimmed) throw new Error("Search query is required");
4961
+ return client.apps.searchCatalog({
4962
+ search: trimmed,
4963
+ category: options?.category,
4964
+ limit: options?.limit,
4965
+ cursor: options?.cursor
4966
+ });
4967
+ }
4968
+ async function runAppShow(client, slug) {
4969
+ const trimmed = slug.trim();
4970
+ if (!trimmed) throw new Error("App slug is required");
4971
+ return client.apps.getCatalogApp(trimmed);
4972
+ }
4973
+ async function runAppActions(client, options) {
4974
+ const slug = options.slug?.trim();
4975
+ const search = options.search?.trim();
4976
+ if (slug) return client.apps.listCatalogActions(slug, {
4977
+ search,
4978
+ limit: options.limit,
4979
+ cursor: options.cursor
4980
+ });
4981
+ if (!search) throw new Error("App slug or --search is required");
4982
+ return client.apps.searchCatalogActions({
4983
+ search,
4984
+ limit: options.limit,
4985
+ cursor: options.cursor
4986
+ });
4987
+ }
4988
+ async function runAppAction(client, toolSlug) {
4989
+ const trimmed = toolSlug.trim();
4990
+ if (!trimmed) throw new Error("Tool slug is required");
4991
+ return client.apps.getCatalogAction(trimmed);
4992
+ }
4993
+ //#endregion
4994
+ //#region src/commands/app/action.ts
4995
+ function registerAppActionCommand(app) {
4996
+ app.command("action").description("Show a Composio action schema from the live catalog").argument("<tool-slug>", "Composio tool slug (e.g. GITHUB_CREATE_ISSUE)").action((toolSlug) => runCliCommand("App action failed", async () => {
4997
+ const config = createCliConfig();
4998
+ const client = createCliPlatformClient(config);
4999
+ await ensureActiveOrganization(config);
5000
+ const result = await runAppAction(client, toolSlug);
5001
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
5002
+ }, void 0, { orgScoped: true }));
5003
+ }
5004
+ //#endregion
5005
+ //#region src/commands/app/actions.ts
5006
+ function registerAppActionsCommand(app) {
5007
+ app.command("actions [slug]").description("List or search Composio actions (per app or globally with --search)").option("--search <query>", "Search actions within the app or across all apps").option("--limit <n>", "Page size", (value) => Number.parseInt(value, 10)).option("--cursor <cursor>", "Pagination cursor").action((slug, options) => runCliCommand("App actions failed", async () => {
5008
+ const config = createCliConfig();
5009
+ const client = createCliPlatformClient(config);
5010
+ await ensureActiveOrganization(config);
5011
+ const result = await runAppActions(client, {
5012
+ slug,
5013
+ ...options
5014
+ });
5015
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
5016
+ }, void 0, { orgScoped: true }));
5017
+ }
5018
+ //#endregion
5019
+ //#region src/commands/app/parse-custom-app-fields.ts
5020
+ function parseCustomAppFieldFlag(value) {
5021
+ const parts = value.split(":").map((part) => part.trim()).filter(Boolean);
5022
+ const key = parts[0];
5023
+ if (!key || !/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key)) throw new Error(`Invalid field key in --field ${JSON.stringify(value)}`);
5024
+ const optional = parts.includes("optional");
5025
+ return {
5026
+ key,
5027
+ secret: parts.includes("public") ? false : parts.includes("secret") || !optional,
5028
+ optional
5029
+ };
5030
+ }
5031
+ function parseCustomAppFields(flags) {
5032
+ const fields = {};
5033
+ for (const flag of flags) {
5034
+ const parsed = parseCustomAppFieldFlag(flag);
5035
+ fields[parsed.key] = {
5036
+ ...parsed.secret ? { secret: true } : {},
5037
+ ...parsed.optional ? { optional: true } : {}
5038
+ };
5039
+ }
5040
+ if (Object.keys(fields).length === 0) throw new Error("At least one --field is required");
5041
+ if (!Object.values(fields).some((field) => field.optional !== true)) return fields;
5042
+ if (!Object.values(fields).some((field) => field.optional !== true)) throw new Error("At least one --field must be required (omit :optional)");
5043
+ return fields;
5044
+ }
5045
+ function collectValues$1(value, previous) {
5046
+ return [...previous, value];
5047
+ }
5048
+ //#endregion
5049
+ //#region src/commands/app/run-app-create.ts
5050
+ async function runAppCreate(client, input) {
5051
+ return client.apps.create(input);
5052
+ }
5053
+ //#endregion
5054
+ //#region src/commands/app/create.ts
5055
+ function registerAppCreateCommand(app) {
5056
+ app.command("create").description("Create a custom org api_key app").requiredOption("--name <name>", "App display name").option("--slug <slug>", "App slug segment (defaults from name)").requiredOption("--description <description>", "App description").option("--field <field>", "Credential field as key, key:secret, key:optional, or key:secret:optional (repeatable)", collectValues$1, []).action((options) => runCliCommand("Create app failed", async () => {
5057
+ const config = createCliConfig();
5058
+ const client = createCliPlatformClient(config);
5059
+ await ensureActiveOrganization(config);
5060
+ const slug = options.slug?.trim() || slugifyAppName(options.name);
5061
+ const created = await runAppCreate(client, {
5062
+ name: options.name.trim(),
5063
+ slug,
5064
+ description: options.description,
5065
+ fields: parseCustomAppFields(options.field)
5066
+ });
5067
+ process.stdout.write(`${JSON.stringify(created, null, 2)}\n`);
5068
+ }, void 0, { orgScoped: true }));
5069
+ }
5070
+ //#endregion
4848
5071
  //#region src/commands/app/run-app-list.ts
4849
5072
  async function runAppList(client) {
4850
5073
  return client.apps.listCatalog();
@@ -4861,26 +5084,111 @@ function registerAppListCommand(app) {
4861
5084
  }, void 0, { orgScoped: true }));
4862
5085
  }
4863
5086
  //#endregion
4864
- //#region src/commands/app/index.ts
4865
- function registerAppCommand(program) {
4866
- registerAppListCommand(program.command("app").description("Browse connectable integrations"));
5087
+ //#region src/commands/app/search.ts
5088
+ function registerAppSearchCommand(app) {
5089
+ app.command("search").description("Search the Composio app catalog (live)").argument("<query>", "Search query").option("--category <category>", "Filter by category").option("--limit <n>", "Page size", (value) => Number.parseInt(value, 10)).option("--cursor <cursor>", "Pagination cursor").action((query, options) => runCliCommand("App search failed", async () => {
5090
+ const config = createCliConfig();
5091
+ const client = createCliPlatformClient(config);
5092
+ await ensureActiveOrganization(config);
5093
+ const result = await runAppSearch(client, query, options);
5094
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
5095
+ }, void 0, { orgScoped: true }));
4867
5096
  }
4868
5097
  //#endregion
4869
- //#region src/commands/connect/parse-connect-scopes.ts
4870
- function parseConnectScopes(scopes, projectSlugs) {
4871
- const normalized = scopes.length > 0 ? scopes : ["org"];
4872
- for (const scope of normalized) if (scope !== "org" && scope !== "user" && scope !== "project") throw new Error(`Unknown scope "${scope}". Expected org, user, or project.`);
4873
- const createOrganizationCredential = normalized.includes("org");
4874
- const createUserProvidedCredential = normalized.includes("user");
4875
- const includesProject = normalized.includes("project");
4876
- const selectedProjects = includesProject ? projectSlugs : [];
4877
- if (includesProject && selectedProjects.length === 0) throw new Error("--project-slug is required when scope includes project");
4878
- if (!createOrganizationCredential && !createUserProvidedCredential && selectedProjects.length === 0) throw new Error("At least one scope is required via --scope and/or --project-slug");
4879
- return {
4880
- createOrganizationCredential,
4881
- createUserProvidedCredential,
4882
- projects: selectedProjects
4883
- };
5098
+ //#region src/commands/app/show.ts
5099
+ function registerAppShowCommand(app) {
5100
+ app.command("show").description("Show a Composio app from the live catalog").argument("<slug>", "App slug").action((slug) => runCliCommand("App show failed", async () => {
5101
+ const config = createCliConfig();
5102
+ const client = createCliPlatformClient(config);
5103
+ await ensureActiveOrganization(config);
5104
+ const result = await runAppShow(client, slug);
5105
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
5106
+ }, void 0, { orgScoped: true }));
5107
+ }
5108
+ //#endregion
5109
+ //#region src/project/resolve-keystroke-config-root.ts
5110
+ const KEYSTROKE_CONFIG = "keystroke.config.ts";
5111
+ /** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
5112
+ function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
5113
+ let dir = resolve(fromDir);
5114
+ while (dir !== dirname(dir)) {
5115
+ if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
5116
+ dir = dirname(dir);
5117
+ }
5118
+ if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
5119
+ return null;
5120
+ }
5121
+ //#endregion
5122
+ //#region src/commands/app/generate-app-stub.ts
5123
+ function slugPartToExportName(part) {
5124
+ return part.split(/[-_]+/).filter(Boolean).map((segment, index) => index === 0 ? segment.charAt(0).toLowerCase() + segment.slice(1) : segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
5125
+ }
5126
+ function resolveSyncedAppExportName(slug) {
5127
+ const parsed = parseAppSlug(slug);
5128
+ const exportName = slugPartToExportName(parsed.kind === "org" ? parsed.name : parsed.slug);
5129
+ if (!exportName) throw new Error(`Cannot derive export name from app slug: ${slug}`);
5130
+ return exportName;
5131
+ }
5132
+ function resolveSyncedAppDirName(slug) {
5133
+ const parsed = parseAppSlug(slug);
5134
+ return parsed.kind === "org" ? parsed.name : parsed.slug;
5135
+ }
5136
+ function generateAppStub(app) {
5137
+ const exportName = resolveSyncedAppExportName(app.slug);
5138
+ const credentialLines = Object.entries(app.credentialFields).map(([key, field]) => {
5139
+ return ` ${key}: ${field.optional ? "z.string().optional()" : "z.string()"},`;
5140
+ }).join("\n");
5141
+ return `import { defineApp } from "@keystrokehq/keystroke/app";
5142
+ import { z } from "zod";
5143
+
5144
+ /** Synced from platform app \`${app.slug}\`. Re-run \`keystroke app sync ${app.slug}\` after template changes. */
5145
+ export const ${exportName} = defineApp({
5146
+ slug: ${JSON.stringify(app.slug)},
5147
+ auth: "api_key",
5148
+ credential: {
5149
+ ${credentialLines}
5150
+ },
5151
+ });
5152
+ `;
5153
+ }
5154
+ //#endregion
5155
+ //#region src/commands/app/run-app-sync.ts
5156
+ async function runAppSync(options) {
5157
+ const projectRoot = resolveKeystrokeConfigRoot(options.dir ?? process.cwd());
5158
+ if (!projectRoot) throw new Error("Not in a keystroke project (keystroke.config.ts not found)");
5159
+ const app = await options.client.apps.get(options.slug);
5160
+ const appDir = join(projectRoot, "src", "apps", resolveSyncedAppDirName(app.slug));
5161
+ const outputPath = join(appDir, "app.ts");
5162
+ await mkdir(appDir, { recursive: true });
5163
+ await writeFile(outputPath, generateAppStub(app), "utf8");
5164
+ return { outputPath };
5165
+ }
5166
+ //#endregion
5167
+ //#region src/commands/app/sync.ts
5168
+ function registerAppSyncCommand(app) {
5169
+ app.command("sync").description("Sync a custom app credential template into src/apps/<name>/app.ts").argument("<slug>", "App slug, e.g. acme/internal-api").option("--dir <dir>", "Project root (defaults to cwd)").action((slug, options) => runCliCommand("Sync app failed", async () => {
5170
+ const config = createCliConfig();
5171
+ const client = createCliPlatformClient(config);
5172
+ await ensureActiveOrganization(config);
5173
+ const result = await runAppSync({
5174
+ client,
5175
+ slug,
5176
+ ...options.dir !== void 0 ? { dir: options.dir } : {}
5177
+ });
5178
+ process.stdout.write(`Wrote ${result.outputPath}\n`);
5179
+ }, void 0, { orgScoped: true }));
5180
+ }
5181
+ //#endregion
5182
+ //#region src/commands/app/index.ts
5183
+ function registerAppCommand(program) {
5184
+ const app = program.command("app").description("Browse connectable integrations");
5185
+ registerAppListCommand(app);
5186
+ registerAppSearchCommand(app);
5187
+ registerAppShowCommand(app);
5188
+ registerAppActionsCommand(app);
5189
+ registerAppActionCommand(app);
5190
+ registerAppCreateCommand(app);
5191
+ registerAppSyncCommand(app);
4884
5192
  }
4885
5193
  //#endregion
4886
5194
  //#region src/auth/open-url.ts
@@ -4893,101 +5201,59 @@ function openUrl(url) {
4893
5201
  }
4894
5202
  //#endregion
4895
5203
  //#region src/commands/connect/run-connect.ts
4896
- function providerLabel(key) {
4897
- return key.charAt(0).toUpperCase() + key.slice(1);
4898
- }
4899
- async function runConnect(options) {
4900
- const { client, platform, provider, serverUrl, printUrl, oauthScopes = [], credentialScopes } = options;
4901
- const label = providerLabel(provider.key);
4902
- if (provider.mode === "browser-redirect") {
4903
- const url = `${serverUrl}${provider.path}`;
4904
- if (printUrl) {
4905
- process.stdout.write(`${url}\n`);
4906
- return;
4907
- }
4908
- openUrl(url);
4909
- process.stdout.write(`Complete ${label} sign-in in your browser.\n`);
4910
- return;
4911
- }
4912
- if (platform) {
4913
- const scopes = credentialScopes ?? parseConnectScopes([], []);
4914
- const result = await platform.credentials.startOAuthConnection({
4915
- appId: provider.key,
4916
- scopes: oauthScopes,
4917
- permissionMode: "default",
4918
- projects: scopes.projects,
4919
- createOrganizationCredential: scopes.createOrganizationCredential,
4920
- createUserProvidedCredential: scopes.createUserProvidedCredential
4921
- });
4922
- if (!result.authorizeUrl) throw new Error("OAuth provider did not return an authorize URL");
4923
- if (oauthScopes.length > 0) {
4924
- process.stdout.write("Requested OAuth scopes:\n");
4925
- for (const scope of oauthScopes) process.stdout.write(` - ${scope}\n`);
4926
- process.stdout.write("\n");
4927
- }
4928
- if (printUrl) {
4929
- process.stdout.write(`${result.authorizeUrl}\n`);
4930
- return;
4931
- }
4932
- openUrl(result.authorizeUrl);
4933
- process.stdout.write(`Complete ${label} sign-in in your browser.\n`);
4934
- return;
5204
+ function resolveConnectAppSlug(slug, apps) {
5205
+ const trimmed = slug.trim();
5206
+ if (!trimmed) throw new Error("App slug is required");
5207
+ try {
5208
+ parseAppSlug(trimmed);
5209
+ } catch {
5210
+ throw new Error(`Invalid app slug: ${slug}`);
4935
5211
  }
4936
- const payload = await client.connect.getAuthorizeUrl(provider.path);
4937
- const scopes = new URL(payload.url).searchParams.get("scope")?.split(" ").filter(Boolean) ?? [];
4938
- if (scopes.length > 0) {
4939
- process.stdout.write("Requested scopes:\n");
4940
- for (const scope of scopes) process.stdout.write(` - ${scope}\n`);
4941
- process.stdout.write("\n");
5212
+ const match = apps.find((app) => app.id === trimmed);
5213
+ if (!match) {
5214
+ const available = apps.map((app) => app.id).join(", ") || "(none)";
5215
+ throw new Error(`Unknown app "${trimmed}". Available: ${available}`);
4942
5216
  }
4943
- if (printUrl) {
4944
- process.stdout.write(`${payload.url}\n`);
5217
+ return match;
5218
+ }
5219
+ function buildConnectDeeplink(options) {
5220
+ const base = options.webUrl.replace(/\/+$/, "");
5221
+ const params = new URLSearchParams({ connect: options.appSlug });
5222
+ return `${base}/${options.orgSlug}/apps?${params.toString()}`;
5223
+ }
5224
+ async function runConnect(options) {
5225
+ const apps = await options.platform.apps.listCatalog();
5226
+ const app = resolveConnectAppSlug(options.slug, apps);
5227
+ const url = buildConnectDeeplink({
5228
+ webUrl: options.webUrl,
5229
+ orgSlug: options.orgSlug,
5230
+ appSlug: app.id
5231
+ });
5232
+ if (options.printUrl) {
5233
+ process.stdout.write(`${url}\n`);
4945
5234
  return;
4946
5235
  }
4947
- openUrl(payload.url);
4948
- process.stdout.write(`Complete ${label} sign-in in your browser.\n`);
5236
+ openUrl(url);
5237
+ process.stdout.write(`Opening ${app.name} connect flow in your browser.\n`);
4949
5238
  }
4950
5239
  //#endregion
4951
5240
  //#region src/commands/connect/index.ts
4952
- function resolveConnectTargetOptions() {
4953
- const { local } = getCliTargetOptions();
4954
- if (local) return { local: true };
4955
- return { orgScoped: true };
4956
- }
4957
- function collectValues$1(value, previous) {
4958
- return previous.concat([value]);
4959
- }
4960
5241
  function registerConnectCommand(program) {
4961
- program.command("connect [provider]").description("Connect third-party accounts (uses apiTarget; --local for local server)").option("--oauth-scope <scope>", "OAuth permission scope to request (repeatable)", collectValues$1, []).option("--scope <scope>", "Credential scope: org, user, or project (repeatable)", collectValues$1, []).option("--project-slug <slug>", "Project slug when scope includes project (repeatable)", collectValues$1, []).option("--print-url", "Print the authorize URL and exit without opening the browser").action((provider, options) => runCliCommand("Could not fetch connect providers", async (ctx) => {
4962
- const providers = await ctx.client.connect.listProviders();
4963
- if (!provider) {
4964
- if (providers.length === 0) {
4965
- process.stdout.write("No connect providers available on this server.\n");
4966
- return;
4967
- }
4968
- process.stdout.write("Available providers:\n");
4969
- for (const entry of providers) process.stdout.write(` ${entry.key}\n`);
4970
- return;
4971
- }
4972
- const match = providers.find((entry) => entry.key === provider);
4973
- if (!match) {
4974
- const available = providers.map((entry) => entry.key).join(", ") || "(none)";
4975
- throw new Error(`Unknown provider "${provider}". Available: ${available}`);
4976
- }
4977
- const hasScopeFlags = options.scope.length > 0 || options.projectSlug.length > 0;
4978
- if (ctx.apiTarget.mode === "local" && hasScopeFlags) throw new Error("--scope and --project-slug are only supported against the platform API (omit --local)");
4979
- const credentialScopes = ctx.apiTarget.mode === "platform" ? parseConnectScopes(options.scope, options.projectSlug) : void 0;
4980
- const platform = ctx.apiTarget.mode === "platform" ? createCliPlatformClient(createCliConfig()) : void 0;
5242
+ program.command("connect <slug>").description("Open the web app to connect an integration by catalog slug").option("--print-url", "Print the connect URL and exit without opening the browser").action((slug, options) => runCliCommand("Connect failed", async () => {
5243
+ const config = createCliConfig();
5244
+ await ensureActiveOrganization(config);
5245
+ const organizations = await listOrganizations(config);
5246
+ const activeOrganizationId = config.get("activeOrganizationId");
5247
+ const activeOrg = organizations.find((entry) => entry.organization.id === activeOrganizationId);
5248
+ if (!activeOrg) throw new Error("No active organization. Run `keystroke auth login` or `keystroke config use org`.");
4981
5249
  await runConnect({
4982
- client: ctx.client,
4983
- platform,
4984
- provider: match,
4985
- serverUrl: ctx.serverUrl,
4986
- printUrl: options.printUrl,
4987
- oauthScopes: options.oauthScope,
4988
- credentialScopes
5250
+ platform: createCliPlatformClient(config),
5251
+ webUrl: getWebUrl(config),
5252
+ orgSlug: activeOrg.organization.slug,
5253
+ slug,
5254
+ printUrl: options.printUrl
4989
5255
  });
4990
- }, void 0, resolveConnectTargetOptions()));
5256
+ }, void 0, { orgScoped: true }));
4991
5257
  }
4992
5258
  //#endregion
4993
5259
  //#region src/commands/credentials/target-options.ts
@@ -5248,6 +5514,23 @@ async function assertNoSetConflict(client, args) {
5248
5514
  }));
5249
5515
  }
5250
5516
  //#endregion
5517
+ //#region src/commands/connect/parse-connect-scopes.ts
5518
+ function parseConnectScopes(scopes, projectSlugs) {
5519
+ const normalized = scopes.length > 0 ? scopes : ["org"];
5520
+ for (const scope of normalized) if (scope !== "org" && scope !== "user" && scope !== "project") throw new Error(`Unknown scope "${scope}". Expected org, user, or project.`);
5521
+ const createOrganizationCredential = normalized.includes("org");
5522
+ const createUserProvidedCredential = normalized.includes("user");
5523
+ const includesProject = normalized.includes("project");
5524
+ const selectedProjects = includesProject ? projectSlugs : [];
5525
+ if (includesProject && selectedProjects.length === 0) throw new Error("--project-slug is required when scope includes project");
5526
+ if (!createOrganizationCredential && !createUserProvidedCredential && selectedProjects.length === 0) throw new Error("At least one scope is required via --scope and/or --project-slug");
5527
+ return {
5528
+ createOrganizationCredential,
5529
+ createUserProvidedCredential,
5530
+ projects: selectedProjects
5531
+ };
5532
+ }
5533
+ //#endregion
5251
5534
  //#region src/commands/credentials/parse-set-scopes.ts
5252
5535
  function parseCredentialSetScopes(scopes, projectSlugs) {
5253
5536
  return parseConnectScopes(scopes, projectSlugs);
@@ -5476,7 +5759,7 @@ function registerBuildCommand(program) {
5476
5759
  program.command("build").description("Build the keystroke project for production").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
5477
5760
  try {
5478
5761
  const root = resolveProjectRoot(options.dir);
5479
- const { buildApp } = await import("./dist-CJL2zYbP.mjs");
5762
+ const { buildApp } = await import("./dist-B9XaHV_2.mjs");
5480
5763
  await buildApp({ root });
5481
5764
  process.stdout.write(`Built ${root}\n`);
5482
5765
  } catch (error) {
@@ -5633,7 +5916,7 @@ async function sleep(ms) {
5633
5916
  }
5634
5917
  async function buildDeployArchive(client, root, projectId, filter) {
5635
5918
  if (filter?.length) {
5636
- const { buildFilteredApp } = await import("./dist-CJL2zYbP.mjs");
5919
+ const { buildFilteredApp } = await import("./dist-B9XaHV_2.mjs");
5637
5920
  const filtered = await buildFilteredApp({
5638
5921
  root,
5639
5922
  filter,
@@ -5655,7 +5938,7 @@ async function buildDeployArchive(client, root, projectId, filter) {
5655
5938
  sourceFiles: filtered.sourceFiles
5656
5939
  };
5657
5940
  }
5658
- const { buildApp } = await import("./dist-CJL2zYbP.mjs");
5941
+ const { buildApp } = await import("./dist-B9XaHV_2.mjs");
5659
5942
  const { sourceFiles } = await buildApp({
5660
5943
  root,
5661
5944
  collectSources: true,
@@ -5768,7 +6051,7 @@ function runtimeChildEnv(parentEnv, overrides) {
5768
6051
  //#region src/project/bootstrap-run.ts
5769
6052
  /** Node args + env for `@keystrokehq/build` bootstrap (shared by start + dev). */
5770
6053
  async function resolveBootstrapRun(options) {
5771
- const { resolveRuntimeBuildArtifact } = await import("./dist-CJL2zYbP.mjs");
6054
+ const { resolveRuntimeBuildArtifact } = await import("./dist-B9XaHV_2.mjs");
5772
6055
  const loader = pathToFileURL(resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/runtime-loader.mjs")).href;
5773
6056
  const bootstrap = resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/standalone-bootstrap.mjs");
5774
6057
  const args = [`--import=${loader}`];
@@ -5919,7 +6202,7 @@ async function runDev(options) {
5919
6202
  process.on("SIGINT", shutdown);
5920
6203
  process.on("SIGTERM", shutdown);
5921
6204
  try {
5922
- const { watchApp } = await import("./dist-CJL2zYbP.mjs");
6205
+ const { watchApp } = await import("./dist-B9XaHV_2.mjs");
5923
6206
  await watchApp({
5924
6207
  root,
5925
6208
  clean: false,
@@ -6745,7 +7028,7 @@ async function runStart(options) {
6745
7028
  const apiPort = Number(new URL(serverUrl).port || 80);
6746
7029
  const runtimeNodeModules = resolveCliRuntimeNodeModules(resolveCliRoot(import.meta.url));
6747
7030
  ensureNativeDeps(runtimeNodeModules);
6748
- const { buildApp } = await import("./dist-CJL2zYbP.mjs");
7031
+ const { buildApp } = await import("./dist-B9XaHV_2.mjs");
6749
7032
  await buildApp({
6750
7033
  root,
6751
7034
  clean: false
@@ -7201,19 +7484,6 @@ function registerSkillsCommand(program) {
7201
7484
  });
7202
7485
  }
7203
7486
  //#endregion
7204
- //#region src/project/resolve-keystroke-config-root.ts
7205
- const KEYSTROKE_CONFIG = "keystroke.config.ts";
7206
- /** Walk up from `fromDir` and return the directory containing `keystroke.config.ts`, if any. */
7207
- function resolveKeystrokeConfigRoot(fromDir = process.cwd()) {
7208
- let dir = resolve(fromDir);
7209
- while (dir !== dirname(dir)) {
7210
- if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
7211
- dir = dirname(dir);
7212
- }
7213
- if (existsSync(join(dir, KEYSTROKE_CONFIG))) return dir;
7214
- return null;
7215
- }
7216
- //#endregion
7217
7487
  //#region src/skills/sync-skills.ts
7218
7488
  function commandPath(command) {
7219
7489
  const names = [];
@@ -7281,7 +7551,7 @@ function createProgram() {
7281
7551
  return program;
7282
7552
  }
7283
7553
  async function runCli(argv) {
7284
- const { maybeAutoUpdate } = await import("./maybe-auto-update-B0kal2FM.mjs");
7554
+ const { maybeAutoUpdate } = await import("./maybe-auto-update-DHt-mVf1.mjs");
7285
7555
  await maybeAutoUpdate(argv);
7286
7556
  createProgram().parse(argv);
7287
7557
  }