diff --git a/packages/cli/src/rest/__tests__/request-interceptor.spec.ts b/packages/cli/src/rest/__tests__/request-interceptor.spec.ts new file mode 100644 index 000000000..99156a14b --- /dev/null +++ b/packages/cli/src/rest/__tests__/request-interceptor.spec.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +// api.ts builds its axios instance and resource classes at import time via +// getDefaults(); stub the config service so importing it needs no credentials. +vi.mock('../../services/config.js', () => ({ + default: { + getApiKey: () => 'test-key', + getAccountId: () => 'test-account', + getApiUrl: () => 'https://api.checklyhq.com', + hasValidCredentials: () => true, + }, +})) + +vi.mock('../../helpers/cli-mode.js', () => ({ + detectCliMode: vi.fn(() => 'interactive'), + detectOperator: vi.fn(() => 'manual'), +})) + +import { requestInterceptor } from '../api.js' +import { detectCliMode } from '../../helpers/cli-mode.js' + +describe('requestInterceptor x-checkly-source', () => { + const sourceHeader = () => { + const config = { headers: {} } as any + return requestInterceptor(config).headers['x-checkly-source'] + } + + beforeEach(() => { + vi.clearAllMocks() + }) + + it('reports AGENT when the CLI is driven by an agent', () => { + vi.mocked(detectCliMode).mockReturnValue('agent') + expect(sourceHeader()).toBe('AGENT') + }) + + it('reports CLI for interactive and CI runs', () => { + vi.mocked(detectCliMode).mockReturnValue('interactive') + expect(sourceHeader()).toBe('CLI') + + vi.mocked(detectCliMode).mockReturnValue('ci') + expect(sourceHeader()).toBe('CLI') + }) +}) diff --git a/packages/cli/src/rest/api.ts b/packages/cli/src/rest/api.ts index a452053d4..9243d07be 100644 --- a/packages/cli/src/rest/api.ts +++ b/packages/cli/src/rest/api.ts @@ -31,7 +31,7 @@ import AlertNotifications from './alert-notifications.js' import Rca from './rca.js' import Cancel from './cancel.js' import { handleErrorResponse, UnauthorizedError } from './errors.js' -import { detectOperator } from '../helpers/cli-mode.js' +import { detectCliMode, detectOperator } from '../helpers/cli-mode.js' export function getDefaults () { const apiKey = config.getApiKey() @@ -82,7 +82,10 @@ export function requestInterceptor (config: InternalAxiosRequestConfig) { config.headers['x-checkly-account'] = accountId } - config.headers['x-checkly-source'] = 'CLI' + // Declare who is driving the CLI. An agent run reports `AGENT` so the backend + // can tell agent-created import plans apart from human ones (same API key) — + // it keys the abandoned-reservation GC off this. Everything else is `CLI`. + config.headers['x-checkly-source'] = detectCliMode() === 'agent' ? 'AGENT' : 'CLI' config.headers['x-checkly-ci-name'] = CIname config.headers['x-checkly-operator'] = detectOperator()