#!/usr/bin/env node /** * daiso CLI 엔트리 * * npx daiso 명령으로 원격 MCP 서버 정보를 확인하고 상태를 점검합니다. */ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { printHelp, printCommandHelp } from './cliHelp.js'; import { createDefaultDeps } from './cli/deps.js'; import type { CliDeps } from './cli/types.js'; import { DEFAULT_BASE_URL, DEFAULT_MCP_URL } from './cli/constants.js'; import { parseCliArgs } from './cli/args.js'; import { validateCommandOptions, type CliOptionCommand } from './cli/commandOptions.js'; import { handleGet, handleProducts, handleProduct, handleStores, handleInventory, handleDisplayLocation, } from './cli/commands/daiso.js'; import { handleCuStores, handleCuInventory, handleEmart24Stores, handleEmart24Products, handleEmart24Inventory, handleCompareProducts, handlePlaces, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenInventory, handleSevenElevenPopwords, handleSevenElevenCatalog, } from './cli/commands/convenience.js'; import { handleCgvTheaters, handleCgvMovies, handleCgvTimetable, handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, handleMegaboxTheaters, handleMegaboxMovies, handleMegaboxSeats, } from './cli/commands/cinema.js'; export type { CliDeps } from './cli/types.js'; export type { InteractiveCliDeps } from './cliInteractive.js'; function rejectTopLevelUnknownOptions( options: string[], command: CliOptionCommand, deps: CliDeps, ): boolean { const parsed = parseCliArgs(options); return validateCommandOptions(command, parsed.options, deps.writeErr); } export async function runCli(argv: string[], deps?: Partial): Promise { const resolvedDeps = { ...createDefaultDeps(), ...deps, } satisfies CliDeps; const nonInteractive = argv.includes('--non-interactive'); const normalizedArgv = argv.filter((arg) => arg !== '--non-interactive'); const [command, ...options] = normalizedArgv; if (!command) { if (!nonInteractive && resolvedDeps.isInteractiveTerminal()) { return await resolvedDeps.runInteractive({ fetchImpl: resolvedDeps.fetchImpl, writeOut: resolvedDeps.writeOut, writeErr: resolvedDeps.writeErr, }); } printHelp(resolvedDeps.writeOut); return 0; } if (command === 'help' || command === '--help' || command === '-h') { const maybeCommand = options[0]; if (maybeCommand) { return printCommandHelp(maybeCommand, resolvedDeps.writeOut, resolvedDeps.writeErr); } printHelp(resolvedDeps.writeOut); return 0; } if (command === 'version' || command === '--version' || command === '-v') { if (rejectTopLevelUnknownOptions(options, 'version', resolvedDeps)) { return 1; } resolvedDeps.writeOut(resolvedDeps.getVersion()); return 0; } if (command === 'url') { if (rejectTopLevelUnknownOptions(options, 'url', resolvedDeps)) { return 1; } resolvedDeps.writeOut(DEFAULT_MCP_URL); return 0; } if (command === 'health') { if (rejectTopLevelUnknownOptions(options, 'health', resolvedDeps)) { return 1; } try { const response = await resolvedDeps.fetchImpl(`${DEFAULT_BASE_URL}/health`); if (!response.ok) { resolvedDeps.writeErr(`서버 상태 확인 실패: HTTP ${response.status}`); return 1; } const payload = (await response.json()) as { status?: string }; resolvedDeps.writeOut( JSON.stringify( { status: payload.status ?? 'unknown', endpoint: DEFAULT_MCP_URL, checkedAt: resolvedDeps.nowIso(), }, null, 2, ), ); return 0; } catch (error) { const message = error instanceof Error ? error.message : String(error); resolvedDeps.writeErr(`서버 상태 확인 중 오류 발생: ${message}`); return 1; } } if (command === 'claude') { if (rejectTopLevelUnknownOptions(options, 'claude', resolvedDeps)) { return 1; } const cliArgs = ['mcp', 'add', 'daiso', DEFAULT_BASE_URL, '--transport', 'sse']; if (options.includes('--exec')) { try { return await resolvedDeps.runCommand('claude', cliArgs); } catch (error) { const message = error instanceof Error ? error.message : String(error); resolvedDeps.writeErr(`Claude CLI 실행 실패: ${message}`); return 1; } } resolvedDeps.writeOut(`claude ${cliArgs.join(' ')}`); return 0; } if (command === 'lottemart-stores' || command === 'lottemart-products') { resolvedDeps.writeErr('롯데마트 서비스 지원이 종료되었습니다.'); return 1; } if (command === 'get') return await handleGet(options, resolvedDeps); if (command === 'products') return await handleProducts(options, resolvedDeps); if (command === 'product') return await handleProduct(options, resolvedDeps); if (command === 'stores') return await handleStores(options, resolvedDeps); if (command === 'inventory') return await handleInventory(options, resolvedDeps); if (command === 'display-location') return await handleDisplayLocation(options, resolvedDeps); if (command === 'compare') return await handleCompareProducts(options, resolvedDeps); if (command === 'places') return await handlePlaces(options, resolvedDeps); if (command === 'cu-stores') return await handleCuStores(options, resolvedDeps); if (command === 'cu-inventory') return await handleCuInventory(options, resolvedDeps); if (command === 'cgv-theaters') return await handleCgvTheaters(options, resolvedDeps); if (command === 'cgv-movies') return await handleCgvMovies(options, resolvedDeps); if (command === 'cgv-timetable') return await handleCgvTimetable(options, resolvedDeps); if (command === 'lottecinema-theaters') return await handleLottecinemaTheaters(options, resolvedDeps); if (command === 'lottecinema-movies') return await handleLottecinemaMovies(options, resolvedDeps); if (command === 'lottecinema-seats') return await handleLottecinemaSeats(options, resolvedDeps); if (command === 'megabox-theaters') return await handleMegaboxTheaters(options, resolvedDeps); if (command === 'megabox-movies') return await handleMegaboxMovies(options, resolvedDeps); if (command === 'megabox-seats') return await handleMegaboxSeats(options, resolvedDeps); if (command === 'emart24-stores') return await handleEmart24Stores(options, resolvedDeps); if (command === 'emart24-products') return await handleEmart24Products(options, resolvedDeps); if (command === 'emart24-inventory') return await handleEmart24Inventory(options, resolvedDeps); if (command === 'gs25-stores') return await handleGs25Stores(options, resolvedDeps); if (command === 'gs25-products') return await handleGs25Products(options, resolvedDeps); if (command === 'gs25-inventory') return await handleGs25Inventory(options, resolvedDeps); if (command === 'seveneleven-products') return await handleSevenElevenProducts(options, resolvedDeps); if (command === 'seveneleven-stores') return await handleSevenElevenStores(options, resolvedDeps); if (command === 'seveneleven-inventory') return await handleSevenElevenInventory(options, resolvedDeps); if (command === 'seveneleven-popwords') return await handleSevenElevenPopwords(options, resolvedDeps); if (command === 'seveneleven-catalog') return await handleSevenElevenCatalog(options, resolvedDeps); resolvedDeps.writeErr(`알 수 없는 명령어: ${command}`); if (command === 'search') { const query = options[0] ?? '<검색어>'; resolvedDeps.writeErr(`혹시 찾으신 명령: daiso products ${query}`); } resolvedDeps.writeErr('도움말: daiso help'); return 1; } export function isDirectExecution( entryPath: string | undefined = process.argv[1], moduleUrl: string = import.meta.url, ): boolean { if (!entryPath) { return false; } return path.resolve(entryPath) === fileURLToPath(moduleUrl); } /* c8 ignore start */ if (isDirectExecution()) { runCli(process.argv.slice(2)).then((exitCode) => { process.exit(exitCode); }); } /* c8 ignore stop */