@autohq/cli 0.1.112 → 0.1.113

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.
@@ -26206,7 +26206,7 @@ Object.assign(lookup, {
26206
26206
  // package.json
26207
26207
  var package_default = {
26208
26208
  name: "@autohq/cli",
26209
- version: "0.1.112",
26209
+ version: "0.1.113",
26210
26210
  license: "SEE LICENSE IN README.md",
26211
26211
  publishConfig: {
26212
26212
  access: "public"
package/dist/index.js CHANGED
@@ -20275,6 +20275,12 @@ function createApiClient(input) {
20275
20275
  }
20276
20276
  return {
20277
20277
  activeOrganization,
20278
+ /**
20279
+ * Resolve the active org/project selection without requiring one: env vars
20280
+ * first, then config, then single-option discovery (which persists what it
20281
+ * finds). Either field may be undefined when nothing resolves.
20282
+ */
20283
+ resolveActiveSelection,
20278
20284
  requestJson,
20279
20285
  async listPendingInvitations(options = {}) {
20280
20286
  const response = await bootstrapFetch(
@@ -21166,7 +21172,7 @@ var init_package = __esm({
21166
21172
  "package.json"() {
21167
21173
  package_default = {
21168
21174
  name: "@autohq/cli",
21169
- version: "0.1.112",
21175
+ version: "0.1.113",
21170
21176
  license: "SEE LICENSE IN README.md",
21171
21177
  publishConfig: {
21172
21178
  access: "public"
@@ -29612,6 +29618,139 @@ async function confirmDestructiveAction(context, input) {
29612
29618
  }
29613
29619
  }
29614
29620
 
29621
+ // src/commands/context-resolution.ts
29622
+ async function requireProjectScope(input) {
29623
+ const resolved = await resolveProjectScope(input);
29624
+ if (resolved) {
29625
+ return resolved;
29626
+ }
29627
+ throw new Error(
29628
+ `No project selected. Run \`auto projects use <project>\` or pass ${input.explicitHint}.`
29629
+ );
29630
+ }
29631
+ async function resolveProjectScope(input) {
29632
+ if (input.identifier?.startsWith("proj_")) {
29633
+ return { projectId: input.identifier, source: "explicit" };
29634
+ }
29635
+ if (input.identifier) {
29636
+ const project = await resolveProject({
29637
+ client: createContextApiClient(input.context),
29638
+ context: input.context,
29639
+ identifier: input.identifier,
29640
+ options: input.options
29641
+ });
29642
+ return {
29643
+ organizationId: project.organizationId,
29644
+ projectId: project.projectId,
29645
+ source: "explicit"
29646
+ };
29647
+ }
29648
+ const selection = await createContextApiClient(
29649
+ input.context
29650
+ ).resolveActiveSelection();
29651
+ if (!selection.projectId) {
29652
+ return void 0;
29653
+ }
29654
+ return {
29655
+ organizationId: selection.organizationId,
29656
+ projectId: selection.projectId,
29657
+ source: "active"
29658
+ };
29659
+ }
29660
+ function withProjectScope(context, scope) {
29661
+ return {
29662
+ ...context,
29663
+ env: {
29664
+ ...context.env,
29665
+ ...scope.organizationId ? { AUTO_ORGANIZATION_ID: scope.organizationId } : {},
29666
+ AUTO_PROJECT_ID: scope.projectId
29667
+ }
29668
+ };
29669
+ }
29670
+ async function requireConnection(input) {
29671
+ const client = createContextApiClient(input.context);
29672
+ const response = await client.listConnections({
29673
+ provider: input.provider,
29674
+ apiBaseUrl: apiUrlFromOptions(input.context, input.options)
29675
+ });
29676
+ const grants = response.connections.flatMap(
29677
+ (connection) => connection.grants.filter((grant) => grant.status === "active").map((grant) => ({ name: grant.name, id: grant.id }))
29678
+ );
29679
+ if (input.identifier) {
29680
+ const identifier = input.identifier;
29681
+ const matches = grants.filter(
29682
+ (grant) => grant.name === identifier || grant.id === identifier
29683
+ );
29684
+ if (matches.length === 0) {
29685
+ const inactive = response.connections.flatMap((connection) => connection.grants).find((grant) => grant.name === identifier || grant.id === identifier);
29686
+ if (inactive) {
29687
+ throw new Error(
29688
+ `${input.provider} connection ${identifier} is ${inactive.status}; only active connections can be selected.`
29689
+ );
29690
+ }
29691
+ throw new Error(
29692
+ `No ${input.provider} connection found matching ${identifier}. Run \`auto connections list\` to see available connections.`
29693
+ );
29694
+ }
29695
+ if (matches.length > 1) {
29696
+ throw new Error(
29697
+ `Multiple ${input.provider} connections match ${identifier}; use the connection id.`
29698
+ );
29699
+ }
29700
+ return matches[0];
29701
+ }
29702
+ if (grants.length === 0) {
29703
+ throw new Error(
29704
+ `No ${input.provider} connections found. Run \`auto connect ${input.provider}\` first.`
29705
+ );
29706
+ }
29707
+ if (grants.length > 1) {
29708
+ const names = grants.map((grant) => grant.name).join(", ");
29709
+ throw new Error(
29710
+ `Multiple ${input.provider} connections found: ${names}. Re-run with --connection <name>.`
29711
+ );
29712
+ }
29713
+ return grants[0];
29714
+ }
29715
+ async function resolveOrganization(input) {
29716
+ const client = createContextApiClient(input.context);
29717
+ const response = await client.listOrganizations({
29718
+ apiBaseUrl: apiUrlFromOptions(input.context, input.options)
29719
+ });
29720
+ return resolveOne(
29721
+ response.organizations.filter(
29722
+ (organization) => matchesOrganization(organization, input.identifier)
29723
+ ),
29724
+ "organization",
29725
+ input.identifier
29726
+ );
29727
+ }
29728
+ async function resolveProject(input) {
29729
+ const activeOrgId = input.client.getActiveSelection().organizationId;
29730
+ const response = await input.client.listProjects({
29731
+ apiBaseUrl: apiUrlFromOptions(input.context, input.options)
29732
+ });
29733
+ const matches = response.projects.filter(
29734
+ (project) => matchesProject(project, input.identifier) && (!activeOrgId || project.organizationId === activeOrgId)
29735
+ );
29736
+ return resolveOne(matches, "project", input.identifier);
29737
+ }
29738
+ function matchesOrganization(organization, identifier) {
29739
+ return organization.organizationId === identifier || organization.organizationSlug === identifier || organization.organizationName === identifier;
29740
+ }
29741
+ function matchesProject(project, identifier) {
29742
+ return project.projectId === identifier || project.projectSlug === identifier || project.projectName === identifier;
29743
+ }
29744
+ function resolveOne(matches, kind, identifier) {
29745
+ if (matches.length === 0) {
29746
+ throw new Error(`No ${kind} found matching ${identifier}`);
29747
+ }
29748
+ if (matches.length > 1) {
29749
+ throw new Error(`Multiple ${kind}s match ${identifier}; use a slug or ID`);
29750
+ }
29751
+ return matches[0];
29752
+ }
29753
+
29615
29754
  // src/commands/connections/format.ts
29616
29755
  function connectionRows(connections, style) {
29617
29756
  const rows = connections.flatMap(
@@ -30163,8 +30302,19 @@ async function connectProviderAction(context, provider, commandOptions) {
30163
30302
  "--token/--stdin are only supported for the telegram provider."
30164
30303
  );
30165
30304
  }
30305
+ const allowScope = await resolveConnectAllowProject(
30306
+ context,
30307
+ provider,
30308
+ commandOptions
30309
+ );
30310
+ const scopedContext = allowScope ? withProjectScope(context, allowScope) : context;
30311
+ const allowProjectId = allowScope?.projectId;
30166
30312
  if (provider === "github") {
30167
- await connectGithub(context, { ...commandOptions, apiBaseUrl });
30313
+ await connectGithub(scopedContext, {
30314
+ ...commandOptions,
30315
+ allow: allowProjectId,
30316
+ apiBaseUrl
30317
+ });
30168
30318
  return;
30169
30319
  }
30170
30320
  if (provider === "telegram") {
@@ -30173,13 +30323,13 @@ async function connectProviderAction(context, provider, commandOptions) {
30173
30323
  }
30174
30324
  await connectTelegramFlow({
30175
30325
  apiBaseUrl,
30176
- client: createConnectionServiceClient(context),
30326
+ client: createConnectionServiceClient(scopedContext),
30177
30327
  writeOutput: context.writeOutput,
30178
30328
  prompt: cliTelegramWizardPrompter(context),
30179
30329
  token: commandOptions.token ?? (commandOptions.stdin ? await readTrimmedStream(context.stdin) : void 0),
30180
30330
  name: commandOptions.name,
30181
30331
  validateOnly: commandOptions.validateOnly,
30182
- allowProjectId: commandOptions.allow,
30332
+ allowProjectId,
30183
30333
  manual: commandOptions.manual
30184
30334
  });
30185
30335
  return;
@@ -30189,9 +30339,9 @@ async function connectProviderAction(context, provider, commandOptions) {
30189
30339
  "--config-refresh-token registers the token once the connection completes; it cannot be combined with --no-wait."
30190
30340
  );
30191
30341
  }
30192
- const client = createConnectionServiceClient(context);
30342
+ const client = createConnectionServiceClient(scopedContext);
30193
30343
  const result = await client.startConnection(provider, {
30194
- allowProjectId: commandOptions.allow,
30344
+ allowProjectId,
30195
30345
  apiBaseUrl
30196
30346
  });
30197
30347
  const style = context.io.style;
@@ -30263,6 +30413,23 @@ async function registerConfigTokenAction(context, provider, commandOptions) {
30263
30413
  `${style.label("expires_at")} ${style.dim(result.expiresAt)}`
30264
30414
  );
30265
30415
  }
30416
+ async function resolveConnectAllowProject(context, provider, commandOptions) {
30417
+ const scopeInput = {
30418
+ context,
30419
+ options: commandOptions,
30420
+ identifier: commandOptions.allow,
30421
+ explicitHint: "--allow <project>"
30422
+ };
30423
+ const resolved = provider === "github" ? await requireProjectScope(scopeInput) : await resolveProjectScope(scopeInput);
30424
+ if (resolved && resolved.source === "active") {
30425
+ context.writeOutput(
30426
+ context.io.style.dim(
30427
+ `Allowing active project ${resolved.projectId} once the connection completes.`
30428
+ )
30429
+ );
30430
+ }
30431
+ return resolved;
30432
+ }
30266
30433
  async function readTrimmedStream(stream) {
30267
30434
  const chunks = [];
30268
30435
  for await (const chunk of stream) {
@@ -30270,25 +30437,42 @@ async function readTrimmedStream(stream) {
30270
30437
  }
30271
30438
  return Buffer.concat(chunks).toString("utf8").trim();
30272
30439
  }
30273
- async function allowConnectionAction(context, provider, projectId, commandOptions) {
30274
- const result = await createConnectionServiceClient(context).allowConnection(
30440
+ async function allowConnectionAction(context, provider, projectIdentifier, commandOptions) {
30441
+ const project = await requireProjectScope({
30442
+ context,
30443
+ options: commandOptions,
30444
+ identifier: projectIdentifier,
30445
+ explicitHint: "a <project> argument"
30446
+ });
30447
+ const scopedContext = withProjectScope(context, project);
30448
+ const connection = await requireConnection({
30449
+ context: scopedContext,
30450
+ options: commandOptions,
30275
30451
  provider,
30276
- projectId,
30277
- {
30278
- connection: commandOptions.connection,
30279
- apiBaseUrl: apiUrlFromOptions(context, commandOptions)
30280
- }
30281
- );
30452
+ identifier: commandOptions.connection
30453
+ });
30454
+ const result = await createConnectionServiceClient(
30455
+ scopedContext
30456
+ ).allowConnection(provider, project.projectId, {
30457
+ connection: connection.name,
30458
+ apiBaseUrl: apiUrlFromOptions(context, commandOptions)
30459
+ });
30282
30460
  context.writeOutput(result.message);
30283
30461
  }
30284
30462
  async function removeConnectionAction(context, provider, commandOptions) {
30463
+ const connection = await requireConnection({
30464
+ context,
30465
+ options: commandOptions,
30466
+ provider,
30467
+ identifier: commandOptions.connection
30468
+ });
30285
30469
  await confirmDestructiveAction(context, {
30286
- message: `This will remove "${provider}" connection "${commandOptions.connection}".`,
30470
+ message: `This will remove "${provider}" connection "${connection.name}".`,
30287
30471
  yes: commandOptions.yes
30288
30472
  });
30289
30473
  const result = await createConnectionServiceClient(context).removeConnection(
30290
30474
  provider,
30291
- commandOptions.connection,
30475
+ connection.name,
30292
30476
  {
30293
30477
  apiBaseUrl: apiUrlFromOptions(context, commandOptions)
30294
30478
  }
@@ -30296,19 +30480,32 @@ async function removeConnectionAction(context, provider, commandOptions) {
30296
30480
  context.writeOutput(result.message);
30297
30481
  }
30298
30482
  async function replaceConnectionAction(context, provider, commandOptions) {
30483
+ const connection = await requireConnection({
30484
+ context,
30485
+ options: commandOptions,
30486
+ provider,
30487
+ identifier: commandOptions.connection
30488
+ });
30299
30489
  if (commandOptions.removeFirst) {
30300
30490
  await confirmDestructiveAction(context, {
30301
- message: `This will remove "${provider}" connection "${commandOptions.connection}" before replacement authorization completes.`,
30491
+ message: `This will remove "${provider}" connection "${connection.name}" before replacement authorization completes.`,
30302
30492
  yes: commandOptions.yes
30303
30493
  });
30304
30494
  }
30495
+ const allowScope = commandOptions.allow ? await requireProjectScope({
30496
+ context,
30497
+ options: commandOptions,
30498
+ identifier: commandOptions.allow,
30499
+ explicitHint: "--allow <project>"
30500
+ }) : void 0;
30501
+ const scopedContext = allowScope ? withProjectScope(context, allowScope) : context;
30305
30502
  const apiBaseUrl = apiUrlFromOptions(context, commandOptions);
30306
- const client = createConnectionServiceClient(context);
30503
+ const client = createConnectionServiceClient(scopedContext);
30307
30504
  const result = await client.startConnection(provider, {
30308
- allowProjectId: commandOptions.allow,
30505
+ allowProjectId: allowScope?.projectId,
30309
30506
  apiBaseUrl,
30310
30507
  replace: {
30311
- connection: commandOptions.connection,
30508
+ connection: connection.name,
30312
30509
  removeFirst: commandOptions.removeFirst === true
30313
30510
  }
30314
30511
  });
@@ -30338,7 +30535,10 @@ function registerConnectionCommands(program, context) {
30338
30535
  connections.command("list").alias("ls").description("List provider connections.").option("--available", "list available providers instead of connections").option("--provider <provider>", "provider name").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (commandOptions) => {
30339
30536
  await listConnectionsAction(context, commandOptions);
30340
30537
  });
30341
- connections.command("remove").description("Remove an existing provider connection.").argument("<provider>", "provider name").requiredOption("--connection <name>", "connection or grant name to remove").option("-y, --yes", "skip confirmation prompt").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (provider, commandOptions) => {
30538
+ connections.command("remove").description("Remove an existing provider connection.").argument("<provider>", "provider name").option(
30539
+ "--connection <name>",
30540
+ "connection name or id to remove; defaults to the provider's only connection"
30541
+ ).option("-y, --yes", "skip confirmation prompt").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (provider, commandOptions) => {
30342
30542
  await removeConnectionAction(context, provider, commandOptions);
30343
30543
  });
30344
30544
  connections.command("config-token").description(
@@ -30350,19 +30550,25 @@ function registerConnectionCommands(program, context) {
30350
30550
  );
30351
30551
  connections.command("replace").description(
30352
30552
  "Start a replacement flow for an existing provider connection."
30353
- ).argument("<provider>", "provider name").requiredOption(
30553
+ ).argument("<provider>", "provider name").option(
30354
30554
  "--connection <name>",
30355
- "connection or grant name to replace"
30555
+ "connection name or id to replace; defaults to the provider's only connection"
30356
30556
  ).option(
30357
30557
  "--remove-first",
30358
30558
  "remove the old connection before the replacement authorization completes"
30359
- ).option("-y, --yes", "skip confirmation prompt when using --remove-first").option("--allow <project>", "project id to allow after connecting").option(
30559
+ ).option("-y, --yes", "skip confirmation prompt when using --remove-first").option(
30560
+ "--allow <project>",
30561
+ "project (id, slug, or name) to allow after connecting"
30562
+ ).option(
30360
30563
  "--no-wait",
30361
30564
  "print the authorization URL and exit without waiting for the replacement to complete"
30362
30565
  ).option("--no-browser", "do not open the authorization URL in a browser").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (provider, commandOptions) => {
30363
30566
  await replaceConnectionAction(context, provider, commandOptions);
30364
30567
  });
30365
- program.command("connect").description("Start a provider connection flow.").argument("<provider>", "provider name").option("--allow <project>", "project id to allow after connecting").option(
30568
+ program.command("connect").description("Start a provider connection flow.").argument("<provider>", "provider name").option(
30569
+ "--allow <project>",
30570
+ "project (id, slug, or name) to allow after connecting; defaults to the active project"
30571
+ ).option(
30366
30572
  "--installation <account-or-id>",
30367
30573
  "existing GitHub installation account or id to connect"
30368
30574
  ).option(
@@ -30386,14 +30592,15 @@ function registerConnectionCommands(program, context) {
30386
30592
  ).option("--no-browser", "do not open the authorization URL in a browser").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (provider, commandOptions) => {
30387
30593
  await connectProviderAction(context, provider, commandOptions);
30388
30594
  });
30389
- program.command("allow").description("Allow a project to use an existing provider connection.").argument("<provider>", "provider name").argument("<project>", "project id").option("--connection <name>", "specific connection or grant name").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(
30390
- async (provider, projectId, commandOptions) => {
30391
- await allowConnectionAction(
30392
- context,
30393
- provider,
30394
- projectId,
30395
- commandOptions
30396
- );
30595
+ program.command("allow").description("Allow a project to use an existing provider connection.").argument("<provider>", "provider name").argument(
30596
+ "[project]",
30597
+ "project id, slug, or name; defaults to the active project"
30598
+ ).option(
30599
+ "--connection <name>",
30600
+ "connection name or id; defaults to the provider's only connection"
30601
+ ).option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(
30602
+ async (provider, project, commandOptions) => {
30603
+ await allowConnectionAction(context, provider, project, commandOptions);
30397
30604
  }
30398
30605
  );
30399
30606
  }
@@ -30479,7 +30686,6 @@ function registerEditCommands(program, context) {
30479
30686
 
30480
30687
  // src/commands/project-directory.ts
30481
30688
  init_active_project();
30482
- init_file();
30483
30689
  function createDirectoryClient(context) {
30484
30690
  return createContextApiClient(context);
30485
30691
  }
@@ -30501,44 +30707,6 @@ function setActiveOrganization(context, organizationId) {
30501
30707
  function setActiveProject(context, project) {
30502
30708
  persistActiveProject(context.configPath, project);
30503
30709
  }
30504
- async function resolveOrganization(input) {
30505
- const client = createDirectoryClient(input.context);
30506
- const response = await client.listOrganizations({
30507
- apiBaseUrl: apiUrlFromOptions(input.context, input.options)
30508
- });
30509
- return resolveOne(
30510
- response.organizations.filter(
30511
- (organization) => matchesOrganization(organization, input.identifier)
30512
- ),
30513
- "organization",
30514
- input.identifier
30515
- );
30516
- }
30517
- async function resolveProject(input) {
30518
- const activeOrgId = readConfig(input.context.configPath).organizationId;
30519
- const response = await input.client.listProjects({
30520
- apiBaseUrl: apiUrlFromOptions(input.context, input.options)
30521
- });
30522
- const matches = response.projects.filter(
30523
- (project) => matchesProject(project, input.identifier) && (!activeOrgId || project.organizationId === activeOrgId)
30524
- );
30525
- return resolveOne(matches, "project", input.identifier);
30526
- }
30527
- function matchesOrganization(organization, identifier) {
30528
- return organization.organizationId === identifier || organization.organizationSlug === identifier || organization.organizationName === identifier;
30529
- }
30530
- function matchesProject(project, identifier) {
30531
- return project.projectId === identifier || project.projectSlug === identifier || project.projectName === identifier;
30532
- }
30533
- function resolveOne(matches, kind, identifier) {
30534
- if (matches.length === 0) {
30535
- throw new Error(`No ${kind} found matching ${identifier}`);
30536
- }
30537
- if (matches.length > 1) {
30538
- throw new Error(`Multiple ${kind}s match ${identifier}; use a slug or ID`);
30539
- }
30540
- return matches[0];
30541
- }
30542
30710
 
30543
30711
  // src/commands/invitations/commands.ts
30544
30712
  function registerInvitationCommands(program, context) {
@@ -32081,7 +32249,7 @@ async function setSecret(input) {
32081
32249
  input.name,
32082
32250
  { value },
32083
32251
  {
32084
- projectId: input.commandOptions.project,
32252
+ projectId: await secretProjectId(input),
32085
32253
  apiBaseUrl: input.commandOptions.apiBaseUrl
32086
32254
  }
32087
32255
  );
@@ -32089,7 +32257,7 @@ async function setSecret(input) {
32089
32257
  }
32090
32258
  async function listSecrets(input) {
32091
32259
  const response = await input.client.listSecrets({
32092
- projectId: input.commandOptions.project,
32260
+ projectId: await secretProjectId(input),
32093
32261
  apiBaseUrl: input.commandOptions.apiBaseUrl
32094
32262
  });
32095
32263
  if (input.commandOptions.json) {
@@ -32112,11 +32280,23 @@ async function removeSecret(input) {
32112
32280
  yes: input.commandOptions.yes
32113
32281
  });
32114
32282
  const response = await input.client.removeSecret(input.name, {
32115
- projectId: input.commandOptions.project,
32283
+ projectId: await secretProjectId(input),
32116
32284
  apiBaseUrl: input.commandOptions.apiBaseUrl
32117
32285
  });
32118
32286
  writeRemoveResponse(response, input);
32119
32287
  }
32288
+ async function secretProjectId(input) {
32289
+ if (!input.commandOptions.project) {
32290
+ return void 0;
32291
+ }
32292
+ const scope = await requireProjectScope({
32293
+ context: input.context,
32294
+ options: input.commandOptions,
32295
+ identifier: input.commandOptions.project,
32296
+ explicitHint: "--project <project>"
32297
+ });
32298
+ return scope.projectId;
32299
+ }
32120
32300
  async function secretValue(input) {
32121
32301
  const sources = [
32122
32302
  input.options.stdin ? "stdin" : void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.112",
3
+ "version": "0.1.113",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"