daiso 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +60 -4
  2. package/dist/api/actionsProxy.js +111 -21
  3. package/dist/api/configStatus.js +5 -1
  4. package/dist/api/gs25Handlers.js +45 -10
  5. package/dist/api/healthChecks.js +50 -9
  6. package/dist/api/placesHandlers.js +44 -0
  7. package/dist/api/routes/healthRoutes.js +1 -1
  8. package/dist/api/routes/placesRoutes.js +9 -0
  9. package/dist/cli/commandOptions.js +64 -6
  10. package/dist/cli/commands/convenience.js +20 -0
  11. package/dist/cli.js +3 -1
  12. package/dist/cliHelp.js +14 -6
  13. package/dist/cliRenderer.js +9 -2
  14. package/dist/core/registry.js +21 -2
  15. package/dist/index.js +17 -3
  16. package/dist/pages/openapiFullSpec.js +3 -0
  17. package/dist/pages/openapiSpecActions.js +308 -42
  18. package/dist/pages/openapiSpecPathsPlaces.js +85 -0
  19. package/dist/pages/promptReferenceText.js +3 -0
  20. package/dist/pages/promptRetailCinemaText.js +22 -1
  21. package/dist/services/gs25/api.js +3 -0
  22. package/dist/services/gs25/client.js +211 -35
  23. package/dist/services/gs25/index.js +11 -3
  24. package/dist/services/gs25/tools/checkInventory.js +39 -12
  25. package/dist/services/gs25/tools/findNearbyStores.js +19 -5
  26. package/dist/services/gs25/tools/searchProducts.js +11 -4
  27. package/dist/services/oliveyoung/client.js +90 -41
  28. package/dist/services/places/client.js +116 -0
  29. package/dist/services/places/index.js +23 -0
  30. package/dist/services/places/tools/searchNearby.js +56 -0
  31. package/dist/services/places/types.js +1 -0
  32. package/dist/utils/zyte.js +45 -18
  33. package/openapi.json +19 -0
  34. package/openapi.yaml +13 -0
  35. package/package.json +3 -2
@@ -12,16 +12,74 @@ export const COMMAND_OPTION_ALLOWLISTS = {
12
12
  stores: ['help', 'json', 'keyword', 'sido', 'gugun', 'dong', 'limit'],
13
13
  inventory: ['help', 'json', 'keyword', 'lat', 'lng', 'page', 'pageSize'],
14
14
  'display-location': ['help', 'json'],
15
+ places: ['help', 'json', 'location', 'category', 'keyword', 'limit', 'start', 'sort'],
15
16
  'cu-stores': ['help', 'json', 'keyword', 'lat', 'lng', 'limit'],
16
- 'cu-inventory': ['help', 'json', 'storeKeyword', 'lat', 'lng', 'size', 'offset', 'searchSort', 'storeLimit'],
17
+ 'cu-inventory': [
18
+ 'help',
19
+ 'json',
20
+ 'storeKeyword',
21
+ 'lat',
22
+ 'lng',
23
+ 'size',
24
+ 'offset',
25
+ 'searchSort',
26
+ 'storeLimit',
27
+ ],
17
28
  'lottecinema-theaters': ['help', 'json', 'keyword', 'lat', 'lng', 'playDate', 'limit'],
18
- 'lottecinema-movies': ['help', 'json', 'keyword', 'playDate', 'theaterId', 'movieId', 'lat', 'lng'],
19
- 'lottecinema-seats': ['help', 'json', 'keyword', 'playDate', 'theaterId', 'movieId', 'lat', 'lng', 'limit'],
20
- 'emart24-stores': ['help', 'json', 'keyword', 'area1', 'area2', 'lat', 'lng', 'service24h', 'limit'],
29
+ 'lottecinema-movies': [
30
+ 'help',
31
+ 'json',
32
+ 'keyword',
33
+ 'playDate',
34
+ 'theaterId',
35
+ 'movieId',
36
+ 'lat',
37
+ 'lng',
38
+ ],
39
+ 'lottecinema-seats': [
40
+ 'help',
41
+ 'json',
42
+ 'keyword',
43
+ 'playDate',
44
+ 'theaterId',
45
+ 'movieId',
46
+ 'lat',
47
+ 'lng',
48
+ 'limit',
49
+ ],
50
+ 'emart24-stores': [
51
+ 'help',
52
+ 'json',
53
+ 'keyword',
54
+ 'area1',
55
+ 'area2',
56
+ 'lat',
57
+ 'lng',
58
+ 'service24h',
59
+ 'limit',
60
+ ],
21
61
  'emart24-products': ['help', 'json', 'page', 'pageSize'],
22
- 'emart24-inventory': ['help', 'json', 'bizNoArr', 'storeKeyword', 'area1', 'area2', 'lat', 'lng', 'storeLimit'],
62
+ 'emart24-inventory': [
63
+ 'help',
64
+ 'json',
65
+ 'bizNoArr',
66
+ 'storeKeyword',
67
+ 'area1',
68
+ 'area2',
69
+ 'lat',
70
+ 'lng',
71
+ 'storeLimit',
72
+ ],
23
73
  'lottemart-stores': ['help', 'json', 'keyword', 'area', 'brandVariant', 'lat', 'lng', 'limit'],
24
- 'lottemart-products': ['help', 'json', 'storeCode', 'storeName', 'area', 'brandVariant', 'pageLimit'],
74
+ 'lottemart-products': [
75
+ 'help',
76
+ 'json',
77
+ 'storeCode',
78
+ 'storeName',
79
+ 'area',
80
+ 'brandVariant',
81
+ 'pageLimit',
82
+ ],
25
83
  'gs25-stores': ['help', 'json', 'keyword', 'lat', 'lng', 'serviceCode', 'limit'],
26
84
  'gs25-products': ['help', 'json', 'serviceCode', 'limit'],
27
85
  'gs25-inventory': ['help', 'json', 'storeKeyword', 'lat', 'lng', 'serviceCode', 'storeLimit'],
@@ -6,6 +6,26 @@ import { printCommandHelp } from '../../cliHelp.js';
6
6
  import { parseCliArgs, toUrl, applyOptionsToQuery, toQueryOptions } from '../args.js';
7
7
  import { validateCommandOptions } from '../commandOptions.js';
8
8
  import { requestAndPrintResponse } from '../http.js';
9
+ export async function handlePlaces(options, deps) {
10
+ const parsed = parseCliArgs(options);
11
+ if (parsed.options.help === 'true') {
12
+ return printCommandHelp('places', deps.writeOut, deps.writeErr);
13
+ }
14
+ const location = parsed.positionals[0];
15
+ if (location) {
16
+ parsed.options = { location, ...parsed.options };
17
+ }
18
+ if (validateCommandOptions('places', parsed.options, deps.writeErr)) {
19
+ return 1;
20
+ }
21
+ if (!parsed.options.location && !parsed.options.keyword) {
22
+ deps.writeErr('places 명령은 위치나 검색어가 필요합니다. 예: daiso places 강남역 --category cafe');
23
+ return 1;
24
+ }
25
+ const targetUrl = toUrl('/api/places/search');
26
+ applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
27
+ return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'places', parsed.options.json === 'true');
28
+ }
9
29
  export async function handleCuStores(options, deps) {
10
30
  const parsed = parseCliArgs(options);
11
31
  if (parsed.options.help === 'true') {
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import { DEFAULT_BASE_URL, DEFAULT_MCP_URL } from './cli/constants.js';
12
12
  import { parseCliArgs } from './cli/args.js';
13
13
  import { validateCommandOptions } from './cli/commandOptions.js';
14
14
  import { handleGet, handleProducts, handleProduct, handleStores, handleInventory, handleDisplayLocation, } from './cli/commands/daiso.js';
15
- import { handleCuStores, handleCuInventory, handleEmart24Stores, handleEmart24Products, handleEmart24Inventory, handleLotteMartStores, handleLotteMartProducts, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenPopwords, handleSevenElevenCatalog, handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, } from './cli/commands/convenience.js';
15
+ import { handleCuStores, handleCuInventory, handleEmart24Stores, handleEmart24Products, handleEmart24Inventory, handleLotteMartStores, handleLotteMartProducts, handlePlaces, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenPopwords, handleSevenElevenCatalog, handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, } from './cli/commands/convenience.js';
16
16
  function rejectTopLevelUnknownOptions(options, command, deps) {
17
17
  const parsed = parseCliArgs(options);
18
18
  return validateCommandOptions(command, parsed.options, deps.writeErr);
@@ -112,6 +112,8 @@ export async function runCli(argv, deps) {
112
112
  return await handleInventory(options, resolvedDeps);
113
113
  if (command === 'display-location')
114
114
  return await handleDisplayLocation(options, resolvedDeps);
115
+ if (command === 'places')
116
+ return await handlePlaces(options, resolvedDeps);
115
117
  if (command === 'cu-stores')
116
118
  return await handleCuStores(options, resolvedDeps);
117
119
  if (command === 'cu-inventory')
package/dist/cliHelp.js CHANGED
@@ -13,6 +13,7 @@ export const COMMAND_LIST = [
13
13
  'stores',
14
14
  'inventory',
15
15
  'display-location',
16
+ 'places',
16
17
  'cu-stores',
17
18
  'cu-inventory',
18
19
  'lottecinema-theaters',
@@ -43,6 +44,7 @@ const COMMAND_SUMMARY = {
43
44
  stores: '다이소 매장 검색',
44
45
  inventory: '다이소 재고 조회',
45
46
  'display-location': '다이소 진열 위치 조회',
47
+ places: '음식점/카페 등 주변 장소 검색',
46
48
  'cu-stores': 'CU 매장 검색',
47
49
  'cu-inventory': 'CU 재고 조회',
48
50
  'lottecinema-theaters': '롯데시네마 주변 지점 조회',
@@ -68,11 +70,7 @@ const COMMAND_DETAIL = {
68
70
  '사용법: daiso help [command]',
69
71
  '예시: daiso help products',
70
72
  ],
71
- version: [
72
- '명령: version',
73
- '설명: 설치된 daiso CLI 버전을 출력합니다.',
74
- '사용법: daiso version',
75
- ],
73
+ version: ['명령: version', '설명: 설치된 daiso CLI 버전을 출력합니다.', '사용법: daiso version'],
76
74
  url: [
77
75
  '명령: url',
78
76
  '설명: MCP 엔드포인트 URL을 출력합니다.',
@@ -141,6 +139,15 @@ const COMMAND_DETAIL = {
141
139
  '옵션: --json',
142
140
  '예시: daiso display-location 1034604 04515',
143
141
  ],
142
+ places: [
143
+ '명령: places',
144
+ '설명: 네이버 지역 검색으로 음식점, 카페, 디저트 가게 등 주변 장소를 검색합니다.',
145
+ '사용법: daiso places <location> [--category cafe|restaurant|food|dessert|all] [--keyword 검색어] [--limit N] [--json]',
146
+ '필수: <location> 또는 --keyword',
147
+ '옵션: --category, --keyword, --limit, --start, --sort, --json',
148
+ '예시: daiso places 강남역 --category cafe --limit 5',
149
+ '예시: daiso places 성수동 --keyword 브런치 --limit 5',
150
+ ],
144
151
  'cu-stores': [
145
152
  '명령: cu-stores',
146
153
  '설명: CU 매장 검색 API를 호출합니다.',
@@ -304,6 +311,7 @@ export function printHelp(writeOut) {
304
311
  writeOut(' npx daiso stores 강남역');
305
312
  writeOut(' npx daiso inventory 1034604 --keyword 강남역');
306
313
  writeOut(' npx daiso display-location 1034604 04515');
314
+ writeOut(' npx daiso places 강남역 --category cafe --limit 5');
307
315
  writeOut(' npx daiso cu-stores 강남');
308
316
  writeOut(' npx daiso cu-inventory 과자 --storeKeyword 강남');
309
317
  writeOut(' npx daiso lottecinema-theaters --keyword "안산 중앙역" --limit 5');
@@ -335,7 +343,7 @@ export function printHelp(writeOut) {
335
343
  export function printCommandHelp(command, writeOut, writeErr) {
336
344
  if (!Object.hasOwn(COMMAND_DETAIL, command)) {
337
345
  writeErr(`도움말을 찾을 수 없는 명령어: ${command}`);
338
- writeErr('사용 가능한 명령어: help, version, url, health, claude, get, products, product, stores, inventory, display-location, cu-stores, cu-inventory, lottecinema-theaters, lottecinema-movies, lottecinema-seats, lottemart-stores, lottemart-products, emart24-stores, emart24-products, emart24-inventory, gs25-stores, gs25-products, gs25-inventory, seveneleven-products, seveneleven-stores, seveneleven-popwords, seveneleven-catalog');
346
+ writeErr('사용 가능한 명령어: help, version, url, health, claude, get, products, product, stores, inventory, display-location, places, cu-stores, cu-inventory, lottecinema-theaters, lottecinema-movies, lottecinema-seats, lottemart-stores, lottemart-products, emart24-stores, emart24-products, emart24-inventory, gs25-stores, gs25-products, gs25-inventory, seveneleven-products, seveneleven-stores, seveneleven-popwords, seveneleven-catalog');
339
347
  return 1;
340
348
  }
341
349
  const detail = COMMAND_DETAIL[command];
@@ -69,8 +69,14 @@ function formatCollection(title, items) {
69
69
  entry.storeCode ??
70
70
  entry.bizNo ??
71
71
  entry.theaterId ??
72
- entry.movieId;
73
- const price = entry.price ?? entry.prc ?? entry.PD_PRC ?? entry.viewPrice ?? entry.priceToPay ?? entry.searchItemSellPrice;
72
+ entry.movieId ??
73
+ entry.link;
74
+ const price = entry.price ??
75
+ entry.prc ??
76
+ entry.PD_PRC ??
77
+ entry.viewPrice ??
78
+ entry.priceToPay ??
79
+ entry.searchItemSellPrice;
74
80
  let detail = `- ${name}`;
75
81
  if (id !== undefined) {
76
82
  detail += ` [${toText(id)}]`;
@@ -204,6 +210,7 @@ export function renderApiEnvelope(command, url, payload) {
204
210
  lines.push(...formatCollection('제품 목록', data.products));
205
211
  lines.push(...formatCollection('매장 목록', data.stores));
206
212
  lines.push(...formatCollection('극장 목록', data.theaters));
213
+ lines.push(...formatCollection('장소 목록', data.places));
207
214
  lines.push(...formatCollection('영화 목록', data.movies));
208
215
  lines.push(...formatCollection('시간표', data.timetable));
209
216
  lines.push(...formatCollection('좌석 목록', data.seats));
@@ -7,7 +7,9 @@ import * as z from 'zod';
7
7
  import { getErrorMessage, toStandardErrorDiagnostics } from './errors.js';
8
8
  const DEFAULT_OUTPUT_SCHEMA = z.object({}).loose().describe('도구 실행 결과(JSON 객체)');
9
9
  function createFallbackOutputSchema(toolName) {
10
- if (toolName.includes('search_products') || toolName.includes('searchProducts') || toolName.endsWith('_products')) {
10
+ if (toolName.includes('search_products') ||
11
+ toolName.includes('searchProducts') ||
12
+ toolName.endsWith('_products')) {
11
13
  return {
12
14
  keyword: z.string().optional(),
13
15
  query: z.string().optional(),
@@ -19,7 +21,9 @@ function createFallbackOutputSchema(toolName) {
19
21
  note: z.string().optional(),
20
22
  };
21
23
  }
22
- if (toolName.includes('find_stores') || toolName.includes('nearby_stores') || toolName.includes('search_stores')) {
24
+ if (toolName.includes('find_stores') ||
25
+ toolName.includes('nearby_stores') ||
26
+ toolName.includes('search_stores')) {
23
27
  return {
24
28
  keyword: z.string().optional(),
25
29
  searchParams: z.unknown().optional(),
@@ -30,6 +34,18 @@ function createFallbackOutputSchema(toolName) {
30
34
  stores: z.array(z.unknown()).optional(),
31
35
  };
32
36
  }
37
+ if (toolName.includes('places')) {
38
+ return {
39
+ provider: z.string().optional(),
40
+ searchMode: z.string().optional(),
41
+ query: z.string().optional(),
42
+ category: z.string().optional(),
43
+ location: z.string().optional(),
44
+ totalCount: z.number().optional(),
45
+ count: z.number().optional(),
46
+ places: z.array(z.unknown()).optional(),
47
+ };
48
+ }
33
49
  if (toolName.includes('inventory')) {
34
50
  return {
35
51
  productId: z.string().optional(),
@@ -178,6 +194,9 @@ function withStandardCollections(payload) {
178
194
  if (Array.isArray(payload.stores)) {
179
195
  standard.stores = normalizeStores(payload.stores);
180
196
  }
197
+ if (Array.isArray(payload.places)) {
198
+ standard.places = normalizeStores(payload.places);
199
+ }
181
200
  if (Array.isArray(payload.theaters)) {
182
201
  standard.theaters = normalizeStores(payload.theaters);
183
202
  }
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import { createCuService } from './services/cu/index.js';
19
19
  import { createEmart24Service } from './services/emart24/index.js';
20
20
  import { createGs25Service } from './services/gs25/index.js';
21
21
  import { createSevenElevenService } from './services/seveneleven/index.js';
22
+ import { createPlacesService } from './services/places/index.js';
22
23
  import { createPromptResponse } from './pages/prompt.js';
23
24
  import { createFullOpenApiJsonResponse, createFullOpenApiYamlResponse, createOpenApiJsonResponse, createOpenApiYamlResponse, } from './pages/openapi.js';
24
25
  import { createPrivacyResponse } from './pages/privacy.js';
@@ -33,6 +34,7 @@ import { registerCuRoutes } from './api/routes/cuRoutes.js';
33
34
  import { registerEmart24Routes } from './api/routes/emart24Routes.js';
34
35
  import { registerGs25Routes } from './api/routes/gs25Routes.js';
35
36
  import { registerSevenElevenRoutes } from './api/routes/sevenelevenRoutes.js';
37
+ import { registerPlacesRoutes } from './api/routes/placesRoutes.js';
36
38
  import { registerHealthRoutes } from './api/routes/healthRoutes.js';
37
39
  import { buildConfigStatus } from './api/configStatus.js';
38
40
  // 서버 메타데이터
@@ -48,8 +50,15 @@ const createRegistry = (bindings) => {
48
50
  const registry = new ServiceRegistry();
49
51
  registry.registerAll([
50
52
  createDaisoService,
51
- createGs25Service,
53
+ () => createGs25Service({
54
+ googleMapsApiKey: bindings?.GOOGLE_MAPS_API_KEY,
55
+ zyteApiKey: bindings?.ZYTE_API_KEY,
56
+ }),
52
57
  createSevenElevenService,
58
+ () => createPlacesService({
59
+ naverClientId: bindings?.NAVER_CLIENT_ID,
60
+ naverClientSecret: bindings?.NAVER_CLIENT_SECRET,
61
+ }),
53
62
  createCuService,
54
63
  createEmart24Service,
55
64
  () => createLotteMartService({
@@ -87,9 +96,13 @@ const createMcpServer = (bindings) => {
87
96
  */
88
97
  const isInitializeRequest = (body) => {
89
98
  if (Array.isArray(body)) {
90
- return body.some((item) => typeof item === 'object' && item !== null && item.method === 'initialize');
99
+ return body.some((item) => typeof item === 'object' &&
100
+ item !== null &&
101
+ item.method === 'initialize');
91
102
  }
92
- return typeof body === 'object' && body !== null && body.method === 'initialize';
103
+ return (typeof body === 'object' &&
104
+ body !== null &&
105
+ body.method === 'initialize');
93
106
  };
94
107
  /**
95
108
  * 새 세션 transport/server를 생성합니다.
@@ -230,6 +243,7 @@ app.get('/privacy', (c) => {
230
243
  registerDaisoRoutes(app);
231
244
  registerGs25Routes(app);
232
245
  registerSevenElevenRoutes(app);
246
+ registerPlacesRoutes(app);
233
247
  registerCuRoutes(app);
234
248
  registerEmart24Routes(app);
235
249
  registerLotteMartRoutes(app);
@@ -10,6 +10,7 @@ import { OPENAPI_PATHS_CGV } from './openapiSpecPathsCgv.js';
10
10
  import { OPENAPI_PATHS_LOTTECINEMA } from './openapiSpecPathsLottecinema.js';
11
11
  import { OPENAPI_PATHS_GS25 } from './openapiSpecPathsGs25.js';
12
12
  import { OPENAPI_PATHS_SEVENELEVEN } from './openapiSpecPathsSeveneleven.js';
13
+ import { OPENAPI_PATHS_PLACES } from './openapiSpecPathsPlaces.js';
13
14
  import { OPENAPI_COMPONENTS } from './openapiSpecComponents.js';
14
15
  export function generateFullOpenApiSpec(baseUrl) {
15
16
  return {
@@ -29,6 +30,7 @@ export function generateFullOpenApiSpec(baseUrl) {
29
30
  - 🎬 **메가박스 지점/영화 조회**: 주변 지점, 상영 목록, 잔여 좌석 조회
30
31
  - 🎞️ **롯데시네마 지점/영화 조회**: 주변 지점, 상영 목록, 잔여 좌석 조회
31
32
  - 🎥 **CGV 지점/영화 조회**: 극장 목록, 영화 목록, 시간표 조회
33
+ - 🍽️ **주변 음식점/카페 검색**: 네이버 지역 검색 기반 장소 조회
32
34
 
33
35
  ## 사용 팁
34
36
  1. 한글 검색어는 URL 인코딩이 자동 처리됩니다
@@ -49,6 +51,7 @@ export function generateFullOpenApiSpec(baseUrl) {
49
51
  ...OPENAPI_PATHS_LOTTEMART,
50
52
  ...OPENAPI_PATHS_GS25,
51
53
  ...OPENAPI_PATHS_SEVENELEVEN,
54
+ ...OPENAPI_PATHS_PLACES,
52
55
  ...OPENAPI_PATHS_MEGABOX,
53
56
  ...OPENAPI_PATHS_LOTTECINEMA,
54
57
  ...OPENAPI_PATHS_CGV,