From 39c6bc8cb2e7fa16e618c4c33d08b1db692792b2 Mon Sep 17 00:00:00 2001 From: Harrison Weinstock Date: Thu, 9 Apr 2026 17:54:43 +0000 Subject: [PATCH] refactor: consolidate cli-config into global-config --- src/cli/__tests__/global-config.test.ts | 22 +++++++++++- src/cli/cli.ts | 2 +- .../telemetry/__tests__/telemetry.test.ts | 2 +- src/cli/commands/telemetry/actions.ts | 2 +- .../post-deploy-observability.test.ts | 14 ++++---- .../deploy/post-deploy-observability.ts | 4 +-- src/cli/telemetry/resolve.ts | 2 +- src/lib/packaging/build-args.ts | 4 +-- src/lib/schemas/io/cli-config.ts | 36 ------------------- src/{cli => lib/schemas/io}/global-config.ts | 23 ++++++++---- src/lib/schemas/io/index.ts | 1 - 11 files changed, 53 insertions(+), 59 deletions(-) delete mode 100644 src/lib/schemas/io/cli-config.ts rename src/{cli => lib/schemas/io}/global-config.ts (78%) diff --git a/src/cli/__tests__/global-config.test.ts b/src/cli/__tests__/global-config.test.ts index 2851a13a4..0027e01ae 100644 --- a/src/cli/__tests__/global-config.test.ts +++ b/src/cli/__tests__/global-config.test.ts @@ -1,4 +1,9 @@ -import { getOrCreateInstallationId, readGlobalConfig, updateGlobalConfig } from '../global-config'; +import { + getOrCreateInstallationId, + readGlobalConfig, + readGlobalConfigSync, + updateGlobalConfig, +} from '../../lib/schemas/io/global-config'; import { createTempConfig } from './helpers/temp-config'; import { readFile, writeFile } from 'fs/promises'; import { afterAll, beforeEach, describe, expect, it } from 'vitest'; @@ -39,6 +44,21 @@ describe('global-config', () => { }); }); + describe('readGlobalConfigSync', () => { + it('returns parsed config when file exists', async () => { + await writeFile(tmp.configFile, JSON.stringify({ telemetry: { enabled: false } })); + + expect(readGlobalConfigSync(tmp.configFile)).toEqual({ telemetry: { enabled: false } }); + }); + + it('returns empty object when file is missing or invalid', async () => { + expect(readGlobalConfigSync(tmp.testDir + '/nonexistent.json')).toEqual({}); + + await writeFile(tmp.configFile, 'not json'); + expect(readGlobalConfigSync(tmp.configFile)).toEqual({}); + }); + }); + describe('updateGlobalConfig', () => { it('creates directory and writes config when none exists', async () => { const fresh = createTempConfig('gc-fresh'); diff --git a/src/cli/cli.ts b/src/cli/cli.ts index e4a44eb40..551cfa54c 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,3 +1,4 @@ +import { getOrCreateInstallationId } from '../lib/schemas/io/global-config'; import { registerAdd } from './commands/add'; import { registerCreate } from './commands/create'; import { registerDeploy } from './commands/deploy'; @@ -19,7 +20,6 @@ import { registerTraces } from './commands/traces'; import { registerUpdate } from './commands/update'; import { registerValidate } from './commands/validate'; import { PACKAGE_VERSION } from './constants'; -import { getOrCreateInstallationId } from './global-config'; import { ALL_PRIMITIVES } from './primitives'; import { App } from './tui/App'; import { LayoutProvider } from './tui/context'; diff --git a/src/cli/commands/telemetry/__tests__/telemetry.test.ts b/src/cli/commands/telemetry/__tests__/telemetry.test.ts index b0e615fcd..efdfd2f23 100644 --- a/src/cli/commands/telemetry/__tests__/telemetry.test.ts +++ b/src/cli/commands/telemetry/__tests__/telemetry.test.ts @@ -1,5 +1,5 @@ +import { readGlobalConfig } from '../../../../lib/schemas/io/global-config'; import { createTempConfig } from '../../../__tests__/helpers/temp-config'; -import { readGlobalConfig } from '../../../global-config'; import { handleTelemetryDisable, handleTelemetryEnable, handleTelemetryStatus } from '../actions'; import { chmod, mkdir, rm, writeFile } from 'fs/promises'; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; diff --git a/src/cli/commands/telemetry/actions.ts b/src/cli/commands/telemetry/actions.ts index 3e1d09697..29fad1e05 100644 --- a/src/cli/commands/telemetry/actions.ts +++ b/src/cli/commands/telemetry/actions.ts @@ -1,4 +1,4 @@ -import { GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_FILE, updateGlobalConfig } from '../../global-config.js'; +import { GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_FILE, updateGlobalConfig } from '../../../lib/schemas/io/global-config.js'; import { resolveTelemetryPreference } from '../../telemetry/resolve.js'; export async function handleTelemetryDisable( diff --git a/src/cli/operations/deploy/__tests__/post-deploy-observability.test.ts b/src/cli/operations/deploy/__tests__/post-deploy-observability.test.ts index 9155a699d..ba069f29e 100644 --- a/src/cli/operations/deploy/__tests__/post-deploy-observability.test.ts +++ b/src/cli/operations/deploy/__tests__/post-deploy-observability.test.ts @@ -1,23 +1,23 @@ import { setupTransactionSearch } from '../post-deploy-observability.js'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -const { mockEnableTransactionSearch, mockReadCliConfig } = vi.hoisted(() => ({ +const { mockEnableTransactionSearch, mockReadGlobalConfigSync } = vi.hoisted(() => ({ mockEnableTransactionSearch: vi.fn(), - mockReadCliConfig: vi.fn(), + mockReadGlobalConfigSync: vi.fn(), })); vi.mock('../../../aws/transaction-search', () => ({ enableTransactionSearch: mockEnableTransactionSearch, })); -vi.mock('../../../../lib/schemas/io/cli-config', () => ({ - readCliConfig: mockReadCliConfig, +vi.mock('../../../../lib/schemas/io/global-config', () => ({ + readGlobalConfigSync: mockReadGlobalConfigSync, })); describe('setupTransactionSearch', () => { beforeEach(() => { vi.clearAllMocks(); - mockReadCliConfig.mockReturnValue({}); + mockReadGlobalConfigSync.mockReturnValue({}); mockEnableTransactionSearch.mockResolvedValue({ success: true }); }); @@ -33,7 +33,7 @@ describe('setupTransactionSearch', () => { }); it('passes custom transactionSearchIndexPercentage from config', async () => { - mockReadCliConfig.mockReturnValue({ transactionSearchIndexPercentage: 25 }); + mockReadGlobalConfigSync.mockReturnValue({ transactionSearchIndexPercentage: 25 }); const result = await setupTransactionSearch({ region: 'us-east-1', @@ -57,7 +57,7 @@ describe('setupTransactionSearch', () => { }); it('skips when disableTransactionSearch is true in config', async () => { - mockReadCliConfig.mockReturnValue({ disableTransactionSearch: true }); + mockReadGlobalConfigSync.mockReturnValue({ disableTransactionSearch: true }); const result = await setupTransactionSearch({ region: 'us-east-1', diff --git a/src/cli/operations/deploy/post-deploy-observability.ts b/src/cli/operations/deploy/post-deploy-observability.ts index 295392629..0616a65dc 100644 --- a/src/cli/operations/deploy/post-deploy-observability.ts +++ b/src/cli/operations/deploy/post-deploy-observability.ts @@ -1,4 +1,4 @@ -import { readCliConfig } from '../../../lib/schemas/io/cli-config'; +import { readGlobalConfigSync } from '../../../lib/schemas/io/global-config'; import { enableTransactionSearch } from '../../aws/transaction-search'; export interface TransactionSearchSetupOptions { @@ -31,7 +31,7 @@ export async function setupTransactionSearch( return { success: true }; } - const config = readCliConfig(); + const config = readGlobalConfigSync(); if (config.disableTransactionSearch) { return { success: true }; } diff --git a/src/cli/telemetry/resolve.ts b/src/cli/telemetry/resolve.ts index e009b59ce..d6a22966f 100644 --- a/src/cli/telemetry/resolve.ts +++ b/src/cli/telemetry/resolve.ts @@ -1,4 +1,4 @@ -import { readGlobalConfig } from '../global-config.js'; +import { readGlobalConfig } from '../../lib/schemas/io/global-config.js'; export interface TelemetryPreference { enabled: boolean; diff --git a/src/lib/packaging/build-args.ts b/src/lib/packaging/build-args.ts index e5af34045..eadc35875 100644 --- a/src/lib/packaging/build-args.ts +++ b/src/lib/packaging/build-args.ts @@ -1,11 +1,11 @@ -import { readCliConfig } from '../schemas/io/cli-config'; +import { readGlobalConfigSync } from '../schemas/io/global-config'; /** * Return Docker --build-arg flags for UV index URLs configured in ~/.agentcore/config.json. * Returns an empty array when no custom indexes are configured. */ export function getUvBuildArgs(): string[] { - const config = readCliConfig(); + const config = readGlobalConfigSync(); const args: string[] = []; if (config.uvDefaultIndex) args.push('--build-arg', `UV_DEFAULT_INDEX=${config.uvDefaultIndex}`); if (config.uvIndex) args.push('--build-arg', `UV_INDEX=${config.uvIndex}`); diff --git a/src/lib/schemas/io/cli-config.ts b/src/lib/schemas/io/cli-config.ts deleted file mode 100644 index aa36d82f1..000000000 --- a/src/lib/schemas/io/cli-config.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { readFileSync } from 'fs'; -import { homedir } from 'os'; -import { join } from 'path'; - -const CONFIG_FILE = join(homedir(), '.agentcore', 'config.json'); - -export interface CliConfig { - uvDefaultIndex?: string; - uvIndex?: string; - disableTransactionSearch?: boolean; - transactionSearchIndexPercentage?: number; -} - -/** - * Read the global CLI config from ~/.agentcore/config.json. - * Returns an empty object if the file doesn't exist or is malformed. - */ -export function readCliConfig(): CliConfig { - try { - const data = readFileSync(CONFIG_FILE, 'utf-8'); - const parsed: Record = JSON.parse(data) as Record; - const config: CliConfig = {}; - if (typeof parsed.uvDefaultIndex === 'string') config.uvDefaultIndex = parsed.uvDefaultIndex; - if (typeof parsed.uvIndex === 'string') config.uvIndex = parsed.uvIndex; - if (parsed.disableTransactionSearch === true) config.disableTransactionSearch = true; - if (typeof parsed.transactionSearchIndexPercentage === 'number') { - const pct = parsed.transactionSearchIndexPercentage; - if (pct >= 0 && pct <= 100) { - config.transactionSearchIndexPercentage = pct; - } - } - return config; - } catch { - return {}; - } -} diff --git a/src/cli/global-config.ts b/src/lib/schemas/io/global-config.ts similarity index 78% rename from src/cli/global-config.ts rename to src/lib/schemas/io/global-config.ts index 267ad6669..a5fa35e44 100644 --- a/src/cli/global-config.ts +++ b/src/lib/schemas/io/global-config.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'fs'; import { mkdir, readFile, writeFile } from 'fs/promises'; import { randomUUID } from 'node:crypto'; import { homedir } from 'os'; @@ -9,18 +10,19 @@ export const GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, 'config.json'); const GlobalConfigSchema = z .object({ - installationId: z.string().optional(), - uvDefaultIndex: z.string().optional(), - uvIndex: z.string().optional(), - disableTransactionSearch: z.boolean().optional(), - transactionSearchIndexPercentage: z.number().min(0).max(100).optional(), + installationId: z.string().optional().catch(undefined), + uvDefaultIndex: z.string().optional().catch(undefined), + uvIndex: z.string().optional().catch(undefined), + disableTransactionSearch: z.boolean().optional().catch(undefined), + transactionSearchIndexPercentage: z.number().int().min(0).max(100).optional().catch(undefined), telemetry: z .object({ enabled: z.boolean().optional(), endpoint: z.string().optional(), audit: z.boolean().optional(), }) - .optional(), + .optional() + .catch(undefined), }) .passthrough(); @@ -35,6 +37,15 @@ export async function readGlobalConfig(configFile = GLOBAL_CONFIG_FILE): Promise } } +export function readGlobalConfigSync(configFile = GLOBAL_CONFIG_FILE): GlobalConfig { + try { + const data = readFileSync(configFile, 'utf-8'); + return GlobalConfigSchema.parse(JSON.parse(data)); + } catch { + return {}; + } +} + export async function updateGlobalConfig( partial: GlobalConfig, configDir = GLOBAL_CONFIG_DIR, diff --git a/src/lib/schemas/io/index.ts b/src/lib/schemas/io/index.ts index e8ddddc0f..212468ffe 100644 --- a/src/lib/schemas/io/index.ts +++ b/src/lib/schemas/io/index.ts @@ -11,4 +11,3 @@ export { type PathConfig, } from './path-resolver'; export { ConfigIO, createConfigIO, getSchemaUrlForVersion } from './config-io'; -export { readCliConfig, type CliConfig } from './cli-config';