diff --git a/src/commands/cloud/connect.ts b/src/commands/cloud/connect.ts index 7210884..235c396 100644 --- a/src/commands/cloud/connect.ts +++ b/src/commands/cloud/connect.ts @@ -327,13 +327,15 @@ export async function connectTurso( } } -const TRIGGERDEV_PROJECT_REF_HINT = - 'The project ref is the `project` line in trigger.config.ts and starts with proj_.'; +const TRIGGERDEV_PROJECT_REF_WHERE = + "Paste the project ref from the project's settings page (it starts with proj_). It is also the `project` line in trigger.config.ts."; + +const TRIGGERDEV_PROJECT_REF_HINT = `This key cannot name its project. ${TRIGGERDEV_PROJECT_REF_WHERE}`; const TRIGGERDEV_HEADLESS_HINT = - 'Create an environment API key in your Trigger.dev project (production environment > API Keys, No restrictions access preset), then re-run:\n' + + 'Create an environment API key in your Trigger.dev project (production environment > API Keys, "No restrictions" access preset), then re-run:\n' + 'polylane cloud connect --provider triggerdev --api-key \n' + - `Add --project-ref when the API asks for it. ${TRIGGERDEV_PROJECT_REF_HINT}`; + `Add --project-ref when the API answers that the key cannot name its project. ${TRIGGERDEV_PROJECT_REF_WHERE}`; // The generated client trails the deployed API spec; the triggerdev body // shape is the contract from the API-side design record. @@ -376,8 +378,8 @@ export async function connectTriggerdev( note(`${err.message}\n${TRIGGERDEV_PROJECT_REF_HINT}`, 'Trigger.dev project ref'); const picked = await promptTextOrBack( { nonInteractive: config.nonInteractive }, - 'Trigger.dev project ref (proj_...)', - { validate: (v: string) => (v.trim() ? undefined : 'Required') } + 'Project ref', + { placeholder: 'proj_…', validate: (v: string) => (v.trim() ? undefined : 'Required') } ); if (picked === BACK) return BACK; return send({ ...body, projectRef: picked.trim() }); @@ -588,7 +590,7 @@ async function connectProvider( { message: 'Trigger.dev environment API key', instructions: - 'In your Trigger.dev project, open the production environment, then API Keys, and create a key with the No restrictions access preset. That preset is the only kind on the Free and Hobby plans; on Pro you may instead use restricted keys such as Observer plus Deploy only, adding them one at a time. Re-running this command with another key adds it to the same account.', + 'In your production environment open API Keys and create a key with the "No restrictions" access preset. That preset is the only kind on the Free and Hobby plans; on Pro you may instead use restricted keys such as "Observer" plus "Deploy only", adding them one at a time. Re-running this command with another key adds it to the same account.', link: 'https://cloud.trigger.dev', linkLabel: 'Open Trigger.dev', }, diff --git a/test/cloud-connect-triggerdev.test.ts b/test/cloud-connect-triggerdev.test.ts index a515be1..649f703 100644 --- a/test/cloud-connect-triggerdev.test.ts +++ b/test/cloud-connect-triggerdev.test.ts @@ -9,8 +9,10 @@ import type { PolylaneAPI } from '../src/generated/client'; const config = { nonInteractive: true } as Config; const body = { workspaceId: 'ws_1', provider: 'triggerdev', apiKey: 'tr_key' } as const; -const REF_REQUIRED = - 'This API key is valid for multiple projects. Specify the project ref of the project to connect.'; +// The API's `detail` strings, verbatim from nominal +// apps/apis/api-cloud-accounts/src/routers/cloud-accounts/connects/triggerdev.ts. +const REF_REQUIRED = 'The Trigger.dev project ref is required for a restricted key'; +const RUNS_REQUIRED = 'This Trigger.dev API key cannot read runs'; function mockApi(connect: (body: unknown) => Promise): PolylaneAPI { return { cloudAccountsConnect: connect } as unknown as PolylaneAPI; @@ -50,6 +52,8 @@ describe('connectTriggerdev', () => { err.exitCode === ExitCode.USAGE && err.message.includes('project ref') && (err.hint?.includes('--project-ref') ?? false) && + (err.hint?.includes('This key cannot name its project.') ?? false) && + (err.hint?.includes("project's settings page") ?? false) && (err.hint?.includes('trigger.config.ts') ?? false) ); }); @@ -66,11 +70,7 @@ describe('connectTriggerdev', () => { }); it('rethrows other 400s untouched, including a key that cannot read runs', async () => { - const original = new ApiError( - 400, - 'This API key cannot read runs. Create a key with the No restrictions access preset, or a restricted key that includes run read access.', - ExitCode.USAGE - ); + const original = new ApiError(400, RUNS_REQUIRED, ExitCode.USAGE); const api = mockApi(async () => { throw original; });