From 425ee81793c88120d0af0165e442e560bdec0c99 Mon Sep 17 00:00:00 2001 From: Ricky Schema Cascade Date: Sun, 21 Jun 2026 14:08:11 +0200 Subject: [PATCH] fix(cli): explain trigger auth source on 403 --- packages/cli/src/list-command.ts | 21 ++++++++++++++++++-- packages/cli/src/trigger-command.test.ts | 23 ++++++++++++++++++++++ packages/cli/src/trigger-command.ts | 25 +++++++++++++++++++++++- packages/deploy/src/login.test.ts | 4 ++-- packages/deploy/src/login.ts | 7 +++++-- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/list-command.ts b/packages/cli/src/list-command.ts index 64ee1c34..bcf912bc 100644 --- a/packages/cli/src/list-command.ts +++ b/packages/cli/src/list-command.ts @@ -345,7 +345,12 @@ export async function resolveDeploymentRequestContext(opts: { workspace?: string; cloudUrl?: string; noPrompt?: boolean; -}): Promise<{ cloudUrl: string; workspace: string; token: string }> { +}): Promise<{ + cloudUrl: string; + workspace: string; + token: string; + authSource?: 'env' | 'cloud-session'; +}> { const io = createTerminalIO(); const cloudUrl = resolveCloudUrl({ ...(opts.cloudUrl ? { flag: opts.cloudUrl } : {}) @@ -360,7 +365,19 @@ export async function resolveDeploymentRequestContext(opts: { if (!workspace) { throw new Error('workspace is required: pass --workspace, set WORKFORCE_WORKSPACE_ID, or run `agentworkforce login`'); } - return { cloudUrl, workspace, token: auth.token }; + const authSource = readAuthSource(auth); + return { + cloudUrl, + workspace, + token: auth.token, + ...(authSource ? { authSource } : {}) + }; +} + +function readAuthSource(value: unknown): 'env' | 'cloud-session' | undefined { + if (!value || typeof value !== 'object') return undefined; + const authSource = (value as { authSource?: unknown }).authSource; + return authSource === 'env' || authSource === 'cloud-session' ? authSource : undefined; } export async function fetchDeployments(args: { diff --git a/packages/cli/src/trigger-command.test.ts b/packages/cli/src/trigger-command.test.ts index abcb90ac..4108e703 100644 --- a/packages/cli/src/trigger-command.test.ts +++ b/packages/cli/src/trigger-command.test.ts @@ -2,6 +2,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { buildTriggerUrl, + formatTriggerAuthHint, formatTriggerResult, parseTriggerArgs, parseTriggerResponse @@ -82,6 +83,28 @@ test('formatTriggerResult prints a concise human summary', () => { ); }); +test('formatTriggerAuthHint identifies env-token 403s', () => { + assert.match( + formatTriggerAuthHint({ authSource: 'env', workspace: 'rw_123' }), + /WORKFORCE_WORKSPACE_TOKEN.*rw_123/ + ); + assert.match( + formatTriggerAuthHint({ authSource: 'env', workspace: 'rw_123' }), + /unset WORKFORCE_WORKSPACE_TOKEN/ + ); +}); + +test('formatTriggerAuthHint identifies cloud-session 403s', () => { + assert.match( + formatTriggerAuthHint({ authSource: 'cloud-session', workspace: 'rw_123' }), + /Agent Relay cloud session.*rw_123/ + ); + assert.match( + formatTriggerAuthHint({ authSource: 'cloud-session', workspace: 'rw_123' }), + /agentworkforce login/ + ); +}); + test('buildTriggerUrl preserves cloud base paths', () => { assert.equal( buildTriggerUrl({ diff --git a/packages/cli/src/trigger-command.ts b/packages/cli/src/trigger-command.ts index e8e13b92..4e192d59 100644 --- a/packages/cli/src/trigger-command.ts +++ b/packages/cli/src/trigger-command.ts @@ -158,12 +158,35 @@ export async function triggerDeployment(opts: TriggerOptions): Promise ''); const hint = formatHttpErrorBody(body, { url: url.toString() }); - throw new Error(`manual trigger failed: ${res.status}${hint ? ` ${hint}` : ''}`); + const authHint = res.status === 403 ? ` ${formatTriggerAuthHint(ctx)}` : ''; + throw new Error(`manual trigger failed: ${res.status}${hint ? ` ${hint}` : ''}${authHint}`); } return parseTriggerResponse(await res.json(), opts.selector); } +export function formatTriggerAuthHint(ctx: { + authSource?: 'env' | 'cloud-session'; + workspace: string; +}): string { + if (ctx.authSource === 'env') { + return ( + `Auth source: WORKFORCE_WORKSPACE_TOKEN for workspace ${ctx.workspace}. ` + + 'If this token is stale or lacks trigger scope, unset WORKFORCE_WORKSPACE_TOKEN and WORKFORCE_WORKSPACE_ID, then run `agentworkforce login`.' + ); + } + if (ctx.authSource === 'cloud-session') { + return ( + `Auth source: Agent Relay cloud session for workspace ${ctx.workspace}. ` + + 'Run `agentworkforce login` to refresh the session, or confirm this account has workspace access.' + ); + } + return ( + `Auth source: unknown for workspace ${ctx.workspace}. ` + + 'Run `agentworkforce login`, or set WORKFORCE_WORKSPACE_ID and WORKFORCE_WORKSPACE_TOKEN explicitly.' + ); +} + export function formatTriggerResult(result: TriggerResponse): string { return ( `triggered: ${result.agentId}\n` + diff --git a/packages/deploy/src/login.test.ts b/packages/deploy/src/login.test.ts index b3bb5c83..e55bcfb4 100644 --- a/packages/deploy/src/login.test.ts +++ b/packages/deploy/src/login.test.ts @@ -101,7 +101,7 @@ test('resolveWorkspaceToken preserves complete WORKFORCE env credentials for CI' io: createBufferedIO(), noPrompt: true }), - { token: 'ci-token', workspace: 'rw_1234abcd' } + { token: 'ci-token', workspace: 'rw_1234abcd', authSource: 'env' } ); }); }); @@ -115,7 +115,7 @@ test('resolveWorkspaceToken lets --workspace pair with WORKFORCE_WORKSPACE_TOKEN io: createBufferedIO(), noPrompt: true }), - { token: 'ci-token', workspace: 'rw_5678abcd' } + { token: 'ci-token', workspace: 'rw_5678abcd', authSource: 'env' } ); }); }); diff --git a/packages/deploy/src/login.ts b/packages/deploy/src/login.ts index a4a6815e..b7187678 100644 --- a/packages/deploy/src/login.ts +++ b/packages/deploy/src/login.ts @@ -27,6 +27,7 @@ export interface WorkspaceAuthToken { workspace?: string; relayfileWorkspaceId?: string; workspaceDescriptor?: ActiveWorkspaceDescriptor; + authSource?: 'env' | 'cloud-session'; } export interface StoredWorkspaceLogin { @@ -123,7 +124,8 @@ export async function resolveWorkspaceToken(args: { if (envToken && (requestedWorkspace || envWorkspace)) { return { token: envToken, - workspace: requestedWorkspace || envWorkspace + workspace: requestedWorkspace || envWorkspace, + authSource: "env" }; } @@ -142,7 +144,8 @@ export async function resolveWorkspaceToken(args: { token: session.auth.accessToken, workspace: descriptor.relaycastWorkspaceId, relayfileWorkspaceId: descriptor.relayfileWorkspaceId, - workspaceDescriptor: descriptor + workspaceDescriptor: descriptor, + authSource: "cloud-session" }; }