@keystrokehq/cli 0.0.154 → 0.0.155

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/README.md CHANGED
@@ -39,6 +39,31 @@ keystroke auth logout
39
39
 
40
40
  Existing commands do not require auth yet. Use `resolveCliAuth()` when adding protected commands.
41
41
 
42
+ ### Platform commands
43
+
44
+ Org-scoped platform commands use `createCliPlatformClient()` and require `keystroke auth login` plus an active organization (`keystroke config use org <slug>` when you belong to more than one).
45
+
46
+ ```bash
47
+ keystroke org update --name "Acme Inc"
48
+ keystroke org members list
49
+ keystroke org members invite --email ada@example.com --role admin
50
+ keystroke org members role <userId> --role builder
51
+ keystroke org members remove <userId>
52
+ keystroke org leave --yes
53
+ keystroke api-key list
54
+ keystroke api-key create --name "CI"
55
+ keystroke api-key revoke <apiKeyId> --yes
56
+ keystroke --project <slug> project members list
57
+ keystroke --project <slug> project members invite --email ada@example.com --role admin
58
+ keystroke --project <slug> project members role <userId> --role builder
59
+ keystroke --project <slug> project members remove <userId>
60
+ keystroke --project <slug> project members leave --yes
61
+ keystroke --project <slug> project settings get
62
+ keystroke --project <slug> project settings update --description "Notes" --default-role builder --invite-permission all
63
+ ```
64
+
65
+ `organization` is an alias for `org`. List/get-style commands print JSON; mutations set a non-zero exit code on failure.
66
+
42
67
  ### `trigger`
43
68
 
44
69
  List triggers and resolve webhook URLs after deploy (requires `keystroke auth login`):
package/dist/index.mjs CHANGED
@@ -4738,6 +4738,129 @@ function registerAgentCommand(program) {
4738
4738
  registerAgentSessionsCommand(agent);
4739
4739
  }
4740
4740
  //#endregion
4741
+ //#region src/commands/project/run-project.ts
4742
+ async function withActivePlatformClient(config, fn) {
4743
+ const platform = createCliPlatformClient(config);
4744
+ let active = await platform.organizations.getActive();
4745
+ if (!active) active = await selectActiveOrganization(config);
4746
+ platform.setActiveOrganizationId(active.organization.id);
4747
+ await fn(platform);
4748
+ }
4749
+ function writeInactiveDeployHints(projects) {
4750
+ const bin = cliBinaryName();
4751
+ for (const project of projects) {
4752
+ if (project.status !== "inactive") continue;
4753
+ process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} deploy --project ${formatProjectLabel(project)}\n`);
4754
+ }
4755
+ }
4756
+ async function runProjectList(config) {
4757
+ await withActivePlatformClient(config, async (platform) => {
4758
+ const projects = await platform.projects.list();
4759
+ process.stdout.write(`${JSON.stringify({ projects }, null, 2)}\n`);
4760
+ if (projects.length === 0) {
4761
+ const bin = cliBinaryName();
4762
+ process.stderr.write(`No projects yet. Create one with: ${bin} project create --name "My project"\n`);
4763
+ return;
4764
+ }
4765
+ writeInactiveDeployHints(projects);
4766
+ });
4767
+ }
4768
+ async function runProjectMetrics(config, options) {
4769
+ await withActivePlatformClient(config, async (platform) => {
4770
+ const metrics = await platform.projectMetrics.list();
4771
+ if (options.projectRef === void 0) {
4772
+ process.stdout.write(`${JSON.stringify(metrics, null, 2)}\n`);
4773
+ return;
4774
+ }
4775
+ const project = await resolveProjectRef(platform, options.projectRef);
4776
+ const output = Object.fromEntries(Object.entries(metrics).filter(([projectId]) => projectId === project.id));
4777
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
4778
+ });
4779
+ }
4780
+ async function runProjectDeploymentsList(config, options) {
4781
+ await withActivePlatformClient(config, async (platform) => {
4782
+ const project = await resolveProjectRef(platform, options.projectRef);
4783
+ const deployments = await platform.deployments.listForProject(project.id);
4784
+ process.stdout.write(`${JSON.stringify({ deployments }, null, 2)}\n`);
4785
+ });
4786
+ }
4787
+ async function runProjectCreate(config, input) {
4788
+ await withActivePlatformClient(config, async (platform) => {
4789
+ const project = await platform.projects.create(input);
4790
+ process.stdout.write(`${JSON.stringify({ project }, null, 2)}\n`);
4791
+ writeInactiveDeployHints([project]);
4792
+ });
4793
+ }
4794
+ async function runProjectUpdate(config, input) {
4795
+ await withActivePlatformClient(config, async (platform) => {
4796
+ const { projectId, ...patch } = input;
4797
+ const project = await platform.projects.update(projectId, patch);
4798
+ process.stdout.write(`${JSON.stringify({ project }, null, 2)}\n`);
4799
+ });
4800
+ }
4801
+ async function runProjectDelete(config, projectId) {
4802
+ await withActivePlatformClient(config, async (platform) => {
4803
+ await platform.projects.delete(projectId);
4804
+ process.stdout.write(`${JSON.stringify({ deleted: projectId }, null, 2)}\n`);
4805
+ });
4806
+ }
4807
+ //#endregion
4808
+ //#region src/commands/api-key/run-api-key.ts
4809
+ async function runApiKeyList(config) {
4810
+ await withActivePlatformClient(config, async (platform) => {
4811
+ const apiKeys = await platform.apiKeys.list();
4812
+ process.stdout.write(`${JSON.stringify({ apiKeys }, null, 2)}\n`);
4813
+ });
4814
+ }
4815
+ async function runApiKeyCreate(config, input) {
4816
+ await withActivePlatformClient(config, async (platform) => {
4817
+ const apiKey = await platform.apiKeys.create(input);
4818
+ process.stdout.write(`${JSON.stringify({ apiKey }, null, 2)}\n`);
4819
+ });
4820
+ }
4821
+ async function runApiKeyRevoke(config, apiKeyId) {
4822
+ await withActivePlatformClient(config, async (platform) => {
4823
+ await platform.apiKeys.revoke(apiKeyId);
4824
+ process.stdout.write(`${JSON.stringify({ revoked: apiKeyId }, null, 2)}\n`);
4825
+ });
4826
+ }
4827
+ //#endregion
4828
+ //#region src/commands/api-key/index.ts
4829
+ async function handleApiKeyError(fallback, action) {
4830
+ const config = createCliConfig();
4831
+ try {
4832
+ await action();
4833
+ } catch (error) {
4834
+ process.stderr.write(`${formatCliError(error, fallback, {
4835
+ serverUrl: getPlatformUrl(config),
4836
+ webUrl: getWebUrl(config)
4837
+ })}\n`);
4838
+ process.exitCode = 1;
4839
+ }
4840
+ }
4841
+ function registerApiKeyCommand(program) {
4842
+ const apiKey = program.command("api-key").description("Manage org-scoped platform API keys");
4843
+ apiKey.command("list").description("List API keys in the active organization").action(async () => {
4844
+ const config = createCliConfig();
4845
+ await handleApiKeyError("List API keys failed", async () => {
4846
+ await runApiKeyList(config);
4847
+ });
4848
+ });
4849
+ apiKey.command("create").description("Create an API key in the active organization").requiredOption("--name <name>", "API key name").action(async (options) => {
4850
+ const config = createCliConfig();
4851
+ await handleApiKeyError("Create API key failed", async () => {
4852
+ await runApiKeyCreate(config, { name: options.name });
4853
+ });
4854
+ });
4855
+ apiKey.command("revoke").description("Revoke an API key").argument("<apiKeyId>", "API key id").option("-y, --yes", "Skip confirmation prompt").action(async (apiKeyId, options) => {
4856
+ const config = createCliConfig();
4857
+ await handleApiKeyError("Revoke API key failed", async () => {
4858
+ if (!options.yes) throw new Error("Pass --yes to confirm API key revocation");
4859
+ await runApiKeyRevoke(config, apiKeyId);
4860
+ });
4861
+ });
4862
+ }
4863
+ //#endregion
4741
4864
  //#region src/commands/app/run-app-list.ts
4742
4865
  async function runAppList(client) {
4743
4866
  return client.apps.listCatalog();
@@ -4883,73 +5006,6 @@ function registerConnectCommand(program) {
4883
5006
  }, void 0, resolveConnectTargetOptions()));
4884
5007
  }
4885
5008
  //#endregion
4886
- //#region src/commands/project/run-project.ts
4887
- async function withActivePlatformClient(config, fn) {
4888
- const platform = createCliPlatformClient(config);
4889
- let active = await platform.organizations.getActive();
4890
- if (!active) active = await selectActiveOrganization(config);
4891
- platform.setActiveOrganizationId(active.organization.id);
4892
- await fn(platform);
4893
- }
4894
- function writeInactiveDeployHints(projects) {
4895
- const bin = cliBinaryName();
4896
- for (const project of projects) {
4897
- if (project.status !== "inactive") continue;
4898
- process.stderr.write(`Project ${formatProjectLabel(project)} is inactive. Deploy with: ${bin} deploy --project ${formatProjectLabel(project)}\n`);
4899
- }
4900
- }
4901
- async function runProjectList(config) {
4902
- await withActivePlatformClient(config, async (platform) => {
4903
- const projects = await platform.projects.list();
4904
- process.stdout.write(`${JSON.stringify({ projects }, null, 2)}\n`);
4905
- if (projects.length === 0) {
4906
- const bin = cliBinaryName();
4907
- process.stderr.write(`No projects yet. Create one with: ${bin} project create --name "My project"\n`);
4908
- return;
4909
- }
4910
- writeInactiveDeployHints(projects);
4911
- });
4912
- }
4913
- async function runProjectMetrics(config, options) {
4914
- await withActivePlatformClient(config, async (platform) => {
4915
- const metrics = await platform.projectMetrics.list();
4916
- if (options.projectRef === void 0) {
4917
- process.stdout.write(`${JSON.stringify(metrics, null, 2)}\n`);
4918
- return;
4919
- }
4920
- const project = await resolveProjectRef(platform, options.projectRef);
4921
- const output = Object.fromEntries(Object.entries(metrics).filter(([projectId]) => projectId === project.id));
4922
- process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
4923
- });
4924
- }
4925
- async function runProjectDeploymentsList(config, options) {
4926
- await withActivePlatformClient(config, async (platform) => {
4927
- const project = await resolveProjectRef(platform, options.projectRef);
4928
- const deployments = await platform.deployments.listForProject(project.id);
4929
- process.stdout.write(`${JSON.stringify({ deployments }, null, 2)}\n`);
4930
- });
4931
- }
4932
- async function runProjectCreate(config, input) {
4933
- await withActivePlatformClient(config, async (platform) => {
4934
- const project = await platform.projects.create(input);
4935
- process.stdout.write(`${JSON.stringify({ project }, null, 2)}\n`);
4936
- writeInactiveDeployHints([project]);
4937
- });
4938
- }
4939
- async function runProjectUpdate(config, input) {
4940
- await withActivePlatformClient(config, async (platform) => {
4941
- const { projectId, ...patch } = input;
4942
- const project = await platform.projects.update(projectId, patch);
4943
- process.stdout.write(`${JSON.stringify({ project }, null, 2)}\n`);
4944
- });
4945
- }
4946
- async function runProjectDelete(config, projectId) {
4947
- await withActivePlatformClient(config, async (platform) => {
4948
- await platform.projects.delete(projectId);
4949
- process.stdout.write(`${JSON.stringify({ deleted: projectId }, null, 2)}\n`);
4950
- });
4951
- }
4952
- //#endregion
4953
5009
  //#region src/commands/credentials/target-options.ts
4954
5010
  function resolveCredentialsTargetOptions(options = {}) {
4955
5011
  if (options.scopeType === "project") return {
@@ -6229,6 +6285,15 @@ function registerInitCommand(program) {
6229
6285
  });
6230
6286
  }
6231
6287
  //#endregion
6288
+ //#region src/active-project.ts
6289
+ function clearActiveProjectIfMatches(config, project) {
6290
+ const activeProjectId = config.get("activeProjectId");
6291
+ if (activeProjectId === project.id || activeProjectId === project.slug) config.delete("activeProjectId");
6292
+ }
6293
+ function clearActiveProject(config) {
6294
+ if (config.get("activeProjectId") !== void 0) config.delete("activeProjectId");
6295
+ }
6296
+ //#endregion
6232
6297
  //#region src/commands/organization/run-organization.ts
6233
6298
  async function runOrganizationUpdate(config, input) {
6234
6299
  await withActivePlatformClient(config, async (platform) => {
@@ -6236,20 +6301,302 @@ async function runOrganizationUpdate(config, input) {
6236
6301
  process.stdout.write(`${JSON.stringify({ organization }, null, 2)}\n`);
6237
6302
  });
6238
6303
  }
6304
+ async function runOrganizationMembersList(config) {
6305
+ await withActivePlatformClient(config, async (platform) => {
6306
+ const members = await platform.members.listOrganizationMembers();
6307
+ process.stdout.write(`${JSON.stringify({ members }, null, 2)}\n`);
6308
+ });
6309
+ }
6310
+ async function runOrganizationMembersInvite(config, input) {
6311
+ await withActivePlatformClient(config, async (platform) => {
6312
+ const result = await platform.members.inviteOrganizationMembers(input);
6313
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
6314
+ });
6315
+ }
6316
+ async function runOrganizationMembersRole(config, input) {
6317
+ await withActivePlatformClient(config, async (platform) => {
6318
+ const member = await platform.members.updateOrganizationMember(input.userId, { role: input.role });
6319
+ process.stdout.write(`${JSON.stringify({ member }, null, 2)}\n`);
6320
+ });
6321
+ }
6322
+ async function runOrganizationMembersRemove(config, userId) {
6323
+ await withActivePlatformClient(config, async (platform) => {
6324
+ await platform.members.removeOrganizationMember(userId);
6325
+ process.stdout.write(`${JSON.stringify({ removed: userId }, null, 2)}\n`);
6326
+ });
6327
+ }
6328
+ async function runOrganizationLeave(config) {
6329
+ const platformUrl = getPlatformUrl(config);
6330
+ const token = getAccessToken(platformUrl);
6331
+ if (!token) throw new Error("Not authenticated. Run `keystroke auth login` first.");
6332
+ const user = await getSessionWithBearer(platformUrl, token);
6333
+ if (!user) throw new Error("Session expired. Run `keystroke auth login` again.");
6334
+ await withActivePlatformClient(config, async (platform) => {
6335
+ await platform.members.removeOrganizationMember(user.id);
6336
+ platform.setActiveOrganizationId(null);
6337
+ config.delete("activeOrganizationId");
6338
+ clearActiveProject(config);
6339
+ process.stdout.write(`${JSON.stringify({ left: user.id }, null, 2)}\n`);
6340
+ });
6341
+ }
6239
6342
  //#endregion
6240
6343
  //#region src/commands/organization/index.ts
6344
+ function collectEmails$1(value, previous) {
6345
+ return [...previous, value];
6346
+ }
6347
+ function parseInvitableRole(role) {
6348
+ if (role === "admin" || role === "builder") return role;
6349
+ throw new Error("Role must be admin or builder");
6350
+ }
6351
+ async function handleOrganizationError(fallback, action) {
6352
+ const config = createCliConfig();
6353
+ try {
6354
+ await action();
6355
+ } catch (error) {
6356
+ process.stderr.write(`${formatCliError(error, fallback, {
6357
+ serverUrl: getPlatformUrl(config),
6358
+ webUrl: getWebUrl(config)
6359
+ })}\n`);
6360
+ process.exitCode = 1;
6361
+ }
6362
+ }
6241
6363
  function registerOrganizationCommand(program) {
6242
- program.command("organization").description("Manage the active organization").command("update").description("Update the active organization's display name").requiredOption("--name <name>", "Organization display name").action(async (options) => {
6364
+ const organization = program.command("organization").alias("org").description("Manage the active organization");
6365
+ organization.command("update").description("Update the active organization's display name").requiredOption("--name <name>", "Organization display name").action(async (options) => {
6243
6366
  const config = createCliConfig();
6244
- try {
6367
+ await handleOrganizationError("Update organization failed", async () => {
6245
6368
  await runOrganizationUpdate(config, { name: options.name });
6246
- } catch (error) {
6247
- process.stderr.write(`${formatCliError(error, "Update organization failed", {
6248
- serverUrl: getPlatformUrl(config),
6249
- webUrl: getWebUrl(config)
6250
- })}\n`);
6251
- process.exitCode = 1;
6252
- }
6369
+ });
6370
+ });
6371
+ organization.command("leave").description("Leave the active organization").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
6372
+ const config = createCliConfig();
6373
+ await handleOrganizationError("Leave organization failed", async () => {
6374
+ if (!options.yes) throw new Error("Pass --yes to confirm leaving the active organization");
6375
+ await runOrganizationLeave(config);
6376
+ });
6377
+ });
6378
+ const members = organization.command("members").description("Manage organization members in the active organization");
6379
+ members.command("list").description("List members in the active organization").action(async () => {
6380
+ const config = createCliConfig();
6381
+ await handleOrganizationError("List organization members failed", async () => {
6382
+ await runOrganizationMembersList(config);
6383
+ });
6384
+ });
6385
+ members.command("invite").description("Invite people to the active organization").requiredOption("--email <email>", "Email address (repeat for multiple)", collectEmails$1, []).requiredOption("--role <role>", "Member role (admin or builder)").action(async (options) => {
6386
+ const config = createCliConfig();
6387
+ await handleOrganizationError("Invite organization members failed", async () => {
6388
+ await runOrganizationMembersInvite(config, {
6389
+ emails: options.email,
6390
+ role: parseInvitableRole(options.role)
6391
+ });
6392
+ });
6393
+ });
6394
+ members.command("role").description("Update a member's role in the active organization").argument("<userId>", "Member user id").requiredOption("--role <role>", "Member role (admin or builder)").action(async (userId, options) => {
6395
+ const config = createCliConfig();
6396
+ await handleOrganizationError("Update organization member role failed", async () => {
6397
+ await runOrganizationMembersRole(config, {
6398
+ userId,
6399
+ role: parseInvitableRole(options.role)
6400
+ });
6401
+ });
6402
+ });
6403
+ members.command("remove").description("Remove a member from the active organization").argument("<userId>", "Member user id").action(async (userId) => {
6404
+ const config = createCliConfig();
6405
+ await handleOrganizationError("Remove organization member failed", async () => {
6406
+ await runOrganizationMembersRemove(config, userId);
6407
+ });
6408
+ });
6409
+ }
6410
+ //#endregion
6411
+ //#region src/commands/project/run-project-members.ts
6412
+ async function resolveMembersProject(platform, config, projectRef) {
6413
+ const ref = projectRef ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
6414
+ if (!ref) throw new Error("Usage: keystroke --project <slug> project members <command>");
6415
+ return resolveProjectRef(platform, ref);
6416
+ }
6417
+ async function runProjectMembersList(config, options) {
6418
+ await withActivePlatformClient(config, async (platform) => {
6419
+ const project = await resolveMembersProject(platform, config, options.projectRef);
6420
+ const members = await platform.members.listForProject(project.id);
6421
+ process.stdout.write(`${JSON.stringify({ members }, null, 2)}\n`);
6422
+ });
6423
+ }
6424
+ async function runProjectMembersInvite(config, input) {
6425
+ await withActivePlatformClient(config, async (platform) => {
6426
+ const project = await resolveMembersProject(platform, config, input.projectRef);
6427
+ const result = await platform.members.inviteToProject({
6428
+ projectId: project.id,
6429
+ emails: input.emails,
6430
+ role: input.role
6431
+ });
6432
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
6433
+ });
6434
+ }
6435
+ async function runProjectMembersRole(config, input) {
6436
+ await withActivePlatformClient(config, async (platform) => {
6437
+ const project = await resolveMembersProject(platform, config, input.projectRef);
6438
+ const member = await platform.members.updateProjectMember(project.id, input.userId, { role: input.role });
6439
+ process.stdout.write(`${JSON.stringify({ member }, null, 2)}\n`);
6440
+ });
6441
+ }
6442
+ async function runProjectMembersRemove(config, input) {
6443
+ await withActivePlatformClient(config, async (platform) => {
6444
+ const project = await resolveMembersProject(platform, config, input.projectRef);
6445
+ await platform.members.removeProjectMember(project.id, input.userId);
6446
+ process.stdout.write(`${JSON.stringify({ removed: input.userId }, null, 2)}\n`);
6447
+ });
6448
+ }
6449
+ async function runProjectMembersLeave(config, options) {
6450
+ await withActivePlatformClient(config, async (platform) => {
6451
+ const project = await resolveMembersProject(platform, config, options.projectRef);
6452
+ await platform.members.leaveProject(project.id);
6453
+ clearActiveProjectIfMatches(config, project);
6454
+ process.stdout.write(`${JSON.stringify({ left: project.id }, null, 2)}\n`);
6455
+ });
6456
+ }
6457
+ //#endregion
6458
+ //#region src/commands/project/members.ts
6459
+ function collectEmails(value, previous) {
6460
+ return [...previous, value];
6461
+ }
6462
+ function parseProjectRole$1(role) {
6463
+ if (role === "admin" || role === "builder") return role;
6464
+ throw new Error("Role must be admin or builder");
6465
+ }
6466
+ function resolveProjectRefOption$1(config, optionsProject) {
6467
+ return optionsProject ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
6468
+ }
6469
+ function projectRefInput$1(config, optionsProject) {
6470
+ const projectRef = resolveProjectRefOption$1(config, optionsProject);
6471
+ return projectRef ? { projectRef } : {};
6472
+ }
6473
+ async function handleProjectMembersError(fallback, action) {
6474
+ const config = createCliConfig();
6475
+ try {
6476
+ await action();
6477
+ } catch (error) {
6478
+ process.stderr.write(`${formatCliError(error, fallback, {
6479
+ serverUrl: getPlatformUrl(config),
6480
+ webUrl: getWebUrl(config)
6481
+ })}\n`);
6482
+ process.exitCode = 1;
6483
+ }
6484
+ }
6485
+ function registerProjectMembersCommand(project) {
6486
+ const members = project.command("members").description("Manage members for a platform project");
6487
+ members.command("list").description("List members for a project").option("--project <slug>", "Project slug").action(async (options) => {
6488
+ const config = createCliConfig();
6489
+ await handleProjectMembersError("List project members failed", async () => {
6490
+ await runProjectMembersList(config, projectRefInput$1(config, options.project));
6491
+ });
6492
+ });
6493
+ members.command("invite").description("Invite people to a project").option("--project <slug>", "Project slug").requiredOption("--email <email>", "Email address (repeat for multiple)", collectEmails, []).requiredOption("--role <role>", "Project role (admin or builder)").action(async (options) => {
6494
+ const config = createCliConfig();
6495
+ await handleProjectMembersError("Invite project members failed", async () => {
6496
+ await runProjectMembersInvite(config, {
6497
+ ...projectRefInput$1(config, options.project),
6498
+ emails: options.email,
6499
+ role: parseProjectRole$1(options.role)
6500
+ });
6501
+ });
6502
+ });
6503
+ members.command("role").description("Update a project member role").argument("<userId>", "Member user id").option("--project <slug>", "Project slug").requiredOption("--role <role>", "Project role (admin or builder)").action(async (userId, options) => {
6504
+ const config = createCliConfig();
6505
+ await handleProjectMembersError("Update project member role failed", async () => {
6506
+ await runProjectMembersRole(config, {
6507
+ ...projectRefInput$1(config, options.project),
6508
+ userId,
6509
+ role: parseProjectRole$1(options.role)
6510
+ });
6511
+ });
6512
+ });
6513
+ members.command("remove").description("Remove a member from a project").argument("<userId>", "Member user id").option("--project <slug>", "Project slug").action(async (userId, options) => {
6514
+ const config = createCliConfig();
6515
+ await handleProjectMembersError("Remove project member failed", async () => {
6516
+ await runProjectMembersRemove(config, {
6517
+ ...projectRefInput$1(config, options.project),
6518
+ userId
6519
+ });
6520
+ });
6521
+ });
6522
+ members.command("leave").description("Leave a project as the current user").option("--project <slug>", "Project slug").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
6523
+ const config = createCliConfig();
6524
+ await handleProjectMembersError("Leave project failed", async () => {
6525
+ if (!options.yes) throw new Error("Pass --yes to confirm leaving the project");
6526
+ await runProjectMembersLeave(config, projectRefInput$1(config, options.project));
6527
+ });
6528
+ });
6529
+ }
6530
+ //#endregion
6531
+ //#region src/commands/project/run-project-settings.ts
6532
+ async function resolveSettingsProject(platform, config, projectRef) {
6533
+ const ref = projectRef ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
6534
+ if (!ref) throw new Error("Usage: keystroke --project <slug> project settings <command>");
6535
+ return resolveProjectRef(platform, ref);
6536
+ }
6537
+ async function runProjectSettingsGet(config, options) {
6538
+ await withActivePlatformClient(config, async (platform) => {
6539
+ const project = await resolveSettingsProject(platform, config, options.projectRef);
6540
+ const settings = await platform.projectSettings.get(project.id);
6541
+ process.stdout.write(`${JSON.stringify({ settings }, null, 2)}\n`);
6542
+ });
6543
+ }
6544
+ async function runProjectSettingsUpdate(config, input) {
6545
+ await withActivePlatformClient(config, async (platform) => {
6546
+ const project = await resolveSettingsProject(platform, config, input.projectRef);
6547
+ const { projectRef: _projectRef, ...patch } = input;
6548
+ const settings = await platform.projectSettings.update(project.id, patch);
6549
+ process.stdout.write(`${JSON.stringify({ settings }, null, 2)}\n`);
6550
+ });
6551
+ }
6552
+ //#endregion
6553
+ //#region src/commands/project/settings.ts
6554
+ function resolveProjectRefOption(config, optionsProject) {
6555
+ return optionsProject ?? getCliTargetOptions().projectId ?? config.get("activeProjectId");
6556
+ }
6557
+ function projectRefInput(config, optionsProject) {
6558
+ const projectRef = resolveProjectRefOption(config, optionsProject);
6559
+ return projectRef ? { projectRef } : {};
6560
+ }
6561
+ function parseProjectRole(role) {
6562
+ if (role === "admin" || role === "builder") return role;
6563
+ throw new Error("Default role must be admin or builder");
6564
+ }
6565
+ function parseInvitePermission(permission) {
6566
+ if (permission === "admin" || permission === "all") return permission;
6567
+ throw new Error("Invite permission must be admin or all");
6568
+ }
6569
+ async function handleProjectSettingsError(fallback, action) {
6570
+ const config = createCliConfig();
6571
+ try {
6572
+ await action();
6573
+ } catch (error) {
6574
+ process.stderr.write(`${formatCliError(error, fallback, {
6575
+ serverUrl: getPlatformUrl(config),
6576
+ webUrl: getWebUrl(config)
6577
+ })}\n`);
6578
+ process.exitCode = 1;
6579
+ }
6580
+ }
6581
+ function registerProjectSettingsCommand(project) {
6582
+ const settings = project.command("settings").description("View and update project governance settings");
6583
+ settings.command("get").description("Get settings for a project").option("--project <slug>", "Project slug").action(async (options) => {
6584
+ const config = createCliConfig();
6585
+ await handleProjectSettingsError("Get project settings failed", async () => {
6586
+ await runProjectSettingsGet(config, projectRefInput(config, options.project));
6587
+ });
6588
+ });
6589
+ settings.command("update").description("Update settings for a project").option("--project <slug>", "Project slug").option("--description <description>", "Project description").option("--default-role <role>", "Default role for new members (admin or builder)").option("--invite-permission <permission>", "Who can invite members (admin or all)").action(async (options) => {
6590
+ const config = createCliConfig();
6591
+ await handleProjectSettingsError("Update project settings failed", async () => {
6592
+ if (options.description === void 0 && options.defaultRole === void 0 && options.invitePermission === void 0) throw new Error("At least one of --description, --default-role, or --invite-permission is required");
6593
+ await runProjectSettingsUpdate(config, {
6594
+ ...projectRefInput(config, options.project),
6595
+ ...options.description !== void 0 ? { description: options.description } : {},
6596
+ ...options.defaultRole !== void 0 ? { defaultRole: parseProjectRole(options.defaultRole) } : {},
6597
+ ...options.invitePermission !== void 0 ? { invitePermission: parseInvitePermission(options.invitePermission) } : {}
6598
+ });
6599
+ });
6253
6600
  });
6254
6601
  }
6255
6602
  //#endregion
@@ -6271,7 +6618,7 @@ function registerProjectCommand(program) {
6271
6618
  project.command("metrics").description("List rollup metrics for projects in the active organization").option("--project <slug>", "Return metrics for a single project").action(async (options) => {
6272
6619
  const config = createCliConfig();
6273
6620
  try {
6274
- await runProjectMetrics(config, { ...options.project ? { projectRef: options.project } : {} });
6621
+ await runProjectMetrics(config, options.project ? { projectRef: options.project } : {});
6275
6622
  } catch (error) {
6276
6623
  process.stderr.write(`${formatCliError(error, "List project metrics failed", {
6277
6624
  serverUrl: getPlatformUrl(config),
@@ -6343,6 +6690,8 @@ function registerProjectCommand(program) {
6343
6690
  process.exitCode = 1;
6344
6691
  }
6345
6692
  });
6693
+ registerProjectMembersCommand(project);
6694
+ registerProjectSettingsCommand(project);
6346
6695
  }
6347
6696
  //#endregion
6348
6697
  //#region src/commands/start.ts
@@ -6845,6 +7194,7 @@ function createProgram() {
6845
7194
  registerConnectCommand(program);
6846
7195
  registerConfigCommand(program);
6847
7196
  registerOrganizationCommand(program);
7197
+ registerApiKeyCommand(program);
6848
7198
  registerProjectCommand(program);
6849
7199
  registerHistoryCommand(program);
6850
7200
  registerCredentialsCommand(program);